@quantform/core 0.3.228 → 0.3.232
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 +5 -5
- package/dist/adapter/adapter-aggregate.js.map +1 -1
- package/dist/adapter/adapter.d.ts +4 -4
- package/dist/adapter/adapter.event.d.ts +1 -1
- package/dist/adapter/adapter.event.js +1 -1
- package/dist/adapter/adapter.js +3 -3
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-adapter.d.ts +2 -2
- package/dist/adapter/backtester/backtester-adapter.js +3 -5
- package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-cursor.d.ts +1 -1
- package/dist/adapter/backtester/backtester-cursor.spec.js.map +1 -1
- package/dist/adapter/backtester/backtester-streamer.d.ts +1 -1
- package/dist/{common/decimals.spec.d.ts → adapter/backtester/backtester-streamer.spec.d.ts} +0 -0
- package/dist/adapter/backtester/backtester-streamer.spec.js +45 -0
- package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -0
- package/dist/adapter/paper/{model/paper-model.d.ts → executor/paper-executor.d.ts} +1 -1
- package/dist/adapter/paper/{model/paper-model.js → executor/paper-executor.js} +4 -4
- package/dist/adapter/paper/executor/paper-executor.js.map +1 -0
- package/dist/adapter/paper/{model/paper-margin-model.d.ts → executor/paper-margin-executor.d.ts} +2 -2
- package/dist/adapter/paper/{model/paper-margin-model.js → executor/paper-margin-executor.js} +11 -11
- package/dist/adapter/paper/executor/paper-margin-executor.js.map +1 -0
- package/dist/adapter/paper/{model/paper-spot-model.d.ts → executor/paper-spot-executor.d.ts} +3 -3
- package/dist/adapter/paper/{model/paper-spot-model.js → executor/paper-spot-executor.js} +7 -7
- package/dist/adapter/paper/executor/paper-spot-executor.js.map +1 -0
- package/dist/adapter/paper/index.d.ts +2 -2
- package/dist/adapter/paper/index.js +2 -2
- package/dist/adapter/paper/index.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.d.ts +3 -3
- package/dist/adapter/paper/paper-adapter.js +6 -6
- package/dist/adapter/paper/paper-adapter.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.spec.js +2 -2
- package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
- package/dist/bin.d.ts +2 -35
- package/dist/bin.js +2 -187
- package/dist/bin.js.map +1 -1
- package/dist/domain/asset.js +1 -1
- package/dist/domain/balance.d.ts +1 -1
- package/dist/domain/balance.js +1 -3
- package/dist/domain/balance.js.map +1 -1
- package/dist/domain/candle-builder.d.ts +1 -1
- package/dist/domain/candle.d.ts +1 -1
- package/dist/domain/candle.spec.js +2 -2
- package/dist/domain/{commision.d.ts → commission.d.ts} +2 -2
- package/dist/domain/{commision.js → commission.js} +7 -7
- package/dist/domain/commission.js.map +1 -0
- package/dist/domain/component.d.ts +1 -1
- package/dist/domain/index.d.ts +1 -1
- package/dist/domain/index.js +1 -1
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/instrument.d.ts +3 -3
- package/dist/domain/order.d.ts +1 -1
- package/dist/domain/orderbook.d.ts +1 -1
- package/dist/domain/position.d.ts +1 -1
- package/dist/domain/position.js +3 -3
- package/dist/domain/trade.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/indicator/truerange.spec.js +6 -6
- package/dist/ipc.d.ts +34 -0
- package/dist/ipc.js +193 -0
- package/dist/ipc.js.map +1 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/index.js.map +1 -1
- package/dist/session/session-descriptor.d.ts +2 -3
- package/dist/session/session-statement.d.ts +5 -0
- package/dist/session/session-statement.js +87 -0
- package/dist/session/session-statement.js.map +1 -0
- package/dist/session/session.d.ts +2 -2
- package/dist/session/session.js +10 -19
- package/dist/session/session.js.map +1 -1
- package/dist/session/session.spec.js +3 -3
- package/dist/session/session.spec.js.map +1 -1
- package/dist/{common → shared}/datetime.d.ts +0 -0
- package/dist/{common → shared}/datetime.js +0 -0
- package/dist/{common → shared}/datetime.js.map +1 -1
- package/dist/{common → shared}/decimals.d.ts +0 -0
- package/dist/{common → shared}/decimals.js +0 -0
- package/dist/{common → shared}/decimals.js.map +1 -1
- package/dist/{common/policy.spec.d.ts → shared/decimals.spec.d.ts} +0 -0
- package/dist/{common → shared}/decimals.spec.js +0 -0
- package/dist/{common → shared}/decimals.spec.js.map +1 -1
- package/dist/{common → shared}/index.d.ts +0 -0
- package/dist/{common → shared}/index.js +0 -0
- package/dist/{common → shared}/index.js.map +1 -1
- package/dist/{common → shared}/io.d.ts +0 -0
- package/dist/{common → shared}/io.js +0 -0
- package/dist/shared/io.js.map +1 -0
- package/dist/{common → shared}/logger.d.ts +0 -0
- package/dist/{common → shared}/logger.js +0 -0
- package/dist/{common → shared}/logger.js.map +1 -1
- package/dist/{common → shared}/policy.d.ts +0 -0
- package/dist/{common → shared}/policy.js +0 -0
- package/dist/{common → shared}/policy.js.map +1 -1
- package/dist/{common/topic.spec.d.ts → shared/policy.spec.d.ts} +0 -0
- package/dist/{common → shared}/policy.spec.js +0 -0
- package/dist/{common → shared}/policy.spec.js.map +1 -1
- package/dist/{common → shared}/topic.d.ts +0 -0
- package/dist/{common → shared}/topic.js +0 -0
- package/dist/{common → shared}/topic.js.map +1 -1
- package/dist/{common/worker.spec.d.ts → shared/topic.spec.d.ts} +0 -0
- package/dist/{common → shared}/topic.spec.js +0 -0
- package/dist/{common → shared}/topic.spec.js.map +1 -1
- package/dist/{common → shared}/worker.d.ts +0 -0
- package/dist/{common → shared}/worker.js +0 -0
- package/dist/{common → shared}/worker.js.map +1 -1
- package/dist/shared/worker.spec.d.ts +1 -0
- package/dist/{common → shared}/worker.spec.js +0 -0
- package/dist/{common → shared}/worker.spec.js.map +1 -1
- package/dist/storage/measurement.d.ts +1 -1
- package/dist/store/event/store-balance.event.d.ts +1 -1
- package/dist/store/event/store-balance.event.js +1 -1
- package/dist/store/event/store-balance.event.spec.js +3 -3
- package/dist/store/event/store-balance.event.spec.js.map +1 -1
- package/dist/store/event/store-candle.event.d.ts +1 -1
- package/dist/store/event/store-candle.event.js +1 -1
- package/dist/store/event/store-candle.event.spec.d.ts +1 -0
- package/dist/store/event/store-candle.event.spec.js +24 -0
- package/dist/store/event/store-candle.event.spec.js.map +1 -0
- package/dist/store/event/store-instrument.event.d.ts +4 -4
- package/dist/store/event/store-instrument.event.js +3 -3
- package/dist/store/event/store-instrument.event.js.map +1 -1
- package/dist/store/event/store-instrument.event.spec.js +3 -3
- package/dist/store/event/store-instrument.event.spec.js.map +1 -1
- package/dist/store/event/store-order.event.d.ts +1 -1
- package/dist/store/event/store-order.event.js +1 -1
- package/dist/store/event/store-order.event.spec.js +2 -2
- package/dist/store/event/store-orderbook.event.d.ts +1 -1
- package/dist/store/event/store-orderbook.event.js +1 -1
- package/dist/store/event/store-position.event.d.ts +1 -1
- package/dist/store/event/store-position.event.js +1 -1
- package/dist/store/event/store-trade.event.d.ts +1 -1
- package/dist/store/event/store-trade.event.js +1 -1
- package/dist/store/event/store-trade.event.spec.js +6 -6
- package/dist/store/event/store-trade.event.spec.js.map +1 -1
- package/dist/store/event/store.event.d.ts +1 -1
- package/dist/store/store.d.ts +1 -1
- package/dist/store/store.js +1 -1
- package/dist/store/store.state.d.ts +1 -1
- package/dist/store/store.state.js.map +1 -1
- package/dist/tests/backtester-adapter.spec.js +5 -5
- package/dist/tests/backtester-adapter.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jestconfig.unit.json +2 -1
- package/package.json +1 -1
- package/src/adapter/adapter-aggregate.ts +23 -4
- package/src/adapter/adapter.event.ts +2 -2
- package/src/adapter/adapter.ts +14 -4
- package/src/adapter/backtester/backtester-adapter.ts +4 -6
- package/src/adapter/backtester/backtester-cursor.spec.ts +2 -2
- package/src/adapter/backtester/backtester-cursor.ts +1 -1
- package/src/adapter/backtester/backtester-streamer.spec.ts +53 -0
- package/src/adapter/backtester/backtester-streamer.ts +1 -1
- package/src/adapter/paper/{model/paper-model.ts → executor/paper-executor.ts} +1 -1
- package/src/adapter/paper/{model/paper-margin-model.ts → executor/paper-margin-executor.ts} +4 -4
- package/src/adapter/paper/{model/paper-spot-model.ts → executor/paper-spot-executor.ts} +5 -5
- package/src/adapter/paper/index.ts +2 -2
- package/src/adapter/paper/paper-adapter.spec.ts +4 -4
- package/src/adapter/paper/paper-adapter.ts +8 -8
- package/src/bin.ts +24 -189
- package/src/domain/asset.ts +2 -2
- package/src/domain/balance.ts +3 -5
- package/src/domain/candle-builder.ts +1 -1
- package/src/domain/candle.spec.ts +1 -1
- package/src/domain/candle.ts +1 -1
- package/src/domain/{commision.ts → commission.ts} +3 -3
- package/src/domain/component.ts +1 -1
- package/src/domain/index.ts +1 -1
- package/src/domain/instrument.ts +3 -3
- package/src/domain/order.ts +1 -1
- package/src/domain/orderbook.ts +1 -1
- package/src/domain/position.ts +2 -2
- package/src/domain/trade.ts +1 -1
- package/src/index.ts +2 -7
- package/src/indicator/truerange.spec.ts +1 -1
- package/src/ipc.ts +277 -0
- package/src/session/index.ts +1 -0
- package/src/session/session-descriptor.ts +35 -6
- package/src/session/session-statement.ts +119 -0
- package/src/session/session.spec.ts +6 -4
- package/src/session/session.ts +62 -20
- package/src/{common → shared}/datetime.ts +0 -0
- package/src/{common → shared}/decimals.spec.ts +0 -0
- package/src/{common → shared}/decimals.ts +0 -0
- package/src/{common → shared}/index.ts +0 -0
- package/src/{common → shared}/io.ts +0 -0
- package/src/{common → shared}/logger.ts +0 -0
- package/src/{common → shared}/policy.spec.ts +0 -0
- package/src/{common → shared}/policy.ts +0 -0
- package/src/{common → shared}/topic.spec.ts +0 -0
- package/src/{common → shared}/topic.ts +0 -0
- package/src/{common → shared}/worker.spec.ts +0 -0
- package/src/{common → shared}/worker.ts +0 -0
- package/src/storage/measurement.ts +1 -1
- package/src/store/event/store-balance.event.spec.ts +3 -3
- package/src/store/event/store-balance.event.ts +2 -2
- package/src/store/event/store-candle.event.spec.ts +32 -0
- package/src/store/event/store-candle.event.ts +2 -2
- package/src/store/event/store-instrument.event.spec.ts +3 -3
- package/src/store/event/store-instrument.event.ts +5 -5
- package/src/store/event/store-order.event.spec.ts +1 -1
- package/src/store/event/store-order.event.ts +2 -2
- package/src/store/event/store-orderbook.event.ts +2 -2
- package/src/store/event/store-position.event.ts +2 -2
- package/src/store/event/store-trade.event.spec.ts +2 -2
- package/src/store/event/store-trade.event.ts +2 -2
- package/src/store/event/store.event.ts +1 -1
- package/src/store/store.state.ts +2 -1
- package/src/store/store.ts +1 -1
- package/src/tests/backtester-adapter.spec.ts +7 -7
- package/dist/adapter/paper/model/paper-margin-model.js.map +0 -1
- package/dist/adapter/paper/model/paper-model.js.map +0 -1
- package/dist/adapter/paper/model/paper-spot-model.js.map +0 -1
- package/dist/behaviour/behaviour.d.ts +0 -6
- package/dist/behaviour/behaviour.js +0 -3
- package/dist/behaviour/behaviour.js.map +0 -1
- package/dist/behaviour/combined-behaviour.d.ts +0 -14
- package/dist/behaviour/combined-behaviour.js +0 -26
- package/dist/behaviour/combined-behaviour.js.map +0 -1
- package/dist/behaviour/index.d.ts +0 -3
- package/dist/behaviour/index.js +0 -16
- package/dist/behaviour/index.js.map +0 -1
- package/dist/behaviour/statement/benchmark-statement.behaviour.d.ts +0 -15
- package/dist/behaviour/statement/benchmark-statement.behaviour.js +0 -41
- package/dist/behaviour/statement/benchmark-statement.behaviour.js.map +0 -1
- package/dist/behaviour/statement/equity-statement.behaviour.d.ts +0 -15
- package/dist/behaviour/statement/equity-statement.behaviour.js +0 -54
- package/dist/behaviour/statement/equity-statement.behaviour.js.map +0 -1
- package/dist/behaviour/statement/index.d.ts +0 -1
- package/dist/behaviour/statement/index.js +0 -14
- package/dist/behaviour/statement/index.js.map +0 -1
- package/dist/behaviour/statement/period-statement.behaviour.d.ts +0 -9
- package/dist/behaviour/statement/period-statement.behaviour.js +0 -20
- package/dist/behaviour/statement/period-statement.behaviour.js.map +0 -1
- package/dist/behaviour/statement/statement.behaviour.d.ts +0 -7
- package/dist/behaviour/statement/statement.behaviour.js +0 -19
- package/dist/behaviour/statement/statement.behaviour.js.map +0 -1
- package/dist/common/io.js.map +0 -1
- package/dist/domain/commision.js.map +0 -1
- package/src/behaviour/behaviour.ts +0 -7
- package/src/behaviour/combined-behaviour.ts +0 -23
- package/src/behaviour/index.ts +0 -3
- package/src/behaviour/statement/benchmark-statement.behaviour.ts +0 -53
- package/src/behaviour/statement/equity-statement.behaviour.ts +0 -71
- package/src/behaviour/statement/index.ts +0 -1
- package/src/behaviour/statement/period-statement.behaviour.ts +0 -26
- package/src/behaviour/statement/statement.behaviour.ts +0 -19
package/src/index.ts
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './common';
|
|
3
|
-
|
|
1
|
+
export * from './shared';
|
|
4
2
|
export * from './domain';
|
|
5
|
-
|
|
6
3
|
export * from './adapter';
|
|
7
4
|
export * from './adapter/backtester';
|
|
8
5
|
export * from './adapter/paper';
|
|
9
|
-
|
|
10
6
|
export * from './indicator';
|
|
11
7
|
export * from './session';
|
|
12
8
|
export * from './storage';
|
|
13
|
-
|
|
14
9
|
export * from './store';
|
|
15
10
|
export * from './store/event';
|
|
16
|
-
|
|
17
11
|
export * from './bin';
|
|
12
|
+
export * from './ipc';
|
|
@@ -2,7 +2,7 @@ import { from } from 'rxjs';
|
|
|
2
2
|
import { Candle } from '../domain';
|
|
3
3
|
import { truerange } from './truerange';
|
|
4
4
|
import { sma } from './sma';
|
|
5
|
-
import { now } from '../
|
|
5
|
+
import { now } from '../shared';
|
|
6
6
|
|
|
7
7
|
describe('truerange atr tests', () => {
|
|
8
8
|
test('should return expected value', done => {
|
package/src/ipc.ts
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { AdapterFeedCommand } from './adapter';
|
|
2
|
+
import { Session, SessionDescriptor } from './session';
|
|
3
|
+
import { instrumentOf } from './domain';
|
|
4
|
+
import { Topic, event, handler } from './shared/topic';
|
|
5
|
+
import { Logger } from './shared';
|
|
6
|
+
import { backtest, idle, live, paper } from './bin';
|
|
7
|
+
import minimist = require('minimist');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base command/query interface for IPC communication.
|
|
11
|
+
*/
|
|
12
|
+
export interface IpcCommand {
|
|
13
|
+
/**
|
|
14
|
+
* The command name to handle.
|
|
15
|
+
*/
|
|
16
|
+
type;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Command to start a new live session.
|
|
21
|
+
*/
|
|
22
|
+
@event
|
|
23
|
+
export class IpcLiveCommand implements IpcCommand {
|
|
24
|
+
type = 'live';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The optional session identifier.
|
|
28
|
+
*/
|
|
29
|
+
id?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Command to start a new paper session.
|
|
34
|
+
*/
|
|
35
|
+
@event
|
|
36
|
+
export class IpcPaperCommand implements IpcCommand {
|
|
37
|
+
type = 'paper';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The optional session identifier.
|
|
41
|
+
*/
|
|
42
|
+
id?: number;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Specifies trading balance, for example:
|
|
46
|
+
* { "binance:usdt": 1000 }
|
|
47
|
+
*/
|
|
48
|
+
balance: { [key: string]: number };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Command to start a new backtest session.
|
|
53
|
+
*/
|
|
54
|
+
@event
|
|
55
|
+
export class IpcBacktestCommand implements IpcCommand {
|
|
56
|
+
type = 'backtest';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Start date of the backtest in unix timestamp.
|
|
60
|
+
*/
|
|
61
|
+
from: number;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Due date of the backtest in unix timestamp.
|
|
65
|
+
*/
|
|
66
|
+
to: number;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Specifies trading balance, for example:
|
|
70
|
+
* { "binance:usdt": 1000 }
|
|
71
|
+
*/
|
|
72
|
+
balance: { [key: string]: number };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@event
|
|
76
|
+
export class IpcUniverseQuery implements IpcCommand {
|
|
77
|
+
type = 'universe';
|
|
78
|
+
exchange: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Feeds specific session descriptor with instrument data.
|
|
83
|
+
*/
|
|
84
|
+
@event
|
|
85
|
+
export class IpcFeedCommand implements IpcCommand {
|
|
86
|
+
type = 'feed';
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Instrument to feed.
|
|
90
|
+
*/
|
|
91
|
+
instrument: string;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Start date of the feed in unix timestamp.
|
|
95
|
+
*/
|
|
96
|
+
from: number;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Due date of the feed in unix timestamp.
|
|
100
|
+
*/
|
|
101
|
+
to: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Stores current session instance.
|
|
106
|
+
*/
|
|
107
|
+
class IpcSessionAccessor {
|
|
108
|
+
session: Session;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Inter process communication handler.
|
|
113
|
+
*/
|
|
114
|
+
class IpcHandler extends Topic<{ type: string }, IpcSessionAccessor> {
|
|
115
|
+
constructor(private readonly descriptor: SessionDescriptor) {
|
|
116
|
+
super();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @see IpcLiveCommand
|
|
121
|
+
*/
|
|
122
|
+
@handler(IpcLiveCommand)
|
|
123
|
+
async onLiveMode(command: IpcLiveCommand, accessor: IpcSessionAccessor) {
|
|
124
|
+
if (command.id) {
|
|
125
|
+
this.descriptor.id = command.id;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
accessor.session = live(this.descriptor);
|
|
129
|
+
|
|
130
|
+
await accessor.session.awake();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @see IpcPaperCommand
|
|
135
|
+
*/
|
|
136
|
+
@handler(IpcPaperCommand)
|
|
137
|
+
async onPaperMode(command: IpcPaperCommand, accessor: IpcSessionAccessor) {
|
|
138
|
+
if (command.id) {
|
|
139
|
+
this.descriptor.id = command.id;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
accessor.session = paper(this.descriptor, {
|
|
143
|
+
balance: command.balance
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
await accessor.session.awake();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @see IpcBacktestCommand
|
|
151
|
+
*/
|
|
152
|
+
@handler(IpcBacktestCommand)
|
|
153
|
+
onBacktestMode(command: IpcBacktestCommand, accessor: IpcSessionAccessor) {
|
|
154
|
+
return new Promise<void>(async resolve => {
|
|
155
|
+
const [session, streamer] = backtest(this.descriptor, {
|
|
156
|
+
from: command.from,
|
|
157
|
+
to: command.to,
|
|
158
|
+
balance: command.balance,
|
|
159
|
+
progress: timestamp =>
|
|
160
|
+
this.notify({
|
|
161
|
+
type: 'backtest:updated',
|
|
162
|
+
timestamp,
|
|
163
|
+
from: command.from,
|
|
164
|
+
to: command.to
|
|
165
|
+
}),
|
|
166
|
+
completed: async () => {
|
|
167
|
+
const statement = {};
|
|
168
|
+
|
|
169
|
+
await accessor.session.dispose();
|
|
170
|
+
|
|
171
|
+
this.notify({ type: 'backtest:completed', statement });
|
|
172
|
+
|
|
173
|
+
resolve();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
accessor.session = session;
|
|
178
|
+
|
|
179
|
+
this.notify({ type: 'backtest:started' });
|
|
180
|
+
|
|
181
|
+
await accessor.session.awake();
|
|
182
|
+
await streamer.tryContinue().catch(it => Logger.error(it));
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @see IpcUniverseQuery
|
|
188
|
+
*/
|
|
189
|
+
@handler(IpcUniverseQuery)
|
|
190
|
+
onUniverse(query: IpcUniverseQuery, accessor: IpcSessionAccessor) {
|
|
191
|
+
accessor.session = accessor.session ?? idle(this.descriptor);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @see IpcFeedCommand
|
|
196
|
+
*/
|
|
197
|
+
@handler(IpcFeedCommand)
|
|
198
|
+
async onFeed(command: IpcFeedCommand, accessor: IpcSessionAccessor) {
|
|
199
|
+
accessor.session = accessor.session ?? idle(this.descriptor);
|
|
200
|
+
const instrument = instrumentOf(command.instrument);
|
|
201
|
+
|
|
202
|
+
await accessor.session.awake();
|
|
203
|
+
|
|
204
|
+
this.notify({ type: 'feed:started' });
|
|
205
|
+
|
|
206
|
+
await accessor.session.aggregate.dispatch(
|
|
207
|
+
instrument.base.exchange,
|
|
208
|
+
new AdapterFeedCommand(
|
|
209
|
+
instrument,
|
|
210
|
+
command.from,
|
|
211
|
+
command.to,
|
|
212
|
+
this.descriptor.feed,
|
|
213
|
+
timestamp =>
|
|
214
|
+
this.notify({
|
|
215
|
+
type: 'feed:updated',
|
|
216
|
+
timestamp,
|
|
217
|
+
from: command.from,
|
|
218
|
+
to: command.to
|
|
219
|
+
})
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
this.notify({ type: 'feed:completed' });
|
|
224
|
+
|
|
225
|
+
await accessor.session.dispose();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Sends a message to parent process.
|
|
230
|
+
*/
|
|
231
|
+
private notify(message: any) {
|
|
232
|
+
if (!process.send) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
process.send(message);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Starts new managed session and subscribes to parent process messages.
|
|
242
|
+
* @param descriptor session descriptor.
|
|
243
|
+
* @param commands collection of commands to execute before session is started.
|
|
244
|
+
* @returns new session.
|
|
245
|
+
*/
|
|
246
|
+
export async function run(
|
|
247
|
+
descriptor: SessionDescriptor,
|
|
248
|
+
...commands: IpcCommand[]
|
|
249
|
+
): Promise<Session> {
|
|
250
|
+
const handler = new IpcHandler(descriptor);
|
|
251
|
+
const accessor = new IpcSessionAccessor();
|
|
252
|
+
const argv = minimist(process.argv.slice(2));
|
|
253
|
+
|
|
254
|
+
if (argv.command) {
|
|
255
|
+
const json = Buffer.from(argv.command, 'base64').toString('utf-8');
|
|
256
|
+
|
|
257
|
+
commands.push(JSON.parse(json));
|
|
258
|
+
} else {
|
|
259
|
+
if (!commands.length) {
|
|
260
|
+
commands.push(new IpcPaperCommand());
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const command of commands) {
|
|
265
|
+
await handler.dispatch(command, accessor);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
process.on('message', async (request: any) => {
|
|
269
|
+
const response = await handler.dispatch(request, accessor);
|
|
270
|
+
|
|
271
|
+
if (response) {
|
|
272
|
+
process.send(response);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
return accessor.session;
|
|
277
|
+
}
|
package/src/session/index.ts
CHANGED
|
@@ -2,20 +2,49 @@ import { Adapter } from '../adapter';
|
|
|
2
2
|
import { Session } from '.';
|
|
3
3
|
import { Measurement } from '../storage/measurement';
|
|
4
4
|
import { Feed } from '../storage';
|
|
5
|
-
import { Behaviour } from '../behaviour';
|
|
6
5
|
import { Observable } from 'rxjs';
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Describes a single session.
|
|
9
|
+
* You can use @run function to start a new session managed by CLI.
|
|
10
|
+
* To start managed session you should install @quantform/cli package and run
|
|
11
|
+
* specific command:
|
|
12
|
+
* - qf paper (to paper trade strategy)
|
|
13
|
+
* - qf backtest (to backtest strategy based on provided feed)
|
|
14
|
+
* - qf live (to live trade strategy)
|
|
15
|
+
* or run on your own in code:
|
|
16
|
+
* - paper(descriptor, options)
|
|
17
|
+
* - backtest(descriptor, options)
|
|
18
|
+
* - live(descriptor)
|
|
19
|
+
*/
|
|
8
20
|
export interface SessionDescriptor {
|
|
9
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Unique session identifier, used to identify session in the storage.
|
|
23
|
+
* You can generate new id every time you start the new session or provide
|
|
24
|
+
* session id explicitly to resume previous session (in code or via CLI).
|
|
25
|
+
* If you don't provide session id, it will generate new one based on time.
|
|
26
|
+
*/
|
|
27
|
+
id?: number;
|
|
10
28
|
|
|
11
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Collection of adapters used to connect to the exchanges.
|
|
31
|
+
*/
|
|
12
32
|
adapter: Adapter[];
|
|
13
33
|
|
|
14
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Provides historical data for backtest, it's not required for live and paper
|
|
36
|
+
* sessions.
|
|
37
|
+
*/
|
|
15
38
|
feed?: Feed;
|
|
16
39
|
|
|
17
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Stores session variables i.e. indicators, orders, or any other type of time
|
|
42
|
+
* series data. You can install @quantform/editor to render this data in your browser.
|
|
43
|
+
*/
|
|
18
44
|
measurement?: Measurement;
|
|
19
45
|
|
|
20
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Describes your trading strategy.
|
|
48
|
+
*/
|
|
49
|
+
describe?: (session: Session) => Observable<any>;
|
|
21
50
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Session } from '.';
|
|
2
|
+
import { InstrumentSelector } from '../domain';
|
|
3
|
+
import { combineLatest, finalize, map, take, tap } from 'rxjs';
|
|
4
|
+
import { drawdown } from '../indicator';
|
|
5
|
+
import { floor, precision } from '../shared';
|
|
6
|
+
|
|
7
|
+
export function period() {
|
|
8
|
+
return (session: Session) => {
|
|
9
|
+
const period = session.useStatement('period');
|
|
10
|
+
|
|
11
|
+
return session.store.changes$.pipe(
|
|
12
|
+
finalize(() => (period.to = new Date(session.timestamp).toISOString())),
|
|
13
|
+
take(1),
|
|
14
|
+
tap(it => (period.from = new Date(it.timestamp).toISOString()))
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function benchmark(instrument: InstrumentSelector) {
|
|
20
|
+
return (session: Session) => {
|
|
21
|
+
const statement = session.useStatement('benchmark');
|
|
22
|
+
|
|
23
|
+
let balance: number, entry: number, exit: number;
|
|
24
|
+
let dd = 0;
|
|
25
|
+
|
|
26
|
+
return combineLatest([
|
|
27
|
+
session.orderbook(instrument),
|
|
28
|
+
session.balance(instrument.quote),
|
|
29
|
+
session.orderbook(instrument).pipe(
|
|
30
|
+
drawdown(it => it.midRate),
|
|
31
|
+
tap(it => (dd = it))
|
|
32
|
+
)
|
|
33
|
+
]).pipe(
|
|
34
|
+
map(([orderbook, quote]) => {
|
|
35
|
+
const price = orderbook.midRate;
|
|
36
|
+
|
|
37
|
+
if (!balance) {
|
|
38
|
+
balance = quote.total;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!entry) {
|
|
42
|
+
entry = price;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exit = price;
|
|
46
|
+
}),
|
|
47
|
+
finalize(() => {
|
|
48
|
+
const pnl = exit / entry - 1;
|
|
49
|
+
|
|
50
|
+
statement['benchmark_entry'] = entry;
|
|
51
|
+
statement['benchmark_exit'] = exit;
|
|
52
|
+
statement['benchmark_pnl'] = floor(balance * pnl, precision(entry));
|
|
53
|
+
statement['benchmark_pnl_pp'] = floor(pnl * 100, 2);
|
|
54
|
+
statement['benchmark_drawdown_pp'] = floor(dd * 100, 2);
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function equity(instrument: InstrumentSelector) {
|
|
61
|
+
return (session: Session) => {
|
|
62
|
+
const statement = session.useStatement('benchmark');
|
|
63
|
+
|
|
64
|
+
let balance: number, min: number, max: number;
|
|
65
|
+
let dd = 0;
|
|
66
|
+
let equity = 0;
|
|
67
|
+
|
|
68
|
+
return combineLatest([
|
|
69
|
+
session.orderbook(instrument),
|
|
70
|
+
session.balance(instrument.base),
|
|
71
|
+
session.balance(instrument.quote),
|
|
72
|
+
session.positions(instrument)
|
|
73
|
+
]).pipe(
|
|
74
|
+
tap(([, , quote]) => {
|
|
75
|
+
if (!balance) {
|
|
76
|
+
balance = quote.total;
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
map(([orderbook, base, quote, positions]) =>
|
|
80
|
+
orderbook.instrument.quote.fixed(
|
|
81
|
+
quote.total +
|
|
82
|
+
base.total * orderbook.bestBidRate +
|
|
83
|
+
positions.reduce(
|
|
84
|
+
(agg, position) => agg + position.calculatePnL(orderbook.bestBidRate),
|
|
85
|
+
0
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
),
|
|
89
|
+
tap(it => {
|
|
90
|
+
if (!min) {
|
|
91
|
+
min = it;
|
|
92
|
+
} else {
|
|
93
|
+
min = Math.min(min, it);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!max) {
|
|
97
|
+
max = it;
|
|
98
|
+
} else {
|
|
99
|
+
max = Math.max(max, it);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
equity = it;
|
|
103
|
+
}),
|
|
104
|
+
drawdown(it => it),
|
|
105
|
+
tap(it => (dd = it)),
|
|
106
|
+
finalize(() => {
|
|
107
|
+
const pnl = equity / balance - 1;
|
|
108
|
+
const scale = precision(equity);
|
|
109
|
+
|
|
110
|
+
statement['equity'] = equity;
|
|
111
|
+
statement['equity_min'] = min;
|
|
112
|
+
statement['equity_max'] = max;
|
|
113
|
+
statement['equity_pnl'] = floor(balance * pnl, scale);
|
|
114
|
+
statement['equity_pnl_pp'] = floor(pnl * 100, 2);
|
|
115
|
+
statement['equity_drawdown_pp'] = floor(dd * 100, 2);
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { InstrumentPatchEvent } from '../store/event';
|
|
2
|
-
import { Asset,
|
|
3
|
-
import { now } from '../
|
|
2
|
+
import { Asset, Commission } from '../domain';
|
|
3
|
+
import { now } from '../shared';
|
|
4
4
|
import { SessionDescriptor } from './session-descriptor';
|
|
5
5
|
import { paper } from '../bin';
|
|
6
|
+
import { Session } from './session';
|
|
7
|
+
import { of } from 'rxjs';
|
|
6
8
|
|
|
7
9
|
describe('session tests', () => {
|
|
8
10
|
const descriptor: SessionDescriptor = {
|
|
@@ -29,14 +31,14 @@ describe('session tests', () => {
|
|
|
29
31
|
now(),
|
|
30
32
|
new Asset('de30', 'cex', 2),
|
|
31
33
|
new Asset('usd', 'cex', 2),
|
|
32
|
-
new
|
|
34
|
+
new Commission(0, 0),
|
|
33
35
|
''
|
|
34
36
|
),
|
|
35
37
|
new InstrumentPatchEvent(
|
|
36
38
|
now(),
|
|
37
39
|
new Asset('wig20', 'cex', 2),
|
|
38
40
|
new Asset('pln', 'cex', 2),
|
|
39
|
-
new
|
|
41
|
+
new Commission(0, 0),
|
|
40
42
|
''
|
|
41
43
|
)
|
|
42
44
|
);
|
package/src/session/session.ts
CHANGED
|
@@ -22,9 +22,8 @@ import {
|
|
|
22
22
|
} from '../domain';
|
|
23
23
|
import { Store } from '../store';
|
|
24
24
|
import { concat, from, Observable, Subject, Subscription } from 'rxjs';
|
|
25
|
-
import { Behaviour, CombinedBehaviour, FunctionBehaviour } from '../behaviour';
|
|
26
25
|
import { AdapterAggregate } from '../adapter/adapter-aggregate';
|
|
27
|
-
import { Worker } from '../
|
|
26
|
+
import { Worker, now } from '../shared';
|
|
28
27
|
import { Trade } from '../domain/trade';
|
|
29
28
|
import { SessionDescriptor } from './session-descriptor';
|
|
30
29
|
import {
|
|
@@ -39,18 +38,24 @@ type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
|
|
|
39
38
|
export class Session {
|
|
40
39
|
private initialized = false;
|
|
41
40
|
private subscription: Subscription;
|
|
42
|
-
private behaviour: Behaviour;
|
|
43
41
|
private worker = new Worker();
|
|
44
42
|
|
|
45
43
|
get timestamp(): number {
|
|
46
44
|
return this.store.snapshot.timestamp;
|
|
47
45
|
}
|
|
48
46
|
|
|
47
|
+
readonly statement: Record<string, Record<string, any>> = {};
|
|
48
|
+
|
|
49
49
|
constructor(
|
|
50
50
|
readonly store: Store,
|
|
51
51
|
readonly aggregate: AdapterAggregate,
|
|
52
52
|
readonly descriptor?: SessionDescriptor
|
|
53
|
-
) {
|
|
53
|
+
) {
|
|
54
|
+
// generate session id based on time if not provided.
|
|
55
|
+
if (descriptor && !descriptor.id) {
|
|
56
|
+
descriptor.id = now();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
54
59
|
|
|
55
60
|
async awake(): Promise<void> {
|
|
56
61
|
if (this.initialized) {
|
|
@@ -59,18 +64,11 @@ export class Session {
|
|
|
59
64
|
|
|
60
65
|
this.initialized = true;
|
|
61
66
|
|
|
67
|
+
// awake all adapters and synchronize trading accounts with store.
|
|
62
68
|
await this.aggregate.awake(this.descriptor != null);
|
|
63
69
|
|
|
64
|
-
if (this.descriptor
|
|
65
|
-
|
|
66
|
-
this.behaviour = new FunctionBehaviour(this.descriptor.behaviour);
|
|
67
|
-
} else {
|
|
68
|
-
this.behaviour = Array.isArray(this.descriptor.behaviour)
|
|
69
|
-
? new CombinedBehaviour(this.descriptor.behaviour)
|
|
70
|
-
: this.descriptor.behaviour;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
this.subscription = this.behaviour.describe(this).subscribe();
|
|
70
|
+
if (this.descriptor.describe) {
|
|
71
|
+
this.subscription = this.descriptor.describe(this).subscribe();
|
|
74
72
|
}
|
|
75
73
|
}
|
|
76
74
|
|
|
@@ -89,14 +87,20 @@ export class Session {
|
|
|
89
87
|
await this.worker.wait();
|
|
90
88
|
}
|
|
91
89
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.behaviour.statement(output);
|
|
90
|
+
useStatement(section: string): Record<string, any> {
|
|
91
|
+
return this.statement[section] ?? (this.statement[section] = {});
|
|
98
92
|
}
|
|
99
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Returns last stored measurement and setter for it in session.
|
|
96
|
+
* For example you can save and restore variables in same session between runs.
|
|
97
|
+
* Example usage:
|
|
98
|
+
* const [order$, setOrder] = session.measurement<Order>('order');
|
|
99
|
+
*
|
|
100
|
+
* order.pipe(tap(it => console.log(`your last order was: ${it}`)));
|
|
101
|
+
*
|
|
102
|
+
* setOrder(order);
|
|
103
|
+
*/
|
|
100
104
|
useMeasure<T extends { timestamp: number }>(
|
|
101
105
|
params: { kind: string; timestamp?: number },
|
|
102
106
|
defaultValue: T = undefined
|
|
@@ -130,10 +134,19 @@ export class Session {
|
|
|
130
134
|
return [concat(stored$, subject$.asObservable()), setter];
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Return values for patch provided in optimization file.
|
|
139
|
+
* Example usage:
|
|
140
|
+
* const orderSize = session.useOptimizer('order.size');
|
|
141
|
+
*/
|
|
133
142
|
useOptimizer(path: string): any {
|
|
134
143
|
return undefined;
|
|
135
144
|
}
|
|
136
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Subscribes to specific instrument. Usually forces adapter to subscribe
|
|
148
|
+
* for orderbook and ticker streams.
|
|
149
|
+
*/
|
|
137
150
|
async subscribe(instrument: Array<InstrumentSelector>): Promise<void> {
|
|
138
151
|
const grouped = instrument
|
|
139
152
|
.filter(it => it != null)
|
|
@@ -154,6 +167,11 @@ export class Session {
|
|
|
154
167
|
}
|
|
155
168
|
}
|
|
156
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Opens collection of orders.
|
|
172
|
+
* Example:
|
|
173
|
+
* session.open(Order.buyMarket(instrument, 100));
|
|
174
|
+
*/
|
|
157
175
|
async open(...orders: Order[]): Promise<void> {
|
|
158
176
|
await Promise.all(
|
|
159
177
|
orders.map(it =>
|
|
@@ -165,6 +183,9 @@ export class Session {
|
|
|
165
183
|
);
|
|
166
184
|
}
|
|
167
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Cancels specific order.
|
|
188
|
+
*/
|
|
168
189
|
cancel(order: Order): Promise<void> {
|
|
169
190
|
return this.aggregate.dispatch(
|
|
170
191
|
order.instrument.base.exchange,
|
|
@@ -172,6 +193,10 @@ export class Session {
|
|
|
172
193
|
);
|
|
173
194
|
}
|
|
174
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Subscribes to specific instrument changes.
|
|
198
|
+
* When adapter awake then it will fetch collection of all available instruments.
|
|
199
|
+
*/
|
|
175
200
|
instrument(selector: InstrumentSelector): Observable<Instrument> {
|
|
176
201
|
this.subscribe([selector]);
|
|
177
202
|
|
|
@@ -181,6 +206,10 @@ export class Session {
|
|
|
181
206
|
);
|
|
182
207
|
}
|
|
183
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Subscribes to instruments changes.
|
|
211
|
+
* When adapter awake then it will fetch collection of all available instruments.
|
|
212
|
+
*/
|
|
184
213
|
instruments(): Observable<Instrument[]> {
|
|
185
214
|
return this.store.changes$.pipe(
|
|
186
215
|
filter(it => it instanceof Instrument),
|
|
@@ -191,6 +220,9 @@ export class Session {
|
|
|
191
220
|
);
|
|
192
221
|
}
|
|
193
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Subscribes to trade/ticker changes.
|
|
225
|
+
*/
|
|
194
226
|
trade(selector?: InstrumentSelector): Observable<Trade> {
|
|
195
227
|
this.subscribe([selector]);
|
|
196
228
|
|
|
@@ -204,6 +236,10 @@ export class Session {
|
|
|
204
236
|
);
|
|
205
237
|
}
|
|
206
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Subscribes to orderbook changes.
|
|
241
|
+
* Right now you can access only best bid and best ask.
|
|
242
|
+
*/
|
|
207
243
|
orderbook(selector?: InstrumentSelector): Observable<Orderbook> {
|
|
208
244
|
this.subscribe([selector]);
|
|
209
245
|
|
|
@@ -217,6 +253,9 @@ export class Session {
|
|
|
217
253
|
);
|
|
218
254
|
}
|
|
219
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Subscribes to position on leveraged market.
|
|
258
|
+
*/
|
|
220
259
|
position(selector?: InstrumentSelector): Observable<Position> {
|
|
221
260
|
this.subscribe([selector]);
|
|
222
261
|
|
|
@@ -230,6 +269,9 @@ export class Session {
|
|
|
230
269
|
);
|
|
231
270
|
}
|
|
232
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Subscribes to positions on leveraged markets.
|
|
274
|
+
*/
|
|
233
275
|
positions(selector: InstrumentSelector): Observable<Position[]> {
|
|
234
276
|
this.subscribe([selector]);
|
|
235
277
|
|
|
File without changes
|
|
File without changes
|