@quantform/core 0.3.251 → 0.3.255

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 (69) hide show
  1. package/dist/adapter/backtester/backtester-cursor.spec.js.map +1 -1
  2. package/dist/adapter/backtester/backtester-streamer.js +3 -0
  3. package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
  4. package/dist/bootstrap.d.ts +11 -0
  5. package/dist/bootstrap.js +58 -0
  6. package/dist/bootstrap.js.map +1 -0
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/ipc.js +21 -37
  11. package/dist/ipc.js.map +1 -1
  12. package/dist/session/session.spec.js +2 -2
  13. package/dist/session/session.spec.js.map +1 -1
  14. package/dist/store/event/store-balance.event.d.ts +5 -6
  15. package/dist/store/event/store-balance.event.js +8 -8
  16. package/dist/store/event/store-balance.event.js.map +1 -1
  17. package/dist/store/event/store-candle.event.d.ts +2 -1
  18. package/dist/store/event/store-candle.event.js +25 -19
  19. package/dist/store/event/store-candle.event.js.map +1 -1
  20. package/dist/store/event/store-candle.event.spec.js +7 -8
  21. package/dist/store/event/store-candle.event.spec.js.map +1 -1
  22. package/dist/store/event/store-instrument.event.d.ts +6 -6
  23. package/dist/store/event/store-instrument.event.js +7 -7
  24. package/dist/store/event/store-instrument.event.js.map +1 -1
  25. package/dist/store/event/store-order.event.d.ts +9 -9
  26. package/dist/store/event/store-order.event.js +16 -16
  27. package/dist/store/event/store-order.event.js.map +1 -1
  28. package/dist/store/event/store-order.event.spec.js +7 -7
  29. package/dist/store/event/store-order.event.spec.js.map +1 -1
  30. package/dist/store/event/store-orderbook.event.d.ts +2 -3
  31. package/dist/store/event/store-orderbook.event.js +2 -2
  32. package/dist/store/event/store-orderbook.event.js.map +1 -1
  33. package/dist/store/event/store-position.event.d.ts +3 -3
  34. package/dist/store/event/store-position.event.js +6 -5
  35. package/dist/store/event/store-position.event.js.map +1 -1
  36. package/dist/store/event/store-trade.event.d.ts +2 -3
  37. package/dist/store/event/store-trade.event.js +2 -2
  38. package/dist/store/event/store-trade.event.js.map +1 -1
  39. package/dist/store/event/store-trade.event.spec.js +16 -17
  40. package/dist/store/event/store-trade.event.spec.js.map +1 -1
  41. package/dist/store/store.d.ts +23 -20
  42. package/dist/store/store.js +29 -28
  43. package/dist/store/store.js.map +1 -1
  44. package/dist/store/store.state.d.ts +5 -1
  45. package/dist/store/store.state.js.map +1 -1
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +1 -1
  48. package/src/adapter/backtester/backtester-cursor.spec.ts +2 -2
  49. package/src/adapter/backtester/backtester-streamer.ts +5 -1
  50. package/src/bootstrap.ts +103 -0
  51. package/src/index.ts +1 -1
  52. package/src/ipc.ts +22 -40
  53. package/src/session/session.spec.ts +2 -4
  54. package/src/store/event/store-balance.event.ts +25 -9
  55. package/src/store/event/store-candle.event.spec.ts +8 -10
  56. package/src/store/event/store-candle.event.ts +44 -30
  57. package/src/store/event/store-instrument.event.ts +12 -7
  58. package/src/store/event/store-order.event.spec.ts +8 -8
  59. package/src/store/event/store-order.event.ts +46 -17
  60. package/src/store/event/store-orderbook.event.ts +7 -3
  61. package/src/store/event/store-position.event.ts +15 -7
  62. package/src/store/event/store-trade.event.spec.ts +18 -20
  63. package/src/store/event/store-trade.event.ts +7 -3
  64. package/src/store/store.state.ts +8 -2
  65. package/src/store/store.ts +33 -31
  66. package/dist/bin.d.ts +0 -5
  67. package/dist/bin.js +0 -28
  68. package/dist/bin.js.map +0 -1
  69. package/src/bin.ts +0 -71
package/src/ipc.ts CHANGED
@@ -2,7 +2,7 @@ import { Session, SessionDescriptor } from './session';
2
2
  import { instrumentOf } from './domain';
3
3
  import { Topic, event, handler } from './shared/topic';
4
4
  import { runTask, Logger } from './shared';
5
- import { backtest, live, paper } from './bin';
5
+ import { Bootstrap } from './bootstrap';
6
6
  import { BacktesterStreamer } from './adapter/backtester';
7
7
  import { Observable } from 'rxjs';
8
8
  import { EventEmitter } from 'events';
@@ -116,7 +116,10 @@ export declare type IpcSessionDescriptor = SessionDescriptor & { ipcSub?: EventE
116
116
  * Inter process communication handler.
117
117
  */
118
118
  class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
119
- constructor(private readonly descriptor: IpcSessionDescriptor) {
119
+ constructor(
120
+ private readonly bootstrap: Bootstrap,
121
+ private readonly ipcSub?: EventEmitter
122
+ ) {
120
123
  super();
121
124
  }
122
125
 
@@ -125,11 +128,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
125
128
  */
126
129
  @handler(IpcLiveCommand)
127
130
  async onLiveMode(command: IpcLiveCommand, accessor: IpcSessionAccessor) {
128
- if (command.id) {
129
- this.descriptor.id = command.id;
130
- }
131
-
132
- accessor.session = live(this.descriptor);
131
+ accessor.session = this.bootstrap.useSessionId(command.id).live();
133
132
 
134
133
  this.emit({
135
134
  type: 'live:started',
@@ -144,11 +143,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
144
143
  */
145
144
  @handler(IpcPaperCommand)
146
145
  async onPaperMode(command: IpcPaperCommand, accessor: IpcSessionAccessor) {
147
- if (command.id) {
148
- this.descriptor.id = command.id;
149
- }
150
-
151
- accessor.session = paper(this.descriptor);
146
+ accessor.session = this.bootstrap.useSessionId(command.id).paper();
152
147
 
153
148
  this.emit({
154
149
  type: 'paper:started',
@@ -163,22 +158,18 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
163
158
  */
164
159
  @handler(IpcBacktestCommand)
165
160
  onBacktestMode(command: IpcBacktestCommand, accessor: IpcSessionAccessor) {
166
- if (command.from) {
167
- this.descriptor.options.backtester.from = command.from;
168
- }
169
- if (command.to) {
170
- this.descriptor.options.backtester.to = command.to;
171
- }
161
+ this.bootstrap.useBacktestPeriod(command.from, command.to).backtest();
162
+ const { from, to } = this.bootstrap.descriptor.options.backtester;
172
163
 
173
164
  return new Promise<void>(async resolve => {
174
- const [session, streamer] = backtest(this.descriptor, {
165
+ const [session, streamer] = this.bootstrap.backtest({
175
166
  onBacktestStarted: (streamer: BacktesterStreamer) => {
176
167
  this.emit({
177
168
  type: 'backtest:started',
178
169
  session: session.descriptor?.id,
179
170
  timestamp: streamer.timestamp,
180
- from: this.descriptor.options.backtester.from,
181
- to: this.descriptor.options.backtester.to
171
+ from,
172
+ to
182
173
  });
183
174
  },
184
175
  onBacktestUpdated: (streamer: BacktesterStreamer) => {
@@ -186,8 +177,8 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
186
177
  type: 'backtest:updated',
187
178
  session: session.descriptor?.id,
188
179
  timestamp: streamer.timestamp,
189
- from: this.descriptor.options.backtester.from,
190
- to: this.descriptor.options.backtester.to
180
+ from,
181
+ to
191
182
  });
192
183
  },
193
184
  onBacktestCompleted: async (streamer: BacktesterStreamer) => {
@@ -197,8 +188,8 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
197
188
  type: 'backtest:completed',
198
189
  session: session.descriptor?.id,
199
190
  timestamp: streamer.timestamp,
200
- from: this.descriptor.options.backtester.from,
201
- to: this.descriptor.options.backtester.to
191
+ from,
192
+ to
202
193
  });
203
194
 
204
195
  resolve();
@@ -217,18 +208,9 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
217
208
  */
218
209
  @handler(IpcFeedCommand)
219
210
  async onFeed(command: IpcFeedCommand, accessor: IpcSessionAccessor) {
220
- if (!this.descriptor.options) {
221
- this.descriptor.options = {};
222
- }
223
-
224
- if (!this.descriptor.options.paper) {
225
- this.descriptor.options.paper = {
226
- balance: {}
227
- };
228
- }
229
-
230
- accessor.session = accessor.session ?? paper(this.descriptor);
211
+ accessor.session = accessor.session ?? this.bootstrap.paper();
231
212
  const instrument = instrumentOf(command.instrument);
213
+ const { feed } = accessor.session.descriptor;
232
214
 
233
215
  await accessor.session.awake(undefined);
234
216
 
@@ -238,7 +220,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
238
220
  instrument,
239
221
  command.from,
240
222
  command.to,
241
- this.descriptor.feed,
223
+ feed,
242
224
  timestamp =>
243
225
  this.emit({
244
226
  type: 'feed:updated',
@@ -258,7 +240,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
258
240
  */
259
241
  @handler(IpcTaskCommand)
260
242
  async onTask(query: IpcTaskCommand, accessor: IpcSessionAccessor) {
261
- accessor.session = accessor.session ?? live(this.descriptor);
243
+ accessor.session = accessor.session ?? this.bootstrap.live();
262
244
 
263
245
  await accessor.session.awake(undefined);
264
246
 
@@ -296,7 +278,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
296
278
  process.send(message);
297
279
  }
298
280
 
299
- this.descriptor.ipcSub?.emit('message', message);
281
+ this.ipcSub?.emit('message', message);
300
282
  }
301
283
  }
302
284
 
@@ -310,7 +292,7 @@ export async function run(
310
292
  descriptor: IpcSessionDescriptor,
311
293
  ...commands: IpcCommand[]
312
294
  ): Promise<Session> {
313
- const handler = new IpcHandler(descriptor);
295
+ const handler = new IpcHandler(new Bootstrap(descriptor), descriptor.ipcSub);
314
296
  const accessor = new IpcSessionAccessor();
315
297
  const argv = minimist(process.argv.slice(2));
316
298
 
@@ -2,9 +2,7 @@ import { InstrumentPatchEvent } from '../store/event';
2
2
  import { Asset, Commission } from '../domain';
3
3
  import { now } from '../shared';
4
4
  import { SessionDescriptor } from './session-descriptor';
5
- import { paper } from '../bin';
6
- import { Session } from './session';
7
- import { of } from 'rxjs';
5
+ import { Bootstrap } from '../bootstrap';
8
6
 
9
7
  describe('session tests', () => {
10
8
  const descriptor: SessionDescriptor = {
@@ -13,7 +11,7 @@ describe('session tests', () => {
13
11
  };
14
12
 
15
13
  test('should trigger once', done => {
16
- const session = paper(descriptor);
14
+ const session = new Bootstrap(descriptor).paper();
17
15
 
18
16
  session.instruments().subscribe({
19
17
  next: it => {
@@ -2,7 +2,7 @@ import { event } from '../../shared/topic';
2
2
  import { timestamp } from '../../shared';
3
3
  import { AssetSelector } from '../../domain/asset';
4
4
  import { Balance } from '../../domain/balance';
5
- import { State } from '../store.state';
5
+ import { State, StateChangeTracker } from '../store.state';
6
6
  import { StoreEvent } from './store.event';
7
7
 
8
8
  /**
@@ -23,7 +23,11 @@ export class BalancePatchEvent implements StoreEvent {
23
23
  /**
24
24
  * @see BalancePatchEvent
25
25
  */
26
- export function BalancePatchEventHandler(event: BalancePatchEvent, state: State) {
26
+ export function BalancePatchEventHandler(
27
+ event: BalancePatchEvent,
28
+ state: State,
29
+ changes: StateChangeTracker
30
+ ) {
27
31
  let balance = state.balance[event.asset.toString()];
28
32
 
29
33
  if (!balance) {
@@ -41,7 +45,7 @@ export function BalancePatchEventHandler(event: BalancePatchEvent, state: State)
41
45
 
42
46
  state.timestamp = event.timestamp;
43
47
 
44
- return balance;
48
+ changes.commit(balance);
45
49
  }
46
50
 
47
51
  /**
@@ -61,7 +65,11 @@ export class BalanceTransactEvent implements StoreEvent {
61
65
  /**
62
66
  * @see BalanceTransactEvent
63
67
  */
64
- export function BalanceTransactEventHandler(event: BalanceTransactEvent, state: State) {
68
+ export function BalanceTransactEventHandler(
69
+ event: BalanceTransactEvent,
70
+ state: State,
71
+ changes: StateChangeTracker
72
+ ) {
65
73
  let balance = state.balance[event.asset.toString()];
66
74
 
67
75
  if (!balance) {
@@ -77,7 +85,7 @@ export function BalanceTransactEventHandler(event: BalanceTransactEvent, state:
77
85
 
78
86
  state.timestamp = event.timestamp;
79
87
 
80
- return balance;
88
+ changes.commit(balance);
81
89
  }
82
90
 
83
91
  /**
@@ -97,7 +105,11 @@ export class BalanceFreezEvent implements StoreEvent {
97
105
  /**
98
106
  * @see BalanceFreezEvent
99
107
  */
100
- export function BalanceFreezEventHandler(event: BalanceFreezEvent, state: State) {
108
+ export function BalanceFreezEventHandler(
109
+ event: BalanceFreezEvent,
110
+ state: State,
111
+ changes: StateChangeTracker
112
+ ) {
101
113
  const balance = state.balance[event.asset.toString()];
102
114
 
103
115
  if (!balance) {
@@ -109,7 +121,7 @@ export function BalanceFreezEventHandler(event: BalanceFreezEvent, state: State)
109
121
 
110
122
  state.timestamp = event.timestamp;
111
123
 
112
- return balance;
124
+ changes.commit(balance);
113
125
  }
114
126
 
115
127
  /**
@@ -129,7 +141,11 @@ export class BalanceUnfreezEvent implements StoreEvent {
129
141
  /**
130
142
  * @see BalanceUnfreezEvent
131
143
  */
132
- export function BalanceUnfreezEventHandler(event: BalanceUnfreezEvent, state: State) {
144
+ export function BalanceUnfreezEventHandler(
145
+ event: BalanceUnfreezEvent,
146
+ state: State,
147
+ changes: StateChangeTracker
148
+ ) {
133
149
  const balance = state.balance[event.asset.toString()];
134
150
 
135
151
  if (!balance) {
@@ -141,5 +157,5 @@ export function BalanceUnfreezEventHandler(event: BalanceUnfreezEvent, state: St
141
157
 
142
158
  state.timestamp = event.timestamp;
143
159
 
144
- return balance;
160
+ changes.commit(balance);
145
161
  }
@@ -1,7 +1,7 @@
1
1
  import { Asset, Instrument } from '../../domain';
2
- import { CandleEvent, CandleEventHandler } from '.';
3
- import { State } from '../store.state';
2
+ import { CandleEvent } from '.';
4
3
  import { now } from '../../shared';
4
+ import { Store } from '../store';
5
5
 
6
6
  const instrument = new Instrument(
7
7
  new Asset('btc', 'binance', 8),
@@ -12,21 +12,19 @@ const instrument = new Instrument(
12
12
  describe('candle patch event tests', () => {
13
13
  test('should patch trade object', () => {
14
14
  const timestamp = now();
15
- const state = new State();
15
+ const store = new Store();
16
16
 
17
- state.universe.instrument[instrument.toString()] = instrument;
18
- state.subscription.instrument[instrument.toString()] = instrument;
17
+ store.snapshot.universe.instrument[instrument.toString()] = instrument;
18
+ store.snapshot.subscription.instrument[instrument.toString()] = instrument;
19
19
 
20
- const event = new CandleEvent(instrument, 1, 1, 1, 1, 1, 1, timestamp);
20
+ store.dispatch(new CandleEvent(instrument, 1, 1, 1, 1, 1, 1, timestamp));
21
21
 
22
- CandleEventHandler(event, state);
23
-
24
- const trade = state.trade[instrument.toString()];
22
+ const trade = store.snapshot.trade[instrument.toString()];
25
23
 
26
24
  expect(trade.timestamp).toEqual(timestamp);
27
25
  expect(trade.instrument.toString()).toEqual(instrument.toString());
28
26
  expect(trade.rate).toEqual(1);
29
27
  expect(trade.quantity).toEqual(1);
30
- expect(state.timestamp).toEqual(timestamp);
28
+ expect(store.snapshot.timestamp).toEqual(timestamp);
31
29
  });
32
30
  });
@@ -5,6 +5,7 @@ import { InstrumentSelector } from '../../domain';
5
5
  import { State } from '../store.state';
6
6
  import { StoreEvent } from './store.event';
7
7
  import { event } from '../../shared/topic';
8
+ import { StateChangeTracker } from '..';
8
9
 
9
10
  @event
10
11
  export class CandleEvent implements StoreEvent {
@@ -22,36 +23,49 @@ export class CandleEvent implements StoreEvent {
22
23
  ) {}
23
24
  }
24
25
 
25
- export function CandleEventHandler(event: CandleEvent, state: State) {
26
+ export function CandleEventHandler(
27
+ event: CandleEvent,
28
+ state: State,
29
+ changes: StateChangeTracker
30
+ ) {
26
31
  const instrument = state.universe.instrument[event.instrument.toString()];
27
32
 
28
- // patch trade object
29
- const trade = TradePatchEventHandler(
30
- {
31
- type: 'trade-patch',
32
- instrument: event.instrument,
33
- quantity: event.volume,
34
- rate: event.close,
35
- timestamp: event.timestamp
36
- },
37
- state
38
- );
39
-
40
- // patch orderbook by assuming candle close price is mid orderbook price
41
- const orderbook = OrderbookPatchEventHandler(
42
- {
43
- type: 'orderbook-patch',
44
- instrument: event.instrument,
45
- bestAskQuantity: event.volume * 0.5,
46
- bestAskRate: event.close + instrument.quote.tickSize,
47
- bestBidQuantity: event.volume * 0.5,
48
- bestBidRate: event.close - instrument.quote.tickSize,
49
- timestamp: event.timestamp
50
- },
51
- state
52
- );
53
-
54
- state.timestamp = event.timestamp;
55
-
56
- return [trade, orderbook];
33
+ const patch = (timestamp: number, rate: number) => {
34
+ // patch trade object
35
+ TradePatchEventHandler(
36
+ {
37
+ type: 'trade-patch',
38
+ instrument: event.instrument,
39
+ quantity: event.volume,
40
+ rate,
41
+ timestamp
42
+ },
43
+ state,
44
+ changes
45
+ );
46
+
47
+ // patch orderbook by assuming candle close price is mid orderbook price
48
+ OrderbookPatchEventHandler(
49
+ {
50
+ type: 'orderbook-patch',
51
+ instrument: event.instrument,
52
+ bestAskQuantity: instrument.base.fixed(event.volume * 0.5),
53
+ bestAskRate: rate,
54
+ bestBidQuantity: instrument.base.fixed(event.volume * 0.5),
55
+ bestBidRate: rate,
56
+ timestamp
57
+ },
58
+ state,
59
+ changes
60
+ );
61
+
62
+ state.timestamp = event.timestamp;
63
+
64
+ changes.commitPendingChanges();
65
+ };
66
+
67
+ patch(event.timestamp, instrument.quote.fixed(event.open));
68
+ patch(event.timestamp, instrument.quote.fixed(event.high));
69
+ patch(event.timestamp, instrument.quote.fixed(event.low));
70
+ patch(event.timestamp, instrument.quote.fixed(event.close));
57
71
  }
@@ -2,7 +2,7 @@ import { event } from '../../shared/topic';
2
2
  import { timestamp } from '../../shared';
3
3
  import { Asset, Commission } from '../../domain';
4
4
  import { Instrument, InstrumentSelector } from '../../domain/instrument';
5
- import { State } from '../../store';
5
+ import { State, StateChangeTracker } from '../../store';
6
6
  import { StoreEvent } from './store.event';
7
7
 
8
8
  @event
@@ -13,13 +13,17 @@ export class InstrumentPatchEvent implements StoreEvent {
13
13
  readonly timestamp: timestamp,
14
14
  readonly base: Asset,
15
15
  readonly quote: Asset,
16
- readonly commision: Commission,
16
+ readonly commission: Commission,
17
17
  readonly raw: string,
18
18
  readonly leverage?: number
19
19
  ) {}
20
20
  }
21
21
 
22
- export function InstrumentPatchEventHandler(event: InstrumentPatchEvent, state: State) {
22
+ export function InstrumentPatchEventHandler(
23
+ event: InstrumentPatchEvent,
24
+ state: State,
25
+ changes: StateChangeTracker
26
+ ) {
23
27
  const selector = new InstrumentSelector(
24
28
  event.base.name,
25
29
  event.quote.name,
@@ -37,13 +41,13 @@ export function InstrumentPatchEventHandler(event: InstrumentPatchEvent, state:
37
41
  }
38
42
 
39
43
  instrument.timestamp = event.timestamp;
40
- instrument.commission = event.commision;
44
+ instrument.commission = event.commission;
41
45
 
42
46
  if (event.leverage) {
43
47
  instrument.leverage = event.leverage;
44
48
  }
45
49
 
46
- return instrument;
50
+ changes.commit(instrument);
47
51
  }
48
52
 
49
53
  @event
@@ -59,7 +63,8 @@ export class InstrumentSubscriptionEvent implements StoreEvent {
59
63
 
60
64
  export function InstrumentSubscriptionEventHandler(
61
65
  event: InstrumentSubscriptionEvent,
62
- state: State
66
+ state: State,
67
+ changes: StateChangeTracker
63
68
  ) {
64
69
  const instrumentKey = event.instrument.toString();
65
70
 
@@ -75,5 +80,5 @@ export function InstrumentSubscriptionEventHandler(
75
80
  state.subscription.asset[instrument.quote.toString()] = instrument.quote;
76
81
  }
77
82
 
78
- return instrument;
83
+ changes.commit(instrument);
79
84
  }
@@ -1,7 +1,7 @@
1
- import { State } from '../';
2
1
  import { now } from '../../shared';
3
2
  import { Asset, Instrument, Order } from '../../domain';
4
- import { OrderLoadEvent, OrderLoadEventHandler } from './store-order.event';
3
+ import { OrderLoadEvent } from './store-order.event';
4
+ import { Store } from '../store';
5
5
 
6
6
  const instrument = new Instrument(
7
7
  new Asset('btc', 'binance', 8),
@@ -12,17 +12,17 @@ const instrument = new Instrument(
12
12
  describe('order load event tests', () => {
13
13
  test('should load order to store', () => {
14
14
  const timestamp = now();
15
- const state = new State();
15
+ const store = new Store();
16
16
  const order = Order.buyMarket(instrument, 1.0);
17
17
 
18
18
  order.state = 'PENDING';
19
19
 
20
- state.universe.instrument[instrument.toString()] = instrument;
21
- state.subscription.instrument[instrument.toString()] = instrument;
20
+ store.snapshot.universe.instrument[instrument.toString()] = instrument;
21
+ store.snapshot.subscription.instrument[instrument.toString()] = instrument;
22
22
 
23
- OrderLoadEventHandler(new OrderLoadEvent(order, timestamp), state);
23
+ store.dispatch(new OrderLoadEvent(order, timestamp));
24
24
 
25
- expect(Object.keys(state.order.pending).length).toEqual(1);
26
- expect(state.order.pending[order.id]).toEqual(order);
25
+ expect(Object.keys(store.snapshot.order.pending).length).toEqual(1);
26
+ expect(store.snapshot.order.pending[order.id]).toEqual(order);
27
27
  });
28
28
  });
@@ -1,7 +1,7 @@
1
1
  import { event } from '../../shared/topic';
2
2
  import { timestamp } from '../../shared';
3
3
  import { Order } from '../../domain';
4
- import { State } from '../store.state';
4
+ import { State, StateChangeTracker } from '../store.state';
5
5
  import { StoreEvent } from './store.event';
6
6
 
7
7
  @event
@@ -11,7 +11,11 @@ export class OrderLoadEvent implements StoreEvent {
11
11
  constructor(readonly order: Order, readonly timestamp: timestamp) {}
12
12
  }
13
13
 
14
- export function OrderLoadEventHandler(event: OrderLoadEvent, state: State) {
14
+ export function OrderLoadEventHandler(
15
+ event: OrderLoadEvent,
16
+ state: State,
17
+ changes: StateChangeTracker
18
+ ) {
15
19
  event.order.timestamp = event.timestamp;
16
20
 
17
21
  switch (event.order.state) {
@@ -31,7 +35,7 @@ export function OrderLoadEventHandler(event: OrderLoadEvent, state: State) {
31
35
  break;
32
36
  }
33
37
 
34
- return event.order;
38
+ changes.commit(event.order);
35
39
  }
36
40
 
37
41
  @event
@@ -41,7 +45,11 @@ export class OrderNewEvent implements StoreEvent {
41
45
  constructor(readonly order: Order, readonly timestamp: timestamp) {}
42
46
  }
43
47
 
44
- export function OrderNewEventHandler(event: OrderNewEvent, state: State) {
48
+ export function OrderNewEventHandler(
49
+ event: OrderNewEvent,
50
+ state: State,
51
+ changes: StateChangeTracker
52
+ ) {
45
53
  if (event.order.state != 'NEW') {
46
54
  throw new Error(`Order is not new`);
47
55
  }
@@ -51,7 +59,7 @@ export function OrderNewEventHandler(event: OrderNewEvent, state: State) {
51
59
 
52
60
  state.order.pending[event.order.id] = event.order;
53
61
 
54
- return event.order;
62
+ changes.commit(event.order);
55
63
  }
56
64
 
57
65
  @event
@@ -61,7 +69,11 @@ export class OrderPendingEvent implements StoreEvent {
61
69
  constructor(readonly id: string, readonly timestamp: timestamp) {}
62
70
  }
63
71
 
64
- export function OrderPendingEventHandler(event: OrderPendingEvent, state: State) {
72
+ export function OrderPendingEventHandler(
73
+ event: OrderPendingEvent,
74
+ state: State,
75
+ changes: StateChangeTracker
76
+ ) {
65
77
  if (!(event.id in state.order.pending)) {
66
78
  throw new Error(`Trying to patch unknown order: ${event.id}`);
67
79
  }
@@ -75,7 +87,7 @@ export function OrderPendingEventHandler(event: OrderPendingEvent, state: State)
75
87
  order.state = 'PENDING';
76
88
  order.timestamp = event.timestamp;
77
89
 
78
- return order;
90
+ changes.commit(order);
79
91
  }
80
92
 
81
93
  @event
@@ -89,7 +101,11 @@ export class OrderFilledEvent implements StoreEvent {
89
101
  ) {}
90
102
  }
91
103
 
92
- export function OrderFilledEventHandler(event: OrderFilledEvent, state: State) {
104
+ export function OrderFilledEventHandler(
105
+ event: OrderFilledEvent,
106
+ state: State,
107
+ changes: StateChangeTracker
108
+ ) {
93
109
  if (!(event.id in state.order.pending)) {
94
110
  throw new Error(`Trying to patch unknown order: ${event.id}`);
95
111
  }
@@ -109,7 +125,7 @@ export function OrderFilledEventHandler(event: OrderFilledEvent, state: State) {
109
125
 
110
126
  state.order.filled[event.id] = order;
111
127
 
112
- return order;
128
+ changes.commit(order);
113
129
  }
114
130
 
115
131
  @event
@@ -119,7 +135,11 @@ export class OrderCancelingEvent implements StoreEvent {
119
135
  constructor(readonly id: string, readonly timestamp: timestamp) {}
120
136
  }
121
137
 
122
- export function OrderCancelingEventHandler(event: OrderCancelingEvent, state: State) {
138
+ export function OrderCancelingEventHandler(
139
+ event: OrderCancelingEvent,
140
+ state: State,
141
+ changes: StateChangeTracker
142
+ ) {
123
143
  if (!(event.id in state.order.pending)) {
124
144
  throw new Error(`Trying to patch unknown order: ${event.id}`);
125
145
  }
@@ -137,7 +157,7 @@ export function OrderCancelingEventHandler(event: OrderCancelingEvent, state: St
137
157
  order.state = 'CANCELING';
138
158
  order.timestamp = event.timestamp;
139
159
 
140
- return order;
160
+ changes.commit(order);
141
161
  }
142
162
 
143
163
  @event
@@ -147,7 +167,11 @@ export class OrderCanceledEvent implements StoreEvent {
147
167
  constructor(readonly id: string, readonly timestamp: timestamp) {}
148
168
  }
149
169
 
150
- export function OrderCanceledEventHandler(event: OrderCanceledEvent, state: State) {
170
+ export function OrderCanceledEventHandler(
171
+ event: OrderCanceledEvent,
172
+ state: State,
173
+ changes: StateChangeTracker
174
+ ) {
151
175
  const order = state.order.pending[event.id];
152
176
 
153
177
  if (order.state == 'CANCELED') {
@@ -165,7 +189,7 @@ export function OrderCanceledEventHandler(event: OrderCanceledEvent, state: Stat
165
189
 
166
190
  state.order.canceled[event.id] = order;
167
191
 
168
- return order;
192
+ changes.commit(order);
169
193
  }
170
194
 
171
195
  @event
@@ -177,7 +201,8 @@ export class OrderCancelFailedEvent implements StoreEvent {
177
201
 
178
202
  export function OrderCancelFailedEventHandler(
179
203
  event: OrderCancelFailedEvent,
180
- state: State
204
+ state: State,
205
+ changes: StateChangeTracker
181
206
  ) {
182
207
  const order = state.order.pending[event.id];
183
208
 
@@ -188,7 +213,7 @@ export function OrderCancelFailedEventHandler(
188
213
  order.state = 'PENDING';
189
214
  order.timestamp = event.timestamp;
190
215
 
191
- return order;
216
+ changes.commit(order);
192
217
  }
193
218
 
194
219
  @event
@@ -198,7 +223,11 @@ export class OrderRejectedEvent implements StoreEvent {
198
223
  constructor(readonly id: string, readonly timestamp: timestamp) {}
199
224
  }
200
225
 
201
- export function OrderRejectedEventHandler(event: OrderRejectedEvent, state: State) {
226
+ export function OrderRejectedEventHandler(
227
+ event: OrderRejectedEvent,
228
+ state: State,
229
+ changes: StateChangeTracker
230
+ ) {
202
231
  const order = state.order.pending[event.id];
203
232
 
204
233
  if (order.state != 'NEW') {
@@ -212,5 +241,5 @@ export function OrderRejectedEventHandler(event: OrderRejectedEvent, state: Stat
212
241
 
213
242
  state.order.rejected[event.id] = order;
214
243
 
215
- return order;
244
+ changes.commit(order);
216
245
  }
@@ -2,7 +2,7 @@ import { event } from '../../shared/topic';
2
2
  import { timestamp } from '../../shared';
3
3
  import { InstrumentSelector } from '../../domain/instrument';
4
4
  import { Orderbook } from '../../domain/orderbook';
5
- import { State } from '../store.state';
5
+ import { State, StateChangeTracker } from '../store.state';
6
6
  import { StoreEvent } from './store.event';
7
7
 
8
8
  @event
@@ -19,7 +19,11 @@ export class OrderbookPatchEvent implements StoreEvent {
19
19
  ) {}
20
20
  }
21
21
 
22
- export function OrderbookPatchEventHandler(event: OrderbookPatchEvent, state: State) {
22
+ export function OrderbookPatchEventHandler(
23
+ event: OrderbookPatchEvent,
24
+ state: State,
25
+ changes: StateChangeTracker
26
+ ) {
23
27
  const instrumentKey = event.instrument.toString();
24
28
 
25
29
  if (!(instrumentKey in state.subscription.instrument)) {
@@ -62,5 +66,5 @@ export function OrderbookPatchEventHandler(event: OrderbookPatchEvent, state: St
62
66
  }
63
67
  }
64
68
 
65
- return orderbook;
69
+ changes.commit(orderbook);
66
70
  }