Libraryless. Click here for Pure Java version (9969L/55K).
1 | sclass Corridor2 { |
2 | settable double smallMove = 0.2; |
3 | settable int capacity = 6; |
4 | settable bool openTwoPositionsAtStart = true; |
5 | |
6 | record noeq Position(double openingPrice, double direction) { |
7 | double closingPrice = Double.NaN; |
8 | |
9 | bool closed() { ret !isNaN(closingPrice); } |
10 | S type() { ret trading_directionToPositionType(direction); } |
11 | |
12 | double profitAtPrice(double price) { |
13 | ret (price-openingPrice)*direction; |
14 | } |
15 | |
16 | double profit() { |
17 | ret profitAtPrice(closed() ? closingPrice : currentPrice()); |
18 | } |
19 | |
20 | void close { |
21 | if (closed()) fail("Can't close again"); |
22 | openPositionsMap(direction).remove(openingPrice); |
23 | closingPrice = currentPrice(); |
24 | closedPositions.add(this); |
25 | realizedProfit += profit(); |
26 | print(this); |
27 | printPositions(); |
28 | } |
29 | |
30 | toString { |
31 | ret renderFunctionCall( |
32 | spaceCombine( |
33 | closed() ? "Closed" : null, |
34 | type() |
35 | ), |
36 | openingPrice, |
37 | "profit=" + profit()); |
38 | } |
39 | } |
40 | |
41 | gettable double currentPrice = 0; |
42 | |
43 | gettable double lastPrice = Double.NaN; |
44 | gettable double startingPrice = Double.NaN; |
45 | gettable double realizedProfit; |
46 | gettable double maxDebtEncountered; |
47 | gettable int stepCount; |
48 | |
49 | // open positions sorted by direction and price |
50 | gettable new TreeMap<Double, Position> openLongs; |
51 | gettable new TreeMap<Double, Position> openShorts; |
52 | |
53 | gettable new L<Position> closedPositions; |
54 | gettable new DoubleBuffer priceHistory; |
55 | |
56 | TreeMap<Double, Position> openPositionsMap(double direction) { |
57 | if (direction > 0) ret openLongs; |
58 | if (direction < 0) ret openShorts; |
59 | fail("direction 0"); |
60 | } |
61 | |
62 | Position openPosition(double direction) { |
63 | var p = new Position(currentPrice(), direction); |
64 | openPositionsMap(direction).put(p.openingPrice, p); |
65 | print("Opening " + p); |
66 | printPositions(); |
67 | ret p; |
68 | } |
69 | |
70 | Position openShort() { ret openPosition(-1); } |
71 | Position openLong() { ret openPosition(1); } |
72 | |
73 | L<Position> openPositions() { |
74 | ret concatLists(values(openLongs), values(openShorts)); |
75 | } |
76 | |
77 | void printPositions { |
78 | var positions = openPositions(); |
79 | print(colonCombine(n2(positions, "open position"), |
80 | joinWithComma(positions))); |
81 | } |
82 | |
83 | double unrealizedProfit() { |
84 | ret doubleSum(map(openPositions(), ->.profit())); |
85 | } |
86 | |
87 | L<Position> negativePositions() { |
88 | ret filter(openPositions(), p -> p.profit() < 0); |
89 | } |
90 | |
91 | double debt() { |
92 | ret doubleSum(map(negativePositions(), ->.profit())); |
93 | } |
94 | |
95 | double profit() { |
96 | ret realizedProfit+unrealizedProfit(); |
97 | } |
98 | |
99 | LS status() { |
100 | ret ll( |
101 | "Step " + n2(stepCount), |
102 | "Profit: " + profit(), |
103 | "Realized profit: " + realizedProfit + " from " + n2(closedPositions, "closed position"), |
104 | "Unrealized profit: " + unrealizedProfit() + " in " + n2(openPositions(), "open position"), |
105 | "Debt: " + debt(), |
106 | ); |
107 | } |
108 | |
109 | toString { ret commaCombine(status()); } |
110 | |
111 | void prices(double... prices) { |
112 | fOr (price : prices) |
113 | price(price); |
114 | } |
115 | |
116 | void currentPrice aka price(double price) { |
117 | lastPrice = currentPrice; |
118 | |
119 | if (lastPrice == price) ret; |
120 | |
121 | currentPrice = price; |
122 | |
123 | priceHistory.add(price); |
124 | ++stepCount; |
125 | printWithPrecedingNL(commaCombine( |
126 | "Step " + stepCount + ". Old price: " + lastPrice, |
127 | "New price: " + price)); |
128 | |
129 | step(); |
130 | maxDebtEncountered = max(maxDebtEncountered, debt()); |
131 | print(this); |
132 | } |
133 | |
134 | bool started() { ret !isNaN(startingPrice); } |
135 | |
136 | void start { |
137 | if (started()) fail("Already started"); |
138 | startingPrice = currentPrice(); |
139 | print("Starting CORRIDOR at " + startingPrice + " +/- " + smallMove); |
140 | if (openTwoPositionsAtStart) { |
141 | openPosition(1); |
142 | openPosition(-1); |
143 | } |
144 | } |
145 | |
146 | int currentDirection() { |
147 | ret sign(currentPrice-lastPrice); |
148 | } |
149 | |
150 | L<Position> positionsAtPriceOrWorse(double direction, double price) { |
151 | var map = openPositionsMap(direction); |
152 | ret direction < 0 |
153 | ? valuesList(map.tailMap(price, true)) |
154 | : valuesList(map.headMap(price, true)); |
155 | } |
156 | |
157 | // Algorithm-specific logic follows |
158 | |
159 | swappable void step { |
160 | // Close winning position(s) |
161 | |
162 | var positions = positionsAtPriceOrWorse(currentDirection(), |
163 | currentPrice()-currentDirection()*smallMove); |
164 | for (p : positions) p.close(); |
165 | |
166 | // If we closed anything, open a position in reverse direction |
167 | if (nempty(positions)) { |
168 | openPosition(-currentDirection()); |
169 | } |
170 | |
171 | // TODO: "travelling" |
172 | } |
173 | } |
Began life as a copy of #1036196
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): elmgxqgtpvxh, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1036204 |
Snippet name: | Corridor2 - the "analog" version that doesn't digitize prices [dev., have to figure out first] |
Eternal ID of this version: | #1036204/11 |
Text MD5: | e1402bffeed571f1edb572b3837d6914 |
Transpilation MD5: | 09659741521ca370c190286f3300a77b |
Author: | stefan |
Category: | javax / gazelle 22 |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-10-17 23:26:03 |
Source code size: | 4701 bytes / 173 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 130 / 239 |
Version history: | 10 change(s) |
Referenced in: | [show references] |