Libraryless. Click here for Pure Java version (30155L/190K).
1 | concept CleanMoveStrategy extends G22TradingStrategy { |
2 | // How clean the move must be |
3 | settable double minCleanness = 2; |
4 | |
5 | // How profitable the move must be |
6 | settable double minProfit = 0.5; |
7 | |
8 | // Factor for turning pullback into callback |
9 | settable double callbackFactor = 1.5; |
10 | |
11 | // Max pullback of move |
12 | settable double maxPullback = 1; |
13 | |
14 | // Price has to move at least this many percent for us to |
15 | // recalculate the moves |
16 | //settable double minChangeToReactTo = 0.1; |
17 | |
18 | // How often to start a new move analysis (seconds) |
19 | settable double calcMovesEvery = 30; |
20 | |
21 | S fieldsToReset() { |
22 | ret lines(ll(super.fieldsToReset(), [[currentMoves lastPriceReactedTo movesCalced]])); |
23 | } |
24 | |
25 | // current short & long move |
26 | settable L<LivePullbackToProfitAnalysis> currentMoves; |
27 | |
28 | //settable double lastPriceReactedTo; |
29 | settable double movesCalced; |
30 | |
31 | persistable class Position extends G22TradingStrategy.Position { |
32 | settable CallbackJuicer juicer; |
33 | } |
34 | |
35 | srecord ProfitBeforeLeverageJuiceable(Position p) is Juiceable { |
36 | public double juiceValue() { ret p.profitBeforeLeverage(); } |
37 | } |
38 | |
39 | void price(double price) { |
40 | if (currentPrice == price) ret; |
41 | currentPrice = price; |
42 | afterwards { afterStep(); change(); } |
43 | |
44 | // Close positions |
45 | |
46 | for (p : openPositions()) { |
47 | if (p.juicer != null) { |
48 | var signals = p.juicer.calculateCloseSignals(); |
49 | var strongest = highestBy(signals, s -> s.strength()); |
50 | if (strongest != null && strongest.isTrigger()) |
51 | p.close(strongest); |
52 | } |
53 | } |
54 | |
55 | // Check if we should start a position |
56 | if (empty(openPositions()) && shouldCalcMoves()) { |
57 | // Calculate 2 current moves backwards in time |
58 | var time = currentTime(); |
59 | currentMoves = map(falseTrue(), isShort -> { |
60 | new LivePullbackToProfitAnalysis a; |
61 | a.pullbackLimit(maxPullback); |
62 | a.isShort(isShort); |
63 | int idx = actualTicker.indexOfTimestamp(currentTime()); |
64 | while (idx >= 0 && !a.done()) |
65 | a.feed(actualTicker.getPricePoint(idx--)); |
66 | ret a; |
67 | }); |
68 | |
69 | bool shouldShort = false, shouldLong = false; |
70 | PullbackToProfit openReason = null; |
71 | |
72 | for (move : currentMoves) |
73 | for (ptp : move.ptpList()) |
74 | if (ptp.cleanness() >= minCleanness && ptp.profit() >= minProfit) { |
75 | openReason = ptp; |
76 | if (openReason.isShort) |
77 | shouldLong = true; |
78 | else |
79 | shouldShort = true; |
80 | } |
81 | |
82 | if (shouldShort && shouldLong) |
83 | print("Conflicting signal (long+short at once)"); |
84 | else if (shouldShort || shouldLong) { |
85 | int direction = shouldShort ? -1 : 1; |
86 | new Position p; |
87 | p.marginToUse = marginPerPosition; |
88 | var callbackRate = openReason.pullback*callbackFactor; |
89 | p.juicer = new CallbackJuicer(callbackRate); |
90 | p.juicer.juiceable(new ProfitBeforeLeverageJuiceable(p)); |
91 | openPosition(p, direction, openReason); |
92 | } |
93 | } |
94 | } |
95 | |
96 | bool shouldCalcMoves() { |
97 | /*if (!differentByEpsilonRatio(lastPriceReactedTo, currentPrice, minChangeToReactTo/100)) |
98 | false; |
99 | lastPriceReactedTo(currentPrice); |
100 | true;*/ |
101 | |
102 | var time = currentTime(); |
103 | if (time >= movesCalced+fromSeconds(calcMovesEvery)) { |
104 | movesCalced = time; |
105 | true; |
106 | } |
107 | false; |
108 | } |
109 | |
110 | L<? extends Position> openPositions() { ret (L) super.openPositions(); } |
111 | |
112 | LS status() { |
113 | ret listCombine( |
114 | map(currentMoves, move -> "Move: " + move), |
115 | super.status() |
116 | ); |
117 | } |
118 | } |
Began life as a copy of #1036259
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): mqqgnosmbjvj, wnsclhtenguj
No comments. add comment
Snippet ID: | #1036518 |
Snippet name: | CleanMoveStrategy [OK, for one coin at a time] |
Eternal ID of this version: | #1036518/24 |
Text MD5: | 94555821f69917dc9700d40612fc0f9f |
Transpilation MD5: | adc080c1457fd60eee2d546e040f7598 |
Author: | stefan |
Category: | javax / trading |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2023-01-22 17:06:35 |
Source code size: | 3681 bytes / 118 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 181 / 353 |
Version history: | 23 change(s) |
Referenced in: | [show references] |