@quantform/core 0.3.234 → 0.3.241

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 (50) hide show
  1. package/dist/adapter/adapter-aggregate.js +5 -5
  2. package/dist/adapter/adapter-aggregate.js.map +1 -1
  3. package/dist/adapter/backtester/backtester-adapter.d.ts +2 -3
  4. package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
  5. package/dist/adapter/backtester/backtester-streamer.d.ts +6 -0
  6. package/dist/adapter/backtester/backtester-streamer.js +10 -7
  7. package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
  8. package/dist/adapter/backtester/backtester-streamer.spec.js +9 -7
  9. package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -1
  10. package/dist/adapter/paper/paper-adapter.js +2 -2
  11. package/dist/adapter/paper/paper-adapter.js.map +1 -1
  12. package/dist/domain/asset.d.ts +3 -3
  13. package/dist/domain/asset.js +8 -8
  14. package/dist/domain/asset.js.map +1 -1
  15. package/dist/domain/asset.spec.js +4 -4
  16. package/dist/domain/asset.spec.js.map +1 -1
  17. package/dist/domain/instrument.d.ts +1 -1
  18. package/dist/domain/instrument.js +6 -6
  19. package/dist/domain/instrument.js.map +1 -1
  20. package/dist/domain/instrument.spec.js +7 -7
  21. package/dist/domain/instrument.spec.js.map +1 -1
  22. package/dist/domain/orderbook.d.ts +0 -1
  23. package/dist/ipc.d.ts +4 -1
  24. package/dist/ipc.js +50 -17
  25. package/dist/ipc.js.map +1 -1
  26. package/dist/ipc.spec.js +28 -0
  27. package/dist/ipc.spec.js.map +1 -1
  28. package/dist/session/session.js +7 -7
  29. package/dist/session/session.js.map +1 -1
  30. package/dist/store/event/store-instrument.event.js +1 -1
  31. package/dist/store/event/store-instrument.event.js.map +1 -1
  32. package/dist/tests/backtester-adapter.spec.js +7 -5
  33. package/dist/tests/backtester-adapter.spec.js.map +1 -1
  34. package/dist/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/src/adapter/adapter-aggregate.ts +5 -5
  37. package/src/adapter/backtester/backtester-adapter.ts +2 -3
  38. package/src/adapter/backtester/backtester-streamer.spec.ts +9 -7
  39. package/src/adapter/backtester/backtester-streamer.ts +36 -7
  40. package/src/adapter/paper/paper-adapter.ts +2 -2
  41. package/src/domain/asset.spec.ts +4 -4
  42. package/src/domain/asset.ts +9 -9
  43. package/src/domain/instrument.spec.ts +7 -7
  44. package/src/domain/instrument.ts +6 -6
  45. package/src/domain/orderbook.ts +1 -1
  46. package/src/ipc.spec.ts +30 -2
  47. package/src/ipc.ts +51 -22
  48. package/src/session/session.ts +7 -7
  49. package/src/store/event/store-instrument.event.ts +1 -1
  50. package/src/tests/backtester-adapter.spec.ts +7 -5
@@ -43,13 +43,13 @@ export class PaperAdapter extends Adapter {
43
43
  @handler(AdapterAccountCommand)
44
44
  onAccount(event: AdapterAccountCommand, context: AdapterContext) {
45
45
  let subscribed = Object.values(this.store.snapshot.subscription.asset).filter(
46
- it => it.exchange == this.name
46
+ it => it.adapter == this.name
47
47
  );
48
48
 
49
49
  for (const balance in this.options.balance) {
50
50
  const asset = assetOf(balance);
51
51
 
52
- if (asset.exchange != this.name) {
52
+ if (asset.adapter != this.name) {
53
53
  continue;
54
54
  }
55
55
 
@@ -5,7 +5,7 @@ describe('asset tests', () => {
5
5
  const sut = new Asset('abc', 'xyz', 4);
6
6
 
7
7
  expect(sut.name).toEqual('abc');
8
- expect(sut.exchange).toEqual('xyz');
8
+ expect(sut.adapter).toEqual('xyz');
9
9
  expect(sut.scale).toEqual(4);
10
10
  expect(sut.tickSize).toEqual(0.0001);
11
11
  expect(sut.fixed(1.1234567)).toEqual(1.1234);
@@ -20,7 +20,7 @@ describe('asset selector tests', () => {
20
20
  const sut = assetOf('xyz:abc');
21
21
 
22
22
  expect(sut.name).toEqual('abc');
23
- expect(sut.exchange).toEqual('xyz');
23
+ expect(sut.adapter).toEqual('xyz');
24
24
  expect(sut.toString()).toEqual('xyz:abc');
25
25
  });
26
26
 
@@ -28,7 +28,7 @@ describe('asset selector tests', () => {
28
28
  const sut = assetOf('XYZ:ABC');
29
29
 
30
30
  expect(sut.name).toEqual('abc');
31
- expect(sut.exchange).toEqual('xyz');
31
+ expect(sut.adapter).toEqual('xyz');
32
32
  expect(sut.toString()).toEqual('xyz:abc');
33
33
  });
34
34
 
@@ -56,7 +56,7 @@ describe('asset selector tests', () => {
56
56
  expect(fn).toThrow(Error);
57
57
  });
58
58
 
59
- test('should throw invalid format message for missing exchange name', () => {
59
+ test('should throw invalid format message for missing adapter name', () => {
60
60
  const fn = () => {
61
61
  assetOf(':abc');
62
62
  };
@@ -7,13 +7,13 @@ export class AssetSelector {
7
7
  private readonly id: string;
8
8
 
9
9
  readonly name: string;
10
- readonly exchange: string;
10
+ readonly adapter: string;
11
11
 
12
- constructor(name: string, exchange: string) {
12
+ constructor(name: string, adapter: string) {
13
13
  this.name = name.toLowerCase();
14
- this.exchange = exchange.toLowerCase();
14
+ this.adapter = adapter.toLowerCase();
15
15
 
16
- this.id = `${this.exchange}:${this.name}`;
16
+ this.id = `${this.adapter}:${this.name}`;
17
17
  }
18
18
 
19
19
  /**
@@ -35,13 +35,13 @@ export function assetOf(asset: string): AssetSelector {
35
35
  }
36
36
 
37
37
  const assetName = section[1];
38
- const exchangeName = section[0];
38
+ const adapterName = section[0];
39
39
 
40
- if (assetName.length == 0 || exchangeName.length == 0) {
40
+ if (assetName.length == 0 || adapterName.length == 0) {
41
41
  throw Error('invalid asset format');
42
42
  }
43
43
 
44
- return new AssetSelector(assetName, exchangeName);
44
+ return new AssetSelector(assetName, adapterName);
45
45
  }
46
46
 
47
47
  /**
@@ -51,8 +51,8 @@ export function assetOf(asset: string): AssetSelector {
51
51
  export class Asset extends AssetSelector {
52
52
  readonly tickSize: number;
53
53
 
54
- constructor(name: string, exchange: string, public readonly scale: number) {
55
- super(name, exchange);
54
+ constructor(name: string, adapter: string, public readonly scale: number) {
55
+ super(name, adapter);
56
56
 
57
57
  this.tickSize = 1.0 / Math.pow(10, this.scale);
58
58
  }
@@ -10,9 +10,9 @@ describe('instrument tests', () => {
10
10
  );
11
11
 
12
12
  expect(sut.base.name).toEqual('abc');
13
- expect(sut.base.exchange).toEqual('xyz');
13
+ expect(sut.base.adapter).toEqual('xyz');
14
14
  expect(sut.quote.name).toEqual('def');
15
- expect(sut.quote.exchange).toEqual('xyz');
15
+ expect(sut.quote.adapter).toEqual('xyz');
16
16
  expect(sut.toString()).toEqual('xyz:abc-def');
17
17
  });
18
18
  });
@@ -22,9 +22,9 @@ describe('instrument selector tests', () => {
22
22
  const sut = instrumentOf('xyz:abc-def');
23
23
 
24
24
  expect(sut.base.name).toEqual('abc');
25
- expect(sut.base.exchange).toEqual('xyz');
25
+ expect(sut.base.adapter).toEqual('xyz');
26
26
  expect(sut.quote.name).toEqual('def');
27
- expect(sut.quote.exchange).toEqual('xyz');
27
+ expect(sut.quote.adapter).toEqual('xyz');
28
28
  expect(sut.toString()).toEqual('xyz:abc-def');
29
29
  });
30
30
 
@@ -32,9 +32,9 @@ describe('instrument selector tests', () => {
32
32
  const sut = instrumentOf('XYZ:ABC-DEF');
33
33
 
34
34
  expect(sut.base.name).toEqual('abc');
35
- expect(sut.base.exchange).toEqual('xyz');
35
+ expect(sut.base.adapter).toEqual('xyz');
36
36
  expect(sut.quote.name).toEqual('def');
37
- expect(sut.quote.exchange).toEqual('xyz');
37
+ expect(sut.quote.adapter).toEqual('xyz');
38
38
  expect(sut.toString()).toEqual('xyz:abc-def');
39
39
  });
40
40
 
@@ -62,7 +62,7 @@ describe('instrument selector tests', () => {
62
62
  expect(fn).toThrow(Error);
63
63
  });
64
64
 
65
- test('should throw invalid format message for missing exchange name', () => {
65
+ test('should throw invalid format message for missing adapter name', () => {
66
66
  const fn = () => {
67
67
  assetOf(':abc-def');
68
68
  };
@@ -9,9 +9,9 @@ export class InstrumentSelector {
9
9
  readonly base: AssetSelector;
10
10
  readonly quote: AssetSelector;
11
11
 
12
- constructor(base: string, quote: string, exchange: string) {
13
- this.base = new AssetSelector(base.toLowerCase(), exchange.toLowerCase());
14
- this.quote = new AssetSelector(quote.toLowerCase(), exchange.toLowerCase());
12
+ constructor(base: string, quote: string, adapter: string) {
13
+ this.base = new AssetSelector(base.toLowerCase(), adapter.toLowerCase());
14
+ this.quote = new AssetSelector(quote.toLowerCase(), adapter.toLowerCase());
15
15
 
16
16
  this.id = `${this.base.toString()}-${this.quote.name}`;
17
17
  }
@@ -31,10 +31,10 @@ export class Instrument extends InstrumentSelector implements Component {
31
31
  leverage?: number = null;
32
32
 
33
33
  constructor(readonly base: Asset, readonly quote: Asset, readonly raw: string) {
34
- super(base.name, quote.name, base.exchange);
34
+ super(base.name, quote.name, base.adapter);
35
35
 
36
- if (base.exchange != quote.exchange) {
37
- throw new Error('Exchange mismatch!');
36
+ if (base.adapter != quote.adapter) {
37
+ throw new Error('Adapter mismatch!');
38
38
  }
39
39
  }
40
40
  }
@@ -11,7 +11,7 @@ export class Orderbook implements Component {
11
11
  bestAskQuantity: number;
12
12
  bestBidRate: number;
13
13
  bestBidQuantity: number;
14
- az;
14
+
15
15
  get midRate(): number {
16
16
  return this.instrument.quote.fixed((this.bestAskRate + this.bestBidRate) / 2);
17
17
  }
package/src/ipc.spec.ts CHANGED
@@ -3,11 +3,13 @@ import {
3
3
  Adapter,
4
4
  AdapterFeedCommand,
5
5
  AdapterAwakeCommand,
6
- AdapterAccountCommand
6
+ AdapterAccountCommand,
7
+ AdapterSubscribeCommand,
8
+ AdapterDisposeCommand
7
9
  } from './adapter';
8
10
  import { PaperAdapter, PaperSpotExecutor } from './adapter/paper';
9
11
  import { PaperExecutor } from './adapter/paper/executor/paper-executor';
10
- import { IpcFeedCommand, run } from './ipc';
12
+ import { ipcSub, run } from './ipc';
11
13
  import { Feed, InMemoryStorage } from './storage';
12
14
  import { instrumentOf } from './domain';
13
15
  import { handler } from './shared';
@@ -26,6 +28,12 @@ class DefaultAdapter extends Adapter {
26
28
  @handler(AdapterAwakeCommand)
27
29
  onAwake(command: AdapterAwakeCommand) {}
28
30
 
31
+ @handler(AdapterDisposeCommand)
32
+ onDispose(command: AdapterDisposeCommand) {}
33
+
34
+ @handler(AdapterSubscribeCommand)
35
+ onSubscribe(command: AdapterSubscribeCommand) {}
36
+
29
37
  @handler(AdapterAccountCommand)
30
38
  onAccount(command: AdapterAccountCommand) {}
31
39
 
@@ -53,4 +61,24 @@ describe('ipc feed tests', () => {
53
61
 
54
62
  expect(session.descriptor).toBeUndefined();
55
63
  });
64
+
65
+ test('should dispatch session started event', done => {
66
+ const command = {
67
+ type: 'paper',
68
+ balance: { 'default:usd': 100 }
69
+ };
70
+
71
+ ipcSub.on('message', (message: any) => {
72
+ expect(message.type).toBe('paper:started');
73
+ done();
74
+ });
75
+
76
+ run(
77
+ {
78
+ adapter: [new DefaultAdapter()],
79
+ describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
80
+ },
81
+ command
82
+ );
83
+ });
56
84
  });
package/src/ipc.ts CHANGED
@@ -4,8 +4,12 @@ import { instrumentOf } from './domain';
4
4
  import { Topic, event, handler } from './shared/topic';
5
5
  import { Logger } from './shared';
6
6
  import { backtest, idle, live, paper } from './bin';
7
+ import { BacktesterStreamer } from './adapter/backtester';
8
+ import { EventEmitter } from 'events';
7
9
  import minimist = require('minimist');
8
10
 
11
+ export const ipcSub = new EventEmitter();
12
+
9
13
  /**
10
14
  * Base command/query interface for IPC communication.
11
15
  */
@@ -75,7 +79,7 @@ export class IpcBacktestCommand implements IpcCommand {
75
79
  @event
76
80
  export class IpcUniverseQuery implements IpcCommand {
77
81
  type = 'universe';
78
- exchange: string;
82
+ adapter: string;
79
83
  }
80
84
 
81
85
  /**
@@ -127,6 +131,11 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
127
131
 
128
132
  accessor.session = live(this.descriptor);
129
133
 
134
+ this.notify({
135
+ type: 'live:started',
136
+ session: accessor.session.descriptor?.id
137
+ });
138
+
130
139
  await accessor.session.awake();
131
140
  }
132
141
 
@@ -143,6 +152,11 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
143
152
  balance: command.balance
144
153
  });
145
154
 
155
+ this.notify({
156
+ type: 'paper:started',
157
+ session: accessor.session.descriptor?.id
158
+ });
159
+
146
160
  await accessor.session.awake();
147
161
  }
148
162
 
@@ -156,28 +170,43 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
156
170
  from: command.from,
157
171
  to: command.to,
158
172
  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();
173
+ listener: {
174
+ onBacktestStarted: (streamer: BacktesterStreamer) => {
175
+ this.notify({
176
+ type: 'backtest:started',
177
+ session: session.descriptor?.id,
178
+ timestamp: streamer.timestamp,
179
+ from: command.from,
180
+ to: command.to
181
+ });
182
+ },
183
+ onBacktestUpdated: (streamer: BacktesterStreamer) => {
184
+ this.notify({
185
+ type: 'backtest:updated',
186
+ session: session.descriptor?.id,
187
+ timestamp: streamer.timestamp,
188
+ from: command.from,
189
+ to: command.to
190
+ });
191
+ },
192
+ onBacktestCompleted: async (streamer: BacktesterStreamer) => {
193
+ await accessor.session.dispose();
194
+
195
+ this.notify({
196
+ type: 'backtest:completed',
197
+ session: session.descriptor?.id,
198
+ timestamp: streamer.timestamp,
199
+ from: command.from,
200
+ to: command.to
201
+ });
202
+
203
+ resolve();
204
+ }
174
205
  }
175
206
  });
176
207
 
177
208
  accessor.session = session;
178
209
 
179
- this.notify({ type: 'backtest:started' });
180
-
181
210
  await accessor.session.awake();
182
211
  await streamer.tryContinue().catch(it => Logger.error(it));
183
212
  });
@@ -204,7 +233,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
204
233
  this.notify({ type: 'feed:started' });
205
234
 
206
235
  await accessor.session.aggregate.dispatch(
207
- instrument.base.exchange,
236
+ instrument.base.adapter,
208
237
  new AdapterFeedCommand(
209
238
  instrument,
210
239
  command.from,
@@ -229,11 +258,11 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
229
258
  * Sends a message to parent process.
230
259
  */
231
260
  private notify(message: any) {
232
- if (!process.send) {
233
- return;
261
+ if (process.send) {
262
+ process.send(message);
234
263
  }
235
264
 
236
- process.send(message);
265
+ ipcSub.emit('message', message);
237
266
  }
238
267
  }
239
268
 
@@ -151,12 +151,12 @@ export class Session {
151
151
  const grouped = instrument
152
152
  .filter(it => it != null)
153
153
  .reduce((aggregate, it) => {
154
- const exchange = it.base.exchange;
154
+ const adapter = it.base.adapter;
155
155
 
156
- if (aggregate[exchange]) {
157
- aggregate[exchange].push(it);
156
+ if (aggregate[adapter]) {
157
+ aggregate[adapter].push(it);
158
158
  } else {
159
- aggregate[exchange] = [it];
159
+ aggregate[adapter] = [it];
160
160
  }
161
161
 
162
162
  return aggregate;
@@ -176,7 +176,7 @@ export class Session {
176
176
  await Promise.all(
177
177
  orders.map(it =>
178
178
  this.aggregate.dispatch<AdapterOrderOpenCommand, void>(
179
- it.instrument.base.exchange,
179
+ it.instrument.base.adapter,
180
180
  new AdapterOrderOpenCommand(it)
181
181
  )
182
182
  )
@@ -188,7 +188,7 @@ export class Session {
188
188
  */
189
189
  cancel(order: Order): Promise<void> {
190
190
  return this.aggregate.dispatch(
191
- order.instrument.base.exchange,
191
+ order.instrument.base.adapter,
192
192
  new AdapterOrderCancelCommand(order)
193
193
  );
194
194
  }
@@ -375,7 +375,7 @@ export class Session {
375
375
  switchMap(() =>
376
376
  from(
377
377
  this.aggregate.dispatch<AdapterHistoryQuery, Candle[]>(
378
- selector.base.exchange,
378
+ selector.base.adapter,
379
379
  new AdapterHistoryQuery(selector, timeframe, length)
380
380
  )
381
381
  )
@@ -23,7 +23,7 @@ export function InstrumentPatchEventHandler(event: InstrumentPatchEvent, state:
23
23
  const selector = new InstrumentSelector(
24
24
  event.base.name,
25
25
  event.quote.name,
26
- event.base.exchange
26
+ event.base.adapter
27
27
  );
28
28
 
29
29
  let instrument = state.universe.instrument[selector.toString()];
@@ -69,12 +69,14 @@ describe('backtester adapter tests', () => {
69
69
  },
70
70
  from: 0,
71
71
  to: 100,
72
- completed: () => {
73
- expect(store.snapshot.timestamp).toEqual(1);
74
- expect(store.snapshot.trade[instrument.toString()].rate).toEqual(100);
75
- expect(store.snapshot.trade[instrument.toString()].quantity).toEqual(10);
72
+ listener: {
73
+ onBacktestCompleted: () => {
74
+ expect(store.snapshot.timestamp).toEqual(1);
75
+ expect(store.snapshot.trade[instrument.toString()].rate).toEqual(100);
76
+ expect(store.snapshot.trade[instrument.toString()].quantity).toEqual(10);
76
77
 
77
- done();
78
+ done();
79
+ }
78
80
  }
79
81
  });
80
82