Libraryless. Click here for Pure Java version (30854L/195K).
1 | abstract concept G22TradingStrategy extends ConceptWithChangeListeners is Comparable<G22TradingStrategy>, Juiceable {
|
2 | S fieldsToReset aka fieldsToReset_G22TradingStrategy() {
|
3 | ret [[ |
4 | globalID active |
5 | q |
6 | archived primed usingLiveData |
7 | backDataFed startTime deactivated closedItself |
8 | log timedLog currentTime currentPrice feedNote |
9 | direction |
10 | digitizer maxDebt direction oldPrice startingPrice |
11 | strategyCrest |
12 | maxInvestment maxDrawdown |
13 | realizedProfit realizedCoinProfit |
14 | realizedWins realizedCoinWins |
15 | realizedLosses realizedCoinLosses |
16 | stepCount stepSince |
17 | openPositions closedPositions positionsThatFailedToOpen |
18 | drift driftSystem |
19 | ]]; |
20 | } |
21 | |
22 | settable bool verbose; |
23 | |
24 | gettable S globalID = aGlobalID(); |
25 | |
26 | // user-given name, overriding the technical name |
27 | settable S customName; |
28 | |
29 | // user-given comment |
30 | settable S comment; |
31 | |
32 | gettable transient Q q = startQ(); |
33 | |
34 | // link to market (optional) |
35 | transient settable IFuturesMarket market; |
36 | |
37 | settable bool mergePositionsForMarket; |
38 | |
39 | settableWithVar S cryptoCoin; |
40 | settableWithVar S marginCoin = "USDT"; |
41 | |
42 | // Which exchange are we using ("Bitget", "ByBit" etc.)
|
43 | settableWithVar S exchangeName; |
44 | |
45 | new Ref<G22TradingAccount> tradingAccount; |
46 | |
47 | // Optional: Exchange we get our price data from, if different from above |
48 | settableWithVar S priceDataFromExchange; |
49 | |
50 | // An identifier of the trading account (as of yet unused) |
51 | settableWithVar S accountName; |
52 | |
53 | // primed = started but waiting for first price |
54 | settableWithVar bool primed; |
55 | |
56 | settableWithVar bool usingLiveData; |
57 | settableWithVar bool doingRealTrades; |
58 | settableWithVar bool demoCoin; |
59 | settableWithVar bool active; |
60 | |
61 | // set to date when strategy ended itself |
62 | settableWithVar long closedItself; |
63 | |
64 | // set to date when strategy was deactivated (by itself or by the user) |
65 | settableWithVar long deactivated; |
66 | |
67 | // moved to archive (out of main list) |
68 | settableWithVar bool archived; |
69 | |
70 | // area where this strategy is held, e.g. "Candidates" |
71 | settableWithVar S area; |
72 | |
73 | settableWithVar double adversity = 0.2; |
74 | |
75 | // should migrate to the timedLog completely |
76 | settableWithVar new LS log; |
77 | |
78 | // O is always a string for now |
79 | settableWithVar new L<WithTimestamp<O>> timedLog; |
80 | |
81 | settableWithVar double epsilon = 1e-6; |
82 | |
83 | // increment of crypto we can bet on |
84 | settableWithVar double cryptoStep = 0.0001; |
85 | |
86 | // minimum crypto we need to bet on |
87 | settableWithVar double minCrypto = 0.0001; |
88 | |
89 | // maximum crypto we can bet on |
90 | settableWithVar double maxCrypto = infinity(); |
91 | |
92 | settableWithVar double maxInvestment; |
93 | |
94 | // highest value of coinProfit |
95 | settableWithVar double strategyCrest; |
96 | |
97 | // Maximum drawdown seen on account (realized+unrealized) |
98 | settableWithVar double maxDrawdown; |
99 | |
100 | settableWithVar double cellSize = 1; |
101 | |
102 | // position options |
103 | settableWithVar double leverage = 1; |
104 | settableWithVar double marginPerPosition = 1; // in USDT |
105 | |
106 | // optional (for compounding): how much of the account's equity to risk per position |
107 | settableWithVar double riskPerTrade; // in percent |
108 | |
109 | // Equity risked in last trade |
110 | settableWithVar double riskedEquity; |
111 | |
112 | settableWithVar bool showClosedPositionsBackwards = true; |
113 | |
114 | // End strategy when this margin coin profit is reached. |
115 | settableWithVar double takeCoinProfit = infinity(); |
116 | settableWithVar bool takeCoinProfitEnabled; |
117 | |
118 | // How many hours of recent back data this algorithm likes |
119 | // to be fed with prior to running live. |
120 | settableWithVar double backDataHoursWanted; |
121 | |
122 | // Did we feed the back data? |
123 | settableWithVar bool backDataFed; |
124 | |
125 | // Juicer that is used to stop the whole strategy depending |
126 | // on its profit (optional) |
127 | settableWithVar AbstractJuicer strategyJuicer; |
128 | |
129 | swappable long currentTime() { ret now(); }
|
130 | |
131 | settable transient bool logToPrint = true; |
132 | |
133 | // Note from price feeder |
134 | settableWithVar S feedNote; |
135 | |
136 | // optimization to save fees |
137 | settableWithVar bool useDriftSystem; |
138 | |
139 | // The drift system we are connected to |
140 | settableWithVar G22DriftSystem driftSystem; |
141 | |
142 | // Set to the actual ticker used by the system |
143 | settableWithVar transient TickerSequence actualTicker; |
144 | |
145 | // How many minutes to wait after closing a position |
146 | // before a new one can be opened (0 for no cooldown) |
147 | settableWithVar double cooldownMinutes; |
148 | |
149 | // An image showing this strategy's current state |
150 | settable transient BufferedImage image; |
151 | settable transient byte[] imageForWebServer; |
152 | |
153 | // Initial assumed equity for backtest. |
154 | // If not zero, marginPerPosition will be scaled up and down |
155 | // relative to compoundingBaseEquity+realizedCoinProfit() |
156 | settableWithVar double compoundingBaseEquity; |
157 | |
158 | // Use the exchange's TP & SL features |
159 | settableWithVar bool tpSlOnPlatform; |
160 | |
161 | // Use a limit order for TP instead of market order |
162 | settableWithVar bool useLimitTP; |
163 | |
164 | // Strategies are listed according to this key field (alphanum-IC) |
165 | settableWithVar S listOrder; |
166 | |
167 | // add fields here |
168 | |
169 | <A> A log(A o) {
|
170 | if (o != null) {
|
171 | S s = str(o); |
172 | long time = currentTime(); |
173 | log.add(printIf(logToPrint, "[" + formatLocalDateWithSeconds(time) + "] " + s)); |
174 | timedLog.add(withTimestamp(time, s)); |
175 | change(); |
176 | } |
177 | ret o; |
178 | } |
179 | |
180 | void logVars(O... o) {
|
181 | log(renderVars(o)); |
182 | } |
183 | |
184 | LS activationStatus() {
|
185 | ret llNempties( |
186 | active ? "Active" : "Inactive", |
187 | exchangeName, |
188 | empty(cryptoCoin) ? null |
189 | : "coin: " + cryptoCoin + " over " + marginCoin |
190 | + stringIf(demoCoin, " (demo coin)"), |
191 | stringIf(market != null, "connected to market"), |
192 | stringIf(usingLiveData, "using live data"), |
193 | stringIf(doingRealTrades, "real trades") |
194 | ); |
195 | } |
196 | |
197 | double unrealizedProfit() {
|
198 | double sum = 0; |
199 | for (p : openPositions()) |
200 | sum += p.profit(); |
201 | ret sum; |
202 | } |
203 | |
204 | double openMargins() {
|
205 | double sum = 0; |
206 | for (p : openPositions()) |
207 | sum += p.margin(); |
208 | ret sum; |
209 | } |
210 | |
211 | double unrealizedCoinProfit() {
|
212 | ret unrealizedCoinProfitAtPrice(currentPrice); |
213 | } |
214 | |
215 | double unrealizedCoinProfitAtPrice(double price) {
|
216 | double sum = 0; |
217 | for (p : openPositions) |
218 | sum += p.coinProfitAtPrice(price); |
219 | ret sum; |
220 | } |
221 | |
222 | L<Position> positionsInDirection(int direction) {
|
223 | ret filter(openPositions, p -> sign(p.direction) == sign(direction)); |
224 | } |
225 | |
226 | L<Position> longPositions() { ret positionsInDirection(1); }
|
227 | L<Position> shortPositions() { ret positionsInDirection(-1); }
|
228 | |
229 | L<Position> shortPositionsAtOrBelowPrice(double openingPrice) {
|
230 | ret filter(openPositions, p -> p.isShort() && p.openingPrice <= openingPrice); |
231 | } |
232 | |
233 | L<Position> longPositionsAtOrAbovePrice(double openingPrice) {
|
234 | ret filter(openPositions, p -> p.isLong() && p.openingPrice >= openingPrice); |
235 | } |
236 | |
237 | L<Position> negativePositions() {
|
238 | ret filter(openPositions, p -> p.profit() < 0); |
239 | } |
240 | |
241 | double debt() {
|
242 | ret max(0, -unrealizedCoinProfit()); |
243 | } |
244 | |
245 | // TODO: remove |
246 | double profit() {
|
247 | ret realizedProfit+unrealizedProfit(); |
248 | } |
249 | |
250 | double coinProfit() {
|
251 | ret realizedCoinProfit+unrealizedCoinProfit(); |
252 | } |
253 | |
254 | double coinProfitAtPrice(double price) {
|
255 | ret realizedCoinProfit+unrealizedCoinProfitAtPrice(price); |
256 | } |
257 | |
258 | S formatProfit(double x) {
|
259 | ret plusMinusFix(formatDouble1(x)); |
260 | } |
261 | |
262 | settableWithVar PriceDigitizer2 digitizer; |
263 | |
264 | // TODO: This doesn't belong here |
265 | settableWithVar int direction; |
266 | |
267 | // maximum debt seen |
268 | settableWithVar double maxDebt; |
269 | //settableWithVar double minCoinProfit; |
270 | //settableWithVar double maxBoundCoin; |
271 | |
272 | class Position {
|
273 | gettable S positionID = aGlobalID(); |
274 | |
275 | settable double marginToUse; |
276 | settable double openingPrice; |
277 | settable double direction; |
278 | settable double digitizedOpeningPrice; |
279 | gettable double closingPrice = Double.NaN; |
280 | long openingStep, closingStep; |
281 | gettable long openingTime; |
282 | gettable long closingTime; |
283 | double leverage; |
284 | settable double cryptoAmount; |
285 | settable O openReason; |
286 | settable O openError; |
287 | settable O closeReason; |
288 | settable O closeError; |
289 | gettable double margin; |
290 | settable bool openedOnMarket; |
291 | settable S openOrderID; // open order ID returned from platform |
292 | settable S tpOrderID; // tp limit order ID returned from platform |
293 | settable bool closedOnMarket; |
294 | settable bool dontCloseOnMarket; |
295 | settable S comment; |
296 | settable L<AbstractJuicer> juicers; |
297 | |
298 | settable Double tpPrice; |
299 | settable Double slPrice; |
300 | |
301 | // highest and lowest unrealized P&L seen (after leverage) |
302 | settable double crest = negativeInfinity(); |
303 | settable double antiCrest = infinity(); |
304 | |
305 | G22TradingStrategy strategy() { ret G22TradingStrategy.this; }
|
306 | |
307 | {
|
308 | if (!dynamicObjectIsLoading()) {
|
309 | marginToUse = marginToUseForNewPosition(); |
310 | openingStep = stepCount; |
311 | leverage = G22TradingStrategy.this.leverage; |
312 | } |
313 | } |
314 | |
315 | bool isLong() { ret direction > 0; }
|
316 | bool isShort() { ret direction < 0; }
|
317 | |
318 | bool closed() { ret !isNaN(closingPrice); }
|
319 | S type() { ret trading_directionToPositionType(direction); }
|
320 | |
321 | long closingOrCurrentTime() { ret closed() ? closingTime() : currentTime(); }
|
322 | |
323 | long duration() { ret closingOrCurrentTime()-openingTime(); }
|
324 | |
325 | double profitAtPrice(double price) {
|
326 | ret profitAtPriceBeforeLeverage(price)*leverage; |
327 | } |
328 | |
329 | double profitAtPriceBeforeLeverage(double price) {
|
330 | ret ((price-openingPrice)/openingPrice*direction*100-adversity); |
331 | } |
332 | |
333 | double workingPrice() {
|
334 | ret closed() ? closingPrice : currentPrice(); |
335 | } |
336 | |
337 | double profit() {
|
338 | ret profitAtPrice(workingPrice()); |
339 | } |
340 | |
341 | double profitBeforeLeverage() {
|
342 | ret profitAtPriceBeforeLeverage(workingPrice()); |
343 | } |
344 | |
345 | settable Double imaginaryProfit; |
346 | |
347 | double coinProfit() {
|
348 | try object imaginaryProfit; |
349 | ret coinProfitAtPrice(workingPrice()); |
350 | } |
351 | |
352 | bool isOpenError() { ret openError != null; }
|
353 | |
354 | double coinProfitAtPrice(double price) {
|
355 | ret isOpenError() ? 0 : profitAtPrice(price)/100*margin(); |
356 | } |
357 | |
358 | void close(O closeReason) {
|
359 | if (closed()) fail("Can't close again");
|
360 | if (closeReason != null) closeReason(closeReason); |
361 | closingPrice = currentPrice(); |
362 | closingTime = currentTime(); |
363 | closingStep = stepCount; |
364 | closeOnMarket(); |
365 | |
366 | // This only executes when the market close succeeds |
367 | postClose(); |
368 | } |
369 | |
370 | void postClose() {
|
371 | openPositions.remove(this); |
372 | if (!isOpenError()) |
373 | closedPositions.add(this); |
374 | addToRealizedStats(); |
375 | log(this); |
376 | //print(this); |
377 | //printPositions(); |
378 | } |
379 | |
380 | Position partialClose(double amount, O closeReason) {
|
381 | new Position p; |
382 | p.cryptoAmount = cryptoAmount-amount; |
383 | log("Partial close (" + amount + ") of " + this + ", creating remainder position with amount " + p.cryptoAmount + ". Reason: " + closeReason);
|
384 | cryptoAmount = amount; |
385 | ret openPosition(p, (int) direction, closeReason); |
386 | } |
387 | |
388 | void makeFake(double coinProfit) {
|
389 | closeReason("Fake");
|
390 | imaginaryProfit(coinProfit); |
391 | closedPositions.add(this); |
392 | addToRealizedStats(); |
393 | log(this); |
394 | } |
395 | |
396 | void addToRealizedStats {
|
397 | double cp = coinProfit(); |
398 | realizedProfit += profit(); |
399 | realizedCoinProfit += cp; |
400 | if (cp > 0) {
|
401 | realizedWins++; |
402 | realizedCoinWins += cp; |
403 | } else {
|
404 | realizedLosses++; |
405 | realizedCoinLosses += cp; |
406 | } |
407 | change(); |
408 | } |
409 | |
410 | S winnerOrLoser() {
|
411 | var profit = coinProfit(); |
412 | if (profit == 0) ret "NEUTRAL"; |
413 | if (profit > 0) ret "WIN"; |
414 | ret "LOSS"; |
415 | } |
416 | |
417 | // Position.toString |
418 | toString {
|
419 | ret commaCombine( |
420 | spaceCombine( |
421 | !closed() ? null : |
422 | winnerOrLoser() |
423 | + appendBracketed(strOrNull(closeReason)) |
424 | + appendBracketed(comment) |
425 | + " " + formatLocalDateWithSeconds(closingTime()) |
426 | + " " + formatProfit(profit()) + "% (" + marginCoin + " " + formatMarginProfit(coinProfit()) + ")"
|
427 | + " (min " + formatProfit(antiCrest()) |
428 | + ", max " + formatProfit(crest()) + ")" |
429 | + ", " + formatDouble1(leverage) + "X " + upper(type()) |
430 | + " held " + formatHoursMinutesColonSeconds(duration()), |
431 | |
432 | ), |
433 | openingTime() == 0 ? null : "opened " + formatLocalDateWithSeconds(openingTime()) |
434 | + (openReason == null ? "" : " " + roundBracket(str(openReason))), |
435 | "before leverage: " + formatProfit(profitBeforeLeverage()) + "%", |
436 | "margin: " + marginCoin + " " + formatMarginPrice(margin()), |
437 | "crypto: " + formatPrice(cryptoAmount), |
438 | "opening price: " + formatPriceX(openingPrice) |
439 | + (isNaN(digitizedOpeningPrice()) ? "" : " (digitized: " + formatPrice(digitizedOpeningPrice()) |
440 | + ")") + (openingStep == 0 ? "" : " @ step " + openingStep), |
441 | !closed() ? null : "closing price: " + formatPriceX(closingPrice) |
442 | + (closingStep == 0 ? "" : " @ step " + closingStep), |
443 | ); |
444 | } |
445 | |
446 | bool shouldCloseOnMarket() {
|
447 | ret !dontCloseOnMarket && !closedOnMarket; |
448 | } |
449 | |
450 | void closeOnMarket {
|
451 | if (!shouldCloseOnMarket()) ret; |
452 | if (isOpenError()) ret with log("Not closing because open error: " + this);
|
453 | try {
|
454 | if (market != null) {
|
455 | if (tpOrderID != null) try {
|
456 | log("Cancelling TP order: " + this);
|
457 | market.cancelOrder(tpOrderID); |
458 | } catch e {
|
459 | log(getStackTrace(e)); |
460 | } |
461 | |
462 | log("Closing on market: " + this);
|
463 | market.closePosition(new IFuturesMarket.CloseOrder() |
464 | .holdSide(HoldSide.fromInt(direction)) |
465 | .cryptoAmount(cryptoAmount) |
466 | .leverage(leverage)); |
467 | closedOnMarket(true); |
468 | change(); |
469 | } |
470 | } on fail e {
|
471 | closeError(toPersistableThrowable(e)); |
472 | } |
473 | } |
474 | |
475 | void open {
|
476 | margin = cryptoAmount*openingPrice/leverage; |
477 | log("Opening: " + this);
|
478 | openingTime = currentTime(); |
479 | openPositions.add(this); |
480 | change(); |
481 | } |
482 | |
483 | void openOnMarket {
|
484 | try {
|
485 | if (market != null) {
|
486 | log("Opening on market: " + this);
|
487 | var order = new IFuturesMarket.OpenOrder() |
488 | .clientOrderID(positionID()) |
489 | .holdSide(HoldSide.fromInt(direction)) |
490 | .cryptoAmount(cryptoAmount) |
491 | .leverage(leverage) |
492 | .isCross(true); |
493 | |
494 | if (tpSlOnPlatform) {
|
495 | if (tpPrice != null && !useLimitTP) |
496 | order.takeProfitPrice(tpPrice); |
497 | if (slPrice != null) |
498 | order.stopLossPrice(slPrice); |
499 | } |
500 | |
501 | market.openPosition(order); |
502 | openOrderID(order.orderID()); |
503 | openedOnMarket(true); |
504 | |
505 | if (tpSlOnPlatform && tpPrice != null && useLimitTP) {
|
506 | log("Creating limit TP on market: " + this);
|
507 | var closeOrder = new IFuturesMarket.CloseOrder() |
508 | .clientOrderID(positionID() + "_Close") |
509 | .holdSide(HoldSide.fromInt(direction)) |
510 | .cryptoAmount(cryptoAmount) |
511 | .limitPrice(tpPrice); |
512 | market.closePosition(closeOrder); |
513 | tpOrderID(closeOrder.orderID()); |
514 | } |
515 | |
516 | change(); |
517 | } |
518 | } catch e {
|
519 | openError(toPersistableThrowable(e)); |
520 | log("Open error: " + getStackTrace(e));
|
521 | positionsThatFailedToOpen.add(this); |
522 | close("Open error");
|
523 | } |
524 | } |
525 | |
526 | void updateStats {
|
527 | double profit = profit(); |
528 | crest(max(crest, profit)); |
529 | antiCrest(min(antiCrest, profit)); |
530 | } |
531 | |
532 | void addJuicer(AbstractJuicer juicer) {
|
533 | juicers(listCreateAndAdd(juicers, juicer)); |
534 | } |
535 | |
536 | void removeJuicer(AbstractJuicer juicer) {
|
537 | juicers(removeDyn(juicers, juicer)); |
538 | } |
539 | |
540 | // Call this when position was closed manually on the platform |
541 | void notification_closedOutsideOfStrategy() {
|
542 | closedOnMarket(true); |
543 | close("Closed outside of strategy");
|
544 | } |
545 | |
546 | } // end of Position |
547 | |
548 | gettable double currentPrice = 0; |
549 | |
550 | gettable double oldPrice = Double.NaN; |
551 | gettable double startingPrice = Double.NaN; |
552 | settable long startTime; |
553 | settableWithVar double realizedProfit; |
554 | settableWithVar double realizedCoinProfit; |
555 | settableWithVar int realizedWins; |
556 | settableWithVar double realizedCoinWins; |
557 | settableWithVar int realizedLosses; |
558 | settableWithVar double realizedCoinLosses; |
559 | settableWithVar long stepCount; |
560 | settableWithVar long stepSince; |
561 | new LinkedHashSet<Position> openPositions; |
562 | gettable new L<Position> closedPositions; |
563 | gettable new L<Position> positionsThatFailedToOpen; |
564 | |
565 | void closeOnMarketMerged(Cl<? extends Position> positions) {
|
566 | if (market == null) ret; |
567 | var list = filter(positions, -> .shouldCloseOnMarket()); |
568 | closeOnMarketMerged_oneDirection(filter(list, -> .isShort())); |
569 | closeOnMarketMerged_oneDirection(filter(list, -> .isLong())); |
570 | } |
571 | |
572 | // all positions must have the same direction |
573 | void closeOnMarketMerged_oneDirection(L<? extends Position> positions) {
|
574 | if (empty(positions)) ret; |
575 | |
576 | double direction = first(positions).direction; |
577 | double cryptoAmount = doubleSum(positions, -> .cryptoAmount); |
578 | log("Closing on market: " + positions);
|
579 | |
580 | try {
|
581 | market.closePosition(new IFuturesMarket.CloseOrder() |
582 | .holdSide(HoldSide.fromInt(direction)) |
583 | .cryptoAmount(cryptoAmount); |
584 | |
585 | for (p : positions) |
586 | p.closedOnMarket(true); |
587 | change(); |
588 | } catch e {
|
589 | var e2 = toPersistableThrowable(e); |
590 | for (p : positions) |
591 | p.closeError(e2); |
592 | throw e; |
593 | } |
594 | } |
595 | |
596 | bool hasPosition(double price, double direction) {
|
597 | ret findPosition(price, direction) != null; |
598 | } |
599 | |
600 | Position closePosition(double price, double direction, O closeReason) {
|
601 | var p = findPosition(price, direction); |
602 | p?.close(closeReason); |
603 | ret p; |
604 | } |
605 | |
606 | void closePositions(Cl<? extends Position> positions, O closeReason default null) {
|
607 | if (mergePositionsForMarket) |
608 | closeOnMarketMerged(positions); |
609 | forEach(positions, -> .close(closeReason)); |
610 | } |
611 | |
612 | Position findPosition(double digitizedPrice, double direction) {
|
613 | ret firstThat(openPositions(), p -> |
614 | diffRatio(p.digitizedOpeningPrice(), digitizedPrice) <= epsilon() && sign(p.direction) == sign(direction)); |
615 | } |
616 | |
617 | void printPositions {
|
618 | print(colonCombine(n2(openPositions, "open position"), |
619 | joinWithComma(openPositions))); |
620 | } |
621 | |
622 | bool started() { ret !isNaN(startingPrice); }
|
623 | void prices(double... prices) {
|
624 | fOr (price : prices) {
|
625 | if (!active()) ret; |
626 | price(price); |
627 | } |
628 | } |
629 | |
630 | swappable PriceCells makePriceCells(double basePrice) {
|
631 | ret new GeometricPriceCells(basePrice, cellSize); |
632 | } |
633 | |
634 | double digitizedPrice() { ret digitizer == null ? Double.NaN : digitizer.digitizedPrice(); }
|
635 | double lastDigitizedPrice() { ret digitizer == null ? Double.NaN : digitizer.lastDigitizedPrice(); }
|
636 | int digitizedCellNumber() { ret digitizer == null ? 0 : digitizer.cellNumber(); }
|
637 | |
638 | void handleNewPriceInQ(double price) {
|
639 | q.add(-> price(price)); |
640 | } |
641 | |
642 | void nextStep {
|
643 | ++stepCount; |
644 | stepSince(currentTime()); |
645 | } |
646 | |
647 | void afterStep {
|
648 | double coinProfit = coinProfit(); |
649 | maxDebt(max(maxDebt, debt())); |
650 | //minCoinProfit = min(minCoinProfit, coinProfit); |
651 | //maxBoundCoin = max(maxBoundCoin, boundCoin()); |
652 | maxInvestment(max(maxInvestment, investment())); |
653 | strategyCrest(max(strategyCrest, coinProfit)); |
654 | maxDrawdown(max(maxDrawdown, strategyCrest-coinProfit)); |
655 | if (takeCoinProfitEnabled() && coinProfit >= takeCoinProfit) {
|
656 | log("Taking coin profit.");
|
657 | closeMyself(); |
658 | } |
659 | |
660 | for (p : openPositions) p.updateStats(); |
661 | } |
662 | |
663 | double investment() {
|
664 | ret boundCoin()-realizedCoinProfit; |
665 | } |
666 | |
667 | double boundCoin() {
|
668 | ret max(openMargins(), max(0, -coinProfit())); |
669 | } |
670 | |
671 | double fromCellNumber(double cellNumber) { ret cells().fromCellNumber(cellNumber); }
|
672 | double toCellNumber(double price) { ret cells().toCellNumber(price); }
|
673 | |
674 | L<Position> shortPositionsAtOrBelowDigitizedPrice(double openingPrice) {
|
675 | ret filter(openPositions, p -> p.isShort() && p.digitizedOpeningPrice() <= openingPrice); |
676 | } |
677 | |
678 | L<Position> shortPositionsAtOrAboveDigitizedPrice(double openingPrice) {
|
679 | ret filter(openPositions, p -> p.isShort() && p.digitizedOpeningPrice() >= openingPrice); |
680 | } |
681 | |
682 | L<Position> longPositionsAtOrAboveDigitizedPrice(double openingPrice) {
|
683 | ret filter(openPositions, p -> p.isLong() && p.digitizedOpeningPrice() >= openingPrice); |
684 | } |
685 | |
686 | L<Position> longPositionsAtOrBelowDigitizedPrice(double openingPrice) {
|
687 | ret filter(openPositions, p -> p.isLong() && p.digitizedOpeningPrice() <= openingPrice); |
688 | } |
689 | |
690 | PriceCells cells aka priceCells() {
|
691 | ret digitizer?.cells; |
692 | } |
693 | |
694 | S formatPriceX(double price) {
|
695 | if (isNaN(price)) ret "-"; |
696 | S s = formatPrice(price); |
697 | if (cells() == null) ret s; |
698 | double num = cells().priceToCellNumber(price); |
699 | ret s + " (C" + formatDouble2(num) + ")"; |
700 | } |
701 | |
702 | abstract void price aka currentPrice(double price); |
703 | |
704 | bool inCooldown() {
|
705 | if (cooldownMinutes <= 0) false; |
706 | if (nempty(openPositions)) true; |
707 | var lastClosed = last(closedPositions); |
708 | if (lastClosed == null) false; |
709 | var minutes = toMinutes(currentTime()-lastClosed.closingTime); |
710 | ret minutes < cooldownMinutes; |
711 | } |
712 | |
713 | void assureCanOpen(Position p) {
|
714 | if (cooldownMinutes > 0) {
|
715 | if (nempty(openPositions)) |
716 | fail("Already have an open position");
|
717 | var lastClosed = last(closedPositions); |
718 | if (lastClosed != null) {
|
719 | var minutes = toMinutes(currentTime()-lastClosed.closingTime); |
720 | if (minutes < cooldownMinutes) |
721 | fail("Last position closed " + formatMinutes(fromMinutes(minutes)) + " ago, cooldown is " + cooldownMinutes);
|
722 | } |
723 | } |
724 | } |
725 | |
726 | <P extends Position> P openPosition(P p, int direction, O openReason default null) {
|
727 | try {
|
728 | assureCanOpen(p); |
729 | |
730 | p.openReason(openReason); |
731 | var price = digitizedPrice(); |
732 | var realPrice = currentPrice(); |
733 | logVars("openPosition", +realPrice, +price, +digitizer);
|
734 | if ((isNaN(price) || price == 0) && digitizer != null) {
|
735 | price = digitizer.digitizeIndividually(currentPrice()); |
736 | print("digitized individually: " + price);
|
737 | } |
738 | p.openingPrice(realPrice); |
739 | p.direction(direction); |
740 | p.digitizedOpeningPrice(price); |
741 | |
742 | // Calculate quantity (cryptoAmount) from margin |
743 | // unless cryptoAmount is already set |
744 | |
745 | if (p.cryptoAmount == 0) |
746 | p.cryptoAmount = p.marginToUse/realPrice*leverage; |
747 | |
748 | // Round cryptoAmount to the allowed increments |
749 | p.cryptoAmount = roundTo(cryptoStep, p.cryptoAmount); |
750 | |
751 | // Clamp to minimum/maximum order |
752 | p.cryptoAmount = clamp(p.cryptoAmount, minCrypto, maxCrypto); |
753 | |
754 | log(renderVars("openPosition", +marginPerPosition, +realPrice, +leverage, cryptoAmount := p.cryptoAmount, +cryptoStep));
|
755 | p.open(); |
756 | //print("Opening " + p);
|
757 | //printPositions(); |
758 | p.openOnMarket(); |
759 | ret p; |
760 | } on fail e {
|
761 | log(e); |
762 | } |
763 | } |
764 | |
765 | LS status() {
|
766 | double mulProf = multiplicativeProfit(); |
767 | ret llNonNulls( |
768 | empty(comment) ? null : "Comment: " + comment, |
769 | "Profit: " + marginCoin + " " + plusMinusFix(formatMarginPrice(coinProfit())), |
770 | "Realized profit: " + marginCoin + " " + formatMarginProfit(realizedCoinProfit) + " from " + n2(closedPositions, "closed position") |
771 | + " (" + formatMarginProfit(realizedCoinWins) + " from " + n2(realizedWins, "win")
|
772 | + ", " + formatMarginProfit(realizedCoinLosses) + " from " + n2(realizedLosses, "loss", "losses") + ")", |
773 | "Unrealized profit: " + marginCoin + " " + formatMarginProfit(unrealizedCoinProfit()) + " in " + n2(openPositions, "open position"), |
774 | isNaN(mulProf) ? null : "Multiplicative profit: " + formatProfit(mulProf) + "%", |
775 | //baseToString(), |
776 | !primed() ? null : "Primed", |
777 | !started() ? null : "Started. current price: " + formatPriceX(currentPrice) |
778 | + (isNaN(digitizedPrice()) ? "" : ", digitized: " + formatPriceX(digitizedPrice())), |
779 | (riskPerTrade == 0 ? "" : "Risk per trade: " + formatDouble1(riskPerTrade) + "%. ") |
780 | + "Position size: " + marginCoin + " " + formatPrice(marginPerPosition) + "x" + formatDouble1(leverage) + " = " + marginCoin + " " + formatPrice(positionSize()), |
781 | !usingCells() ? null : "Cell size: " + formatCellSize(cellSize), |
782 | spaceCombine("Step " + n2(stepCount), renderStepSince()),
|
783 | //"Debt: " + marginCoin + " " + formatMarginProfit(debt()) + " (max seen: " + formatMarginProfit(maxDebt) + ")", |
784 | "Investment used: " + marginCoin + " " + formatMarginPrice(maxInvestment()), |
785 | strategyJuicer == null ?: "Strategy juicer: " + strategyJuicer, |
786 | //"Drift: " + cryptoCoin + " " + plusMinusFix(formatCryptoAmount(drift())), |
787 | ); |
788 | } |
789 | |
790 | S renderStepSince() {
|
791 | if (stepSince == 0) ret ""; |
792 | ret "since " + (active() |
793 | ? formatHoursMinutesColonSeconds(currentTime()-stepSince) |
794 | : formatLocalDateWithSeconds(stepSince)); |
795 | } |
796 | |
797 | LS fullStatus() {
|
798 | ret listCombine( |
799 | tradingAccount, |
800 | status(), |
801 | "", |
802 | n2(openPositions, "open position") + ":", |
803 | reversed(openPositions), |
804 | "", |
805 | n2(closedPositions, "closed position") |
806 | + " (" + (showClosedPositionsBackwards ? "latest first" : "oldest first") + "):",
|
807 | showClosedPositionsBackwards ? reversed(closedPositions) : closedPositions, |
808 | ); |
809 | } |
810 | |
811 | void feed(PricePoint pricePoint) {
|
812 | if (!active()) ret; |
813 | setTime(pricePoint.timestamp); |
814 | price(pricePoint.price); |
815 | } |
816 | |
817 | void feed(TickerSequence ts) {
|
818 | if (!active()) ret; |
819 | if (ts == null) ret; |
820 | for (pricePoint : ts.pricePoints()) |
821 | feed(pricePoint); |
822 | } |
823 | |
824 | public int compareTo(G22TradingStrategy s) {
|
825 | ret s == null ? 1 : cmp(coinProfit(), s.coinProfit()); |
826 | } |
827 | |
828 | // Returns closed positions |
829 | L<Position> closeAllPositions(O reason default "User close") {
|
830 | var positions = openPositions(); |
831 | closePositions(positions, reason); |
832 | ret (L) positions; |
833 | } |
834 | |
835 | void closeMyself() {
|
836 | closedItself(currentTime()); |
837 | closeAllPositionsAndDeactivate(); |
838 | } |
839 | |
840 | void closeAllPositionsAndDeactivate {
|
841 | deactivate(); |
842 | closeAllPositions(); |
843 | } |
844 | |
845 | void deactivate {
|
846 | market(null); |
847 | if (!active) ret; |
848 | active(false); |
849 | deactivated(currentTime()); |
850 | log("Strategy deactivated.");
|
851 | } |
852 | |
853 | void reset aka reset_G22TradingStrategy() {
|
854 | resetFields(this, fieldsToReset()); |
855 | change(); |
856 | } |
857 | |
858 | selfType emptyClone aka emptyClone_G22TradingStrategy() {
|
859 | var clone = shallowCloneToUnlistedConcept(this); |
860 | clone.reset(); |
861 | ret clone; |
862 | } |
863 | |
864 | L<Position> allPositions() {
|
865 | ret concatLists(openPositions, closedPositions); |
866 | } |
867 | |
868 | L<Position> sortedPositions() {
|
869 | var allPositions = allPositions(); |
870 | ret sortedByCalculatedField(allPositions, -> .openingTime()); |
871 | } |
872 | |
873 | bool positionsAreNonOverlapping() {
|
874 | for (a, b : unpair overlappingPairs(sortedPositions())) |
875 | if (b.openingTime() < a.closingTime()) |
876 | false; |
877 | true; |
878 | } |
879 | |
880 | // Profit when applying all positions (somewhat theoretical because you |
881 | // might go below platform limits) |
882 | // Also only works when positions are linear |
883 | double multiplicativeProfit() {
|
884 | if (!positionsAreNonOverlapping()) ret Double.NaN; |
885 | double profit = 1; |
886 | for (p : sortedPositions()) |
887 | profit *= 1+p.profit()/100; |
888 | ret (profit-1)*100; |
889 | } |
890 | |
891 | bool haveBackData() { ret backDataHoursWanted == 0 | backDataFed; }
|
892 | |
893 | bool didRealTrades() {
|
894 | ret any(allPositions(), p -> p.openedOnMarket() || p.closedOnMarket()); |
895 | } |
896 | |
897 | S formatCellSize(double cellSize) {
|
898 | ret formatPercentage(cellSize, 3); |
899 | } |
900 | |
901 | S areaDesc() {
|
902 | if (eq(area, "Candidates")) ret "Candidate"; |
903 | ret nempty(area) ? area : archived ? "Archived" : ""; |
904 | } |
905 | |
906 | selfType setTime aka currentTime(long time) {
|
907 | int age = ifloor(ageInHours()); |
908 | long lastMod = mod(currentTime()-startTime, hoursToMS(1)); |
909 | currentTime = -> time; |
910 | if (ifloor(ageInHours()) > age) |
911 | log("Hourly profit log: " + formatMarginProfit(coinProfit()));
|
912 | this; |
913 | } |
914 | |
915 | double ageInHours() { ret startTime == 0 ? 0 : msToHours(currentTime()-startTime); }
|
916 | |
917 | // only use near start of strategy, untested otherwise |
918 | selfType changeCellSize(double newCellSize) {
|
919 | double oldCellSize = cellSize(); |
920 | if (oldCellSize == newCellSize) this; |
921 | cellSize(newCellSize); |
922 | if (digitizer != null) |
923 | digitizer.swapPriceCells(makePriceCells(priceCells().basePrice())); |
924 | log("Changed cell size from " + oldCellSize + " to " + newCellSize);
|
925 | this; |
926 | } |
927 | |
928 | bool hasClosedItself() { ret closedItself != 0; }
|
929 | |
930 | public double juiceValue() { ret coinProfit(); }
|
931 | |
932 | // drift is our cumulative delta (=sum of all signed position |
933 | // quantities) |
934 | double drift() {
|
935 | double drift = 0; |
936 | for (p : openPositions()) |
937 | drift += p.cryptoAmount()*p.direction(); |
938 | ret drift; |
939 | } |
940 | |
941 | S formatCryptoAmount(double amount) {
|
942 | ret formatDouble3(amount); |
943 | } |
944 | |
945 | Position openShort() { ret openPosition(-1); }
|
946 | Position openLong() { ret openPosition(1); }
|
947 | |
948 | Position openPosition(int direction, O openReason default null) {
|
949 | new Position p; |
950 | p.marginToUse = marginToUseForNewPosition(); |
951 | ret openPosition(p, direction, openReason); |
952 | } |
953 | |
954 | // Open or close positions until the drift (delta) is |
955 | // equal to targetDrift (or as close as the platform's |
956 | // restrictions allow). |
957 | // Returns the list of positions opened or closed |
958 | // (Function is in dev.) |
959 | L<Position> adjustDrift(double targetDrift, O reason default null) {
|
960 | new L<Position> changeList; |
961 | |
962 | // target 0? close all |
963 | if (targetDrift == 0) |
964 | ret closeAllPositions(reason); |
965 | |
966 | double drift = drift(); |
967 | |
968 | // target already reached? done |
969 | if (drift == targetDrift) ret changeList; |
970 | |
971 | int direction = sign(targetDrift); |
972 | |
973 | // Are we changing direction? Then close everything first. |
974 | if (sign(drift) != direction) |
975 | changeList.addAll(closeAllPositions(reason)); |
976 | |
977 | // Now we know targetDrift and drift have the same sign. |
978 | double diff = abs(targetDrift)-abs(drift); |
979 | |
980 | // Round to allow increments |
981 | diff = roundTo(cryptoStep, diff); |
982 | |
983 | if (diff > 0) {
|
984 | // We need to open a new position - that's easy |
985 | |
986 | new Position p; |
987 | p.cryptoAmount = diff; |
988 | changeList.add(openPosition(p, direction, reason)); |
989 | } else {
|
990 | double toClose = -diff; |
991 | |
992 | // We need to close positions - that's a bit more tricky |
993 | |
994 | // Filter by direction to be sure in case there is |
995 | // hedging (you shouldn't do hedging, really, though) |
996 | var positions = filter(openPositions(), p -> p.direction() == direction); |
997 | |
998 | // Let's look for an exact size match first. |
999 | // We rounded to cryptoStep, so using == is ok. |
1000 | var _toClose = toClose; |
1001 | var exactMatch = firstThat(positions, p -> p.cryptoAmount == _toClose); |
1002 | |
1003 | if (exactMatch != null) {
|
1004 | exactMatch.close(reason); |
1005 | changeList.add(exactMatch); |
1006 | ret changeList; |
1007 | } |
1008 | |
1009 | // No exact match. Go through positions starting with |
1010 | // the oldest one. |
1011 | |
1012 | for (p : positions) {
|
1013 | toClose = roundTo(cryptoStep, toClose); |
1014 | if (toClose == 0) break; |
1015 | if (toClose >= p.cryptoAmount) {
|
1016 | // Need to close the whole position |
1017 | toClose -= p.cryptoAmount; |
1018 | p.close(reason); |
1019 | changeList.add(p); |
1020 | } else {
|
1021 | // Need a partial close. |
1022 | |
1023 | changeList.add(p); |
1024 | var remainderPosition = p.partialClose(toClose, reason); |
1025 | changeList.add(remainderPosition); |
1026 | } |
1027 | } |
1028 | } |
1029 | |
1030 | ret changeList; |
1031 | } |
1032 | |
1033 | L<Position> winners() { ret filter(closedPositions(), p -> p.coinProfit() > 0); }
|
1034 | L<Position> losers() { ret filter(closedPositions(), p -> p.coinProfit() < 0); }
|
1035 | |
1036 | L<? extends Position> openPositions() { ret cloneList(openPositions); }
|
1037 | |
1038 | double winRate() {
|
1039 | ret l(winners()) * 100.0 / l(closedPositions); |
1040 | } |
1041 | |
1042 | bool usingCells() { true; }
|
1043 | |
1044 | double positionSize() {
|
1045 | ret marginToUseForNewPosition()*leverage; |
1046 | } |
1047 | |
1048 | double marginToUseForNewPosition() {
|
1049 | ret scaleMargin(marginPerPosition); |
1050 | } |
1051 | |
1052 | // to simulate compounding in backtest |
1053 | double scaleMargin(double margin) {
|
1054 | // Live? Then no scaling. |
1055 | if (usingLiveData) ret margin; |
1056 | |
1057 | ret margin*compoundingFactor(); |
1058 | } |
1059 | |
1060 | double compoundingFactor() {
|
1061 | // No compounding selected? |
1062 | if (compoundingBaseEquity == 0) ret 1; |
1063 | |
1064 | ret max(0, remainingEquity()/compoundingBaseEquity); |
1065 | } |
1066 | |
1067 | // only for backtesting |
1068 | double remainingEquity() {
|
1069 | ret compoundingBaseEquity+realizedCoinProfit(); |
1070 | } |
1071 | |
1072 | class RiskToMargin {
|
1073 | // in percent |
1074 | settable double riskForTrade = riskPerTrade; |
1075 | |
1076 | settable double stopLossPercent; |
1077 | settable double price = currentPrice(); |
1078 | settable double fullEquity; |
1079 | |
1080 | double riskedEquity() {
|
1081 | ret fullEquity*riskForTrade/100; |
1082 | } |
1083 | |
1084 | double qty() {
|
1085 | ret riskedEquity()/(price*stopLossPercent/100); |
1086 | } |
1087 | |
1088 | double margin aka get() {
|
1089 | ret qty()*price/leverage(); |
1090 | } |
1091 | } |
1092 | |
1093 | void fixRealizedStats {
|
1094 | realizedProfit(0); |
1095 | realizedCoinProfit(0); |
1096 | realizedWins(0); |
1097 | realizedCoinWins(0); |
1098 | realizedLosses(0); |
1099 | realizedCoinLosses(0); |
1100 | for (p : closedPositions()) p.addToRealizedStats(); |
1101 | } |
1102 | |
1103 | // p must not be open |
1104 | bool deletePosition(Position p) {
|
1105 | if (closedPositions.remove(p) || positionsThatFailedToOpen.remove(p)) {
|
1106 | fixRealizedStats(); |
1107 | true; |
1108 | } |
1109 | false; |
1110 | } |
1111 | |
1112 | void deletePositionsThatFailedToOpen() {
|
1113 | for (p : cloneList(positionsThatFailedToOpen)) |
1114 | deletePosition(p); |
1115 | } |
1116 | |
1117 | void deleteAllPositions() {
|
1118 | for (p : cloneList(closedPositions)) |
1119 | deletePosition(p); |
1120 | } |
1121 | |
1122 | bool hasRiskPerTrade() { ret riskPerTrade != 0; }
|
1123 | |
1124 | Position addFakePosition(double coinProfit) {
|
1125 | new Position p; |
1126 | p.makeFake(coinProfit); |
1127 | ret p; |
1128 | } |
1129 | |
1130 | // If there is exactly one open position: |
1131 | // Open another position in the same direction |
1132 | // with same size |
1133 | Position pyramid(O openReason default "Pyramiding") {
|
1134 | ret extendedPyramid(2, openReason); |
1135 | } |
1136 | |
1137 | // level: pyramid level to be reached |
1138 | // (must be one higher than current number of positions |
1139 | // to trigger - no double adding at once) |
1140 | Position extendedPyramid(int level, O openReason default "Pyramiding") {
|
1141 | if (l(openPositions()) != level-1) |
1142 | null; |
1143 | |
1144 | var p = first(openPositions); |
1145 | new Position p2; |
1146 | p2.cryptoAmount(p.cryptoAmount); |
1147 | ret openPosition(p2, sign(p.direction), openReason); |
1148 | } |
1149 | |
1150 | // level: pyramid level to be reached |
1151 | // can do double adding |
1152 | L<Position> extendedPyramid2(int level, O openReason default "Pyramiding") {
|
1153 | new L<Position> newPositions; |
1154 | |
1155 | while (l(openPositions()) < level && l(newPositions) < level) {
|
1156 | var p = first(openPositions); |
1157 | new Position p2; |
1158 | p2.cryptoAmount(p.cryptoAmount); |
1159 | newPositions.add(p2); |
1160 | openPosition(p2, sign(p.direction), openReason); |
1161 | } |
1162 | |
1163 | ret newPositions; |
1164 | } |
1165 | |
1166 | Position newPosition() {
|
1167 | ret new Position; |
1168 | } |
1169 | |
1170 | void postCloseFix() {
|
1171 | for (p : openPositions()) |
1172 | if (p.closed()) try {
|
1173 | p.postClose(); |
1174 | } catch e {
|
1175 | log(e); |
1176 | } |
1177 | } |
1178 | |
1179 | void dryCloseAll() {
|
1180 | for (p : openPositions()) |
1181 | p.dontCloseOnMarket(true); |
1182 | closeAllPositions(); |
1183 | } |
1184 | } |
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): elmgxqgtpvxh, iveijnkanddl, mqqgnosmbjvj, wnsclhtenguj
| ID | Author/Program | Comment | Date |
|---|---|---|---|
| 2726 | wumrlvwz | 1 | 2026-03-13 20:17:04 |
| 2724 | wumrlvwz | 1 | 2026-03-13 20:17:04 |
| 2714 | wumrlvwz | 1 | 2026-03-13 20:16:58 |
| 2713 | wumrlvwz | 1 | 2026-03-13 20:16:58 |
| 2711 | wumrlvwz | 1 | 2026-03-13 20:16:46 |
| 2710 | wumrlvwz | 1 | 2026-03-13 20:16:45 |
| 2699 | wumrlvwz | 555 | 2026-03-13 20:16:41 |
| 2698 | wumrlvwz | 555 | 2026-03-13 20:16:41 |
| 2697 | wumrlvwz | 555 | 2026-03-13 20:16:41 |
| 2696 | wumrlvwz | 555 | 2026-03-13 20:16:41 |
| 2688 | wumrlvwz | 555 | 2026-03-13 20:16:40 |
| 2681 | wumrlvwz | 555 | 2026-03-13 20:16:38 |
| 2674 | wumrlvwz | 555 | 2026-03-13 20:16:37 |
| 2671 | wumrlvwz | 555 | 2026-03-13 20:16:35 |
| 2668 | wumrlvwz | 555 | 2026-03-13 20:16:34 |
| 2665 | wumrlvwz | 555 | 2026-03-13 20:16:32 |
| 2662 | wumrlvwz | 555 | 2026-03-13 20:16:31 |
| 2659 | wumrlvwz | 555 | 2026-03-13 20:16:29 |
| 2656 | wumrlvwz | 555 | 2026-03-13 20:16:28 |
| 2653 | wumrlvwz | 555 | 2026-03-13 20:16:26 |
| 2650 | wumrlvwz | 555 | 2026-03-13 20:16:25 |
| 2639 | wumrlvwz | 555 | 2026-03-13 20:16:19 |
| 2631 | wumrlvwz | 555 | 2026-03-13 20:16:17 |
| 2629 | wumrlvwz | 1 | 2026-03-13 20:16:16 |
| 2627 | wumrlvwz | 1 | 2026-03-13 20:16:15 |
| 2626 | wumrlvwz | 1 | 2026-03-13 20:16:15 |
| 2625 | wumrlvwz | 1 | 2026-03-13 20:16:15 |
| 2624 | wumrlvwz | 1 | 2026-03-13 20:16:15 |
| 2623 | wumrlvwz | 555 | 2026-03-13 20:16:15 |
| 2615 | wumrlvwz | 1 | 2026-03-13 20:16:14 |
| 2614 | wumrlvwz | @@d6Kfi | 2026-03-13 20:16:14 |
| 2613 | wumrlvwz | 555????%2527%2522\'\" | 2026-03-13 20:16:14 |
| 2612 | wumrlvwz | 555'" | 2026-03-13 20:16:14 |
| 2611 | wumrlvwz | 555 | 2026-03-13 20:16:14 |
| 2610 | wumrlvwz | 555'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||' | 2026-03-13 20:16:14 |
| 2604 | wumrlvwz | 1 | 2026-03-13 20:16:12 |
| 2597 | wumrlvwz | 555*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15) | 2026-03-13 20:16:12 |
| 2596 | wumrlvwz | 1 | 2026-03-13 20:16:11 |
| 2593 | wumrlvwz | 555ylCAZ1VG')) OR 193=(SELECT 193 FROM PG_SLEEP(15))-- | 2026-03-13 20:16:10 |
| 2592 | wumrlvwz | 1 | 2026-03-13 20:16:09 |
| 2587 | wumrlvwz | 555FgQ1SF5G') OR 808=(SELECT 808 FROM PG_SLEEP(15))-- | 2026-03-13 20:16:07 |
| 2583 | wumrlvwz | 1 | 2026-03-13 20:16:06 |
| 2581 | wumrlvwz | 555OUVSFK5A' OR 25=(SELECT 25 FROM PG_SLEEP(15))-- | 2026-03-13 20:16:06 |
| 2579 | wumrlvwz | 1 | 2026-03-13 20:16:05 |
| 2577 | wumrlvwz | 555-1)) OR 874=(SELECT 874 FROM PG_SLEEP(15))-- | 2026-03-13 20:16:04 |
| 2573 | wumrlvwz | 1 | 2026-03-13 20:16:03 |
| 2571 | wumrlvwz | 555-1) OR 170=(SELECT 170 FROM PG_SLEEP(15))-- | 2026-03-13 20:16:03 |
| 2569 | wumrlvwz | 1 | 2026-03-13 20:16:02 |
| 2567 | wumrlvwz | 555-1 OR 582=(SELECT 582 FROM PG_SLEEP(15))-- | 2026-03-13 20:16:01 |
| 2565 | wumrlvwz | 1 | 2026-03-13 20:16:00 |
| 2563 | wumrlvwz | 555 | 2026-03-13 20:16:00 |
| 2562 | wumrlvwz | 555 | 2026-03-13 20:16:00 |
| 2561 | wumrlvwz | 555l8MIo0eg'; waitfor delay '0:0:15' -- | 2026-03-13 20:16:00 |
| 2560 | wumrlvwz | 555 | 2026-03-13 20:16:00 |
| 2559 | wumrlvwz | 555 | 2026-03-13 20:16:00 |
| 2557 | wumrlvwz | 1 | 2026-03-13 20:15:59 |
| 2555 | wumrlvwz | 555-1 waitfor delay '0:0:15' -- | 2026-03-13 20:15:58 |
| 2554 | wumrlvwz | 555 | 2026-03-13 20:15:58 |
| 2550 | wumrlvwz | 1 | 2026-03-13 20:15:57 |
| 2548 | wumrlvwz | 555-1); waitfor delay '0:0:15' -- | 2026-03-13 20:15:57 |
| 2545 | wumrlvwz | 1 | 2026-03-13 20:15:56 |
| 2543 | wumrlvwz | 555-1; waitfor delay '0:0:15' -- | 2026-03-13 20:15:55 |
| 2540 | wumrlvwz | 1 | 2026-03-13 20:15:54 |
| 2538 | wumrlvwz | (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/ | 2026-03-13 20:15:54 |
| 2537 | wumrlvwz | 555 | 2026-03-13 20:15:54 |
| 2535 | wumrlvwz | 1 | 2026-03-13 20:15:53 |
| 2534 | wumrlvwz | 5550"XOR(555*if(now()=sysdate(),sleep(15),0))XOR"Z | 2026-03-13 20:15:53 |
| 2533 | wumrlvwz | 555 | 2026-03-13 20:15:53 |
| 2531 | wumrlvwz | 1 | 2026-03-13 20:15:51 |
| 2529 | wumrlvwz | 5550'XOR(555*if(now()=sysdate(),sleep(15),0))XOR'Z | 2026-03-13 20:15:51 |
| 2528 | wumrlvwz | 555 | 2026-03-13 20:15:51 |
| 2526 | wumrlvwz | 1 | 2026-03-13 20:15:50 |
| 2525 | wumrlvwz | 1 | 2026-03-13 20:15:50 |
| 2523 | wumrlvwz | 1 | 2026-03-13 20:15:50 |
| 2522 | wumrlvwz | 1 | 2026-03-13 20:15:50 |
| 2521 | wumrlvwz | 1 | 2026-03-13 20:15:50 |
| 2520 | wumrlvwz | 1 | 2026-03-13 20:15:50 |
| 2519 | wumrlvwz | 555*if(now()=sysdate(),sleep(15),0) | 2026-03-13 20:15:50 |
| 2518 | wumrlvwz | 555 | 2026-03-13 20:15:50 |
| 2515 | wumrlvwz | 1 | 2026-03-13 20:15:48 |
| 2514 | wumrlvwz | -1" OR 2+497-497-1=0+0+0+1 -- | 2026-03-13 20:15:48 |
| 2513 | wumrlvwz | -1' OR 2+622-622-1=0+0+0+1 or '4R42mdQN'=' | 2026-03-13 20:15:48 |
| 2512 | wumrlvwz | 555 | 2026-03-13 20:15:48 |
| 2511 | wumrlvwz | -1' OR 2+21-21-1=0+0+0+1 -- | 2026-03-13 20:15:48 |
| 2510 | wumrlvwz | -1 OR 2+615-615-1=0+0+0+1 | 2026-03-13 20:15:48 |
| 2509 | wumrlvwz | -1 OR 2+451-451-1=0+0+0+1 -- | 2026-03-13 20:15:48 |
| 2508 | wumrlvwz | 555 | 2026-03-13 20:15:48 |
| 2501 | wumrlvwz | @@LPidq | 2026-03-13 20:15:47 |
| 2499 | wumrlvwz | 1????%2527%2522\'\" | 2026-03-13 20:15:47 |
| 2498 | wumrlvwz | 1'" | 2026-03-13 20:15:47 |
| 2497 | wumrlvwz | 1 | 2026-03-13 20:15:47 |
| 2496 | wumrlvwz | 1'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||' | 2026-03-13 20:15:47 |
| 2495 | wumrlvwz | 555 | 2026-03-13 20:15:47 |
| 2494 | wumrlvwz | 555 | 2026-03-13 20:15:47 |
| 2489 | wumrlvwz | 1*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15) | 2026-03-13 20:15:46 |
| 2488 | wumrlvwz | 555 | 2026-03-13 20:15:46 |
| 2485 | wumrlvwz | 1oYRUUJQL')) OR 231=(SELECT 231 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:45 |
| 2483 | wumrlvwz | 555 | 2026-03-13 20:15:45 |
| 2475 | wumrlvwz | 555 | 2026-03-13 20:15:43 |
| 2472 | wumrlvwz | 1tTsoLYob') OR 313=(SELECT 313 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:42 |
| 2469 | wumrlvwz | 1uV2Ntzm8' OR 340=(SELECT 340 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:38 |
| 2465 | wumrlvwz | 1-1)) OR 482=(SELECT 482 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:34 |
| 2458 | wumrlvwz | 1-1) OR 495=(SELECT 495 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:32 |
| 2457 | wumrlvwz | 555 | 2026-03-13 20:15:32 |
| 2454 | wumrlvwz | 1-1 OR 932=(SELECT 932 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:30 |
| 2446 | wumrlvwz | 555 | 2026-03-13 20:15:30 |
| 2445 | wumrlvwz | 1ojeO6uop'; waitfor delay '0:0:15' -- | 2026-03-13 20:15:29 |
| 2442 | wumrlvwz | 555 | 2026-03-13 20:15:28 |
| 2441 | wumrlvwz | 1-1 waitfor delay '0:0:15' -- | 2026-03-13 20:15:27 |
| 2438 | wumrlvwz | @@8C2Ub | 2026-03-13 20:15:27 |
| 2437 | wumrlvwz | 555????%2527%2522\'\" | 2026-03-13 20:15:27 |
| 2436 | wumrlvwz | 555'" | 2026-03-13 20:15:26 |
| 2435 | wumrlvwz | 555 | 2026-03-13 20:15:26 |
| 2434 | wumrlvwz | 555'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||' | 2026-03-13 20:15:26 |
| 2433 | wumrlvwz | 1-1); waitfor delay '0:0:15' -- | 2026-03-13 20:15:25 |
| 2430 | wumrlvwz | 555*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15) | 2026-03-13 20:15:25 |
| 2429 | wumrlvwz | 1-1; waitfor delay '0:0:15' -- | 2026-03-13 20:15:24 |
| 2426 | wumrlvwz | 555v7NQ4gA6')) OR 393=(SELECT 393 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:23 |
| 2425 | wumrlvwz | (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/ | 2026-03-13 20:15:22 |
| 2422 | wumrlvwz | 555iLX2Fo7F') OR 200=(SELECT 200 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:21 |
| 2421 | wumrlvwz | 10"XOR(1*if(now()=sysdate(),sleep(15),0))XOR"Z | 2026-03-13 20:15:20 |
| 2418 | wumrlvwz | 555UsRAzpE6' OR 499=(SELECT 499 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:19 |
| 2417 | wumrlvwz | 10'XOR(1*if(now()=sysdate(),sleep(15),0))XOR'Z | 2026-03-13 20:15:18 |
| 2414 | wumrlvwz | 555-1)) OR 10=(SELECT 10 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:18 |
| 2413 | wumrlvwz | 1*if(now()=sysdate(),sleep(15),0) | 2026-03-13 20:15:16 |
| 2406 | wumrlvwz | 555-1) OR 467=(SELECT 467 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:14 |
| 2404 | wumrlvwz | -1" OR 2+895-895-1=0+0+0+1 -- | 2026-03-13 20:15:12 |
| 2403 | wumrlvwz | -1' OR 2+823-823-1=0+0+0+1 or 'gvnaXIqa'=' | 2026-03-13 20:15:12 |
| 2402 | wumrlvwz | -1' OR 2+740-740-1=0+0+0+1 -- | 2026-03-13 20:15:12 |
| 2401 | wumrlvwz | -1 OR 2+503-503-1=0+0+0+1 | 2026-03-13 20:15:12 |
| 2400 | wumrlvwz | -1 OR 2+650-650-1=0+0+0+1 -- | 2026-03-13 20:15:12 |
| 2399 | wumrlvwz | 1 | 2026-03-13 20:15:12 |
| 2397 | wumrlvwz | 555-1 OR 90=(SELECT 90 FROM PG_SLEEP(15))-- | 2026-03-13 20:15:11 |
| 2395 | wumrlvwz | 1 | 2026-03-13 20:15:10 |
| 2393 | wumrlvwz | 5552rbVs7SB'; waitfor delay '0:0:15' -- | 2026-03-13 20:15:10 |
| 2386 | wumrlvwz | 555-1 waitfor delay '0:0:15' -- | 2026-03-13 20:15:08 |
| 2384 | wumrlvwz | 555-1); waitfor delay '0:0:15' -- | 2026-03-13 20:15:06 |
| 2382 | wumrlvwz | 555-1; waitfor delay '0:0:15' -- | 2026-03-13 20:15:04 |
| 2376 | wumrlvwz | (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/ | 2026-03-13 20:15:01 |
| 2374 | wumrlvwz | 5550"XOR(555*if(now()=sysdate(),sleep(15),0))XOR"Z | 2026-03-13 20:15:00 |
| 2372 | wumrlvwz | 5550'XOR(555*if(now()=sysdate(),sleep(15),0))XOR'Z | 2026-03-13 20:14:58 |
| 2370 | wumrlvwz | 555*if(now()=sysdate(),sleep(15),0) | 2026-03-13 20:14:54 |
| 2368 | wumrlvwz | -1" OR 2+606-606-1=0+0+0+1 -- | 2026-03-13 20:14:47 |
| 2367 | wumrlvwz | -1' OR 2+174-174-1=0+0+0+1 or 'EKF1eZUz'=' | 2026-03-13 20:14:47 |
| 2366 | wumrlvwz | -1' OR 2+588-588-1=0+0+0+1 -- | 2026-03-13 20:14:47 |
| 2365 | wumrlvwz | -1 OR 2+476-476-1=0+0+0+1 | 2026-03-13 20:14:47 |
| 2364 | wumrlvwz | -1 OR 2+438-438-1=0+0+0+1 -- | 2026-03-13 20:14:47 |
| 2363 | wumrlvwz | 555 | 2026-03-13 20:14:41 |
| 2361 | wumrlvwz | 555 | 2026-03-13 20:14:34 |
| 2359 | wumrlvwz | 555 | 2026-03-13 20:14:33 |
| 2358 | wumrlvwz | 555 | 2026-03-13 20:14:28 |
| 2357 | wumrlvwz | 555 | 2026-03-13 20:14:28 |
| 2356 | wumrlvwz | 555 | 2026-03-13 20:14:28 |
| 2355 | wumrlvwz | 555 | 2026-03-13 20:14:28 |
| 2353 | wumrlvwz | 555 | 2026-03-13 20:14:22 |
| 2351 | wumrlvwz | 555 | 2026-03-13 20:14:20 |
| 2349 | wumrlvwz | 555 | 2026-03-13 20:14:17 |
| 2347 | wumrlvwz | 555 | 2026-03-13 20:14:15 |
| 2345 | wumrlvwz | 555 | 2026-03-13 20:14:13 |
| 2343 | wumrlvwz | 555 | 2026-03-13 20:14:06 |
| 2337 | wumrlvwz | 555 | 2026-03-13 20:13:40 |
| 2335 | wumrlvwz | 555 | 2026-03-13 20:13:38 |
| 2333 | wumrlvwz | 555 | 2026-03-13 20:13:36 |
| 2324 | wumrlvwz | 555 | 2026-03-13 20:13:35 |
| 2323 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2322 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2321 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2320 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2319 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2318 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2317 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2316 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2314 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2313 | wumrlvwz | 555 | 2026-03-13 20:13:34 |
| 2144 | ubataecj | 555 | 2024-05-01 03:52:27 |
| 2142 | ubataecj | @@yT0NO | 2024-05-01 03:38:41 |
| 2141 | ubataecj | 555????%2527%2522\'\" | 2024-05-01 03:38:41 |
| 2140 | ubataecj | 555'" | 2024-05-01 03:38:41 |
| 2139 | ubataecj | 555'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||' | 2024-05-01 03:38:41 |
| 2135 | ubataecj | 555*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15) | 2024-05-01 03:38:37 |
| 2133 | ubataecj | 555v1xDKDb2')) OR 528=(SELECT 528 FROM PG_SLEEP(15))-- | 2024-05-01 03:38:34 |
| 2131 | ubataecj | 555t5BAWglH') OR 402=(SELECT 402 FROM PG_SLEEP(15))-- | 2024-05-01 03:38:28 |
| 2129 | ubataecj | 5557t8zcvB8' OR 234=(SELECT 234 FROM PG_SLEEP(15))-- | 2024-05-01 03:38:25 |
| 2127 | ubataecj | 555-1)) OR 88=(SELECT 88 FROM PG_SLEEP(15))-- | 2024-05-01 03:38:21 |
| 2125 | ubataecj | 555-1) OR 510=(SELECT 510 FROM PG_SLEEP(15))-- | 2024-05-01 03:38:18 |
| 2123 | ubataecj | 555-1 OR 510=(SELECT 510 FROM PG_SLEEP(15))-- | 2024-05-01 03:38:13 |
| 2121 | ubataecj | 555bz5UAASv'; waitfor delay '0:0:15' -- | 2024-05-01 03:38:09 |
| 2119 | ubataecj | 555-1 waitfor delay '0:0:15' -- | 2024-05-01 03:38:07 |
| 2117 | ubataecj | 555-1); waitfor delay '0:0:15' -- | 2024-05-01 03:38:05 |
| 2115 | ubataecj | 555-1; waitfor delay '0:0:15' -- | 2024-05-01 03:38:00 |
| 2113 | ubataecj | (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/ | 2024-05-01 03:37:57 |
| 2109 | ubataecj | 5550"XOR(555*if(now()=sysdate(),sleep(15),0))XOR"Z | 2024-05-01 03:37:53 |
| 2106 | ubataecj | 5550'XOR(555*if(now()=sysdate(),sleep(15),0))XOR'Z | 2024-05-01 03:37:51 |
| 2103 | ubataecj | 555*if(now()=sysdate(),sleep(15),0) | 2024-05-01 03:37:49 |
| 2100 | ubataecj | -1" OR 2+540-540-1=0+0+0+1 -- | 2024-05-01 03:37:46 |
| 2099 | ubataecj | -1' OR 2+480-480-1=0+0+0+1 or 'b3MLjESj'=' | 2024-05-01 03:37:46 |
| 2098 | ubataecj | -1' OR 2+65-65-1=0+0+0+1 -- | 2024-05-01 03:37:46 |
| 2097 | ubataecj | -1 OR 2+107-107-1=0+0+0+1 | 2024-05-01 03:37:46 |
| 2096 | ubataecj | -1 OR 2+606-606-1=0+0+0+1 -- | 2024-05-01 03:37:46 |
| 2095 | ubataecj | 555 | 2024-05-01 03:37:46 |
| 2087 | ubataecj | 555 | 2024-05-01 03:37:42 |
| 2084 | ubataecj | 555 | 2024-05-01 03:37:39 |
| 2083 | ubataecj | 555 | 2024-05-01 03:37:39 |
| 2082 | ubataecj | 555 | 2024-05-01 03:37:39 |
| 2081 | ubataecj | 555 | 2024-05-01 03:37:39 |
| 2075 | ubataecj | 555 | 2024-05-01 03:37:37 |
| 2072 | ubataecj | 555 | 2024-05-01 03:37:33 |
| 2069 | ubataecj | 555 | 2024-05-01 03:37:31 |
| 2066 | ubataecj | 555 | 2024-05-01 03:37:28 |
| 2063 | ubataecj | 555 | 2024-05-01 03:37:25 |
| 2060 | ubataecj | 555 | 2024-05-01 03:37:22 |
| 2058 | ubataecj | 555 | 2024-05-01 03:37:19 |
| 2055 | ubataecj | 555 | 2024-05-01 03:37:15 |
| 2052 | ubataecj | 555 | 2024-05-01 03:37:12 |
| 2049 | ubataecj | 555 | 2024-05-01 03:37:10 |
| 2048 | ubataecj | 555 | 2024-05-01 03:37:09 |
| 2047 | ubataecj | 555 | 2024-05-01 03:37:09 |
| 2046 | ubataecj | 555 | 2024-05-01 03:37:09 |
| 2045 | ubataecj | 555 | 2024-05-01 03:37:09 |
| 2044 | ubataecj | 555 | 2024-05-01 03:37:08 |
| 2036 | ubataecj | 555 | 2024-05-01 03:37:01 |
| Snippet ID: | #1036209 |
| Snippet name: | G22TradingStrategy |
| Eternal ID of this version: | #1036209/297 |
| Text MD5: | f6e548c2d1154244a8b2ff8caa89849c |
| Transpilation MD5: | bf52ca7256a54ccdadf379ea304c1d5e |
| Author: | stefan |
| Category: | javax / trading |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | Yes |
| Created/modified: | 2026-02-15 14:38:51 |
| Source code size: | 37110 bytes / 1184 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 4659 / 6263 |
| Version history: | 296 change(s) |
| Referenced in: | [show references] |