@quantform/core 0.3.241 → 0.3.247
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/backtester/backtester-adapter.d.ts +1 -2
- package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-streamer.d.ts +2 -1
- package/dist/adapter/backtester/backtester-streamer.js +8 -7
- package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
- package/dist/adapter/backtester/backtester-streamer.spec.js +9 -10
- package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -1
- package/dist/bin.d.ts +3 -5
- package/dist/bin.js +6 -12
- package/dist/bin.js.map +1 -1
- package/dist/ipc.d.ts +10 -16
- package/dist/ipc.js +82 -67
- package/dist/ipc.js.map +1 -1
- package/dist/ipc.spec.js +24 -8
- package/dist/ipc.spec.js.map +1 -1
- package/dist/session/session-descriptor.d.ts +6 -3
- package/dist/session/session.d.ts +2 -2
- package/dist/session/session.js +3 -4
- package/dist/session/session.js.map +1 -1
- package/dist/session/session.spec.js +1 -5
- package/dist/session/session.spec.js.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/task.d.ts +6 -0
- package/dist/shared/task.js +25 -0
- package/dist/shared/task.js.map +1 -0
- package/dist/store/event/store-order.event.js +0 -8
- package/dist/store/event/store-order.event.js.map +1 -1
- package/dist/tests/backtester-adapter.spec.js +7 -8
- package/dist/tests/backtester-adapter.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/adapter/backtester/backtester-adapter.ts +2 -3
- package/src/adapter/backtester/backtester-streamer.spec.ts +10 -6
- package/src/adapter/backtester/backtester-streamer.ts +8 -7
- package/src/bin.ts +17 -18
- package/src/ipc.spec.ts +36 -8
- package/src/ipc.ts +98 -87
- package/src/session/session-descriptor.ts +7 -4
- package/src/session/session.spec.ts +1 -5
- package/src/session/session.ts +4 -4
- package/src/shared/index.ts +1 -0
- package/src/shared/task.ts +30 -0
- package/src/store/event/store-order.event.ts +0 -12
- package/src/tests/backtester-adapter.spec.ts +11 -7
package/src/bin.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BacktesterAdapter,
|
|
3
|
-
|
|
3
|
+
BacktesterListener,
|
|
4
4
|
BacktesterStreamer
|
|
5
5
|
} from './adapter/backtester';
|
|
6
6
|
import { AdapterAggregate } from './adapter';
|
|
7
|
-
import { PaperAdapter
|
|
7
|
+
import { PaperAdapter } from './adapter/paper';
|
|
8
8
|
import { Session, SessionDescriptor } from './session';
|
|
9
9
|
import { Store } from './store';
|
|
10
10
|
|
|
@@ -16,15 +16,25 @@ import { Store } from './store';
|
|
|
16
16
|
*/
|
|
17
17
|
export function backtest(
|
|
18
18
|
descriptor: SessionDescriptor,
|
|
19
|
-
|
|
19
|
+
listener?: BacktesterListener
|
|
20
20
|
): [Session, BacktesterStreamer] {
|
|
21
21
|
const store = new Store();
|
|
22
22
|
|
|
23
|
-
const streamer = new BacktesterStreamer(
|
|
23
|
+
const streamer = new BacktesterStreamer(
|
|
24
|
+
store,
|
|
25
|
+
descriptor.feed,
|
|
26
|
+
descriptor.options.backtester,
|
|
27
|
+
listener
|
|
28
|
+
);
|
|
24
29
|
const aggregate = new AdapterAggregate(
|
|
25
30
|
store,
|
|
26
31
|
descriptor.adapter.map(
|
|
27
|
-
it =>
|
|
32
|
+
it =>
|
|
33
|
+
new PaperAdapter(
|
|
34
|
+
new BacktesterAdapter(it, streamer),
|
|
35
|
+
store,
|
|
36
|
+
descriptor.options.backtester
|
|
37
|
+
)
|
|
28
38
|
)
|
|
29
39
|
);
|
|
30
40
|
|
|
@@ -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
|
|
50
|
+
export function paper(descriptor: SessionDescriptor): Session {
|
|
41
51
|
const store = new Store();
|
|
42
52
|
|
|
43
53
|
const aggregate = new AdapterAggregate(
|
|
44
54
|
store,
|
|
45
|
-
descriptor.adapter.map(it => new PaperAdapter(it, store, options))
|
|
55
|
+
descriptor.adapter.map(it => new PaperAdapter(it, store, descriptor.options.paper))
|
|
46
56
|
);
|
|
47
57
|
|
|
48
58
|
return new Session(store, aggregate, descriptor);
|
|
@@ -59,14 +69,3 @@ export function live(descriptor: SessionDescriptor): Session {
|
|
|
59
69
|
|
|
60
70
|
return new Session(store, aggregate, descriptor);
|
|
61
71
|
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Starts a new idle session.
|
|
65
|
-
* @param descriptor session descriptor.
|
|
66
|
-
*/
|
|
67
|
-
export function idle(descriptor: SessionDescriptor): Session {
|
|
68
|
-
const store = new Store();
|
|
69
|
-
const aggregate = new AdapterAggregate(store, descriptor.adapter);
|
|
70
|
-
|
|
71
|
-
return new Session(store, aggregate);
|
|
72
|
-
}
|
package/src/ipc.spec.ts
CHANGED
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
} from './adapter';
|
|
10
10
|
import { PaperAdapter, PaperSpotExecutor } from './adapter/paper';
|
|
11
11
|
import { PaperExecutor } from './adapter/paper/executor/paper-executor';
|
|
12
|
-
import {
|
|
12
|
+
import { run } from './ipc';
|
|
13
13
|
import { Feed, InMemoryStorage } from './storage';
|
|
14
14
|
import { instrumentOf } from './domain';
|
|
15
|
-
import { handler } from './shared';
|
|
15
|
+
import { handler, task } from './shared';
|
|
16
|
+
import { EventEmitter } from 'events';
|
|
17
|
+
import { from, of, take, tap } from 'rxjs';
|
|
16
18
|
|
|
17
19
|
class DefaultAdapter extends Adapter {
|
|
18
20
|
name = 'default';
|
|
@@ -53,21 +55,21 @@ describe('ipc feed tests', () => {
|
|
|
53
55
|
const session = await run(
|
|
54
56
|
{
|
|
55
57
|
adapter: [new DefaultAdapter()],
|
|
56
|
-
feed: new Feed(new InMemoryStorage())
|
|
57
|
-
describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
|
|
58
|
+
feed: new Feed(new InMemoryStorage())
|
|
58
59
|
},
|
|
59
60
|
command
|
|
60
61
|
);
|
|
61
62
|
|
|
62
|
-
expect(session.descriptor).toBeUndefined();
|
|
63
|
+
//expect(session.descriptor).toBeUndefined();
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
test('should dispatch session started event', done => {
|
|
66
67
|
const command = {
|
|
67
|
-
type: 'paper'
|
|
68
|
-
balance: { 'default:usd': 100 }
|
|
68
|
+
type: 'paper'
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
const ipcSub = new EventEmitter();
|
|
72
|
+
|
|
71
73
|
ipcSub.on('message', (message: any) => {
|
|
72
74
|
expect(message.type).toBe('paper:started');
|
|
73
75
|
done();
|
|
@@ -76,7 +78,33 @@ describe('ipc feed tests', () => {
|
|
|
76
78
|
run(
|
|
77
79
|
{
|
|
78
80
|
adapter: [new DefaultAdapter()],
|
|
79
|
-
|
|
81
|
+
ipcSub,
|
|
82
|
+
options: {
|
|
83
|
+
paper: {
|
|
84
|
+
balance: { 'default:usd': 100 }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
command
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('should execute user task', done => {
|
|
93
|
+
task('hello-world', session => {
|
|
94
|
+
return of(1).pipe(
|
|
95
|
+
take(1),
|
|
96
|
+
tap(() => done())
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const command = {
|
|
101
|
+
type: 'task',
|
|
102
|
+
taskName: 'hello-world'
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
run(
|
|
106
|
+
{
|
|
107
|
+
adapter: [new DefaultAdapter()]
|
|
80
108
|
},
|
|
81
109
|
command
|
|
82
110
|
);
|
package/src/ipc.ts
CHANGED
|
@@ -2,13 +2,17 @@ import { AdapterFeedCommand } from './adapter';
|
|
|
2
2
|
import { Session, SessionDescriptor } from './session';
|
|
3
3
|
import { instrumentOf } from './domain';
|
|
4
4
|
import { Topic, event, handler } from './shared/topic';
|
|
5
|
-
import { Logger } from './shared';
|
|
6
|
-
import { backtest,
|
|
5
|
+
import { runTask, Logger } from './shared';
|
|
6
|
+
import { backtest, live, paper } from './bin';
|
|
7
7
|
import { BacktesterStreamer } from './adapter/backtester';
|
|
8
|
+
import { Observable } from 'rxjs';
|
|
8
9
|
import { EventEmitter } from 'events';
|
|
10
|
+
import { join } from 'path';
|
|
9
11
|
import minimist = require('minimist');
|
|
12
|
+
import dotenv = require('dotenv');
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
// force to load environment variables from .env file if this file imported.
|
|
15
|
+
dotenv.config();
|
|
12
16
|
|
|
13
17
|
/**
|
|
14
18
|
* Base command/query interface for IPC communication.
|
|
@@ -44,12 +48,6 @@ export class IpcPaperCommand implements IpcCommand {
|
|
|
44
48
|
* The optional session identifier.
|
|
45
49
|
*/
|
|
46
50
|
id?: number;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Specifies trading balance, for example:
|
|
50
|
-
* { "binance:usdt": 1000 }
|
|
51
|
-
*/
|
|
52
|
-
balance: { [key: string]: number };
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
/**
|
|
@@ -58,28 +56,6 @@ export class IpcPaperCommand implements IpcCommand {
|
|
|
58
56
|
@event
|
|
59
57
|
export class IpcBacktestCommand implements IpcCommand {
|
|
60
58
|
type = 'backtest';
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Start date of the backtest in unix timestamp.
|
|
64
|
-
*/
|
|
65
|
-
from: number;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Due date of the backtest in unix timestamp.
|
|
69
|
-
*/
|
|
70
|
-
to: number;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Specifies trading balance, for example:
|
|
74
|
-
* { "binance:usdt": 1000 }
|
|
75
|
-
*/
|
|
76
|
-
balance: { [key: string]: number };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@event
|
|
80
|
-
export class IpcUniverseQuery implements IpcCommand {
|
|
81
|
-
type = 'universe';
|
|
82
|
-
adapter: string;
|
|
83
59
|
}
|
|
84
60
|
|
|
85
61
|
/**
|
|
@@ -97,12 +73,25 @@ export class IpcFeedCommand implements IpcCommand {
|
|
|
97
73
|
/**
|
|
98
74
|
* Start date of the feed in unix timestamp.
|
|
99
75
|
*/
|
|
100
|
-
from
|
|
76
|
+
from?: number;
|
|
101
77
|
|
|
102
78
|
/**
|
|
103
79
|
* Due date of the feed in unix timestamp.
|
|
104
80
|
*/
|
|
105
|
-
to
|
|
81
|
+
to?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Executes user task defined in quantform.ts file.
|
|
86
|
+
*/
|
|
87
|
+
@event
|
|
88
|
+
export class IpcTaskCommand implements IpcCommand {
|
|
89
|
+
type = 'task';
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Name of the task to execute.
|
|
93
|
+
*/
|
|
94
|
+
taskName: string;
|
|
106
95
|
}
|
|
107
96
|
|
|
108
97
|
/**
|
|
@@ -112,11 +101,13 @@ class IpcSessionAccessor {
|
|
|
112
101
|
session: Session;
|
|
113
102
|
}
|
|
114
103
|
|
|
104
|
+
export declare type IpcSessionDescriptor = SessionDescriptor & { ipcSub?: EventEmitter };
|
|
105
|
+
|
|
115
106
|
/**
|
|
116
107
|
* Inter process communication handler.
|
|
117
108
|
*/
|
|
118
109
|
class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
119
|
-
constructor(private readonly descriptor:
|
|
110
|
+
constructor(private readonly descriptor: IpcSessionDescriptor) {
|
|
120
111
|
super();
|
|
121
112
|
}
|
|
122
113
|
|
|
@@ -136,7 +127,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
136
127
|
session: accessor.session.descriptor?.id
|
|
137
128
|
});
|
|
138
129
|
|
|
139
|
-
await accessor.session.awake();
|
|
130
|
+
await accessor.session.awake(this.describe());
|
|
140
131
|
}
|
|
141
132
|
|
|
142
133
|
/**
|
|
@@ -148,16 +139,14 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
148
139
|
this.descriptor.id = command.id;
|
|
149
140
|
}
|
|
150
141
|
|
|
151
|
-
accessor.session = paper(this.descriptor
|
|
152
|
-
balance: command.balance
|
|
153
|
-
});
|
|
142
|
+
accessor.session = paper(this.descriptor);
|
|
154
143
|
|
|
155
144
|
this.notify({
|
|
156
145
|
type: 'paper:started',
|
|
157
146
|
session: accessor.session.descriptor?.id
|
|
158
147
|
});
|
|
159
148
|
|
|
160
|
-
await accessor.session.awake();
|
|
149
|
+
await accessor.session.awake(this.describe());
|
|
161
150
|
}
|
|
162
151
|
|
|
163
152
|
/**
|
|
@@ -167,68 +156,55 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
167
156
|
onBacktestMode(command: IpcBacktestCommand, accessor: IpcSessionAccessor) {
|
|
168
157
|
return new Promise<void>(async resolve => {
|
|
169
158
|
const [session, streamer] = backtest(this.descriptor, {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
to: command.to
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
resolve();
|
|
204
|
-
}
|
|
159
|
+
onBacktestStarted: (streamer: BacktesterStreamer) => {
|
|
160
|
+
this.notify({
|
|
161
|
+
type: 'backtest:started',
|
|
162
|
+
session: session.descriptor?.id,
|
|
163
|
+
timestamp: streamer.timestamp,
|
|
164
|
+
from: this.descriptor.options.backtester.from,
|
|
165
|
+
to: this.descriptor.options.backtester.to
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
onBacktestUpdated: (streamer: BacktesterStreamer) => {
|
|
169
|
+
this.notify({
|
|
170
|
+
type: 'backtest:updated',
|
|
171
|
+
session: session.descriptor?.id,
|
|
172
|
+
timestamp: streamer.timestamp,
|
|
173
|
+
from: this.descriptor.options.backtester.from,
|
|
174
|
+
to: this.descriptor.options.backtester.to
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
onBacktestCompleted: async (streamer: BacktesterStreamer) => {
|
|
178
|
+
await accessor.session.dispose();
|
|
179
|
+
|
|
180
|
+
this.notify({
|
|
181
|
+
type: 'backtest:completed',
|
|
182
|
+
session: session.descriptor?.id,
|
|
183
|
+
timestamp: streamer.timestamp,
|
|
184
|
+
from: this.descriptor.options.backtester.from,
|
|
185
|
+
to: this.descriptor.options.backtester.to
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
resolve();
|
|
205
189
|
}
|
|
206
190
|
});
|
|
207
191
|
|
|
208
192
|
accessor.session = session;
|
|
209
193
|
|
|
210
|
-
await accessor.session.awake();
|
|
194
|
+
await accessor.session.awake(this.describe());
|
|
211
195
|
await streamer.tryContinue().catch(it => Logger.error(it));
|
|
212
196
|
});
|
|
213
197
|
}
|
|
214
198
|
|
|
215
|
-
/**
|
|
216
|
-
* @see IpcUniverseQuery
|
|
217
|
-
*/
|
|
218
|
-
@handler(IpcUniverseQuery)
|
|
219
|
-
onUniverse(query: IpcUniverseQuery, accessor: IpcSessionAccessor) {
|
|
220
|
-
accessor.session = accessor.session ?? idle(this.descriptor);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
199
|
/**
|
|
224
200
|
* @see IpcFeedCommand
|
|
225
201
|
*/
|
|
226
202
|
@handler(IpcFeedCommand)
|
|
227
203
|
async onFeed(command: IpcFeedCommand, accessor: IpcSessionAccessor) {
|
|
228
|
-
accessor.session = accessor.session ??
|
|
204
|
+
accessor.session = accessor.session ?? live(this.descriptor);
|
|
229
205
|
const instrument = instrumentOf(command.instrument);
|
|
230
206
|
|
|
231
|
-
await accessor.session.awake();
|
|
207
|
+
await accessor.session.awake(undefined);
|
|
232
208
|
|
|
233
209
|
this.notify({ type: 'feed:started' });
|
|
234
210
|
|
|
@@ -254,6 +230,41 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
254
230
|
await accessor.session.dispose();
|
|
255
231
|
}
|
|
256
232
|
|
|
233
|
+
/**
|
|
234
|
+
* @see IpcTaskCommand
|
|
235
|
+
*/
|
|
236
|
+
@handler(IpcTaskCommand)
|
|
237
|
+
async onTask(query: IpcTaskCommand, accessor: IpcSessionAccessor) {
|
|
238
|
+
accessor.session = accessor.session ?? live(this.descriptor);
|
|
239
|
+
|
|
240
|
+
await accessor.session.awake(undefined);
|
|
241
|
+
|
|
242
|
+
this.notify({ type: 'task:started', taskName: query.taskName });
|
|
243
|
+
|
|
244
|
+
let result = undefined;
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
result = await runTask(query.taskName, accessor.session);
|
|
248
|
+
} catch (e) {
|
|
249
|
+
result = e;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.notify({ type: 'task:completed', taskName: query.taskName, result });
|
|
253
|
+
|
|
254
|
+
await accessor.session.dispose();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
describe(): (session: Session) => Observable<any> {
|
|
258
|
+
const pkg = require(join(process.cwd(), 'package.json'));
|
|
259
|
+
const describe = require(join(process.cwd(), pkg.main))?.default;
|
|
260
|
+
|
|
261
|
+
if (describe instanceof Function) {
|
|
262
|
+
return describe;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
257
268
|
/**
|
|
258
269
|
* Sends a message to parent process.
|
|
259
270
|
*/
|
|
@@ -262,7 +273,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
262
273
|
process.send(message);
|
|
263
274
|
}
|
|
264
275
|
|
|
265
|
-
ipcSub
|
|
276
|
+
this.descriptor.ipcSub?.emit('message', message);
|
|
266
277
|
}
|
|
267
278
|
}
|
|
268
279
|
|
|
@@ -273,7 +284,7 @@ class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
|
273
284
|
* @returns new session.
|
|
274
285
|
*/
|
|
275
286
|
export async function run(
|
|
276
|
-
descriptor:
|
|
287
|
+
descriptor: IpcSessionDescriptor,
|
|
277
288
|
...commands: IpcCommand[]
|
|
278
289
|
): Promise<Session> {
|
|
279
290
|
const handler = new IpcHandler(descriptor);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Adapter } from '../adapter';
|
|
2
|
-
import { Session } from '.';
|
|
3
2
|
import { Measurement } from '../storage/measurement';
|
|
4
3
|
import { Feed } from '../storage';
|
|
5
|
-
import {
|
|
4
|
+
import { BacktesterOptions } from './../adapter/backtester';
|
|
5
|
+
import { PaperOptions } from './../adapter/paper';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Describes a single session.
|
|
@@ -44,7 +44,10 @@ export interface SessionDescriptor {
|
|
|
44
44
|
measurement?: Measurement;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* Session additional options.
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
options?: {
|
|
50
|
+
backtester?: BacktesterOptions;
|
|
51
|
+
paper?: PaperOptions;
|
|
52
|
+
};
|
|
50
53
|
}
|
|
@@ -13,11 +13,7 @@ describe('session tests', () => {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
test('should trigger once', done => {
|
|
16
|
-
const session = paper(descriptor
|
|
17
|
-
balance: {
|
|
18
|
-
['binance:btc']: 1.23
|
|
19
|
-
}
|
|
20
|
-
});
|
|
16
|
+
const session = paper(descriptor);
|
|
21
17
|
|
|
22
18
|
session.instruments().subscribe({
|
|
23
19
|
next: it => {
|
package/src/session/session.ts
CHANGED
|
@@ -57,7 +57,7 @@ export class Session {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
async awake(): Promise<void> {
|
|
60
|
+
async awake(describe: (session: Session) => Observable<any>): Promise<void> {
|
|
61
61
|
if (this.initialized) {
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
@@ -67,8 +67,8 @@ export class Session {
|
|
|
67
67
|
// awake all adapters and synchronize trading accounts with store.
|
|
68
68
|
await this.aggregate.awake(this.descriptor != null);
|
|
69
69
|
|
|
70
|
-
if (
|
|
71
|
-
this.subscription =
|
|
70
|
+
if (describe) {
|
|
71
|
+
this.subscription = describe(this).subscribe();
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -352,7 +352,7 @@ export class Session {
|
|
|
352
352
|
);
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
balance(selector
|
|
355
|
+
balance(selector: AssetSelector): Observable<Balance> {
|
|
356
356
|
return this.store.changes$.pipe(
|
|
357
357
|
startWith(selector ? this.store.snapshot.balance[selector.toString()] : null),
|
|
358
358
|
filter(
|
package/src/shared/index.ts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { lastValueFrom, Observable } from 'rxjs';
|
|
2
|
+
import { Session } from './../session';
|
|
3
|
+
|
|
4
|
+
declare type Task = (session: Session) => Observable<any> | Promise<any> | any;
|
|
5
|
+
|
|
6
|
+
const tasks: Record<string, Task> = {};
|
|
7
|
+
|
|
8
|
+
export function task(name: string, fn: Task) {
|
|
9
|
+
tasks[name] = fn;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function runTask(name: string, session: Session): Promise<void> {
|
|
13
|
+
const task = tasks[name];
|
|
14
|
+
|
|
15
|
+
if (!task) {
|
|
16
|
+
throw new Error('Unknown task: ' + name);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const result = tasks[name](session);
|
|
20
|
+
|
|
21
|
+
if (result instanceof Observable) {
|
|
22
|
+
return lastValueFrom(result);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (result instanceof Promise) {
|
|
26
|
+
return await result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
@@ -12,12 +12,6 @@ export class OrderLoadEvent implements StoreEvent {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function OrderLoadEventHandler(event: OrderLoadEvent, state: State) {
|
|
15
|
-
const instrumentKey = event.order.instrument.toString();
|
|
16
|
-
|
|
17
|
-
if (!(instrumentKey in state.subscription.instrument)) {
|
|
18
|
-
throw new Error(`Trying to load order for unsubscribed instrument: ${instrumentKey}`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
event.order.timestamp = event.timestamp;
|
|
22
16
|
|
|
23
17
|
switch (event.order.state) {
|
|
@@ -48,12 +42,6 @@ export class OrderNewEvent implements StoreEvent {
|
|
|
48
42
|
}
|
|
49
43
|
|
|
50
44
|
export function OrderNewEventHandler(event: OrderNewEvent, state: State) {
|
|
51
|
-
const instrumentKey = event.order.instrument.toString();
|
|
52
|
-
|
|
53
|
-
if (!(instrumentKey in state.subscription.instrument)) {
|
|
54
|
-
throw new Error(`Trying to order for unsubscribed instrument: ${instrumentKey}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
45
|
if (event.order.state != 'NEW') {
|
|
58
46
|
throw new Error(`Order is not new`);
|
|
59
47
|
}
|
|
@@ -63,13 +63,17 @@ describe('backtester adapter tests', () => {
|
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
test('should stream data from input array', done => {
|
|
66
|
-
const streamer = new BacktesterStreamer(
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const streamer = new BacktesterStreamer(
|
|
67
|
+
store,
|
|
68
|
+
feed,
|
|
69
|
+
{
|
|
70
|
+
balance: {
|
|
71
|
+
['binance:usdt']: 1000
|
|
72
|
+
},
|
|
73
|
+
from: 0,
|
|
74
|
+
to: 100
|
|
69
75
|
},
|
|
70
|
-
|
|
71
|
-
to: 100,
|
|
72
|
-
listener: {
|
|
76
|
+
{
|
|
73
77
|
onBacktestCompleted: () => {
|
|
74
78
|
expect(store.snapshot.timestamp).toEqual(1);
|
|
75
79
|
expect(store.snapshot.trade[instrument.toString()].rate).toEqual(100);
|
|
@@ -78,7 +82,7 @@ describe('backtester adapter tests', () => {
|
|
|
78
82
|
done();
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
|
-
|
|
85
|
+
);
|
|
82
86
|
|
|
83
87
|
feed.save(instrument, [new TradePatchEvent(instrument, 100, 10, 1)]);
|
|
84
88
|
|