@quantform/core 0.3.228 → 0.3.232

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/dist/adapter/adapter-aggregate.d.ts +1 -1
  2. package/dist/adapter/adapter-aggregate.js +5 -5
  3. package/dist/adapter/adapter-aggregate.js.map +1 -1
  4. package/dist/adapter/adapter.d.ts +4 -4
  5. package/dist/adapter/adapter.event.d.ts +1 -1
  6. package/dist/adapter/adapter.event.js +1 -1
  7. package/dist/adapter/adapter.js +3 -3
  8. package/dist/adapter/adapter.js.map +1 -1
  9. package/dist/adapter/backtester/backtester-adapter.d.ts +2 -2
  10. package/dist/adapter/backtester/backtester-adapter.js +3 -5
  11. package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
  12. package/dist/adapter/backtester/backtester-cursor.d.ts +1 -1
  13. package/dist/adapter/backtester/backtester-cursor.spec.js.map +1 -1
  14. package/dist/adapter/backtester/backtester-streamer.d.ts +1 -1
  15. package/dist/{common/decimals.spec.d.ts → adapter/backtester/backtester-streamer.spec.d.ts} +0 -0
  16. package/dist/adapter/backtester/backtester-streamer.spec.js +45 -0
  17. package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -0
  18. package/dist/adapter/paper/{model/paper-model.d.ts → executor/paper-executor.d.ts} +1 -1
  19. package/dist/adapter/paper/{model/paper-model.js → executor/paper-executor.js} +4 -4
  20. package/dist/adapter/paper/executor/paper-executor.js.map +1 -0
  21. package/dist/adapter/paper/{model/paper-margin-model.d.ts → executor/paper-margin-executor.d.ts} +2 -2
  22. package/dist/adapter/paper/{model/paper-margin-model.js → executor/paper-margin-executor.js} +11 -11
  23. package/dist/adapter/paper/executor/paper-margin-executor.js.map +1 -0
  24. package/dist/adapter/paper/{model/paper-spot-model.d.ts → executor/paper-spot-executor.d.ts} +3 -3
  25. package/dist/adapter/paper/{model/paper-spot-model.js → executor/paper-spot-executor.js} +7 -7
  26. package/dist/adapter/paper/executor/paper-spot-executor.js.map +1 -0
  27. package/dist/adapter/paper/index.d.ts +2 -2
  28. package/dist/adapter/paper/index.js +2 -2
  29. package/dist/adapter/paper/index.js.map +1 -1
  30. package/dist/adapter/paper/paper-adapter.d.ts +3 -3
  31. package/dist/adapter/paper/paper-adapter.js +6 -6
  32. package/dist/adapter/paper/paper-adapter.js.map +1 -1
  33. package/dist/adapter/paper/paper-adapter.spec.js +2 -2
  34. package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
  35. package/dist/bin.d.ts +2 -35
  36. package/dist/bin.js +2 -187
  37. package/dist/bin.js.map +1 -1
  38. package/dist/domain/asset.js +1 -1
  39. package/dist/domain/balance.d.ts +1 -1
  40. package/dist/domain/balance.js +1 -3
  41. package/dist/domain/balance.js.map +1 -1
  42. package/dist/domain/candle-builder.d.ts +1 -1
  43. package/dist/domain/candle.d.ts +1 -1
  44. package/dist/domain/candle.spec.js +2 -2
  45. package/dist/domain/{commision.d.ts → commission.d.ts} +2 -2
  46. package/dist/domain/{commision.js → commission.js} +7 -7
  47. package/dist/domain/commission.js.map +1 -0
  48. package/dist/domain/component.d.ts +1 -1
  49. package/dist/domain/index.d.ts +1 -1
  50. package/dist/domain/index.js +1 -1
  51. package/dist/domain/index.js.map +1 -1
  52. package/dist/domain/instrument.d.ts +3 -3
  53. package/dist/domain/order.d.ts +1 -1
  54. package/dist/domain/orderbook.d.ts +1 -1
  55. package/dist/domain/position.d.ts +1 -1
  56. package/dist/domain/position.js +3 -3
  57. package/dist/domain/trade.d.ts +1 -1
  58. package/dist/index.d.ts +2 -2
  59. package/dist/index.js +2 -2
  60. package/dist/index.js.map +1 -1
  61. package/dist/indicator/truerange.spec.js +6 -6
  62. package/dist/ipc.d.ts +34 -0
  63. package/dist/ipc.js +193 -0
  64. package/dist/ipc.js.map +1 -0
  65. package/dist/session/index.d.ts +1 -0
  66. package/dist/session/index.js +1 -0
  67. package/dist/session/index.js.map +1 -1
  68. package/dist/session/session-descriptor.d.ts +2 -3
  69. package/dist/session/session-statement.d.ts +5 -0
  70. package/dist/session/session-statement.js +87 -0
  71. package/dist/session/session-statement.js.map +1 -0
  72. package/dist/session/session.d.ts +2 -2
  73. package/dist/session/session.js +10 -19
  74. package/dist/session/session.js.map +1 -1
  75. package/dist/session/session.spec.js +3 -3
  76. package/dist/session/session.spec.js.map +1 -1
  77. package/dist/{common → shared}/datetime.d.ts +0 -0
  78. package/dist/{common → shared}/datetime.js +0 -0
  79. package/dist/{common → shared}/datetime.js.map +1 -1
  80. package/dist/{common → shared}/decimals.d.ts +0 -0
  81. package/dist/{common → shared}/decimals.js +0 -0
  82. package/dist/{common → shared}/decimals.js.map +1 -1
  83. package/dist/{common/policy.spec.d.ts → shared/decimals.spec.d.ts} +0 -0
  84. package/dist/{common → shared}/decimals.spec.js +0 -0
  85. package/dist/{common → shared}/decimals.spec.js.map +1 -1
  86. package/dist/{common → shared}/index.d.ts +0 -0
  87. package/dist/{common → shared}/index.js +0 -0
  88. package/dist/{common → shared}/index.js.map +1 -1
  89. package/dist/{common → shared}/io.d.ts +0 -0
  90. package/dist/{common → shared}/io.js +0 -0
  91. package/dist/shared/io.js.map +1 -0
  92. package/dist/{common → shared}/logger.d.ts +0 -0
  93. package/dist/{common → shared}/logger.js +0 -0
  94. package/dist/{common → shared}/logger.js.map +1 -1
  95. package/dist/{common → shared}/policy.d.ts +0 -0
  96. package/dist/{common → shared}/policy.js +0 -0
  97. package/dist/{common → shared}/policy.js.map +1 -1
  98. package/dist/{common/topic.spec.d.ts → shared/policy.spec.d.ts} +0 -0
  99. package/dist/{common → shared}/policy.spec.js +0 -0
  100. package/dist/{common → shared}/policy.spec.js.map +1 -1
  101. package/dist/{common → shared}/topic.d.ts +0 -0
  102. package/dist/{common → shared}/topic.js +0 -0
  103. package/dist/{common → shared}/topic.js.map +1 -1
  104. package/dist/{common/worker.spec.d.ts → shared/topic.spec.d.ts} +0 -0
  105. package/dist/{common → shared}/topic.spec.js +0 -0
  106. package/dist/{common → shared}/topic.spec.js.map +1 -1
  107. package/dist/{common → shared}/worker.d.ts +0 -0
  108. package/dist/{common → shared}/worker.js +0 -0
  109. package/dist/{common → shared}/worker.js.map +1 -1
  110. package/dist/shared/worker.spec.d.ts +1 -0
  111. package/dist/{common → shared}/worker.spec.js +0 -0
  112. package/dist/{common → shared}/worker.spec.js.map +1 -1
  113. package/dist/storage/measurement.d.ts +1 -1
  114. package/dist/store/event/store-balance.event.d.ts +1 -1
  115. package/dist/store/event/store-balance.event.js +1 -1
  116. package/dist/store/event/store-balance.event.spec.js +3 -3
  117. package/dist/store/event/store-balance.event.spec.js.map +1 -1
  118. package/dist/store/event/store-candle.event.d.ts +1 -1
  119. package/dist/store/event/store-candle.event.js +1 -1
  120. package/dist/store/event/store-candle.event.spec.d.ts +1 -0
  121. package/dist/store/event/store-candle.event.spec.js +24 -0
  122. package/dist/store/event/store-candle.event.spec.js.map +1 -0
  123. package/dist/store/event/store-instrument.event.d.ts +4 -4
  124. package/dist/store/event/store-instrument.event.js +3 -3
  125. package/dist/store/event/store-instrument.event.js.map +1 -1
  126. package/dist/store/event/store-instrument.event.spec.js +3 -3
  127. package/dist/store/event/store-instrument.event.spec.js.map +1 -1
  128. package/dist/store/event/store-order.event.d.ts +1 -1
  129. package/dist/store/event/store-order.event.js +1 -1
  130. package/dist/store/event/store-order.event.spec.js +2 -2
  131. package/dist/store/event/store-orderbook.event.d.ts +1 -1
  132. package/dist/store/event/store-orderbook.event.js +1 -1
  133. package/dist/store/event/store-position.event.d.ts +1 -1
  134. package/dist/store/event/store-position.event.js +1 -1
  135. package/dist/store/event/store-trade.event.d.ts +1 -1
  136. package/dist/store/event/store-trade.event.js +1 -1
  137. package/dist/store/event/store-trade.event.spec.js +6 -6
  138. package/dist/store/event/store-trade.event.spec.js.map +1 -1
  139. package/dist/store/event/store.event.d.ts +1 -1
  140. package/dist/store/store.d.ts +1 -1
  141. package/dist/store/store.js +1 -1
  142. package/dist/store/store.state.d.ts +1 -1
  143. package/dist/store/store.state.js.map +1 -1
  144. package/dist/tests/backtester-adapter.spec.js +5 -5
  145. package/dist/tests/backtester-adapter.spec.js.map +1 -1
  146. package/dist/tsconfig.tsbuildinfo +1 -1
  147. package/jestconfig.unit.json +2 -1
  148. package/package.json +1 -1
  149. package/src/adapter/adapter-aggregate.ts +23 -4
  150. package/src/adapter/adapter.event.ts +2 -2
  151. package/src/adapter/adapter.ts +14 -4
  152. package/src/adapter/backtester/backtester-adapter.ts +4 -6
  153. package/src/adapter/backtester/backtester-cursor.spec.ts +2 -2
  154. package/src/adapter/backtester/backtester-cursor.ts +1 -1
  155. package/src/adapter/backtester/backtester-streamer.spec.ts +53 -0
  156. package/src/adapter/backtester/backtester-streamer.ts +1 -1
  157. package/src/adapter/paper/{model/paper-model.ts → executor/paper-executor.ts} +1 -1
  158. package/src/adapter/paper/{model/paper-margin-model.ts → executor/paper-margin-executor.ts} +4 -4
  159. package/src/adapter/paper/{model/paper-spot-model.ts → executor/paper-spot-executor.ts} +5 -5
  160. package/src/adapter/paper/index.ts +2 -2
  161. package/src/adapter/paper/paper-adapter.spec.ts +4 -4
  162. package/src/adapter/paper/paper-adapter.ts +8 -8
  163. package/src/bin.ts +24 -189
  164. package/src/domain/asset.ts +2 -2
  165. package/src/domain/balance.ts +3 -5
  166. package/src/domain/candle-builder.ts +1 -1
  167. package/src/domain/candle.spec.ts +1 -1
  168. package/src/domain/candle.ts +1 -1
  169. package/src/domain/{commision.ts → commission.ts} +3 -3
  170. package/src/domain/component.ts +1 -1
  171. package/src/domain/index.ts +1 -1
  172. package/src/domain/instrument.ts +3 -3
  173. package/src/domain/order.ts +1 -1
  174. package/src/domain/orderbook.ts +1 -1
  175. package/src/domain/position.ts +2 -2
  176. package/src/domain/trade.ts +1 -1
  177. package/src/index.ts +2 -7
  178. package/src/indicator/truerange.spec.ts +1 -1
  179. package/src/ipc.ts +277 -0
  180. package/src/session/index.ts +1 -0
  181. package/src/session/session-descriptor.ts +35 -6
  182. package/src/session/session-statement.ts +119 -0
  183. package/src/session/session.spec.ts +6 -4
  184. package/src/session/session.ts +62 -20
  185. package/src/{common → shared}/datetime.ts +0 -0
  186. package/src/{common → shared}/decimals.spec.ts +0 -0
  187. package/src/{common → shared}/decimals.ts +0 -0
  188. package/src/{common → shared}/index.ts +0 -0
  189. package/src/{common → shared}/io.ts +0 -0
  190. package/src/{common → shared}/logger.ts +0 -0
  191. package/src/{common → shared}/policy.spec.ts +0 -0
  192. package/src/{common → shared}/policy.ts +0 -0
  193. package/src/{common → shared}/topic.spec.ts +0 -0
  194. package/src/{common → shared}/topic.ts +0 -0
  195. package/src/{common → shared}/worker.spec.ts +0 -0
  196. package/src/{common → shared}/worker.ts +0 -0
  197. package/src/storage/measurement.ts +1 -1
  198. package/src/store/event/store-balance.event.spec.ts +3 -3
  199. package/src/store/event/store-balance.event.ts +2 -2
  200. package/src/store/event/store-candle.event.spec.ts +32 -0
  201. package/src/store/event/store-candle.event.ts +2 -2
  202. package/src/store/event/store-instrument.event.spec.ts +3 -3
  203. package/src/store/event/store-instrument.event.ts +5 -5
  204. package/src/store/event/store-order.event.spec.ts +1 -1
  205. package/src/store/event/store-order.event.ts +2 -2
  206. package/src/store/event/store-orderbook.event.ts +2 -2
  207. package/src/store/event/store-position.event.ts +2 -2
  208. package/src/store/event/store-trade.event.spec.ts +2 -2
  209. package/src/store/event/store-trade.event.ts +2 -2
  210. package/src/store/event/store.event.ts +1 -1
  211. package/src/store/store.state.ts +2 -1
  212. package/src/store/store.ts +1 -1
  213. package/src/tests/backtester-adapter.spec.ts +7 -7
  214. package/dist/adapter/paper/model/paper-margin-model.js.map +0 -1
  215. package/dist/adapter/paper/model/paper-model.js.map +0 -1
  216. package/dist/adapter/paper/model/paper-spot-model.js.map +0 -1
  217. package/dist/behaviour/behaviour.d.ts +0 -6
  218. package/dist/behaviour/behaviour.js +0 -3
  219. package/dist/behaviour/behaviour.js.map +0 -1
  220. package/dist/behaviour/combined-behaviour.d.ts +0 -14
  221. package/dist/behaviour/combined-behaviour.js +0 -26
  222. package/dist/behaviour/combined-behaviour.js.map +0 -1
  223. package/dist/behaviour/index.d.ts +0 -3
  224. package/dist/behaviour/index.js +0 -16
  225. package/dist/behaviour/index.js.map +0 -1
  226. package/dist/behaviour/statement/benchmark-statement.behaviour.d.ts +0 -15
  227. package/dist/behaviour/statement/benchmark-statement.behaviour.js +0 -41
  228. package/dist/behaviour/statement/benchmark-statement.behaviour.js.map +0 -1
  229. package/dist/behaviour/statement/equity-statement.behaviour.d.ts +0 -15
  230. package/dist/behaviour/statement/equity-statement.behaviour.js +0 -54
  231. package/dist/behaviour/statement/equity-statement.behaviour.js.map +0 -1
  232. package/dist/behaviour/statement/index.d.ts +0 -1
  233. package/dist/behaviour/statement/index.js +0 -14
  234. package/dist/behaviour/statement/index.js.map +0 -1
  235. package/dist/behaviour/statement/period-statement.behaviour.d.ts +0 -9
  236. package/dist/behaviour/statement/period-statement.behaviour.js +0 -20
  237. package/dist/behaviour/statement/period-statement.behaviour.js.map +0 -1
  238. package/dist/behaviour/statement/statement.behaviour.d.ts +0 -7
  239. package/dist/behaviour/statement/statement.behaviour.js +0 -19
  240. package/dist/behaviour/statement/statement.behaviour.js.map +0 -1
  241. package/dist/common/io.js.map +0 -1
  242. package/dist/domain/commision.js.map +0 -1
  243. package/src/behaviour/behaviour.ts +0 -7
  244. package/src/behaviour/combined-behaviour.ts +0 -23
  245. package/src/behaviour/index.ts +0 -3
  246. package/src/behaviour/statement/benchmark-statement.behaviour.ts +0 -53
  247. package/src/behaviour/statement/equity-statement.behaviour.ts +0 -71
  248. package/src/behaviour/statement/index.ts +0 -1
  249. package/src/behaviour/statement/period-statement.behaviour.ts +0 -26
  250. package/src/behaviour/statement/statement.behaviour.ts +0 -19
package/src/index.ts CHANGED
@@ -1,17 +1,12 @@
1
- export * from './behaviour';
2
- export * from './common';
3
-
1
+ export * from './shared';
4
2
  export * from './domain';
5
-
6
3
  export * from './adapter';
7
4
  export * from './adapter/backtester';
8
5
  export * from './adapter/paper';
9
-
10
6
  export * from './indicator';
11
7
  export * from './session';
12
8
  export * from './storage';
13
-
14
9
  export * from './store';
15
10
  export * from './store/event';
16
-
17
11
  export * from './bin';
12
+ export * from './ipc';
@@ -2,7 +2,7 @@ import { from } from 'rxjs';
2
2
  import { Candle } from '../domain';
3
3
  import { truerange } from './truerange';
4
4
  import { sma } from './sma';
5
- import { now } from '../common';
5
+ import { now } from '../shared';
6
6
 
7
7
  describe('truerange atr tests', () => {
8
8
  test('should return expected value', done => {
package/src/ipc.ts ADDED
@@ -0,0 +1,277 @@
1
+ import { AdapterFeedCommand } from './adapter';
2
+ import { Session, SessionDescriptor } from './session';
3
+ import { instrumentOf } from './domain';
4
+ import { Topic, event, handler } from './shared/topic';
5
+ import { Logger } from './shared';
6
+ import { backtest, idle, live, paper } from './bin';
7
+ import minimist = require('minimist');
8
+
9
+ /**
10
+ * Base command/query interface for IPC communication.
11
+ */
12
+ export interface IpcCommand {
13
+ /**
14
+ * The command name to handle.
15
+ */
16
+ type;
17
+ }
18
+
19
+ /**
20
+ * Command to start a new live session.
21
+ */
22
+ @event
23
+ export class IpcLiveCommand implements IpcCommand {
24
+ type = 'live';
25
+
26
+ /**
27
+ * The optional session identifier.
28
+ */
29
+ id?: number;
30
+ }
31
+
32
+ /**
33
+ * Command to start a new paper session.
34
+ */
35
+ @event
36
+ export class IpcPaperCommand implements IpcCommand {
37
+ type = 'paper';
38
+
39
+ /**
40
+ * The optional session identifier.
41
+ */
42
+ id?: number;
43
+
44
+ /**
45
+ * Specifies trading balance, for example:
46
+ * { "binance:usdt": 1000 }
47
+ */
48
+ balance: { [key: string]: number };
49
+ }
50
+
51
+ /**
52
+ * Command to start a new backtest session.
53
+ */
54
+ @event
55
+ export class IpcBacktestCommand implements IpcCommand {
56
+ type = 'backtest';
57
+
58
+ /**
59
+ * Start date of the backtest in unix timestamp.
60
+ */
61
+ from: number;
62
+
63
+ /**
64
+ * Due date of the backtest in unix timestamp.
65
+ */
66
+ to: number;
67
+
68
+ /**
69
+ * Specifies trading balance, for example:
70
+ * { "binance:usdt": 1000 }
71
+ */
72
+ balance: { [key: string]: number };
73
+ }
74
+
75
+ @event
76
+ export class IpcUniverseQuery implements IpcCommand {
77
+ type = 'universe';
78
+ exchange: string;
79
+ }
80
+
81
+ /**
82
+ * Feeds specific session descriptor with instrument data.
83
+ */
84
+ @event
85
+ export class IpcFeedCommand implements IpcCommand {
86
+ type = 'feed';
87
+
88
+ /**
89
+ * Instrument to feed.
90
+ */
91
+ instrument: string;
92
+
93
+ /**
94
+ * Start date of the feed in unix timestamp.
95
+ */
96
+ from: number;
97
+
98
+ /**
99
+ * Due date of the feed in unix timestamp.
100
+ */
101
+ to: number;
102
+ }
103
+
104
+ /**
105
+ * Stores current session instance.
106
+ */
107
+ class IpcSessionAccessor {
108
+ session: Session;
109
+ }
110
+
111
+ /**
112
+ * Inter process communication handler.
113
+ */
114
+ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
115
+ constructor(private readonly descriptor: SessionDescriptor) {
116
+ super();
117
+ }
118
+
119
+ /**
120
+ * @see IpcLiveCommand
121
+ */
122
+ @handler(IpcLiveCommand)
123
+ async onLiveMode(command: IpcLiveCommand, accessor: IpcSessionAccessor) {
124
+ if (command.id) {
125
+ this.descriptor.id = command.id;
126
+ }
127
+
128
+ accessor.session = live(this.descriptor);
129
+
130
+ await accessor.session.awake();
131
+ }
132
+
133
+ /**
134
+ * @see IpcPaperCommand
135
+ */
136
+ @handler(IpcPaperCommand)
137
+ async onPaperMode(command: IpcPaperCommand, accessor: IpcSessionAccessor) {
138
+ if (command.id) {
139
+ this.descriptor.id = command.id;
140
+ }
141
+
142
+ accessor.session = paper(this.descriptor, {
143
+ balance: command.balance
144
+ });
145
+
146
+ await accessor.session.awake();
147
+ }
148
+
149
+ /**
150
+ * @see IpcBacktestCommand
151
+ */
152
+ @handler(IpcBacktestCommand)
153
+ onBacktestMode(command: IpcBacktestCommand, accessor: IpcSessionAccessor) {
154
+ return new Promise<void>(async resolve => {
155
+ const [session, streamer] = backtest(this.descriptor, {
156
+ from: command.from,
157
+ to: command.to,
158
+ balance: command.balance,
159
+ progress: timestamp =>
160
+ this.notify({
161
+ type: 'backtest:updated',
162
+ timestamp,
163
+ from: command.from,
164
+ to: command.to
165
+ }),
166
+ completed: async () => {
167
+ const statement = {};
168
+
169
+ await accessor.session.dispose();
170
+
171
+ this.notify({ type: 'backtest:completed', statement });
172
+
173
+ resolve();
174
+ }
175
+ });
176
+
177
+ accessor.session = session;
178
+
179
+ this.notify({ type: 'backtest:started' });
180
+
181
+ await accessor.session.awake();
182
+ await streamer.tryContinue().catch(it => Logger.error(it));
183
+ });
184
+ }
185
+
186
+ /**
187
+ * @see IpcUniverseQuery
188
+ */
189
+ @handler(IpcUniverseQuery)
190
+ onUniverse(query: IpcUniverseQuery, accessor: IpcSessionAccessor) {
191
+ accessor.session = accessor.session ?? idle(this.descriptor);
192
+ }
193
+
194
+ /**
195
+ * @see IpcFeedCommand
196
+ */
197
+ @handler(IpcFeedCommand)
198
+ async onFeed(command: IpcFeedCommand, accessor: IpcSessionAccessor) {
199
+ accessor.session = accessor.session ?? idle(this.descriptor);
200
+ const instrument = instrumentOf(command.instrument);
201
+
202
+ await accessor.session.awake();
203
+
204
+ this.notify({ type: 'feed:started' });
205
+
206
+ await accessor.session.aggregate.dispatch(
207
+ instrument.base.exchange,
208
+ new AdapterFeedCommand(
209
+ instrument,
210
+ command.from,
211
+ command.to,
212
+ this.descriptor.feed,
213
+ timestamp =>
214
+ this.notify({
215
+ type: 'feed:updated',
216
+ timestamp,
217
+ from: command.from,
218
+ to: command.to
219
+ })
220
+ )
221
+ );
222
+
223
+ this.notify({ type: 'feed:completed' });
224
+
225
+ await accessor.session.dispose();
226
+ }
227
+
228
+ /**
229
+ * Sends a message to parent process.
230
+ */
231
+ private notify(message: any) {
232
+ if (!process.send) {
233
+ return;
234
+ }
235
+
236
+ process.send(message);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Starts new managed session and subscribes to parent process messages.
242
+ * @param descriptor session descriptor.
243
+ * @param commands collection of commands to execute before session is started.
244
+ * @returns new session.
245
+ */
246
+ export async function run(
247
+ descriptor: SessionDescriptor,
248
+ ...commands: IpcCommand[]
249
+ ): Promise<Session> {
250
+ const handler = new IpcHandler(descriptor);
251
+ const accessor = new IpcSessionAccessor();
252
+ const argv = minimist(process.argv.slice(2));
253
+
254
+ if (argv.command) {
255
+ const json = Buffer.from(argv.command, 'base64').toString('utf-8');
256
+
257
+ commands.push(JSON.parse(json));
258
+ } else {
259
+ if (!commands.length) {
260
+ commands.push(new IpcPaperCommand());
261
+ }
262
+ }
263
+
264
+ for (const command of commands) {
265
+ await handler.dispatch(command, accessor);
266
+ }
267
+
268
+ process.on('message', async (request: any) => {
269
+ const response = await handler.dispatch(request, accessor);
270
+
271
+ if (response) {
272
+ process.send(response);
273
+ }
274
+ });
275
+
276
+ return accessor.session;
277
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './session';
2
2
  export * from './session-descriptor';
3
3
  export * from './session-optimizer';
4
+ export * from './session-statement';
@@ -2,20 +2,49 @@ import { Adapter } from '../adapter';
2
2
  import { Session } from '.';
3
3
  import { Measurement } from '../storage/measurement';
4
4
  import { Feed } from '../storage';
5
- import { Behaviour } from '../behaviour';
6
5
  import { Observable } from 'rxjs';
7
6
 
7
+ /**
8
+ * Describes a single session.
9
+ * You can use @run function to start a new session managed by CLI.
10
+ * To start managed session you should install @quantform/cli package and run
11
+ * specific command:
12
+ * - qf paper (to paper trade strategy)
13
+ * - qf backtest (to backtest strategy based on provided feed)
14
+ * - qf live (to live trade strategy)
15
+ * or run on your own in code:
16
+ * - paper(descriptor, options)
17
+ * - backtest(descriptor, options)
18
+ * - live(descriptor)
19
+ */
8
20
  export interface SessionDescriptor {
9
- id: number;
21
+ /**
22
+ * Unique session identifier, used to identify session in the storage.
23
+ * You can generate new id every time you start the new session or provide
24
+ * session id explicitly to resume previous session (in code or via CLI).
25
+ * If you don't provide session id, it will generate new one based on time.
26
+ */
27
+ id?: number;
10
28
 
11
- // provides trading adapters.
29
+ /**
30
+ * Collection of adapters used to connect to the exchanges.
31
+ */
12
32
  adapter: Adapter[];
13
33
 
14
- // provides input and output feed for backtesting purposes.
34
+ /**
35
+ * Provides historical data for backtest, it's not required for live and paper
36
+ * sessions.
37
+ */
15
38
  feed?: Feed;
16
39
 
17
- // defines measurement storage.
40
+ /**
41
+ * Stores session variables i.e. indicators, orders, or any other type of time
42
+ * series data. You can install @quantform/editor to render this data in your browser.
43
+ */
18
44
  measurement?: Measurement;
19
45
 
20
- behaviour?: Behaviour | Behaviour[] | ((session: Session) => Observable<any>);
46
+ /**
47
+ * Describes your trading strategy.
48
+ */
49
+ describe?: (session: Session) => Observable<any>;
21
50
  }
@@ -0,0 +1,119 @@
1
+ import { Session } from '.';
2
+ import { InstrumentSelector } from '../domain';
3
+ import { combineLatest, finalize, map, take, tap } from 'rxjs';
4
+ import { drawdown } from '../indicator';
5
+ import { floor, precision } from '../shared';
6
+
7
+ export function period() {
8
+ return (session: Session) => {
9
+ const period = session.useStatement('period');
10
+
11
+ return session.store.changes$.pipe(
12
+ finalize(() => (period.to = new Date(session.timestamp).toISOString())),
13
+ take(1),
14
+ tap(it => (period.from = new Date(it.timestamp).toISOString()))
15
+ );
16
+ };
17
+ }
18
+
19
+ export function benchmark(instrument: InstrumentSelector) {
20
+ return (session: Session) => {
21
+ const statement = session.useStatement('benchmark');
22
+
23
+ let balance: number, entry: number, exit: number;
24
+ let dd = 0;
25
+
26
+ return combineLatest([
27
+ session.orderbook(instrument),
28
+ session.balance(instrument.quote),
29
+ session.orderbook(instrument).pipe(
30
+ drawdown(it => it.midRate),
31
+ tap(it => (dd = it))
32
+ )
33
+ ]).pipe(
34
+ map(([orderbook, quote]) => {
35
+ const price = orderbook.midRate;
36
+
37
+ if (!balance) {
38
+ balance = quote.total;
39
+ }
40
+
41
+ if (!entry) {
42
+ entry = price;
43
+ }
44
+
45
+ exit = price;
46
+ }),
47
+ finalize(() => {
48
+ const pnl = exit / entry - 1;
49
+
50
+ statement['benchmark_entry'] = entry;
51
+ statement['benchmark_exit'] = exit;
52
+ statement['benchmark_pnl'] = floor(balance * pnl, precision(entry));
53
+ statement['benchmark_pnl_pp'] = floor(pnl * 100, 2);
54
+ statement['benchmark_drawdown_pp'] = floor(dd * 100, 2);
55
+ })
56
+ );
57
+ };
58
+ }
59
+
60
+ export function equity(instrument: InstrumentSelector) {
61
+ return (session: Session) => {
62
+ const statement = session.useStatement('benchmark');
63
+
64
+ let balance: number, min: number, max: number;
65
+ let dd = 0;
66
+ let equity = 0;
67
+
68
+ return combineLatest([
69
+ session.orderbook(instrument),
70
+ session.balance(instrument.base),
71
+ session.balance(instrument.quote),
72
+ session.positions(instrument)
73
+ ]).pipe(
74
+ tap(([, , quote]) => {
75
+ if (!balance) {
76
+ balance = quote.total;
77
+ }
78
+ }),
79
+ map(([orderbook, base, quote, positions]) =>
80
+ orderbook.instrument.quote.fixed(
81
+ quote.total +
82
+ base.total * orderbook.bestBidRate +
83
+ positions.reduce(
84
+ (agg, position) => agg + position.calculatePnL(orderbook.bestBidRate),
85
+ 0
86
+ )
87
+ )
88
+ ),
89
+ tap(it => {
90
+ if (!min) {
91
+ min = it;
92
+ } else {
93
+ min = Math.min(min, it);
94
+ }
95
+
96
+ if (!max) {
97
+ max = it;
98
+ } else {
99
+ max = Math.max(max, it);
100
+ }
101
+
102
+ equity = it;
103
+ }),
104
+ drawdown(it => it),
105
+ tap(it => (dd = it)),
106
+ finalize(() => {
107
+ const pnl = equity / balance - 1;
108
+ const scale = precision(equity);
109
+
110
+ statement['equity'] = equity;
111
+ statement['equity_min'] = min;
112
+ statement['equity_max'] = max;
113
+ statement['equity_pnl'] = floor(balance * pnl, scale);
114
+ statement['equity_pnl_pp'] = floor(pnl * 100, 2);
115
+ statement['equity_drawdown_pp'] = floor(dd * 100, 2);
116
+ })
117
+ );
118
+ };
119
+ }
@@ -1,8 +1,10 @@
1
1
  import { InstrumentPatchEvent } from '../store/event';
2
- import { Asset, Commision } from '../domain';
3
- import { now } from '../common';
2
+ import { Asset, Commission } from '../domain';
3
+ import { now } from '../shared';
4
4
  import { SessionDescriptor } from './session-descriptor';
5
5
  import { paper } from '../bin';
6
+ import { Session } from './session';
7
+ import { of } from 'rxjs';
6
8
 
7
9
  describe('session tests', () => {
8
10
  const descriptor: SessionDescriptor = {
@@ -29,14 +31,14 @@ describe('session tests', () => {
29
31
  now(),
30
32
  new Asset('de30', 'cex', 2),
31
33
  new Asset('usd', 'cex', 2),
32
- new Commision(0, 0),
34
+ new Commission(0, 0),
33
35
  ''
34
36
  ),
35
37
  new InstrumentPatchEvent(
36
38
  now(),
37
39
  new Asset('wig20', 'cex', 2),
38
40
  new Asset('pln', 'cex', 2),
39
- new Commision(0, 0),
41
+ new Commission(0, 0),
40
42
  ''
41
43
  )
42
44
  );
@@ -22,9 +22,8 @@ import {
22
22
  } from '../domain';
23
23
  import { Store } from '../store';
24
24
  import { concat, from, Observable, Subject, Subscription } from 'rxjs';
25
- import { Behaviour, CombinedBehaviour, FunctionBehaviour } from '../behaviour';
26
25
  import { AdapterAggregate } from '../adapter/adapter-aggregate';
27
- import { Worker } from '../common';
26
+ import { Worker, now } from '../shared';
28
27
  import { Trade } from '../domain/trade';
29
28
  import { SessionDescriptor } from './session-descriptor';
30
29
  import {
@@ -39,18 +38,24 @@ type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
39
38
  export class Session {
40
39
  private initialized = false;
41
40
  private subscription: Subscription;
42
- private behaviour: Behaviour;
43
41
  private worker = new Worker();
44
42
 
45
43
  get timestamp(): number {
46
44
  return this.store.snapshot.timestamp;
47
45
  }
48
46
 
47
+ readonly statement: Record<string, Record<string, any>> = {};
48
+
49
49
  constructor(
50
50
  readonly store: Store,
51
51
  readonly aggregate: AdapterAggregate,
52
52
  readonly descriptor?: SessionDescriptor
53
- ) {}
53
+ ) {
54
+ // generate session id based on time if not provided.
55
+ if (descriptor && !descriptor.id) {
56
+ descriptor.id = now();
57
+ }
58
+ }
54
59
 
55
60
  async awake(): Promise<void> {
56
61
  if (this.initialized) {
@@ -59,18 +64,11 @@ export class Session {
59
64
 
60
65
  this.initialized = true;
61
66
 
67
+ // awake all adapters and synchronize trading accounts with store.
62
68
  await this.aggregate.awake(this.descriptor != null);
63
69
 
64
- if (this.descriptor?.behaviour) {
65
- if (this.descriptor.behaviour instanceof Function) {
66
- this.behaviour = new FunctionBehaviour(this.descriptor.behaviour);
67
- } else {
68
- this.behaviour = Array.isArray(this.descriptor.behaviour)
69
- ? new CombinedBehaviour(this.descriptor.behaviour)
70
- : this.descriptor.behaviour;
71
- }
72
-
73
- this.subscription = this.behaviour.describe(this).subscribe();
70
+ if (this.descriptor.describe) {
71
+ this.subscription = this.descriptor.describe(this).subscribe();
74
72
  }
75
73
  }
76
74
 
@@ -89,14 +87,20 @@ export class Session {
89
87
  await this.worker.wait();
90
88
  }
91
89
 
92
- statement(output: Record<string, any>) {
93
- if (!this.behaviour?.statement) {
94
- return;
95
- }
96
-
97
- this.behaviour.statement(output);
90
+ useStatement(section: string): Record<string, any> {
91
+ return this.statement[section] ?? (this.statement[section] = {});
98
92
  }
99
93
 
94
+ /**
95
+ * Returns last stored measurement and setter for it in session.
96
+ * For example you can save and restore variables in same session between runs.
97
+ * Example usage:
98
+ * const [order$, setOrder] = session.measurement<Order>('order');
99
+ *
100
+ * order.pipe(tap(it => console.log(`your last order was: ${it}`)));
101
+ *
102
+ * setOrder(order);
103
+ */
100
104
  useMeasure<T extends { timestamp: number }>(
101
105
  params: { kind: string; timestamp?: number },
102
106
  defaultValue: T = undefined
@@ -130,10 +134,19 @@ export class Session {
130
134
  return [concat(stored$, subject$.asObservable()), setter];
131
135
  }
132
136
 
137
+ /**
138
+ * Return values for patch provided in optimization file.
139
+ * Example usage:
140
+ * const orderSize = session.useOptimizer('order.size');
141
+ */
133
142
  useOptimizer(path: string): any {
134
143
  return undefined;
135
144
  }
136
145
 
146
+ /**
147
+ * Subscribes to specific instrument. Usually forces adapter to subscribe
148
+ * for orderbook and ticker streams.
149
+ */
137
150
  async subscribe(instrument: Array<InstrumentSelector>): Promise<void> {
138
151
  const grouped = instrument
139
152
  .filter(it => it != null)
@@ -154,6 +167,11 @@ export class Session {
154
167
  }
155
168
  }
156
169
 
170
+ /**
171
+ * Opens collection of orders.
172
+ * Example:
173
+ * session.open(Order.buyMarket(instrument, 100));
174
+ */
157
175
  async open(...orders: Order[]): Promise<void> {
158
176
  await Promise.all(
159
177
  orders.map(it =>
@@ -165,6 +183,9 @@ export class Session {
165
183
  );
166
184
  }
167
185
 
186
+ /**
187
+ * Cancels specific order.
188
+ */
168
189
  cancel(order: Order): Promise<void> {
169
190
  return this.aggregate.dispatch(
170
191
  order.instrument.base.exchange,
@@ -172,6 +193,10 @@ export class Session {
172
193
  );
173
194
  }
174
195
 
196
+ /**
197
+ * Subscribes to specific instrument changes.
198
+ * When adapter awake then it will fetch collection of all available instruments.
199
+ */
175
200
  instrument(selector: InstrumentSelector): Observable<Instrument> {
176
201
  this.subscribe([selector]);
177
202
 
@@ -181,6 +206,10 @@ export class Session {
181
206
  );
182
207
  }
183
208
 
209
+ /**
210
+ * Subscribes to instruments changes.
211
+ * When adapter awake then it will fetch collection of all available instruments.
212
+ */
184
213
  instruments(): Observable<Instrument[]> {
185
214
  return this.store.changes$.pipe(
186
215
  filter(it => it instanceof Instrument),
@@ -191,6 +220,9 @@ export class Session {
191
220
  );
192
221
  }
193
222
 
223
+ /**
224
+ * Subscribes to trade/ticker changes.
225
+ */
194
226
  trade(selector?: InstrumentSelector): Observable<Trade> {
195
227
  this.subscribe([selector]);
196
228
 
@@ -204,6 +236,10 @@ export class Session {
204
236
  );
205
237
  }
206
238
 
239
+ /**
240
+ * Subscribes to orderbook changes.
241
+ * Right now you can access only best bid and best ask.
242
+ */
207
243
  orderbook(selector?: InstrumentSelector): Observable<Orderbook> {
208
244
  this.subscribe([selector]);
209
245
 
@@ -217,6 +253,9 @@ export class Session {
217
253
  );
218
254
  }
219
255
 
256
+ /**
257
+ * Subscribes to position on leveraged market.
258
+ */
220
259
  position(selector?: InstrumentSelector): Observable<Position> {
221
260
  this.subscribe([selector]);
222
261
 
@@ -230,6 +269,9 @@ export class Session {
230
269
  );
231
270
  }
232
271
 
272
+ /**
273
+ * Subscribes to positions on leveraged markets.
274
+ */
233
275
  positions(selector: InstrumentSelector): Observable<Position[]> {
234
276
  this.subscribe([selector]);
235
277
 
File without changes
File without changes