Transpiled version (30017L) is out of date.
1 | concept Corridor extends G22TradingStrategy { |
2 | // how many cells the corridor has |
3 | settableWithVar int capacity = 3; |
4 | |
5 | settableWithVar int maxPositionsPerDirection = 100; |
6 | |
7 | settableWithVar bool instantRestart = true; |
8 | |
9 | // initial position behavior |
10 | settableWithVar bool openTwoPositionsAtStart; |
11 | settableWithVar bool openRandomPositionAtStart = true; |
12 | settableWithVar int openFixedDirectionAtStart; // 1 or -1 or 0 |
13 | |
14 | // later position opening behavior |
15 | settableWithVar bool alwaysOpenTwoPositions; |
16 | |
17 | // Pre-run to check if coin is in corridor mode (dev.) |
18 | //settableWithVar bool usePreRun; |
19 | //settableWithVar AbstractJuicer preRunJuicer; |
20 | |
21 | settableWithVar bool endStrategyWhenCapacityExceeded = true; |
22 | |
23 | settableWithVar bool useStrategyJuicer; |
24 | !include #1036390 // MultiPullbackJuicer maker include |
25 | settableWithVar SignalWithStrength closeSignal; |
26 | |
27 | // internal |
28 | |
29 | persistable class Position extends G22TradingStrategy.Position { |
30 | } |
31 | |
32 | // direction: -1 or 1 for normal short or long |
33 | // To change size of of position, use -2, 2 etc |
34 | Position openPosition(int direction) { |
35 | if (l(positionsInDirection(direction)) >= maxPositionsPerDirection) { |
36 | log("Not opening new position in direction " + direction + ", max reached"); |
37 | null; |
38 | } |
39 | |
40 | new Position p; |
41 | p.marginToUse = marginPerPosition*abs(direction); |
42 | ret openPosition(p, direction); |
43 | } |
44 | |
45 | void start { |
46 | if (started()) fail("Already started"); |
47 | if (currentPrice() == 0) |
48 | ret with primed(true); |
49 | |
50 | // Save starting price, create price cells & digitizer |
51 | |
52 | startingPrice = currentPrice(); |
53 | startTime = currentTime(); |
54 | var priceCells = makePriceCells(startingPrice); |
55 | print("Made price cells: " + priceCells); |
56 | digitizer = new PriceDigitizer2(priceCells); |
57 | digitizer.verbose(verbose); |
58 | //digitizer.init(startingPrice); |
59 | |
60 | makeStrategyJuicer(); |
61 | |
62 | log("Starting CORRIDOR at " + startingPrice + " +/- " + cellSize); |
63 | |
64 | startAction(); |
65 | } |
66 | |
67 | swappable void startAction { |
68 | if (openTwoPositionsAtStart) { |
69 | nextStep(); |
70 | openPosition(1); |
71 | openPosition(-1); |
72 | afterStep(); |
73 | } else if (openRandomPositionAtStart) { |
74 | nextStep(); |
75 | log("Opening random initial position."); |
76 | openPosition(securelyTossCoin() ? 1 : - 1); |
77 | afterStep(); |
78 | } else if (openFixedDirectionAtStart != 0) { |
79 | nextStep(); |
80 | log("Opening fixed initial position."); |
81 | openPosition(sign(openFixedDirectionAtStart)); |
82 | afterStep(); |
83 | } |
84 | } |
85 | |
86 | void price(double price) { |
87 | if (currentPrice == price) ret; |
88 | currentPrice = price; |
89 | |
90 | if (hasClosedItself()) ret; |
91 | |
92 | if (!started()) { |
93 | if (primed) { |
94 | primed(false); |
95 | start(); |
96 | } else |
97 | ret; |
98 | } |
99 | |
100 | digitizer.digitize(price); |
101 | direction = sign(digitizedPrice()-lastDigitizedPrice()); |
102 | |
103 | if (applyStrategyJuicer()) ret; |
104 | |
105 | // No cell move? |
106 | if (direction == 0) ret with afterStep(); |
107 | |
108 | nextStep(); |
109 | //printWithPrecedingNL("New digitized price: " + digitizedPrice()); |
110 | |
111 | if (started()) |
112 | step(); |
113 | |
114 | print(this); |
115 | } |
116 | |
117 | // true if exit |
118 | bool applyStrategyJuicer() { |
119 | // Check strategy juicer |
120 | closeSignal(strongestSignal(strategyJuicer?.calculateCloseSignals())); |
121 | if (closeSignal != null && closeSignal.isTrigger()) { |
122 | log("Juicer close signal! " + closeSignal); |
123 | closeOrRestart(); |
124 | afterStep(); |
125 | true; |
126 | } |
127 | |
128 | false; |
129 | } |
130 | |
131 | swappable void step { |
132 | double p1 = lastDigitizedPrice(); |
133 | double p2 = digitizedPrice(); |
134 | direction = sign(p2-p1); |
135 | if (direction == 0) ret; |
136 | |
137 | // Find winning positions to close |
138 | |
139 | L<G22TradingStrategy.Position> toClose = new L; |
140 | for (p : direction > 0 |
141 | ? longPositionsAtOrBelowDigitizedPrice(p1) |
142 | : shortPositionsAtOrAboveDigitizedPrice(p1)) { |
143 | cast p to Position; |
144 | p.closeReason("WIN"); |
145 | log("Closing winner position at " + formatPriceX(currentPrice) + " (" + p1 + " -> " + p2 + "): " + p); |
146 | toClose.add(p); |
147 | } |
148 | |
149 | // Dismantle/close corridor if too large |
150 | double limit = fromCellNumber(toCellNumber(digitizedPrice())-capacity*direction); |
151 | var toDismantle = direction > 0 |
152 | ? shortPositionsAtOrBelowDigitizedPrice(limit) |
153 | : longPositionsAtOrAboveDigitizedPrice(limit); |
154 | if (logToPrint) printVars(dismantleLimit := formatPriceX(limit), |
155 | +direction, |
156 | +capacity, |
157 | +digitizedPrice(), |
158 | +toDismantle); |
159 | |
160 | if (nempty(toDismantle)) { |
161 | if (endStrategyWhenCapacityExceeded) { |
162 | log("Capacity exceeded!"); |
163 | closeOrRestart(); |
164 | afterStep(); |
165 | ret; |
166 | } else { |
167 | for (p : toDismantle) { |
168 | cast p to Position; |
169 | p.closeReason("DISMANTLE"); |
170 | toClose.add(p); |
171 | } |
172 | } |
173 | } |
174 | |
175 | //printVars("step", +p1, +p2, +direction, +closed, +opened); |
176 | |
177 | if (nempty(toClose)) |
178 | closePositions(toClose); |
179 | |
180 | // Apply take profits etc BEFORE possibly opening a new position |
181 | afterStep(); |
182 | |
183 | openNewPositionAfterMove(); |
184 | |
185 | // Apply take profits, stop loss etc |
186 | afterStep(); |
187 | } |
188 | |
189 | void closeOrRestart { |
190 | if (instantRestart) |
191 | restart(); |
192 | else |
193 | closeMyself(); |
194 | } |
195 | |
196 | void restart { |
197 | closeMyself(); |
198 | active(true); |
199 | closedItself(0); |
200 | startAction(); |
201 | } |
202 | |
203 | swappable void openNewPositionAfterMove { |
204 | // Open backward position here (if not there yet) |
205 | openPositionIfNotThere(-direction); |
206 | |
207 | // Open forward position if configured as such |
208 | if (alwaysOpenTwoPositions) |
209 | openPositionIfNotThere(direction); |
210 | } |
211 | |
212 | Position openPositionIfNotThere(int direction) { |
213 | if (!hasPosition(digitizedPrice(), direction)) |
214 | ret openPosition(direction); |
215 | else |
216 | null; |
217 | } |
218 | |
219 | swappable S mainDesc() { |
220 | try answer renderCustomName(); |
221 | |
222 | ret capacity + "x" + formatCellSize(cellSize) + " Corridor"; |
223 | } |
224 | |
225 | S baseToString() { |
226 | ret colonCombine( |
227 | _conceptID() == 0 ? "" : "Strategy " + _conceptID(), |
228 | spaceCombine( |
229 | squareBracketUnlessEmpty(areaDesc()), |
230 | mainDesc(), |
231 | nempty(cryptoCoin) ? "on " + cryptoCoin : "" |
232 | ), |
233 | ); |
234 | } |
235 | |
236 | LS status() { |
237 | var r = currentCorridorPriceRange(); |
238 | ret listCombine( |
239 | super.status(), |
240 | "Max capacity: " + capacity, |
241 | !started() ? null : "Current corridor: " + (r == null ? "-" : formatPrice(r.start) + " to " + formatPrice(r.end) + " (" + formatPercentage(currentCorridorSizeInPercent(), 3) + ")"), |
242 | "Profit unit: " + formatMarginPrice(profitUnitBeforeAdversity()) + "/" + marginCoin + " " + formatMarginPrice(profitUnitAfterAdversity()) + ", loss unit: " + marginCoin + " " + formatMarginPrice(lossUnit()), |
243 | closeSignal == null ?: "Close signal: " + closeSignal, |
244 | ); |
245 | } |
246 | |
247 | // Corridor.toString |
248 | toString { |
249 | ret baseToString(); |
250 | } |
251 | |
252 | S renderCustomName() { |
253 | ret empty(customName) ? null : simpleQuoteOpt(trim(customName)); |
254 | } |
255 | |
256 | double maxCorridorPercentSize() { |
257 | ret capacity*cellSize; |
258 | } |
259 | |
260 | DoubleRange currentCorridorPriceRange() { |
261 | double[] openingPrices = mapToDoubleArray(openPositions, ->.openingPrice); |
262 | ret doubleRange(doubleMin(openingPrices), doubleMax(openingPrices)); |
263 | } |
264 | |
265 | double currentCorridorSizeInPercent() { |
266 | DoubleRange r = currentCorridorPriceRange(); |
267 | ret r == null ? 0 : asPercentIncrease(r.end, r.start); |
268 | } |
269 | |
270 | Position openShort() { ret openPosition(-1); } |
271 | Position openLong() { ret openPosition(1); } |
272 | |
273 | { |
274 | cellSize(3.0); |
275 | } |
276 | |
277 | double positionSize() { ret marginPerPosition*leverage; } |
278 | double profitUnitBeforeAdversity() { ret cellSize/100*positionSize(); } |
279 | double profitUnitAfterAdversity() { ret (cellSize-adversity)/100*positionSize(); } |
280 | double lossUnit() { ret (cellSize+adversity)/100*positionSize(); } |
281 | |
282 | // Uses profit calculated in profit units |
283 | // (could alternatively use investment units, but like this the |
284 | // loss ratio should stay more consistent between coins [=leverage values]) |
285 | class ProfitUnitsJuiceable is Juiceable { |
286 | public double juiceValue() { |
287 | ret coinProfit()/profitUnitBeforeAdversity(); |
288 | } |
289 | } |
290 | |
291 | // Update strategy juicer if it exists |
292 | void updateStrategyJuicer { |
293 | if (strategyJuicer != null) { |
294 | makeStrategyJuicer(); |
295 | applyStrategyJuicer(); |
296 | } |
297 | } |
298 | |
299 | // Can also update existing juicer with new params |
300 | void makeStrategyJuicer { |
301 | if (!useStrategyJuicer) ret with strategyJuicer(null); |
302 | var j = makeJuicer(); |
303 | j.copyTransientValuesFrom(strategyJuicer); |
304 | j.juiceable(new ProfitUnitsJuiceable); |
305 | strategyJuicer(j); |
306 | } |
307 | |
308 | S fieldsToReset() { |
309 | ret lines(ll(super.fieldsToReset(), [[closeSignal strategyJuicer]])); |
310 | } |
311 | |
312 | double currentCellNumber() { |
313 | ret toCellNumber(currentPrice()); |
314 | } |
315 | |
316 | // Cell we're in |
317 | int currentDigitizedCell() { |
318 | ret iround(toCellNumber(digitizedPrice())); |
319 | } |
320 | |
321 | int priceDirection() { |
322 | ret sign(currentCellNumber()-currentDigitizedCell()); |
323 | } |
324 | |
325 | // Cell we're currently moving to |
326 | int targetCell() { |
327 | ret currentDigitizedCell()+priceDirection(); |
328 | } |
329 | |
330 | double targetPrice() { |
331 | ret fromCellNumber(targetCell()); |
332 | } |
333 | |
334 | // Coin profit when next target is reached |
335 | double targetProfit() { |
336 | ret coinProfitAtPrice(targetPrice()); |
337 | } |
338 | |
339 | // Is profit currently going up or down |
340 | int profitDirection() { |
341 | ret sign(targetProfit()-coinProfit()); |
342 | } |
343 | |
344 | // progress to next target in percent |
345 | double progressInProfitDirection() { |
346 | ret absDiff(currentCellNumber(), currentDigitizedCell())*100; |
347 | } |
348 | } |
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): elmgxqgtpvxh, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1036196 |
Snippet name: | Corridor |
Eternal ID of this version: | #1036196/345 |
Text MD5: | 8cfd09e821b755ad662489afd55962ee |
Author: | stefan |
Category: | javax / gazelle 22 |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-12-06 21:56:41 |
Source code size: | 9923 bytes / 348 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 827 / 2252 |
Version history: | 344 change(s) |
Referenced in: | [show references] |