sclass Corridor2 { settable double smallMove = 0.2; settable int capacity = 6; settable bool openTwoPositionsAtStart = true; record noeq Position(double openingPrice, double direction) { double closingPrice = Double.NaN; bool closed() { ret !isNaN(closingPrice); } S type() { ret trading_directionToPositionType(direction); } double profitAtPrice(double price) { ret (price-openingPrice)*direction; } double profit() { ret profitAtPrice(closed() ? closingPrice : currentPrice()); } void close { if (closed()) fail("Can't close again"); openPositionsMap(direction).remove(openingPrice); closingPrice = currentPrice(); closedPositions.add(this); realizedProfit += profit(); print(this); printPositions(); } toString { ret renderFunctionCall( spaceCombine( closed() ? "Closed" : null, type() ), openingPrice, "profit=" + profit()); } } gettable double currentPrice = 0; gettable double lastPrice = Double.NaN; gettable double startingPrice = Double.NaN; gettable double realizedProfit; gettable double maxDebtEncountered; gettable int stepCount; // open positions sorted by direction and price gettable new TreeMap openLongs; gettable new TreeMap openShorts; gettable new L closedPositions; gettable new DoubleBuffer priceHistory; TreeMap openPositionsMap(double direction) { if (direction > 0) ret openLongs; if (direction < 0) ret openShorts; fail("direction 0"); } Position openPosition(double direction) { var p = new Position(currentPrice(), direction); openPositionsMap(direction).put(p.openingPrice, p); print("Opening " + p); printPositions(); ret p; } Position openShort() { ret openPosition(-1); } Position openLong() { ret openPosition(1); } L openPositions() { ret concatLists(values(openLongs), values(openShorts)); } void printPositions { var positions = openPositions(); print(colonCombine(n2(positions, "open position"), joinWithComma(positions))); } double unrealizedProfit() { ret doubleSum(map(openPositions(), ->.profit())); } L negativePositions() { ret filter(openPositions(), p -> p.profit() < 0); } double debt() { ret doubleSum(map(negativePositions(), ->.profit())); } double profit() { ret realizedProfit+unrealizedProfit(); } LS status() { ret ll( "Step " + n2(stepCount), "Profit: " + profit(), "Realized profit: " + realizedProfit + " from " + n2(closedPositions, "closed position"), "Unrealized profit: " + unrealizedProfit() + " in " + n2(openPositions(), "open position"), "Debt: " + debt(), ); } toString { ret commaCombine(status()); } void prices(double... prices) { fOr (price : prices) price(price); } void currentPrice aka price(double price) { lastPrice = currentPrice; if (lastPrice == price) ret; currentPrice = price; priceHistory.add(price); ++stepCount; printWithPrecedingNL(commaCombine( "Step " + stepCount + ". Old price: " + lastPrice, "New price: " + price)); step(); maxDebtEncountered = max(maxDebtEncountered, debt()); print(this); } bool started() { ret !isNaN(startingPrice); } void start { if (started()) fail("Already started"); startingPrice = currentPrice(); print("Starting CORRIDOR at " + startingPrice + " +/- " + smallMove); if (openTwoPositionsAtStart) { openPosition(1); openPosition(-1); } } int currentDirection() { ret sign(currentPrice-lastPrice); } L positionsAtPriceOrWorse(double direction, double price) { var map = openPositionsMap(direction); ret direction < 0 ? valuesList(map.tailMap(price, true)) : valuesList(map.headMap(price, true)); } // Algorithm-specific logic follows swappable void step { // Close winning position(s) var positions = positionsAtPriceOrWorse(currentDirection(), currentPrice()-currentDirection()*smallMove); for (p : positions) p.close(); // If we closed anything, open a position in reverse direction if (nempty(positions)) { openPosition(-currentDirection()); } // TODO: "travelling" } }