@quantform/core 0.3.243 → 0.3.251

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 (39) hide show
  1. package/dist/adapter/adapter-aggregate.d.ts +11 -5
  2. package/dist/adapter/adapter-aggregate.js +38 -8
  3. package/dist/adapter/adapter-aggregate.js.map +1 -1
  4. package/dist/adapter/backtester/backtester-adapter.d.ts +1 -2
  5. package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
  6. package/dist/adapter/backtester/backtester-streamer.d.ts +2 -1
  7. package/dist/adapter/backtester/backtester-streamer.js +8 -7
  8. package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
  9. package/dist/adapter/backtester/backtester-streamer.spec.js +9 -10
  10. package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -1
  11. package/dist/bin.d.ts +3 -4
  12. package/dist/bin.js +6 -6
  13. package/dist/bin.js.map +1 -1
  14. package/dist/ipc.d.ts +6 -12
  15. package/dist/ipc.js +71 -55
  16. package/dist/ipc.js.map +1 -1
  17. package/dist/ipc.spec.js +9 -9
  18. package/dist/ipc.spec.js.map +1 -1
  19. package/dist/session/session-descriptor.d.ts +6 -3
  20. package/dist/session/session.d.ts +1 -1
  21. package/dist/session/session.js +9 -25
  22. package/dist/session/session.js.map +1 -1
  23. package/dist/session/session.spec.js +1 -5
  24. package/dist/session/session.spec.js.map +1 -1
  25. package/dist/tests/backtester-adapter.spec.js +7 -8
  26. package/dist/tests/backtester-adapter.spec.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +1 -1
  29. package/src/adapter/adapter-aggregate.ts +110 -13
  30. package/src/adapter/backtester/backtester-adapter.ts +2 -3
  31. package/src/adapter/backtester/backtester-streamer.spec.ts +10 -6
  32. package/src/adapter/backtester/backtester-streamer.ts +8 -7
  33. package/src/bin.ts +21 -11
  34. package/src/ipc.spec.ts +9 -8
  35. package/src/ipc.ts +97 -90
  36. package/src/session/session-descriptor.ts +7 -4
  37. package/src/session/session.spec.ts +1 -5
  38. package/src/session/session.ts +9 -48
  39. package/src/tests/backtester-adapter.spec.ts +11 -7
@@ -4,8 +4,15 @@ import { Logger } from '../shared';
4
4
  import {
5
5
  AdapterAccountCommand,
6
6
  AdapterAwakeCommand,
7
- AdapterDisposeCommand
7
+ AdapterDisposeCommand,
8
+ AdapterFeedCommand,
9
+ AdapterHistoryQuery,
10
+ AdapterOrderCancelCommand,
11
+ AdapterOrderOpenCommand,
12
+ AdapterSubscribeCommand
8
13
  } from './adapter.event';
14
+ import { InstrumentSelector, Order, Candle } from '../domain';
15
+ import { Feed } from './../storage';
9
16
 
10
17
  /**
11
18
  * Manages instances of all adapters provided in session descriptor.
@@ -14,21 +21,26 @@ import {
14
21
  export class AdapterAggregate {
15
22
  private readonly adapter: Record<string, Adapter> = {};
16
23
 
17
- constructor(private readonly store: Store, adapters: Adapter[]) {
24
+ constructor(adapters: Adapter[], private readonly store: Store) {
18
25
  adapters.forEach(it => (this.adapter[it.name] = it));
19
26
  }
20
27
 
28
+ /**
29
+ * Returns adapter by name.
30
+ * @param adapterName adapter name.
31
+ * @returns
32
+ */
33
+ get(adapterName: string): Adapter {
34
+ return this.adapter[adapterName];
35
+ }
36
+
21
37
  /**
22
38
  * Sets up all adapters.
23
- * @param usePrivateScope use private api (api keys needed).
24
39
  */
25
- async awake(usePrivateScope = true): Promise<void> {
40
+ async awake(): Promise<void> {
26
41
  for (const adapter in this.adapter) {
27
42
  await this.dispatch(adapter, new AdapterAwakeCommand());
28
-
29
- if (usePrivateScope) {
30
- await this.dispatch(adapter, new AdapterAccountCommand());
31
- }
43
+ await this.dispatch(adapter, new AdapterAccountCommand());
32
44
  }
33
45
  }
34
46
 
@@ -41,17 +53,102 @@ export class AdapterAggregate {
41
53
  }
42
54
  }
43
55
 
56
+ /**
57
+ * Subscribe to collection of instruments.
58
+ * @param selectors
59
+ */
60
+ async subscribe(selectors: InstrumentSelector[]): Promise<void> {
61
+ const grouped = selectors
62
+ .filter(it => it != null)
63
+ .reduce((aggregate, it) => {
64
+ const adapter = it.base.adapter;
65
+
66
+ if (aggregate[adapter]) {
67
+ aggregate[adapter].push(it);
68
+ } else {
69
+ aggregate[adapter] = [it];
70
+ }
71
+
72
+ return aggregate;
73
+ }, {});
74
+
75
+ for (const adapterName in grouped) {
76
+ await this.dispatch(adapterName, new AdapterSubscribeCommand(grouped[adapterName]));
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Opens new order.
82
+ * @param order an order to open.
83
+ */
84
+ open(order: Order): Promise<void> {
85
+ return this.dispatch<AdapterOrderOpenCommand, void>(
86
+ order.instrument.base.adapter,
87
+ new AdapterOrderOpenCommand(order)
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Cancels specific order.
93
+ */
94
+ cancel(order: Order): Promise<void> {
95
+ return this.dispatch(
96
+ order.instrument.base.adapter,
97
+ new AdapterOrderCancelCommand(order)
98
+ );
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param selector Returns collection of candles for specific history.
104
+ * @param timeframe
105
+ * @param length
106
+ * @returns
107
+ */
108
+ history(
109
+ selector: InstrumentSelector,
110
+ timeframe: number,
111
+ length: number
112
+ ): Promise<Candle[]> {
113
+ return this.dispatch<AdapterHistoryQuery, Candle[]>(
114
+ selector.base.adapter,
115
+ new AdapterHistoryQuery(selector, timeframe, length)
116
+ );
117
+ }
118
+
119
+ /**
120
+ * Feeds a storage with historical instrument data.
121
+ * @param selector
122
+ * @param from
123
+ * @param to
124
+ * @param destination
125
+ * @param callback
126
+ * @returns
127
+ */
128
+ feed(
129
+ selector: InstrumentSelector,
130
+ from: number,
131
+ to: number,
132
+ destination: Feed,
133
+ callback: (timestamp: number) => void
134
+ ): Promise<void> {
135
+ return this.dispatch(
136
+ selector.base.adapter,
137
+ new AdapterFeedCommand(selector, from, to, destination, callback)
138
+ );
139
+ }
140
+
44
141
  /**
45
142
  * Routes and executes command to a specific adapter.
46
143
  * @param adapterName name of adapter
47
- * @param event
144
+ * @param command
48
145
  * @returns
49
146
  */
50
- dispatch<TEvent extends { type: string }, TResponse>(
147
+ private dispatch<TCommand extends { type: string }, TResponse>(
51
148
  adapterName: string,
52
- event: TEvent
149
+ command: TCommand
53
150
  ): Promise<TResponse> {
54
- const adapter = this.adapter[adapterName];
151
+ const adapter = this.get(adapterName);
55
152
 
56
153
  if (!adapter) {
57
154
  throw new Error(
@@ -60,7 +157,7 @@ export class AdapterAggregate {
60
157
  }
61
158
 
62
159
  try {
63
- return adapter.dispatch(event, new AdapterContext(adapter, this.store));
160
+ return adapter.dispatch(command, new AdapterContext(adapter, this.store));
64
161
  } catch (e) {
65
162
  Logger.error(e);
66
163
  }
@@ -1,15 +1,14 @@
1
1
  import { Adapter, AdapterContext } from '..';
2
- import { BacktesterListener, BacktesterStreamer } from './backtester-streamer';
2
+ import { BacktesterStreamer } from './backtester-streamer';
3
3
  import { PaperAdapter, PaperOptions } from '../paper';
4
4
  import { handler } from '../../shared/topic';
5
- import { Logger, timestamp } from '../../shared';
5
+ import { timestamp } from '../../shared';
6
6
  import { AdapterSubscribeCommand, AdapterHistoryQuery } from '../adapter.event';
7
7
  import { InstrumentSubscriptionEvent } from '../../store/event';
8
8
 
9
9
  export class BacktesterOptions extends PaperOptions {
10
10
  from: timestamp;
11
11
  to: timestamp;
12
- listener?: BacktesterListener;
13
12
  }
14
13
 
15
14
  export class BacktesterAdapter extends Adapter {
@@ -18,11 +18,15 @@ describe('backtester streamer tests', () => {
18
18
  store.snapshot.universe.instrument[instrument.toString()] = instrument;
19
19
  store.snapshot.subscription.instrument[instrument.toString()] = instrument;
20
20
 
21
- const streamer = new BacktesterStreamer(store, feed, {
22
- balance: {},
23
- from: 0,
24
- to: 10,
25
- listener: {
21
+ const streamer = new BacktesterStreamer(
22
+ store,
23
+ feed,
24
+ {
25
+ balance: {},
26
+ from: 0,
27
+ to: 10
28
+ },
29
+ {
26
30
  onBacktestCompleted: () => {
27
31
  const trade = store.snapshot.trade[instrument.toString()];
28
32
 
@@ -34,7 +38,7 @@ describe('backtester streamer tests', () => {
34
38
  done();
35
39
  }
36
40
  }
37
- });
41
+ );
38
42
 
39
43
  feed
40
44
  .save(instrument, [
@@ -36,7 +36,8 @@ export class BacktesterStreamer {
36
36
  constructor(
37
37
  private readonly store: Store,
38
38
  private readonly feed: Feed,
39
- private readonly options: BacktesterOptions
39
+ private readonly options: BacktesterOptions,
40
+ private readonly listener?: BacktesterListener
40
41
  ) {
41
42
  this.timestamp = this.options.from;
42
43
  }
@@ -73,15 +74,15 @@ export class BacktesterStreamer {
73
74
  }
74
75
 
75
76
  if (this.sequence == 0) {
76
- if (this.options.listener.onBacktestStarted) {
77
- this.options.listener.onBacktestStarted(this);
77
+ if (this.listener.onBacktestStarted) {
78
+ this.listener.onBacktestStarted(this);
78
79
  }
79
80
  }
80
81
 
81
82
  while (await this.processNext()) {
82
83
  if (this.sequence % this.sequenceUpdateBatch == 0) {
83
- if (this.options.listener.onBacktestUpdated) {
84
- this.options.listener.onBacktestUpdated(this);
84
+ if (this.listener.onBacktestUpdated) {
85
+ this.listener.onBacktestUpdated(this);
85
86
  }
86
87
  }
87
88
 
@@ -90,8 +91,8 @@ export class BacktesterStreamer {
90
91
  }
91
92
  }
92
93
 
93
- if (this.options.listener.onBacktestCompleted) {
94
- this.options.listener.onBacktestCompleted(this);
94
+ if (this.listener.onBacktestCompleted) {
95
+ this.listener.onBacktestCompleted(this);
95
96
  }
96
97
  }
97
98
 
package/src/bin.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  BacktesterAdapter,
3
- BacktesterOptions,
3
+ BacktesterListener,
4
4
  BacktesterStreamer
5
5
  } from './adapter/backtester';
6
6
  import { AdapterAggregate } from './adapter';
7
- import { PaperAdapter, PaperOptions } from './adapter/paper';
7
+ import { PaperAdapter } from './adapter/paper';
8
8
  import { Session, SessionDescriptor } from './session';
9
9
  import { Store } from './store';
10
10
 
@@ -16,16 +16,26 @@ import { Store } from './store';
16
16
  */
17
17
  export function backtest(
18
18
  descriptor: SessionDescriptor,
19
- options: BacktesterOptions
19
+ listener?: BacktesterListener
20
20
  ): [Session, BacktesterStreamer] {
21
21
  const store = new Store();
22
22
 
23
- const streamer = new BacktesterStreamer(store, descriptor.feed, options);
24
- const aggregate = new AdapterAggregate(
23
+ const streamer = new BacktesterStreamer(
25
24
  store,
25
+ descriptor.feed,
26
+ descriptor.options.backtester,
27
+ listener
28
+ );
29
+ const aggregate = new AdapterAggregate(
26
30
  descriptor.adapter.map(
27
- it => new PaperAdapter(new BacktesterAdapter(it, streamer), store, options)
28
- )
31
+ it =>
32
+ new PaperAdapter(
33
+ new BacktesterAdapter(it, streamer),
34
+ store,
35
+ descriptor.options.backtester
36
+ )
37
+ ),
38
+ store
29
39
  );
30
40
 
31
41
  return [new Session(store, aggregate, descriptor), streamer];
@@ -37,12 +47,12 @@ export function backtest(
37
47
  * @param options backtest options.
38
48
  * @returns new session object.
39
49
  */
40
- export function paper(descriptor: SessionDescriptor, options: PaperOptions): Session {
50
+ export function paper(descriptor: SessionDescriptor): Session {
41
51
  const store = new Store();
42
52
 
43
53
  const aggregate = new AdapterAggregate(
44
- store,
45
- descriptor.adapter.map(it => new PaperAdapter(it, store, options))
54
+ descriptor.adapter.map(it => new PaperAdapter(it, store, descriptor.options.paper)),
55
+ store
46
56
  );
47
57
 
48
58
  return new Session(store, aggregate, descriptor);
@@ -55,7 +65,7 @@ export function paper(descriptor: SessionDescriptor, options: PaperOptions): Ses
55
65
  */
56
66
  export function live(descriptor: SessionDescriptor): Session {
57
67
  const store = new Store();
58
- const aggregate = new AdapterAggregate(store, descriptor.adapter);
68
+ const aggregate = new AdapterAggregate(descriptor.adapter, store);
59
69
 
60
70
  return new Session(store, aggregate, descriptor);
61
71
  }
package/src/ipc.spec.ts CHANGED
@@ -55,8 +55,7 @@ describe('ipc feed tests', () => {
55
55
  const session = await run(
56
56
  {
57
57
  adapter: [new DefaultAdapter()],
58
- feed: new Feed(new InMemoryStorage()),
59
- describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
58
+ feed: new Feed(new InMemoryStorage())
60
59
  },
61
60
  command
62
61
  );
@@ -66,8 +65,7 @@ describe('ipc feed tests', () => {
66
65
 
67
66
  test('should dispatch session started event', done => {
68
67
  const command = {
69
- type: 'paper',
70
- balance: { 'default:usd': 100 }
68
+ type: 'paper'
71
69
  };
72
70
 
73
71
  const ipcSub = new EventEmitter();
@@ -80,8 +78,12 @@ describe('ipc feed tests', () => {
80
78
  run(
81
79
  {
82
80
  adapter: [new DefaultAdapter()],
83
- describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt')),
84
- ipcSub
81
+ ipcSub,
82
+ options: {
83
+ paper: {
84
+ balance: { 'default:usd': 100 }
85
+ }
86
+ }
85
87
  },
86
88
  command
87
89
  );
@@ -102,8 +104,7 @@ describe('ipc feed tests', () => {
102
104
 
103
105
  run(
104
106
  {
105
- adapter: [new DefaultAdapter()],
106
- describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
107
+ adapter: [new DefaultAdapter()]
107
108
  },
108
109
  command
109
110
  );
package/src/ipc.ts CHANGED
@@ -1,11 +1,12 @@
1
- import { AdapterFeedCommand } from './adapter';
2
1
  import { Session, SessionDescriptor } from './session';
3
2
  import { instrumentOf } from './domain';
4
3
  import { Topic, event, handler } from './shared/topic';
5
4
  import { runTask, Logger } from './shared';
6
5
  import { backtest, live, paper } from './bin';
7
6
  import { BacktesterStreamer } from './adapter/backtester';
7
+ import { Observable } from 'rxjs';
8
8
  import { EventEmitter } from 'events';
9
+ import { join } from 'path';
9
10
  import minimist = require('minimist');
10
11
  import dotenv = require('dotenv');
11
12
 
@@ -46,12 +47,6 @@ export class IpcPaperCommand implements IpcCommand {
46
47
  * The optional session identifier.
47
48
  */
48
49
  id?: number;
49
-
50
- /**
51
- * Specifies trading balance, for example:
52
- * { "binance:usdt": 1000 }
53
- */
54
- balance: { [key: string]: number };
55
50
  }
56
51
 
57
52
  /**
@@ -62,20 +57,14 @@ export class IpcBacktestCommand implements IpcCommand {
62
57
  type = 'backtest';
63
58
 
64
59
  /**
65
- * Start date of the backtest in unix timestamp.
66
- */
67
- from: number;
68
-
69
- /**
70
- * Due date of the backtest in unix timestamp.
60
+ * Start date of the feed in unix timestamp.
71
61
  */
72
- to: number;
62
+ from?: number;
73
63
 
74
64
  /**
75
- * Specifies trading balance, for example:
76
- * { "binance:usdt": 1000 }
65
+ * Due date of the feed in unix timestamp.
77
66
  */
78
- balance: { [key: string]: number };
67
+ to?: number;
79
68
  }
80
69
 
81
70
  /**
@@ -93,12 +82,12 @@ export class IpcFeedCommand implements IpcCommand {
93
82
  /**
94
83
  * Start date of the feed in unix timestamp.
95
84
  */
96
- from: number;
85
+ from?: number;
97
86
 
98
87
  /**
99
88
  * Due date of the feed in unix timestamp.
100
89
  */
101
- to: number;
90
+ to?: number;
102
91
  }
103
92
 
104
93
  /**
@@ -121,13 +110,13 @@ class IpcSessionAccessor {
121
110
  session: Session;
122
111
  }
123
112
 
124
- export declare type SessionRunDescriptor = SessionDescriptor & { ipcSub?: EventEmitter };
113
+ export declare type IpcSessionDescriptor = SessionDescriptor & { ipcSub?: EventEmitter };
125
114
 
126
115
  /**
127
116
  * Inter process communication handler.
128
117
  */
129
118
  class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
130
- constructor(private readonly descriptor: SessionRunDescriptor) {
119
+ constructor(private readonly descriptor: IpcSessionDescriptor) {
131
120
  super();
132
121
  }
133
122
 
@@ -142,12 +131,12 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
142
131
 
143
132
  accessor.session = live(this.descriptor);
144
133
 
145
- this.notify({
134
+ this.emit({
146
135
  type: 'live:started',
147
136
  session: accessor.session.descriptor?.id
148
137
  });
149
138
 
150
- await accessor.session.awake();
139
+ await accessor.session.awake(this.describe());
151
140
  }
152
141
 
153
142
  /**
@@ -159,16 +148,14 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
159
148
  this.descriptor.id = command.id;
160
149
  }
161
150
 
162
- accessor.session = paper(this.descriptor, {
163
- balance: command.balance
164
- });
151
+ accessor.session = paper(this.descriptor);
165
152
 
166
- this.notify({
153
+ this.emit({
167
154
  type: 'paper:started',
168
155
  session: accessor.session.descriptor?.id
169
156
  });
170
157
 
171
- await accessor.session.awake();
158
+ await accessor.session.awake(this.describe());
172
159
  }
173
160
 
174
161
  /**
@@ -176,49 +163,51 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
176
163
  */
177
164
  @handler(IpcBacktestCommand)
178
165
  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
+ }
172
+
179
173
  return new Promise<void>(async resolve => {
180
174
  const [session, streamer] = backtest(this.descriptor, {
181
- from: command.from,
182
- to: command.to,
183
- balance: command.balance,
184
- listener: {
185
- onBacktestStarted: (streamer: BacktesterStreamer) => {
186
- this.notify({
187
- type: 'backtest:started',
188
- session: session.descriptor?.id,
189
- timestamp: streamer.timestamp,
190
- from: command.from,
191
- to: command.to
192
- });
193
- },
194
- onBacktestUpdated: (streamer: BacktesterStreamer) => {
195
- this.notify({
196
- type: 'backtest:updated',
197
- session: session.descriptor?.id,
198
- timestamp: streamer.timestamp,
199
- from: command.from,
200
- to: command.to
201
- });
202
- },
203
- onBacktestCompleted: async (streamer: BacktesterStreamer) => {
204
- await accessor.session.dispose();
205
-
206
- this.notify({
207
- type: 'backtest:completed',
208
- session: session.descriptor?.id,
209
- timestamp: streamer.timestamp,
210
- from: command.from,
211
- to: command.to
212
- });
213
-
214
- resolve();
215
- }
175
+ onBacktestStarted: (streamer: BacktesterStreamer) => {
176
+ this.emit({
177
+ type: 'backtest:started',
178
+ session: session.descriptor?.id,
179
+ timestamp: streamer.timestamp,
180
+ from: this.descriptor.options.backtester.from,
181
+ to: this.descriptor.options.backtester.to
182
+ });
183
+ },
184
+ onBacktestUpdated: (streamer: BacktesterStreamer) => {
185
+ this.emit({
186
+ type: 'backtest:updated',
187
+ session: session.descriptor?.id,
188
+ timestamp: streamer.timestamp,
189
+ from: this.descriptor.options.backtester.from,
190
+ to: this.descriptor.options.backtester.to
191
+ });
192
+ },
193
+ onBacktestCompleted: async (streamer: BacktesterStreamer) => {
194
+ await accessor.session.dispose();
195
+
196
+ this.emit({
197
+ type: 'backtest:completed',
198
+ session: session.descriptor?.id,
199
+ timestamp: streamer.timestamp,
200
+ from: this.descriptor.options.backtester.from,
201
+ to: this.descriptor.options.backtester.to
202
+ });
203
+
204
+ resolve();
216
205
  }
217
206
  });
218
207
 
219
208
  accessor.session = session;
220
209
 
221
- await accessor.session.awake();
210
+ await accessor.session.awake(this.describe());
222
211
  await streamer.tryContinue().catch(it => Logger.error(it));
223
212
  });
224
213
  }
@@ -228,31 +217,38 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
228
217
  */
229
218
  @handler(IpcFeedCommand)
230
219
  async onFeed(command: IpcFeedCommand, accessor: IpcSessionAccessor) {
231
- accessor.session = accessor.session ?? live(this.descriptor);
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);
232
231
  const instrument = instrumentOf(command.instrument);
233
232
 
234
- await accessor.session.awake(true);
235
-
236
- this.notify({ type: 'feed:started' });
237
-
238
- await accessor.session.aggregate.dispatch(
239
- instrument.base.adapter,
240
- new AdapterFeedCommand(
241
- instrument,
242
- command.from,
243
- command.to,
244
- this.descriptor.feed,
245
- timestamp =>
246
- this.notify({
247
- type: 'feed:updated',
248
- timestamp,
249
- from: command.from,
250
- to: command.to
251
- })
252
- )
233
+ await accessor.session.awake(undefined);
234
+
235
+ this.emit({ type: 'feed:started' });
236
+
237
+ await accessor.session.aggregate.feed(
238
+ instrument,
239
+ command.from,
240
+ command.to,
241
+ this.descriptor.feed,
242
+ timestamp =>
243
+ this.emit({
244
+ type: 'feed:updated',
245
+ timestamp,
246
+ from: command.from,
247
+ to: command.to
248
+ })
253
249
  );
254
250
 
255
- this.notify({ type: 'feed:completed' });
251
+ this.emit({ type: 'feed:completed' });
256
252
 
257
253
  await accessor.session.dispose();
258
254
  }
@@ -264,9 +260,9 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
264
260
  async onTask(query: IpcTaskCommand, accessor: IpcSessionAccessor) {
265
261
  accessor.session = accessor.session ?? live(this.descriptor);
266
262
 
267
- await accessor.session.awake(true);
263
+ await accessor.session.awake(undefined);
268
264
 
269
- this.notify({ type: 'task:started', taskName: query.taskName });
265
+ this.emit({ type: 'task:started', taskName: query.taskName });
270
266
 
271
267
  let result = undefined;
272
268
 
@@ -276,15 +272,26 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
276
272
  result = e;
277
273
  }
278
274
 
279
- this.notify({ type: 'task:completed', taskName: query.taskName, result });
275
+ this.emit({ type: 'task:completed', taskName: query.taskName, result });
280
276
 
281
277
  await accessor.session.dispose();
282
278
  }
283
279
 
280
+ describe(): (session: Session) => Observable<any> {
281
+ const pkg = require(join(process.cwd(), 'package.json'));
282
+ const describe = require(join(process.cwd(), pkg.main))?.default;
283
+
284
+ if (describe instanceof Function) {
285
+ return describe;
286
+ }
287
+
288
+ return undefined;
289
+ }
290
+
284
291
  /**
285
292
  * Sends a message to parent process.
286
293
  */
287
- private notify(message: any) {
294
+ private emit(message: any) {
288
295
  if (process.send) {
289
296
  process.send(message);
290
297
  }
@@ -300,7 +307,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
300
307
  * @returns new session.
301
308
  */
302
309
  export async function run(
303
- descriptor: SessionRunDescriptor,
310
+ descriptor: IpcSessionDescriptor,
304
311
  ...commands: IpcCommand[]
305
312
  ): Promise<Session> {
306
313
  const handler = new IpcHandler(descriptor);