Transpiled version (11080L) is out of date.
1 | set flag Reparse. |
2 | replace real with double. |
3 | replace Real with Double. |
4 | replace price with float. |
5 | |
6 | srecord noeq MPM2(price[] ticker, long[] timestamps) { |
7 | // How much percentual slippage we have for each position on |
8 | // average plus the fees for the position. |
9 | // Obviously dependent on the market (trading site) we are using. |
10 | srecord Market(real adversity) {} |
11 | |
12 | srecord Position(real openingTime, real direction) {} |
13 | |
14 | record ClosedPosition(Position p, real closingTime) { |
15 | settable Ticker ticker; |
16 | settable real profit; |
17 | |
18 | MPM2 mpm() { ret MPM2.this; } |
19 | |
20 | real openingPrice() { ret MPM2.this.ticker(p.openingTime); } |
21 | real closingPrice() { ret MPM2.this.ticker(closingTime); } |
22 | } |
23 | |
24 | srecord Juicer(real lossTolerance, real pullback) {} |
25 | |
26 | abstract sclass Eye { |
27 | abstract real adviseDirection(Ticker ticker); |
28 | } |
29 | |
30 | // only the first length items in data and timestamps are looked at |
31 | srecord noeq Ticker(price[] data, long[] timestamps, int length, long currentTime) { |
32 | real currentPrice() { ret data[length-1]; } |
33 | |
34 | Real lookback(real time) { |
35 | int index = binarySearch_insertionPoint(wrapLongArrayAsList(timestamps), lceil(currentTime-time)); |
36 | ret index >= 0 ? (real) data[index] : null; |
37 | } |
38 | } |
39 | |
40 | sclass LiveTicker extends Ticker { |
41 | void add(price value, long timestamp) { |
42 | data = addToArrayWithDoublingStrategy(data, length, value); |
43 | timestamps = addToArrayWithDoublingStrategy(timestamps, length, timestamp); |
44 | length++; |
45 | } |
46 | } |
47 | |
48 | Ticker ticker() { ret new Ticker(ticker, timestamps, l(timestamps), last(timestamps)); } |
49 | |
50 | srecord SimpleEye(real time, real minMove) extends Eye { |
51 | real adviseDirection(Ticker ticker) { |
52 | Real currentPrice = ticker.currentPrice(); |
53 | if (currentPrice == null) ret 0; |
54 | Real before = ticker.lookback(time); |
55 | if (before == null) ret 0; |
56 | real move = currentPrice/before*100-100; |
57 | if (abs(move) >= minMove) ret (real) sign(move); |
58 | ret 0; |
59 | } |
60 | } |
61 | |
62 | real ticker(real time) { ret ticker[iceil(time)]; } |
63 | |
64 | real openingPrice(Position p) { ret ticker(p.openingTime); } |
65 | |
66 | // all "profit" functions return a positive or negative percent value |
67 | real profit(Position p, real time, Market m) { |
68 | ret (ticker(time)/openingPrice(p)*100-100)*p.direction - m.adversity; |
69 | } |
70 | |
71 | Real closingTime(Position p, Juicer j, Market m) { |
72 | int time = iceil(p.openingTime); |
73 | real openingPrice = ticker(time); |
74 | real crest = -infinity(); |
75 | while (time < ticker.length-1) { |
76 | real profit = profit(p, time, m); |
77 | crest = max(crest, profit); |
78 | if (profit < (profit < 0 ? -j.lossTolerance : crest-j.pullback)) |
79 | ret (real) time; |
80 | ++time; |
81 | } |
82 | null; |
83 | } |
84 | |
85 | Real profit(Position p, Juicer j, Market m) { |
86 | Real closingTime = closingTime(p, j, m); |
87 | ret closingTime == null ? null : profit(p, closingTime, m); |
88 | } |
89 | |
90 | // How profitable is it to open a position of any direction |
91 | // at a certain point in time? |
92 | Real profitability(real time, Juicer j, Market m) { |
93 | ret maxAllowingNull( |
94 | profit(new Position(time, -1), j, m), |
95 | profit(new Position(time, 1), j, m)); |
96 | } |
97 | |
98 | // eye can be null, then we just test the juicer |
99 | record noeq Backtest(Eye eye, Juicer j, Market m) extends Convergent<Real> { |
100 | Iterator<Int> openingTimes = new WeightlessShuffledIterator<Int>(intRangeList(ticker.length)); |
101 | |
102 | new Average profitPerPosition; |
103 | // TODO new Average averageHoldTime; |
104 | new Average positionsOpenedPercentage; |
105 | |
106 | // percentage of positions closed before reaching the end of the ticker "tape" |
107 | new Average positionsClosedPercentage; |
108 | |
109 | new L<ClosedPosition> closedPositions; |
110 | |
111 | *(Juicer *j, Market *m) {} |
112 | |
113 | // internal, don't call from outside. Use Convergent/Iterator methods |
114 | void step { |
115 | if (!openingTimes.hasNext()) |
116 | ret with done = true; |
117 | |
118 | int openingTime = openingTimes.next(); |
119 | |
120 | Real profit = null; |
121 | ClosedPosition closedPosition = null; |
122 | |
123 | if (eye == null) // Test only juicer |
124 | profit = profitability(openingTime, j, m); |
125 | else { |
126 | // Test eye + juicer |
127 | real direction = eye.adviseDirection(new Ticker(ticker, timestamps, openingTime, timestamps[openingTime])); |
128 | positionsOpenedPercentage.addSample(direction != 0 ? 100 : 0); |
129 | if (direction != 0) { |
130 | Position position = new Position(openingTime, direction); |
131 | Real closingTime = closingTime(position, j, m); |
132 | if (closingTime != null) { |
133 | profit = profit(position, closingTime, m); |
134 | closedPosition = new ClosedPosition(position, closingTime).ticker(ticker()).profit(profit); |
135 | closedPositions.add(closedPosition); |
136 | } |
137 | } |
138 | } |
139 | |
140 | positionsClosedPercentage.addSample(profit != null ? 100 : 0); |
141 | if (profit != null) { |
142 | profitPerPosition.addSample(profit); |
143 | //averageHoldTime.addSample(TODO); |
144 | } else // Profit for unclosed positions = 0 |
145 | profitPerPosition.addSample(0); |
146 | value = profitPerPosition!; |
147 | } |
148 | } |
149 | } |
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): elmgxqgtpvxh, mqqgnosmbjvj, wnsclhtenguj
No comments. add comment
Snippet ID: | #1036148 |
Snippet name: | MPM2 - Trading Bot v2 |
Eternal ID of this version: | #1036148/72 |
Text MD5: | 013c41bc0ed1e77ae089892172dc2d5a |
Author: | stefan |
Category: | javax / trading bot |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-10-04 13:46:26 |
Source code size: | 5250 bytes / 149 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 282 / 737 |
Version history: | 71 change(s) |
Referenced in: | [show references] |