sclass MPM { new Set imaginaryPositions; new Set realPositions; settable double cryptoPrice; new DoubleBuffer cryptoPriceLog; // -1, 0 [initial value only] or 1 settable int cryptoDirection; event cryptoDirectionChanged; Position newShort() { ret new Position().direction(-1); } Position newLong() { ret new Position().direction(1); } abstract class CloseReason {} class LossClose > CloseReason {} class HappyClose > CloseReason {} class RegularClose > CloseReason {} // pullback close settable double fees = 0.12; settable double minReasonableProfit = .1; // in percent before leverage class Position { settable bool real; // -1 (short) or 1 (long) settable int direction; settable double openingTime; settable double openingPrice; simplyCached double openingPriceWithFees() { ret openingPrice*(1-fees/100*direction); } settable double relativeValue; settable double maxRelativeValue; settable double pullbackThreshold; // null when position is open, not null when position was closed settable CloseReason closeReason; Set motherList() { ret real ? realPositions : imaginaryPositions; } void close(CloseReason reason) { closeReason(reason); motherList().remove(this); } void launch { update(cryptoPrice); motherList().add(this); } void update(double cryptoPrice) { relativeValue((cryptoPrice/openingPriceWithFees()-1)*direction*100); maxRelativeValue(max(maxRelativeValue, relativeValue)); pullbackThreshold(maxRelativeValue-pullback(this)); } } swappable double pullback(Position p) { ret 0.5; } double time() { ret l(cryptoPriceLog); } void newCryptoPrice(double price) { // don't store non-changes if (price == cryptoPrice) ret; int direction = sign(cryptoPrice-price); cryptoPriceLog.add(price); cryptoPrice(price); if (direction != cryptoDirection) { cryptoDirection(direction); cryptoDirectionChanged(); } } { init(); } void init { onCryptoDirectionChanged(-> { for (p : ll(newLong(), newShort())) p.openingTime(time()).openingPrice(cryptoPrice).launch(); }); } }