@quantform/core 0.3.230 → 0.3.234

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.
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
+ }
@@ -67,7 +67,7 @@ export class Session {
67
67
  // awake all adapters and synchronize trading accounts with store.
68
68
  await this.aggregate.awake(this.descriptor != null);
69
69
 
70
- if (this.descriptor.describe) {
70
+ if (this.descriptor?.describe) {
71
71
  this.subscription = this.descriptor.describe(this).subscribe();
72
72
  }
73
73
  }