@quantform/core 0.3.230 → 0.3.234
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 +1 -1
- package/dist/adapter/adapter-aggregate.js +3 -3
- package/dist/adapter/adapter-aggregate.js.map +1 -1
- package/dist/adapter/adapter.event.js +1 -1
- package/dist/adapter/adapter.event.js.map +1 -1
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/bin.d.ts +0 -33
- package/dist/bin.js +1 -188
- package/dist/bin.js.map +1 -1
- package/dist/domain/instrument.js.map +1 -1
- package/dist/domain/orderbook.d.ts +1 -0
- package/dist/domain/orderbook.js.map +1 -1
- package/dist/domain/trade.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc.d.ts +34 -0
- package/dist/ipc.js +193 -0
- package/dist/ipc.js.map +1 -0
- package/dist/ipc.spec.d.ts +1 -0
- package/dist/ipc.spec.js +67 -0
- package/dist/ipc.spec.js.map +1 -0
- package/dist/session/session.js +2 -1
- package/dist/session/session.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/adapter/adapter-aggregate.ts +20 -3
- package/src/adapter/adapter.event.ts +1 -1
- package/src/adapter/adapter.ts +10 -0
- package/src/bin.ts +22 -189
- package/src/domain/asset.ts +3 -3
- package/src/domain/instrument.ts +3 -0
- package/src/domain/orderbook.ts +4 -1
- package/src/domain/trade.ts +4 -0
- package/src/index.ts +1 -0
- package/src/ipc.spec.ts +56 -0
- package/src/ipc.ts +277 -0
- package/src/session/session.ts +1 -1
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
AdapterDisposeCommand
|
|
8
8
|
} from './adapter.event';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Manages instances of all adapters provided in session descriptor.
|
|
12
|
+
* Awakes and disposes adapters, routes and executes commands.
|
|
13
|
+
*/
|
|
10
14
|
export class AdapterAggregate {
|
|
11
15
|
private readonly adapter: Record<string, Adapter> = {};
|
|
12
16
|
|
|
@@ -14,6 +18,10 @@ export class AdapterAggregate {
|
|
|
14
18
|
adapters.forEach(it => (this.adapter[it.name] = it));
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Sets up all adapters.
|
|
23
|
+
* @param usePrivateScope use private api (api keys needed).
|
|
24
|
+
*/
|
|
17
25
|
async awake(usePrivateScope = true): Promise<void> {
|
|
18
26
|
for (const exchange in this.adapter) {
|
|
19
27
|
await this.dispatch(exchange, new AdapterAwakeCommand());
|
|
@@ -24,21 +32,30 @@ export class AdapterAggregate {
|
|
|
24
32
|
}
|
|
25
33
|
}
|
|
26
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Disposes all adapters.
|
|
37
|
+
*/
|
|
27
38
|
async dispose(): Promise<any> {
|
|
28
39
|
for (const exchange in this.adapter) {
|
|
29
40
|
await this.dispatch(exchange, new AdapterDisposeCommand());
|
|
30
41
|
}
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Routes and executes command to a specific adapter.
|
|
46
|
+
* @param adapterName name of adapter
|
|
47
|
+
* @param event
|
|
48
|
+
* @returns
|
|
49
|
+
*/
|
|
33
50
|
dispatch<TEvent extends { type: string }, TResponse>(
|
|
34
|
-
|
|
51
|
+
adapterName: string,
|
|
35
52
|
event: TEvent
|
|
36
53
|
): Promise<TResponse> {
|
|
37
|
-
const adapter = this.adapter[
|
|
54
|
+
const adapter = this.adapter[adapterName];
|
|
38
55
|
|
|
39
56
|
if (!adapter) {
|
|
40
57
|
throw new Error(
|
|
41
|
-
`Unknown adapter: ${
|
|
58
|
+
`Unknown adapter: ${adapterName}. You should provide adapter in session descriptor.`
|
|
42
59
|
);
|
|
43
60
|
}
|
|
44
61
|
|
package/src/adapter/adapter.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { PaperAdapter } from './paper';
|
|
|
4
4
|
import { Topic } from '../shared/topic';
|
|
5
5
|
import { Store } from '../store';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Shared context for adapter execution. Provides access to the store.
|
|
9
|
+
*/
|
|
7
10
|
export class AdapterContext {
|
|
11
|
+
/**
|
|
12
|
+
* Returns the current unix timestamp (points to historical date in case of backtest).
|
|
13
|
+
*/
|
|
8
14
|
get timestamp(): timestamp {
|
|
9
15
|
return this.adapter.timestamp();
|
|
10
16
|
}
|
|
@@ -12,6 +18,10 @@ export class AdapterContext {
|
|
|
12
18
|
constructor(private readonly adapter: Adapter, readonly store: Store) {}
|
|
13
19
|
}
|
|
14
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Base adapter class, you should derive your own adapter from this class.
|
|
23
|
+
* @abstract
|
|
24
|
+
*/
|
|
15
25
|
export abstract class Adapter extends Topic<{ type: string }, AdapterContext> {
|
|
16
26
|
abstract name: string;
|
|
17
27
|
abstract createPaperExecutor(adapter: PaperAdapter): PaperExecutor;
|
package/src/bin.ts
CHANGED
|
@@ -3,199 +3,17 @@ import {
|
|
|
3
3
|
BacktesterOptions,
|
|
4
4
|
BacktesterStreamer
|
|
5
5
|
} from './adapter/backtester';
|
|
6
|
-
import { AdapterAggregate
|
|
6
|
+
import { AdapterAggregate } from './adapter';
|
|
7
7
|
import { PaperAdapter, PaperOptions } from './adapter/paper';
|
|
8
8
|
import { Session, SessionDescriptor } from './session';
|
|
9
9
|
import { Store } from './store';
|
|
10
|
-
import { instrumentOf } from './domain';
|
|
11
|
-
import { Topic, event, handler } from './shared/topic';
|
|
12
|
-
import minimist = require('minimist');
|
|
13
|
-
import { Logger } from './shared';
|
|
14
|
-
|
|
15
|
-
export interface IpcCommand {
|
|
16
|
-
type;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@event
|
|
20
|
-
export class IpcPaperModeCommand implements IpcCommand {
|
|
21
|
-
type = 'paper';
|
|
22
|
-
id?: number;
|
|
23
|
-
balance: { [key: string]: number };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@event
|
|
27
|
-
export class IpcBacktestModeCommand implements IpcCommand {
|
|
28
|
-
type = 'backtest';
|
|
29
|
-
from: number;
|
|
30
|
-
to: number;
|
|
31
|
-
balance: { [key: string]: number };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
@event
|
|
35
|
-
export class IpcLiveModeCommand implements IpcCommand {
|
|
36
|
-
id?: number;
|
|
37
|
-
type = 'live';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@event
|
|
41
|
-
export class IpcUniverseQuery implements IpcCommand {
|
|
42
|
-
type = 'universe';
|
|
43
|
-
exchange: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@event
|
|
47
|
-
export class IpcFeedCommand implements IpcCommand {
|
|
48
|
-
type = 'feed';
|
|
49
|
-
instrument: string;
|
|
50
|
-
from: number;
|
|
51
|
-
to: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
class ExecutionAccessor {
|
|
55
|
-
session: Session;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
class ExecutionHandler extends Topic<{ type: string }, ExecutionAccessor> {
|
|
59
|
-
constructor(private readonly descriptor: SessionDescriptor) {
|
|
60
|
-
super();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@handler(IpcLiveModeCommand)
|
|
64
|
-
async onLiveMode(command: IpcLiveModeCommand, accessor: ExecutionAccessor) {
|
|
65
|
-
if (command.id) {
|
|
66
|
-
this.descriptor.id = command.id;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
accessor.session = live(this.descriptor);
|
|
70
|
-
|
|
71
|
-
await accessor.session.awake();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
@handler(IpcPaperModeCommand)
|
|
75
|
-
async onPaperMode(command: IpcPaperModeCommand, accessor: ExecutionAccessor) {
|
|
76
|
-
if (command.id) {
|
|
77
|
-
this.descriptor.id = command.id;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
accessor.session = paper(this.descriptor, {
|
|
81
|
-
balance: command.balance
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
await accessor.session.awake();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@handler(IpcBacktestModeCommand)
|
|
88
|
-
onBacktestMode(command: IpcBacktestModeCommand, accessor: ExecutionAccessor) {
|
|
89
|
-
return new Promise<void>(async resolve => {
|
|
90
|
-
const [session, streamer] = backtest(this.descriptor, {
|
|
91
|
-
from: command.from,
|
|
92
|
-
to: command.to,
|
|
93
|
-
balance: command.balance,
|
|
94
|
-
progress: timestamp =>
|
|
95
|
-
this.notify({
|
|
96
|
-
type: 'backtest:updated',
|
|
97
|
-
timestamp,
|
|
98
|
-
from: command.from,
|
|
99
|
-
to: command.to
|
|
100
|
-
}),
|
|
101
|
-
completed: async () => {
|
|
102
|
-
const statement = {};
|
|
103
|
-
|
|
104
|
-
await accessor.session.dispose();
|
|
105
|
-
|
|
106
|
-
this.notify({ type: 'backtest:completed', statement });
|
|
107
|
-
|
|
108
|
-
resolve();
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
accessor.session = session;
|
|
113
|
-
|
|
114
|
-
this.notify({ type: 'backtest:started' });
|
|
115
|
-
|
|
116
|
-
await accessor.session.awake();
|
|
117
|
-
await streamer.tryContinue().catch(it => Logger.error(it));
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@handler(IpcUniverseQuery)
|
|
122
|
-
onUniverse(query: IpcUniverseQuery, accessor: ExecutionAccessor) {
|
|
123
|
-
accessor.session = accessor.session ?? idle(this.descriptor);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
@handler(IpcFeedCommand)
|
|
127
|
-
async onFeed(command: IpcFeedCommand, accessor: ExecutionAccessor) {
|
|
128
|
-
accessor.session = accessor.session ?? idle(this.descriptor);
|
|
129
|
-
const instrument = instrumentOf(command.instrument);
|
|
130
|
-
|
|
131
|
-
await accessor.session.awake();
|
|
132
|
-
|
|
133
|
-
this.notify({ type: 'feed:started' });
|
|
134
|
-
|
|
135
|
-
await accessor.session.aggregate.dispatch(
|
|
136
|
-
instrument.base.exchange,
|
|
137
|
-
new AdapterFeedCommand(
|
|
138
|
-
instrument,
|
|
139
|
-
command.from,
|
|
140
|
-
command.to,
|
|
141
|
-
this.descriptor.feed,
|
|
142
|
-
timestamp =>
|
|
143
|
-
this.notify({
|
|
144
|
-
type: 'feed:updated',
|
|
145
|
-
timestamp,
|
|
146
|
-
from: command.from,
|
|
147
|
-
to: command.to
|
|
148
|
-
})
|
|
149
|
-
)
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
this.notify({ type: 'feed:completed' });
|
|
153
|
-
|
|
154
|
-
await accessor.session.dispose();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private notify(message: any) {
|
|
158
|
-
if (!process.send) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
process.send(message);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export async function run(
|
|
167
|
-
descriptor: SessionDescriptor,
|
|
168
|
-
...commands: IpcCommand[]
|
|
169
|
-
): Promise<Session> {
|
|
170
|
-
const handler = new ExecutionHandler(descriptor);
|
|
171
|
-
const accessor = new ExecutionAccessor();
|
|
172
|
-
const argv = minimist(process.argv.slice(2));
|
|
173
|
-
|
|
174
|
-
if (argv.command) {
|
|
175
|
-
const json = Buffer.from(argv.command, 'base64').toString('utf-8');
|
|
176
|
-
|
|
177
|
-
commands.push(JSON.parse(json));
|
|
178
|
-
} else {
|
|
179
|
-
if (!commands.length) {
|
|
180
|
-
commands.push(new IpcPaperModeCommand());
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
for (const command of commands) {
|
|
185
|
-
await handler.dispatch(command, accessor);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
process.on('message', async (request: any) => {
|
|
189
|
-
const response = await handler.dispatch(request, accessor);
|
|
190
|
-
|
|
191
|
-
if (response) {
|
|
192
|
-
process.send(response);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
return accessor.session;
|
|
197
|
-
}
|
|
198
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Starts a new backtest session.
|
|
13
|
+
* @param descriptor session descriptor.
|
|
14
|
+
* @param options backtest options.
|
|
15
|
+
* @returns new session object.
|
|
16
|
+
*/
|
|
199
17
|
export function backtest(
|
|
200
18
|
descriptor: SessionDescriptor,
|
|
201
19
|
options: BacktesterOptions
|
|
@@ -213,6 +31,12 @@ export function backtest(
|
|
|
213
31
|
return [new Session(store, aggregate, descriptor), streamer];
|
|
214
32
|
}
|
|
215
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Starts a new paper session.
|
|
36
|
+
* @param descriptor session descriptor.
|
|
37
|
+
* @param options backtest options.
|
|
38
|
+
* @returns new session object.
|
|
39
|
+
*/
|
|
216
40
|
export function paper(descriptor: SessionDescriptor, options: PaperOptions): Session {
|
|
217
41
|
const store = new Store();
|
|
218
42
|
|
|
@@ -224,6 +48,11 @@ export function paper(descriptor: SessionDescriptor, options: PaperOptions): Ses
|
|
|
224
48
|
return new Session(store, aggregate, descriptor);
|
|
225
49
|
}
|
|
226
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Starts a new live session.
|
|
53
|
+
* @param descriptor session descriptor.
|
|
54
|
+
* @returns new session object.
|
|
55
|
+
*/
|
|
227
56
|
export function live(descriptor: SessionDescriptor): Session {
|
|
228
57
|
const store = new Store();
|
|
229
58
|
const aggregate = new AdapterAggregate(store, descriptor.adapter);
|
|
@@ -231,6 +60,10 @@ export function live(descriptor: SessionDescriptor): Session {
|
|
|
231
60
|
return new Session(store, aggregate, descriptor);
|
|
232
61
|
}
|
|
233
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Starts a new idle session.
|
|
65
|
+
* @param descriptor session descriptor.
|
|
66
|
+
*/
|
|
234
67
|
export function idle(descriptor: SessionDescriptor): Session {
|
|
235
68
|
const store = new Store();
|
|
236
69
|
const aggregate = new AdapterAggregate(store, descriptor.adapter);
|
package/src/domain/asset.ts
CHANGED
|
@@ -58,21 +58,21 @@ export class Asset extends AssetSelector {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* Trims a number to the
|
|
61
|
+
* Trims a number to the asset precision.
|
|
62
62
|
*/
|
|
63
63
|
fixed(number: number): number {
|
|
64
64
|
return fixed(number, this.scale);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Rounds down a number to the
|
|
68
|
+
* Rounds down a number to the asset precision.
|
|
69
69
|
*/
|
|
70
70
|
floor(number: number): number {
|
|
71
71
|
return floor(number, this.scale);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Rounds up a number to the
|
|
75
|
+
* Rounds up a number to the asset precision.
|
|
76
76
|
*/
|
|
77
77
|
ceil(number: number): number {
|
|
78
78
|
return ceil(number, this.scale);
|
package/src/domain/instrument.ts
CHANGED
|
@@ -21,6 +21,9 @@ export class InstrumentSelector {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Represents trading market which is made up by two trading assets (base and quoted).
|
|
26
|
+
*/
|
|
24
27
|
export class Instrument extends InstrumentSelector implements Component {
|
|
25
28
|
timestamp: timestamp;
|
|
26
29
|
commission: Commission;
|
package/src/domain/orderbook.ts
CHANGED
|
@@ -2,13 +2,16 @@ import { timestamp } from '../shared';
|
|
|
2
2
|
import { Instrument } from '../domain';
|
|
3
3
|
import { Component } from './component';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Provides an access to pending buy and sell orders on the specific market.
|
|
7
|
+
*/
|
|
5
8
|
export class Orderbook implements Component {
|
|
6
9
|
timestamp: timestamp;
|
|
7
10
|
bestAskRate: number;
|
|
8
11
|
bestAskQuantity: number;
|
|
9
12
|
bestBidRate: number;
|
|
10
13
|
bestBidQuantity: number;
|
|
11
|
-
|
|
14
|
+
az;
|
|
12
15
|
get midRate(): number {
|
|
13
16
|
return this.instrument.quote.fixed((this.bestAskRate + this.bestBidRate) / 2);
|
|
14
17
|
}
|
package/src/domain/trade.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { timestamp } from '../shared';
|
|
|
2
2
|
import { Instrument } from '.';
|
|
3
3
|
import { Component } from './component';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Simple trade or ticker executed on the market, it's a match of buyer
|
|
7
|
+
* and seller of the same asset.
|
|
8
|
+
*/
|
|
5
9
|
export class Trade implements Component {
|
|
6
10
|
timestamp: timestamp;
|
|
7
11
|
rate: number;
|
package/src/index.ts
CHANGED
package/src/ipc.spec.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Session } from './session';
|
|
2
|
+
import {
|
|
3
|
+
Adapter,
|
|
4
|
+
AdapterFeedCommand,
|
|
5
|
+
AdapterAwakeCommand,
|
|
6
|
+
AdapterAccountCommand
|
|
7
|
+
} from './adapter';
|
|
8
|
+
import { PaperAdapter, PaperSpotExecutor } from './adapter/paper';
|
|
9
|
+
import { PaperExecutor } from './adapter/paper/executor/paper-executor';
|
|
10
|
+
import { IpcFeedCommand, run } from './ipc';
|
|
11
|
+
import { Feed, InMemoryStorage } from './storage';
|
|
12
|
+
import { instrumentOf } from './domain';
|
|
13
|
+
import { handler } from './shared';
|
|
14
|
+
|
|
15
|
+
class DefaultAdapter extends Adapter {
|
|
16
|
+
name = 'default';
|
|
17
|
+
|
|
18
|
+
timestamp() {
|
|
19
|
+
return 123;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
createPaperExecutor(adapter: PaperAdapter): PaperExecutor {
|
|
23
|
+
return new PaperSpotExecutor(adapter);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@handler(AdapterAwakeCommand)
|
|
27
|
+
onAwake(command: AdapterAwakeCommand) {}
|
|
28
|
+
|
|
29
|
+
@handler(AdapterAccountCommand)
|
|
30
|
+
onAccount(command: AdapterAccountCommand) {}
|
|
31
|
+
|
|
32
|
+
@handler(AdapterFeedCommand)
|
|
33
|
+
onFeed(command: AdapterFeedCommand) {}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('ipc feed tests', () => {
|
|
37
|
+
test('should trigger adapter feed command', async () => {
|
|
38
|
+
const command = {
|
|
39
|
+
type: 'feed',
|
|
40
|
+
instrument: 'default:btc-usdt',
|
|
41
|
+
from: 0,
|
|
42
|
+
to: 100
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const session = await run(
|
|
46
|
+
{
|
|
47
|
+
adapter: [new DefaultAdapter()],
|
|
48
|
+
feed: new Feed(new InMemoryStorage()),
|
|
49
|
+
describe: (session: Session) => session.trade(instrumentOf('default:btc-usdt'))
|
|
50
|
+
},
|
|
51
|
+
command
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
expect(session.descriptor).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
});
|