@quantform/core 0.3.246 → 0.3.252
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/dist/adapter/adapter-aggregate.d.ts +11 -5
- package/dist/adapter/adapter-aggregate.js +38 -8
- package/dist/adapter/adapter-aggregate.js.map +1 -1
- package/dist/adapter/backtester/backtester-streamer.js +3 -0
- package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
- package/dist/bootstrap.d.ts +11 -0
- package/dist/bootstrap.js +57 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc.d.ts +2 -0
- package/dist/ipc.js +34 -36
- package/dist/ipc.js.map +1 -1
- package/dist/session/session.js +6 -21
- package/dist/session/session.js.map +1 -1
- package/dist/session/session.spec.js +2 -2
- package/dist/session/session.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/adapter/adapter-aggregate.ts +110 -13
- package/src/adapter/backtester/backtester-streamer.ts +4 -0
- package/src/bootstrap.ts +100 -0
- package/src/index.ts +1 -1
- package/src/ipc.ts +56 -51
- package/src/session/session.spec.ts +2 -4
- package/src/session/session.ts +6 -45
- package/dist/bin.d.ts +0 -5
- package/dist/bin.js +0 -28
- package/dist/bin.js.map +0 -1
- package/src/bin.ts +0 -71
|
@@ -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
|
|
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(
|
|
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
|
|
144
|
+
* @param command
|
|
48
145
|
* @returns
|
|
49
146
|
*/
|
|
50
|
-
dispatch<
|
|
147
|
+
private dispatch<TCommand extends { type: string }, TResponse>(
|
|
51
148
|
adapterName: string,
|
|
52
|
-
|
|
149
|
+
command: TCommand
|
|
53
150
|
): Promise<TResponse> {
|
|
54
|
-
const adapter = this.
|
|
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(
|
|
160
|
+
return adapter.dispatch(command, new AdapterContext(adapter, this.store));
|
|
64
161
|
} catch (e) {
|
|
65
162
|
Logger.error(e);
|
|
66
163
|
}
|
|
@@ -39,6 +39,10 @@ export class BacktesterStreamer {
|
|
|
39
39
|
private readonly options: BacktesterOptions,
|
|
40
40
|
private readonly listener?: BacktesterListener
|
|
41
41
|
) {
|
|
42
|
+
if (options.from == undefined || options.to == undefined) {
|
|
43
|
+
throw new Error('invalid backtest options, please provide from and to period.');
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
this.timestamp = this.options.from;
|
|
43
47
|
}
|
|
44
48
|
|
package/src/bootstrap.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BacktesterAdapter,
|
|
3
|
+
BacktesterListener,
|
|
4
|
+
BacktesterStreamer
|
|
5
|
+
} from './adapter/backtester';
|
|
6
|
+
import { AdapterAggregate } from './adapter';
|
|
7
|
+
import { PaperAdapter } from './adapter/paper';
|
|
8
|
+
import { Session, SessionDescriptor } from './session';
|
|
9
|
+
import { Store } from './store';
|
|
10
|
+
|
|
11
|
+
export class Bootstrap {
|
|
12
|
+
constructor(readonly descriptor: SessionDescriptor) {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set session id.
|
|
16
|
+
* @param id session id.
|
|
17
|
+
*/
|
|
18
|
+
useSessionId(id?: number): Bootstrap {
|
|
19
|
+
if (id) {
|
|
20
|
+
this.descriptor.id = id;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @param from
|
|
29
|
+
* @param to
|
|
30
|
+
*/
|
|
31
|
+
useBacktestPeriod(from?: number, to?: number): Bootstrap {
|
|
32
|
+
if (from) {
|
|
33
|
+
this.descriptor.options.backtester.from = from;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (to) {
|
|
37
|
+
this.descriptor.options.backtester.to = to;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Starts a new backtest session.
|
|
45
|
+
* @param listener backtest event listener.
|
|
46
|
+
* @returns new session object.
|
|
47
|
+
*/
|
|
48
|
+
backtest(listener?: BacktesterListener): [Session, BacktesterStreamer] {
|
|
49
|
+
const store = new Store();
|
|
50
|
+
const { feed } = this.descriptor;
|
|
51
|
+
const { backtester } = this.descriptor.options;
|
|
52
|
+
|
|
53
|
+
const streamer = new BacktesterStreamer(store, feed, backtester, listener);
|
|
54
|
+
const aggregate = new AdapterAggregate(
|
|
55
|
+
this.descriptor.adapter.map(
|
|
56
|
+
it => new PaperAdapter(new BacktesterAdapter(it, streamer), store, backtester)
|
|
57
|
+
),
|
|
58
|
+
store
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return [new Session(store, aggregate, this.descriptor), streamer];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Starts a new paper session.
|
|
66
|
+
* @returns new session object.
|
|
67
|
+
*/
|
|
68
|
+
paper(): Session {
|
|
69
|
+
if (!this.descriptor.options) {
|
|
70
|
+
this.descriptor.options = {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!this.descriptor.options.paper) {
|
|
74
|
+
this.descriptor.options.paper = {
|
|
75
|
+
balance: {}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const store = new Store();
|
|
80
|
+
const { paper } = this.descriptor.options;
|
|
81
|
+
|
|
82
|
+
const aggregate = new AdapterAggregate(
|
|
83
|
+
this.descriptor.adapter.map(it => new PaperAdapter(it, store, paper)),
|
|
84
|
+
store
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return new Session(store, aggregate, this.descriptor);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Starts a new live session.
|
|
92
|
+
* @returns new session object.
|
|
93
|
+
*/
|
|
94
|
+
live(): Session {
|
|
95
|
+
const store = new Store();
|
|
96
|
+
const aggregate = new AdapterAggregate(this.descriptor.adapter, store);
|
|
97
|
+
|
|
98
|
+
return new Session(store, aggregate, this.descriptor);
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/index.ts
CHANGED
package/src/ipc.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
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
|
-
import {
|
|
5
|
+
import { Bootstrap } from './bootstrap';
|
|
7
6
|
import { BacktesterStreamer } from './adapter/backtester';
|
|
8
7
|
import { Observable } from 'rxjs';
|
|
9
8
|
import { EventEmitter } from 'events';
|
|
@@ -56,6 +55,16 @@ export class IpcPaperCommand implements IpcCommand {
|
|
|
56
55
|
@event
|
|
57
56
|
export class IpcBacktestCommand implements IpcCommand {
|
|
58
57
|
type = 'backtest';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Start date of the feed in unix timestamp.
|
|
61
|
+
*/
|
|
62
|
+
from?: number;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Due date of the feed in unix timestamp.
|
|
66
|
+
*/
|
|
67
|
+
to?: number;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
/**
|
|
@@ -107,7 +116,10 @@ export declare type IpcSessionDescriptor = SessionDescriptor & { ipcSub?: EventE
|
|
|
107
116
|
* Inter process communication handler.
|
|
108
117
|
*/
|
|
109
118
|
class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
110
|
-
constructor(
|
|
119
|
+
constructor(
|
|
120
|
+
private readonly bootstrap: Bootstrap,
|
|
121
|
+
private readonly ipcSub?: EventEmitter
|
|
122
|
+
) {
|
|
111
123
|
super();
|
|
112
124
|
}
|
|
113
125
|
|
|
@@ -116,13 +128,9 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
116
128
|
*/
|
|
117
129
|
@handler(IpcLiveCommand)
|
|
118
130
|
async onLiveMode(command: IpcLiveCommand, accessor: IpcSessionAccessor) {
|
|
119
|
-
|
|
120
|
-
this.descriptor.id = command.id;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
accessor.session = live(this.descriptor);
|
|
131
|
+
accessor.session = this.bootstrap.useSessionId(command.id).live();
|
|
124
132
|
|
|
125
|
-
this.
|
|
133
|
+
this.emit({
|
|
126
134
|
type: 'live:started',
|
|
127
135
|
session: accessor.session.descriptor?.id
|
|
128
136
|
});
|
|
@@ -135,13 +143,9 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
135
143
|
*/
|
|
136
144
|
@handler(IpcPaperCommand)
|
|
137
145
|
async onPaperMode(command: IpcPaperCommand, accessor: IpcSessionAccessor) {
|
|
138
|
-
|
|
139
|
-
this.descriptor.id = command.id;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
accessor.session = paper(this.descriptor);
|
|
146
|
+
accessor.session = this.bootstrap.useSessionId(command.id).paper();
|
|
143
147
|
|
|
144
|
-
this.
|
|
148
|
+
this.emit({
|
|
145
149
|
type: 'paper:started',
|
|
146
150
|
session: accessor.session.descriptor?.id
|
|
147
151
|
});
|
|
@@ -154,35 +158,38 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
154
158
|
*/
|
|
155
159
|
@handler(IpcBacktestCommand)
|
|
156
160
|
onBacktestMode(command: IpcBacktestCommand, accessor: IpcSessionAccessor) {
|
|
161
|
+
this.bootstrap.useBacktestPeriod(command.from, command.to).backtest();
|
|
162
|
+
const { from, to } = this.bootstrap.descriptor.options.backtester;
|
|
163
|
+
|
|
157
164
|
return new Promise<void>(async resolve => {
|
|
158
|
-
const [session, streamer] = backtest(
|
|
165
|
+
const [session, streamer] = this.bootstrap.backtest({
|
|
159
166
|
onBacktestStarted: (streamer: BacktesterStreamer) => {
|
|
160
|
-
this.
|
|
167
|
+
this.emit({
|
|
161
168
|
type: 'backtest:started',
|
|
162
169
|
session: session.descriptor?.id,
|
|
163
170
|
timestamp: streamer.timestamp,
|
|
164
|
-
from
|
|
165
|
-
to
|
|
171
|
+
from,
|
|
172
|
+
to
|
|
166
173
|
});
|
|
167
174
|
},
|
|
168
175
|
onBacktestUpdated: (streamer: BacktesterStreamer) => {
|
|
169
|
-
this.
|
|
176
|
+
this.emit({
|
|
170
177
|
type: 'backtest:updated',
|
|
171
178
|
session: session.descriptor?.id,
|
|
172
179
|
timestamp: streamer.timestamp,
|
|
173
|
-
from
|
|
174
|
-
to
|
|
180
|
+
from,
|
|
181
|
+
to
|
|
175
182
|
});
|
|
176
183
|
},
|
|
177
184
|
onBacktestCompleted: async (streamer: BacktesterStreamer) => {
|
|
178
185
|
await accessor.session.dispose();
|
|
179
186
|
|
|
180
|
-
this.
|
|
187
|
+
this.emit({
|
|
181
188
|
type: 'backtest:completed',
|
|
182
189
|
session: session.descriptor?.id,
|
|
183
190
|
timestamp: streamer.timestamp,
|
|
184
|
-
from
|
|
185
|
-
to
|
|
191
|
+
from,
|
|
192
|
+
to
|
|
186
193
|
});
|
|
187
194
|
|
|
188
195
|
resolve();
|
|
@@ -201,31 +208,29 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
201
208
|
*/
|
|
202
209
|
@handler(IpcFeedCommand)
|
|
203
210
|
async onFeed(command: IpcFeedCommand, accessor: IpcSessionAccessor) {
|
|
204
|
-
accessor.session = accessor.session ??
|
|
211
|
+
accessor.session = accessor.session ?? this.bootstrap.paper();
|
|
205
212
|
const instrument = instrumentOf(command.instrument);
|
|
213
|
+
const { feed } = accessor.session.descriptor;
|
|
206
214
|
|
|
207
215
|
await accessor.session.awake(undefined);
|
|
208
216
|
|
|
209
|
-
this.
|
|
210
|
-
|
|
211
|
-
await accessor.session.aggregate.
|
|
212
|
-
instrument
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
to: command.to
|
|
224
|
-
})
|
|
225
|
-
)
|
|
217
|
+
this.emit({ type: 'feed:started' });
|
|
218
|
+
|
|
219
|
+
await accessor.session.aggregate.feed(
|
|
220
|
+
instrument,
|
|
221
|
+
command.from,
|
|
222
|
+
command.to,
|
|
223
|
+
feed,
|
|
224
|
+
timestamp =>
|
|
225
|
+
this.emit({
|
|
226
|
+
type: 'feed:updated',
|
|
227
|
+
timestamp,
|
|
228
|
+
from: command.from,
|
|
229
|
+
to: command.to
|
|
230
|
+
})
|
|
226
231
|
);
|
|
227
232
|
|
|
228
|
-
this.
|
|
233
|
+
this.emit({ type: 'feed:completed' });
|
|
229
234
|
|
|
230
235
|
await accessor.session.dispose();
|
|
231
236
|
}
|
|
@@ -235,11 +240,11 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
235
240
|
*/
|
|
236
241
|
@handler(IpcTaskCommand)
|
|
237
242
|
async onTask(query: IpcTaskCommand, accessor: IpcSessionAccessor) {
|
|
238
|
-
accessor.session = accessor.session ?? live(
|
|
243
|
+
accessor.session = accessor.session ?? this.bootstrap.live();
|
|
239
244
|
|
|
240
245
|
await accessor.session.awake(undefined);
|
|
241
246
|
|
|
242
|
-
this.
|
|
247
|
+
this.emit({ type: 'task:started', taskName: query.taskName });
|
|
243
248
|
|
|
244
249
|
let result = undefined;
|
|
245
250
|
|
|
@@ -249,14 +254,14 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
249
254
|
result = e;
|
|
250
255
|
}
|
|
251
256
|
|
|
252
|
-
this.
|
|
257
|
+
this.emit({ type: 'task:completed', taskName: query.taskName, result });
|
|
253
258
|
|
|
254
259
|
await accessor.session.dispose();
|
|
255
260
|
}
|
|
256
261
|
|
|
257
262
|
describe(): (session: Session) => Observable<any> {
|
|
258
263
|
const pkg = require(join(process.cwd(), 'package.json'));
|
|
259
|
-
const describe = require(join(process.cwd(), pkg.main));
|
|
264
|
+
const describe = require(join(process.cwd(), pkg.main))?.default;
|
|
260
265
|
|
|
261
266
|
if (describe instanceof Function) {
|
|
262
267
|
return describe;
|
|
@@ -268,12 +273,12 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
268
273
|
/**
|
|
269
274
|
* Sends a message to parent process.
|
|
270
275
|
*/
|
|
271
|
-
private
|
|
276
|
+
private emit(message: any) {
|
|
272
277
|
if (process.send) {
|
|
273
278
|
process.send(message);
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
this.
|
|
281
|
+
this.ipcSub?.emit('message', message);
|
|
277
282
|
}
|
|
278
283
|
}
|
|
279
284
|
|
|
@@ -287,7 +292,7 @@ export async function run(
|
|
|
287
292
|
descriptor: IpcSessionDescriptor,
|
|
288
293
|
...commands: IpcCommand[]
|
|
289
294
|
): Promise<Session> {
|
|
290
|
-
const handler = new IpcHandler(descriptor);
|
|
295
|
+
const handler = new IpcHandler(new Bootstrap(descriptor), descriptor.ipcSub);
|
|
291
296
|
const accessor = new IpcSessionAccessor();
|
|
292
297
|
const argv = minimist(process.argv.slice(2));
|
|
293
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 {
|
|
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 =
|
|
14
|
+
const session = new Bootstrap(descriptor).paper();
|
|
17
15
|
|
|
18
16
|
session.instruments().subscribe({
|
|
19
17
|
next: it => {
|
package/src/session/session.ts
CHANGED
|
@@ -26,12 +26,6 @@ import { AdapterAggregate } from '../adapter/adapter-aggregate';
|
|
|
26
26
|
import { Worker, now } from '../shared';
|
|
27
27
|
import { Trade } from '../domain/trade';
|
|
28
28
|
import { SessionDescriptor } from './session-descriptor';
|
|
29
|
-
import {
|
|
30
|
-
AdapterHistoryQuery,
|
|
31
|
-
AdapterOrderCancelCommand,
|
|
32
|
-
AdapterOrderOpenCommand,
|
|
33
|
-
AdapterSubscribeCommand
|
|
34
|
-
} from '../adapter';
|
|
35
29
|
|
|
36
30
|
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
|
|
37
31
|
|
|
@@ -65,7 +59,7 @@ export class Session {
|
|
|
65
59
|
this.initialized = true;
|
|
66
60
|
|
|
67
61
|
// awake all adapters and synchronize trading accounts with store.
|
|
68
|
-
await this.aggregate.awake(
|
|
62
|
+
await this.aggregate.awake();
|
|
69
63
|
|
|
70
64
|
if (describe) {
|
|
71
65
|
this.subscription = describe(this).subscribe();
|
|
@@ -147,24 +141,8 @@ export class Session {
|
|
|
147
141
|
* Subscribes to specific instrument. Usually forces adapter to subscribe
|
|
148
142
|
* for orderbook and ticker streams.
|
|
149
143
|
*/
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
.filter(it => it != null)
|
|
153
|
-
.reduce((aggregate, it) => {
|
|
154
|
-
const adapter = it.base.adapter;
|
|
155
|
-
|
|
156
|
-
if (aggregate[adapter]) {
|
|
157
|
-
aggregate[adapter].push(it);
|
|
158
|
-
} else {
|
|
159
|
-
aggregate[adapter] = [it];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return aggregate;
|
|
163
|
-
}, {});
|
|
164
|
-
|
|
165
|
-
for (const group in grouped) {
|
|
166
|
-
this.aggregate.dispatch(group, new AdapterSubscribeCommand(grouped[group]));
|
|
167
|
-
}
|
|
144
|
+
subscribe(instrument: Array<InstrumentSelector>): Promise<void> {
|
|
145
|
+
return this.aggregate.subscribe(instrument);
|
|
168
146
|
}
|
|
169
147
|
|
|
170
148
|
/**
|
|
@@ -173,24 +151,14 @@ export class Session {
|
|
|
173
151
|
* session.open(Order.buyMarket(instrument, 100));
|
|
174
152
|
*/
|
|
175
153
|
async open(...orders: Order[]): Promise<void> {
|
|
176
|
-
await Promise.all(
|
|
177
|
-
orders.map(it =>
|
|
178
|
-
this.aggregate.dispatch<AdapterOrderOpenCommand, void>(
|
|
179
|
-
it.instrument.base.adapter,
|
|
180
|
-
new AdapterOrderOpenCommand(it)
|
|
181
|
-
)
|
|
182
|
-
)
|
|
183
|
-
);
|
|
154
|
+
await Promise.all(orders.map(it => this.aggregate.open(it)));
|
|
184
155
|
}
|
|
185
156
|
|
|
186
157
|
/**
|
|
187
158
|
* Cancels specific order.
|
|
188
159
|
*/
|
|
189
160
|
cancel(order: Order): Promise<void> {
|
|
190
|
-
return this.aggregate.
|
|
191
|
-
order.instrument.base.adapter,
|
|
192
|
-
new AdapterOrderCancelCommand(order)
|
|
193
|
-
);
|
|
161
|
+
return this.aggregate.cancel(order);
|
|
194
162
|
}
|
|
195
163
|
|
|
196
164
|
/**
|
|
@@ -372,14 +340,7 @@ export class Session {
|
|
|
372
340
|
return this.store.changes$.pipe(
|
|
373
341
|
startWith(this.store.snapshot.universe.instrument[selector.toString()]),
|
|
374
342
|
filter(it => it instanceof Instrument && it.toString() == selector.toString()),
|
|
375
|
-
switchMap(() =>
|
|
376
|
-
from(
|
|
377
|
-
this.aggregate.dispatch<AdapterHistoryQuery, Candle[]>(
|
|
378
|
-
selector.base.adapter,
|
|
379
|
-
new AdapterHistoryQuery(selector, timeframe, length)
|
|
380
|
-
)
|
|
381
|
-
)
|
|
382
|
-
),
|
|
343
|
+
switchMap(() => from(this.aggregate.history(selector, timeframe, length))),
|
|
383
344
|
take(1),
|
|
384
345
|
shareReplay(),
|
|
385
346
|
mergeMap(it => it)
|
package/dist/bin.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { BacktesterListener, BacktesterStreamer } from './adapter/backtester';
|
|
2
|
-
import { Session, SessionDescriptor } from './session';
|
|
3
|
-
export declare function backtest(descriptor: SessionDescriptor, listener?: BacktesterListener): [Session, BacktesterStreamer];
|
|
4
|
-
export declare function paper(descriptor: SessionDescriptor): Session;
|
|
5
|
-
export declare function live(descriptor: SessionDescriptor): Session;
|
package/dist/bin.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.live = exports.paper = exports.backtest = void 0;
|
|
4
|
-
const backtester_1 = require("./adapter/backtester");
|
|
5
|
-
const adapter_1 = require("./adapter");
|
|
6
|
-
const paper_1 = require("./adapter/paper");
|
|
7
|
-
const session_1 = require("./session");
|
|
8
|
-
const store_1 = require("./store");
|
|
9
|
-
function backtest(descriptor, listener) {
|
|
10
|
-
const store = new store_1.Store();
|
|
11
|
-
const streamer = new backtester_1.BacktesterStreamer(store, descriptor.feed, descriptor.options.backtester, listener);
|
|
12
|
-
const aggregate = new adapter_1.AdapterAggregate(store, descriptor.adapter.map(it => new paper_1.PaperAdapter(new backtester_1.BacktesterAdapter(it, streamer), store, descriptor.options.backtester)));
|
|
13
|
-
return [new session_1.Session(store, aggregate, descriptor), streamer];
|
|
14
|
-
}
|
|
15
|
-
exports.backtest = backtest;
|
|
16
|
-
function paper(descriptor) {
|
|
17
|
-
const store = new store_1.Store();
|
|
18
|
-
const aggregate = new adapter_1.AdapterAggregate(store, descriptor.adapter.map(it => new paper_1.PaperAdapter(it, store, descriptor.options.paper)));
|
|
19
|
-
return new session_1.Session(store, aggregate, descriptor);
|
|
20
|
-
}
|
|
21
|
-
exports.paper = paper;
|
|
22
|
-
function live(descriptor) {
|
|
23
|
-
const store = new store_1.Store();
|
|
24
|
-
const aggregate = new adapter_1.AdapterAggregate(store, descriptor.adapter);
|
|
25
|
-
return new session_1.Session(store, aggregate, descriptor);
|
|
26
|
-
}
|
|
27
|
-
exports.live = live;
|
|
28
|
-
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";;;AAAA,qDAI8B;AAC9B,uCAA6C;AAC7C,2CAA+C;AAC/C,uCAAuD;AACvD,mCAAgC;AAQhC,SAAgB,QAAQ,CACtB,UAA6B,EAC7B,QAA6B;IAE7B,MAAM,KAAK,GAAG,IAAI,aAAK,EAAE,CAAC;IAE1B,MAAM,QAAQ,GAAG,IAAI,+BAAkB,CACrC,KAAK,EACL,UAAU,CAAC,IAAI,EACf,UAAU,CAAC,OAAO,CAAC,UAAU,EAC7B,QAAQ,CACT,CAAC;IACF,MAAM,SAAS,GAAG,IAAI,0BAAgB,CACpC,KAAK,EACL,UAAU,CAAC,OAAO,CAAC,GAAG,CACpB,EAAE,CAAC,EAAE,CACH,IAAI,oBAAY,CACd,IAAI,8BAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,EACnC,KAAK,EACL,UAAU,CAAC,OAAO,CAAC,UAAU,CAC9B,CACJ,CACF,CAAC;IAEF,OAAO,CAAC,IAAI,iBAAO,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC/D,CAAC;AAzBD,4BAyBC;AAQD,SAAgB,KAAK,CAAC,UAA6B;IACjD,MAAM,KAAK,GAAG,IAAI,aAAK,EAAE,CAAC;IAE1B,MAAM,SAAS,GAAG,IAAI,0BAAgB,CACpC,KAAK,EACL,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,oBAAY,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACpF,CAAC;IAEF,OAAO,IAAI,iBAAO,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACnD,CAAC;AATD,sBASC;AAOD,SAAgB,IAAI,CAAC,UAA6B;IAChD,MAAM,KAAK,GAAG,IAAI,aAAK,EAAE,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,0BAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAElE,OAAO,IAAI,iBAAO,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACnD,CAAC;AALD,oBAKC"}
|