sclass Corridor { record noeq Position(/*settable*/ double openingPrice, /*settable*/ double direction) { /*settable*/ double closingPrice = Double.NaN; bool closed() { ret !isNaN(closingPrice); } void close { if (closed()) fail("Can't close again"); openPositions.remove(this); closingPrice = currentPrice(); closedPositions.add(this); } } record noeq Threshold(/*settable*/ double price, /*settable*/ double direction, Runnable action) { void trigger { callF(action); } } settable double epsilon = 0.001; settable double ladderStep = 1; gettable double currentPrice = 0; new L loops; new TreeMultiMap thresholds; new LinkedHashSet openPositions; new L closedPositions; record noeq Loop(/*settable*/ double startingPrice, /*settable*/ double direction) { Position position; Loop successor; selfType init() { loops.add(this); addThreshold(startingPrice+ladderStep, direction, -> { position = openPosition(direction); if (successor == null) successor = new Loop(startingPrice+ladderStep, direction).init(); }); addThreshold(startingPrice-ladderStep, -direction, -> { position.close(); }); this; } } Position openPosition(double direction) { ret addAndReturn(openPositions, new Position(currentPrice(), direction)); } Threshold addThreshold(double price, double direction, Runnable action) { var t = new Threshold(price, direction, action); thresholds.put(t.price, t); if (absDiff(currentPrice, price) < epsilon) t.trigger(); ret t; } void start { double price = currentPrice(); new Loop(price-ladderStep, 1).init(); new Loop(price+ladderStep, -1).init(); } selfType currentPrice(double price) { double oldPrice = currentPrice; currentPrice = price; int direction = sign(price-oldPrice); if (direction != 0) { NavigableMap> map = thresholds.innerMap(); map = direction > 0 ? map.subMap(oldPrice, false, price, true) : map.subMap(price, true, oldPrice, false); LL thresholdsCrossed = cloneValues(map); if (direction < 0) reverseInPlace(thresholdsCrossed); for (actions : thresholdsCrossed) for (t : actions) if (t.direction == direction) t.trigger(); } this; } LS status() { ret ll( n2(openPositions, "open position"), n2(closedPositions, "closed position") ); } toString { ret commaCombine(status); } }