@quantform/core 0.4.9 → 0.5.14
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 +4 -2
- package/dist/adapter/adapter-aggregate.js +4 -3
- package/dist/adapter/adapter-aggregate.js.map +1 -1
- package/dist/adapter/adapter.d.ts +3 -2
- package/dist/adapter/adapter.js +5 -2
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-adapter.d.ts +1 -1
- package/dist/adapter/backtester/backtester-adapter.js +2 -1
- package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
- package/dist/adapter/backtester/index.js +5 -1
- package/dist/adapter/backtester/index.js.map +1 -1
- package/dist/adapter/index.js +5 -1
- package/dist/adapter/index.js.map +1 -1
- package/dist/adapter/paper/index.js +5 -1
- package/dist/adapter/paper/index.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.d.ts +1 -1
- package/dist/adapter/paper/paper-adapter.js +1 -1
- package/dist/adapter/paper/paper-adapter.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.spec.js +5 -3
- package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
- package/dist/adapter/paper/simulator/paper-margin-simulator.d.ts +1 -1
- package/dist/adapter/paper/simulator/paper-margin-simulator.js.map +1 -1
- package/dist/adapter/paper/simulator/paper-simulator.d.ts +1 -1
- package/dist/adapter/paper/simulator/paper-simulator.js.map +1 -1
- package/dist/adapter/paper/simulator/paper-spot-simulator.d.ts +1 -1
- package/dist/adapter/paper/simulator/paper-spot-simulator.js +1 -1
- package/dist/adapter/paper/simulator/paper-spot-simulator.js.map +1 -1
- package/dist/bootstrap.js +31 -15
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli/build.d.ts +1 -0
- package/dist/cli/build.js +16 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/dev.d.ts +1 -0
- package/dist/cli/dev.js +17 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +72 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/internal/workspace.d.ts +8 -0
- package/dist/cli/internal/workspace.js +14 -0
- package/dist/cli/internal/workspace.js.map +1 -0
- package/dist/cli/pull.d.ts +1 -0
- package/dist/cli/pull.js +50 -0
- package/dist/cli/pull.js.map +1 -0
- package/dist/cli/run.d.ts +1 -0
- package/dist/cli/run.js +19 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/test.d.ts +1 -0
- package/dist/cli/test.js +37 -0
- package/dist/cli/test.js.map +1 -0
- package/dist/domain/balance.d.ts +4 -3
- package/dist/domain/balance.js +20 -19
- package/dist/domain/balance.js.map +1 -1
- package/dist/domain/balance.spec.js +2 -2
- package/dist/domain/balance.spec.js.map +1 -1
- package/dist/domain/candle.d.ts +4 -2
- package/dist/domain/candle.js +13 -9
- package/dist/domain/candle.js.map +1 -1
- package/dist/domain/candle.spec.js.map +1 -1
- package/dist/domain/component.d.ts +1 -0
- package/dist/domain/index.js +5 -1
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/instrument.d.ts +1 -0
- package/dist/domain/instrument.js +1 -0
- package/dist/domain/instrument.js.map +1 -1
- package/dist/domain/order.d.ts +3 -2
- package/dist/domain/order.js +2 -0
- package/dist/domain/order.js.map +1 -1
- package/dist/domain/orderbook.d.ts +1 -0
- package/dist/domain/orderbook.js +1 -0
- package/dist/domain/orderbook.js.map +1 -1
- package/dist/domain/position.d.ts +1 -0
- package/dist/domain/position.js +1 -0
- package/dist/domain/position.js.map +1 -1
- package/dist/domain/session.d.ts +4 -18
- package/dist/domain/session.js +7 -23
- package/dist/domain/session.js.map +1 -1
- package/dist/domain/statement.js +11 -11
- package/dist/domain/statement.js.map +1 -1
- package/dist/domain/trade.d.ts +2 -1
- package/dist/domain/trade.js +1 -0
- package/dist/domain/trade.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/indicator/atr.js.map +1 -1
- package/dist/indicator/cross.spec.js.map +1 -1
- package/dist/indicator/donchian.js.map +1 -1
- package/dist/indicator/ema.js.map +1 -1
- package/dist/indicator/ema.spec.js.map +1 -1
- package/dist/indicator/envelope.js.map +1 -1
- package/dist/indicator/index.js +5 -1
- package/dist/indicator/index.js.map +1 -1
- package/dist/indicator/macd.js.map +1 -1
- package/dist/indicator/min-max.js.map +1 -1
- package/dist/indicator/rma.js.map +1 -1
- package/dist/indicator/sma.js.map +1 -1
- package/dist/indicator/sma.spec.js.map +1 -1
- package/dist/indicator/swma.js.map +1 -1
- package/dist/indicator/tma.js.map +1 -1
- package/dist/indicator/tma.spec.js.map +1 -1
- package/dist/indicator/trailing.spec.js.map +1 -1
- package/dist/indicator/truerange.js.map +1 -1
- package/dist/indicator/truerange.spec.js.map +1 -1
- package/dist/indicator/window.js.map +1 -1
- package/dist/indicator/wma.js.map +1 -1
- package/dist/indicator/wma.spec.js.map +1 -1
- package/dist/shared/index.d.ts +0 -2
- package/dist/shared/index.js +5 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/policy.spec.js +2 -6
- package/dist/shared/policy.spec.js.map +1 -1
- package/dist/storage/cache.d.ts +9 -0
- package/dist/storage/cache.js +32 -0
- package/dist/storage/cache.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +6 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/storage.d.ts +7 -0
- package/dist/storage/storage.js +11 -1
- package/dist/storage/storage.js.map +1 -1
- package/dist/store/event/index.js +5 -1
- package/dist/store/event/index.js.map +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.map +1 -1
- package/dist/store/event/store-candle.event.spec.js +1 -1
- package/dist/store/event/store-candle.event.spec.js.map +1 -1
- package/dist/store/event/store-instrument.event.spec.js +1 -1
- package/dist/store/event/store-instrument.event.spec.js.map +1 -1
- package/dist/store/event/store-trade.event.spec.js +2 -2
- package/dist/store/event/store-trade.event.spec.js.map +1 -1
- package/dist/store/index.js +5 -1
- package/dist/store/index.js.map +1 -1
- package/dist/store/store.js.map +1 -1
- package/dist/tests/backtester-adapter.spec.js +2 -1
- package/dist/tests/backtester-adapter.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -21
- package/src/adapter/adapter-aggregate.ts +8 -3
- package/src/adapter/adapter.ts +9 -3
- package/src/adapter/backtester/backtester-adapter.ts +3 -1
- package/src/adapter/paper/paper-adapter.spec.ts +5 -3
- package/src/adapter/paper/paper-adapter.ts +1 -1
- package/src/adapter/paper/simulator/paper-margin-simulator.ts +1 -1
- package/src/adapter/paper/simulator/paper-simulator.ts +2 -1
- package/src/adapter/paper/simulator/paper-spot-simulator.ts +2 -2
- package/src/bootstrap.ts +47 -18
- package/src/cli/build.ts +15 -0
- package/src/cli/dev.ts +18 -0
- package/src/cli/index.ts +81 -0
- package/src/cli/internal/workspace.ts +18 -0
- package/src/cli/pull.ts +67 -0
- package/src/cli/run.ts +22 -0
- package/src/cli/test.ts +50 -0
- package/src/domain/balance.spec.ts +2 -2
- package/src/domain/balance.ts +20 -19
- package/src/domain/candle.spec.ts +1 -0
- package/src/domain/candle.ts +20 -10
- package/src/domain/component.ts +1 -0
- package/src/domain/instrument.ts +1 -0
- package/src/domain/order.ts +3 -1
- package/src/domain/orderbook.ts +1 -0
- package/src/domain/position.ts +2 -0
- package/src/domain/session.ts +17 -67
- package/src/domain/statement.ts +13 -12
- package/src/domain/trade.ts +2 -1
- package/src/index.ts +0 -1
- package/src/indicator/atr.ts +1 -0
- package/src/indicator/cross.spec.ts +1 -0
- package/src/indicator/donchian.ts +1 -0
- package/src/indicator/ema.spec.ts +1 -0
- package/src/indicator/ema.ts +1 -0
- package/src/indicator/envelope.ts +1 -0
- package/src/indicator/macd.ts +1 -0
- package/src/indicator/min-max.ts +1 -0
- package/src/indicator/rma.ts +1 -0
- package/src/indicator/sma.spec.ts +1 -0
- package/src/indicator/sma.ts +1 -0
- package/src/indicator/swma.ts +1 -0
- package/src/indicator/tma.spec.ts +1 -0
- package/src/indicator/tma.ts +1 -0
- package/src/indicator/trailing.spec.ts +1 -0
- package/src/indicator/truerange.spec.ts +1 -0
- package/src/indicator/truerange.ts +1 -0
- package/src/indicator/window.ts +1 -0
- package/src/indicator/wma.spec.ts +1 -0
- package/src/indicator/wma.ts +1 -0
- package/src/shared/index.ts +0 -2
- package/src/shared/policy.spec.ts +2 -6
- package/src/storage/cache.ts +35 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/storage.ts +15 -0
- package/src/store/event/store-balance.event.spec.ts +3 -3
- package/src/store/event/store-candle.event.spec.ts +1 -1
- package/src/store/event/store-candle.event.ts +2 -2
- package/src/store/event/store-instrument.event.spec.ts +1 -1
- package/src/store/event/store-trade.event.spec.ts +2 -2
- package/src/store/store.ts +1 -0
- package/src/tests/backtester-adapter.spec.ts +3 -2
- package/tsconfig.json +2 -2
- package/dist/ipc.d.ts +0 -33
- package/dist/ipc.js +0 -244
- package/dist/ipc.js.map +0 -1
- package/dist/ipc.spec.d.ts +0 -1
- package/dist/ipc.spec.js +0 -57
- package/dist/ipc.spec.js.map +0 -1
- package/dist/shared/task.d.ts +0 -6
- package/dist/shared/task.js +0 -25
- package/dist/shared/task.js.map +0 -1
- package/dist/shared/worker.d.ts +0 -10
- package/dist/shared/worker.js +0 -46
- package/dist/shared/worker.js.map +0 -1
- package/dist/shared/worker.spec.d.ts +0 -1
- package/dist/shared/worker.spec.js +0 -18
- package/dist/shared/worker.spec.js.map +0 -1
- package/src/ipc.spec.ts +0 -73
- package/src/ipc.ts +0 -321
- package/src/shared/task.ts +0 -30
- package/src/shared/worker.spec.ts +0 -25
- package/src/shared/worker.ts +0 -55
package/src/adapter/adapter.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Candle, InstrumentSelector, Order } from '../domain';
|
|
2
2
|
import { now, timestamp } from '../shared';
|
|
3
|
-
import { Feed } from '../storage';
|
|
3
|
+
import { Cache, Feed } from '../storage';
|
|
4
4
|
import { State, Store, StoreEvent } from '../store';
|
|
5
5
|
import { PaperAdapter } from './paper';
|
|
6
6
|
import { PaperSimulator } from './paper/simulator/paper-simulator';
|
|
@@ -20,7 +20,11 @@ export class AdapterContext {
|
|
|
20
20
|
return this.store.snapshot;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
constructor(
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly adapter: Adapter,
|
|
25
|
+
private readonly store: Store,
|
|
26
|
+
readonly cache: Cache
|
|
27
|
+
) {}
|
|
24
28
|
|
|
25
29
|
dispatch(...events: StoreEvent[]) {
|
|
26
30
|
return this.store.dispatch(...events);
|
|
@@ -65,7 +69,9 @@ export abstract class Adapter {
|
|
|
65
69
|
/**
|
|
66
70
|
* Dispose an adapter.
|
|
67
71
|
*/
|
|
68
|
-
async dispose(): Promise<void> {
|
|
72
|
+
async dispose(): Promise<void> {
|
|
73
|
+
throw new Error('method not implemented');
|
|
74
|
+
}
|
|
69
75
|
|
|
70
76
|
/**
|
|
71
77
|
* Subscribe to collection of instruments.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Adapter, AdapterContext } from '..';
|
|
2
1
|
import { Candle, InstrumentSelector, Order } from '../../domain';
|
|
3
2
|
import { timestamp } from '../../shared';
|
|
4
3
|
import { InstrumentSubscriptionEvent } from '../../store';
|
|
4
|
+
import { Adapter, AdapterContext } from '..';
|
|
5
5
|
import { FeedQuery, HistoryQuery } from '../adapter';
|
|
6
6
|
import { PaperAdapter, PaperOptions } from '../paper';
|
|
7
7
|
import { PaperSimulator } from '../paper/simulator/paper-simulator';
|
|
@@ -42,6 +42,8 @@ export class BacktesterAdapter extends Adapter {
|
|
|
42
42
|
it => new InstrumentSubscriptionEvent(this.context.timestamp, it, true)
|
|
43
43
|
)
|
|
44
44
|
);
|
|
45
|
+
|
|
46
|
+
this.streamer.tryContinue();
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
account(): Promise<void> {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AdapterContext } from '..';
|
|
1
|
+
import { Cache, InMemoryStorage } from '../../storage';
|
|
3
2
|
import { InstrumentPatchEvent, Store } from '../../store';
|
|
3
|
+
import { AdapterContext } from '..';
|
|
4
4
|
import { Adapter } from '../adapter';
|
|
5
5
|
import { Asset, Commission, instrumentOf, Order } from './../../domain';
|
|
6
|
+
import { PaperSpotSimulator } from '.';
|
|
6
7
|
import { PaperAdapter } from './paper-adapter';
|
|
7
8
|
import { PaperSimulator } from './simulator/paper-simulator';
|
|
8
9
|
|
|
@@ -52,10 +53,11 @@ describe('paper adapter tests', () => {
|
|
|
52
53
|
test('', async () => {
|
|
53
54
|
const store = new Store();
|
|
54
55
|
const adapter = new DefaultAdapter();
|
|
56
|
+
const cache = new Cache(new InMemoryStorage());
|
|
55
57
|
|
|
56
58
|
const sut = new PaperAdapter(adapter, store, options);
|
|
57
59
|
|
|
58
|
-
await sut.awake(new AdapterContext(sut, store));
|
|
60
|
+
await sut.awake(new AdapterContext(sut, store, cache));
|
|
59
61
|
await sut.account();
|
|
60
62
|
|
|
61
63
|
const order = Order.buyMarket(instrumentOf('default:a-b'), 1.0);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Adapter, AdapterContext } from '..';
|
|
2
1
|
import { assetOf, Candle, InstrumentSelector, Order } from '../../domain';
|
|
3
2
|
import { BalancePatchEvent, Store } from '../../store';
|
|
3
|
+
import { Adapter, AdapterContext } from '..';
|
|
4
4
|
import { FeedQuery, HistoryQuery } from '../adapter';
|
|
5
5
|
import { PaperSimulator } from './simulator/paper-simulator';
|
|
6
6
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PaperAdapter } from '..';
|
|
2
1
|
import { Order } from '../../../domain';
|
|
3
2
|
import { pnl, weightedMean } from '../../../shared';
|
|
4
3
|
import {
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
OrderPendingEvent,
|
|
11
10
|
PositionPatchEvent
|
|
12
11
|
} from '../../../store';
|
|
12
|
+
import { PaperAdapter } from '..';
|
|
13
13
|
import { PaperSimulator } from './paper-simulator';
|
|
14
14
|
|
|
15
15
|
export class PaperMarginSimulator extends PaperSimulator {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { tap } from 'rxjs';
|
|
2
2
|
import { Set } from 'typescript-collections';
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import { Component, InstrumentSelector, Order, Orderbook, Trade } from '../../../domain';
|
|
5
|
+
import { PaperAdapter } from '..';
|
|
5
6
|
|
|
6
7
|
export abstract class PaperSimulator {
|
|
7
8
|
private readonly pending: Record<string, Set<Order>> = {};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PaperAdapter } from '..';
|
|
2
1
|
import { Order } from '../../../domain';
|
|
3
2
|
import { timestamp } from '../../../shared';
|
|
4
3
|
import {
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
OrderNewEvent,
|
|
12
11
|
OrderPendingEvent
|
|
13
12
|
} from '../../../store';
|
|
13
|
+
import { PaperAdapter } from '..';
|
|
14
14
|
import { PaperSimulator } from './paper-simulator';
|
|
15
15
|
|
|
16
16
|
export class PaperSpotSimulator extends PaperSimulator {
|
|
@@ -111,7 +111,7 @@ export class PaperSpotSimulator extends PaperSimulator {
|
|
|
111
111
|
switch (order.type) {
|
|
112
112
|
case 'MARKET':
|
|
113
113
|
return [
|
|
114
|
-
new BalanceUnfreezEvent(order.instrument.quote, quote.
|
|
114
|
+
new BalanceUnfreezEvent(order.instrument.quote, quote.locked, timestamp)
|
|
115
115
|
];
|
|
116
116
|
|
|
117
117
|
case 'LIMIT':
|
package/src/bootstrap.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
PaperAdapter
|
|
7
7
|
} from './adapter';
|
|
8
8
|
import { Session, SessionDescriptor } from './domain';
|
|
9
|
+
import { Cache, Feed, InMemoryStorage, InMemoryStorageFactory } from './storage';
|
|
9
10
|
import { Store } from './store';
|
|
10
11
|
|
|
11
12
|
export class Bootstrap {
|
|
@@ -29,12 +30,20 @@ export class Bootstrap {
|
|
|
29
30
|
* @param to
|
|
30
31
|
*/
|
|
31
32
|
useBacktestPeriod(from?: number, to?: number): Bootstrap {
|
|
33
|
+
if (!this.descriptor.simulation) {
|
|
34
|
+
this.descriptor.simulation = {
|
|
35
|
+
balance: {},
|
|
36
|
+
from: undefined,
|
|
37
|
+
to: undefined
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
if (from) {
|
|
33
|
-
this.descriptor.
|
|
42
|
+
this.descriptor.simulation.from = from;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
if (to) {
|
|
37
|
-
this.descriptor.
|
|
46
|
+
this.descriptor.simulation.to = to;
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
return this;
|
|
@@ -47,16 +56,27 @@ export class Bootstrap {
|
|
|
47
56
|
*/
|
|
48
57
|
backtest(listener?: BacktesterListener): [Session, BacktesterStreamer] {
|
|
49
58
|
const store = new Store();
|
|
50
|
-
const {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
const { storage } = this.descriptor;
|
|
60
|
+
const feed = new Feed(storage.create('feed'));
|
|
61
|
+
const cache = new Cache(storage.create('cache'));
|
|
62
|
+
|
|
63
|
+
const streamer = new BacktesterStreamer(
|
|
64
|
+
store,
|
|
65
|
+
feed,
|
|
66
|
+
this.descriptor.simulation,
|
|
67
|
+
listener
|
|
68
|
+
);
|
|
54
69
|
|
|
55
70
|
const aggregate = new AdapterAggregate(
|
|
56
71
|
this.descriptor.adapter.map(
|
|
57
|
-
it =>
|
|
72
|
+
it =>
|
|
73
|
+
new BacktesterAdapter(
|
|
74
|
+
new PaperAdapter(it, store, this.descriptor.simulation),
|
|
75
|
+
streamer
|
|
76
|
+
)
|
|
58
77
|
),
|
|
59
|
-
store
|
|
78
|
+
store,
|
|
79
|
+
cache
|
|
60
80
|
);
|
|
61
81
|
|
|
62
82
|
const session = new Session(store, aggregate, this.descriptor);
|
|
@@ -69,22 +89,28 @@ export class Bootstrap {
|
|
|
69
89
|
* @returns new session object.
|
|
70
90
|
*/
|
|
71
91
|
paper(): Session {
|
|
72
|
-
if (!this.descriptor.
|
|
73
|
-
this.descriptor.
|
|
92
|
+
if (!this.descriptor.simulation) {
|
|
93
|
+
this.descriptor.simulation = {
|
|
94
|
+
balance: {},
|
|
95
|
+
from: undefined,
|
|
96
|
+
to: undefined
|
|
97
|
+
};
|
|
74
98
|
}
|
|
75
99
|
|
|
76
|
-
if (!this.descriptor.
|
|
77
|
-
this.descriptor.
|
|
78
|
-
balance: {}
|
|
79
|
-
};
|
|
100
|
+
if (!this.descriptor.simulation.balance) {
|
|
101
|
+
this.descriptor.simulation.balance = {};
|
|
80
102
|
}
|
|
81
103
|
|
|
82
104
|
const store = new Store();
|
|
83
|
-
const
|
|
105
|
+
const storage = this.descriptor.storage ?? new InMemoryStorageFactory();
|
|
106
|
+
const cache = new Cache(storage.create('cache'));
|
|
84
107
|
|
|
85
108
|
const aggregate = new AdapterAggregate(
|
|
86
|
-
this.descriptor.adapter.map(
|
|
87
|
-
|
|
109
|
+
this.descriptor.adapter.map(
|
|
110
|
+
it => new PaperAdapter(it, store, this.descriptor.simulation)
|
|
111
|
+
),
|
|
112
|
+
store,
|
|
113
|
+
cache
|
|
88
114
|
);
|
|
89
115
|
|
|
90
116
|
return new Session(store, aggregate, this.descriptor);
|
|
@@ -96,7 +122,10 @@ export class Bootstrap {
|
|
|
96
122
|
*/
|
|
97
123
|
live(): Session {
|
|
98
124
|
const store = new Store();
|
|
99
|
-
const
|
|
125
|
+
const storage = this.descriptor.storage ?? new InMemoryStorageFactory();
|
|
126
|
+
const cache = new Cache(storage.create('cache'));
|
|
127
|
+
|
|
128
|
+
const aggregate = new AdapterAggregate(this.descriptor.adapter, store, cache);
|
|
100
129
|
|
|
101
130
|
return new Session(store, aggregate, this.descriptor);
|
|
102
131
|
}
|
package/src/cli/build.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
|
|
3
|
+
import { buildDirectory } from './internal/workspace';
|
|
4
|
+
|
|
5
|
+
export default async function (): Promise<number> {
|
|
6
|
+
return new Promise<number>((resolve, reject) => {
|
|
7
|
+
const process = spawn('swc', ['./src', '--out-dir', buildDirectory()], {
|
|
8
|
+
stdio: 'inherit',
|
|
9
|
+
shell: false
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
process.once('exit', resolve);
|
|
13
|
+
process.once('error', reject);
|
|
14
|
+
});
|
|
15
|
+
}
|
package/src/cli/dev.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Bootstrap } from '../bootstrap';
|
|
2
|
+
import build from './build';
|
|
3
|
+
import { getModule } from './internal/workspace';
|
|
4
|
+
|
|
5
|
+
export default async function (name: string, options: any) {
|
|
6
|
+
if (await build()) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const id = options.id ? Number(options.id) : undefined;
|
|
11
|
+
|
|
12
|
+
const module = await getModule(name);
|
|
13
|
+
|
|
14
|
+
const bootstrap = new Bootstrap(module.descriptor);
|
|
15
|
+
const session = bootstrap.useSessionId(id).paper();
|
|
16
|
+
|
|
17
|
+
await session.awake(module.default);
|
|
18
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { ChildProcess, spawn } from 'child_process';
|
|
4
|
+
import { program } from 'commander';
|
|
5
|
+
import watch from 'node-watch';
|
|
6
|
+
|
|
7
|
+
import build from './build';
|
|
8
|
+
import dev from './dev';
|
|
9
|
+
import pull from './pull';
|
|
10
|
+
import run from './run';
|
|
11
|
+
import test from './test';
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command('build')
|
|
15
|
+
.description('builds a production version of the app')
|
|
16
|
+
.action(async () => {
|
|
17
|
+
await build();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command('run')
|
|
22
|
+
.argument('<name>', 'strategy to execute')
|
|
23
|
+
.option('-i, --id <id>', 'session identifier')
|
|
24
|
+
.option('-w', 'watch mode')
|
|
25
|
+
.description('executes strategy in live trading mode')
|
|
26
|
+
.action(run);
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('dev')
|
|
30
|
+
.argument('<name>', 'strategy to execute')
|
|
31
|
+
.option('-i, --id <id>', 'session identifier')
|
|
32
|
+
.option('-w', 'watch mode')
|
|
33
|
+
.description('executes strategy in paper e.g. simulation mode')
|
|
34
|
+
.action(dev);
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.command('test')
|
|
38
|
+
.description('executes strategy in backtesting mode for specified period')
|
|
39
|
+
.argument('<name>', 'strategy to execute')
|
|
40
|
+
.option('-f, --from <from>', 'date from')
|
|
41
|
+
.option('-t, --to <to>', 'date to')
|
|
42
|
+
.option('-w', 'watch mode')
|
|
43
|
+
.action(test);
|
|
44
|
+
|
|
45
|
+
program
|
|
46
|
+
.command('pull')
|
|
47
|
+
.description('pulls instrument historical data to storage')
|
|
48
|
+
.argument('<name>', 'strategy to execute')
|
|
49
|
+
.argument('<instrument>', 'instrument to import')
|
|
50
|
+
.option('-f, --from <from>', 'date from')
|
|
51
|
+
.option('-t, --to <to>', 'date to')
|
|
52
|
+
.action(pull);
|
|
53
|
+
|
|
54
|
+
program.name('quantform').description('quantform tools');
|
|
55
|
+
|
|
56
|
+
if (process.argv.length < 3) {
|
|
57
|
+
program.help();
|
|
58
|
+
} else {
|
|
59
|
+
if (process.argv.every(it => it != '-w')) {
|
|
60
|
+
program.parse(process.argv);
|
|
61
|
+
} else {
|
|
62
|
+
const argv = process.argv.splice(1).filter(it => it != '-w');
|
|
63
|
+
let child: ChildProcess;
|
|
64
|
+
|
|
65
|
+
const spawnChildProcess = () => {
|
|
66
|
+
console.clear();
|
|
67
|
+
|
|
68
|
+
if (child) {
|
|
69
|
+
child.kill();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
child = spawn('node', argv, {
|
|
73
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
spawnChildProcess();
|
|
78
|
+
|
|
79
|
+
watch(process.cwd() + '/src', { recursive: true }, () => spawnChildProcess());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
import { Session, SessionDescriptor } from './../../domain';
|
|
5
|
+
import { workingDirectory } from './../../shared';
|
|
6
|
+
|
|
7
|
+
export type StrategyModule = {
|
|
8
|
+
descriptor: SessionDescriptor;
|
|
9
|
+
default: (session: Session) => Observable<any>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function buildDirectory() {
|
|
13
|
+
return join(process.cwd(), workingDirectory(), 'build');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function getModule(name: string): Promise<StrategyModule> {
|
|
17
|
+
return await import(join(buildDirectory(), name));
|
|
18
|
+
}
|
package/src/cli/pull.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Presets, SingleBar } from 'cli-progress';
|
|
2
|
+
|
|
3
|
+
import { Bootstrap } from '../bootstrap';
|
|
4
|
+
import { instrumentOf } from '../domain';
|
|
5
|
+
import { Feed } from '../storage';
|
|
6
|
+
import build from './build';
|
|
7
|
+
import { getModule } from './internal/workspace';
|
|
8
|
+
|
|
9
|
+
export default async function (name: string, instrument: string, options: any) {
|
|
10
|
+
if (await build()) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const id = options.id ? Number(options.id) : undefined;
|
|
15
|
+
|
|
16
|
+
const module = await getModule(name);
|
|
17
|
+
|
|
18
|
+
const bootstrap = new Bootstrap(module.descriptor);
|
|
19
|
+
const session = bootstrap.useSessionId(id).paper();
|
|
20
|
+
|
|
21
|
+
if (!module.descriptor.storage) {
|
|
22
|
+
throw new Error('Please provide a "storage" property in session descriptor.');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const from = options.from
|
|
26
|
+
? Date.parse(options.from)
|
|
27
|
+
: module.descriptor.simulation.from;
|
|
28
|
+
|
|
29
|
+
if (!from) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'Please set a "from" date in session descriptor or provide the date as parameter.'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const to = options.to ? Date.parse(options.to) : module.descriptor.simulation.to;
|
|
36
|
+
|
|
37
|
+
if (!to) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'Please set a "to" date in session descriptor or provide the date as parameter.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await session.awake(undefined);
|
|
44
|
+
|
|
45
|
+
const bar = new SingleBar({}, Presets.shades_classic);
|
|
46
|
+
const feed = new Feed(module.descriptor.storage.create('feed'));
|
|
47
|
+
|
|
48
|
+
bar.start(100, 0);
|
|
49
|
+
|
|
50
|
+
await session.aggregate.feed({
|
|
51
|
+
instrument: instrumentOf(instrument),
|
|
52
|
+
from,
|
|
53
|
+
to,
|
|
54
|
+
destination: feed,
|
|
55
|
+
callback: timestamp => {
|
|
56
|
+
const duration = to - from;
|
|
57
|
+
const completed = timestamp - from;
|
|
58
|
+
|
|
59
|
+
bar.update(Math.floor((completed / duration) * 100));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
bar.update(100);
|
|
64
|
+
bar.stop();
|
|
65
|
+
|
|
66
|
+
await session.dispose();
|
|
67
|
+
}
|
package/src/cli/run.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as dotenv from 'dotenv';
|
|
2
|
+
|
|
3
|
+
import { Bootstrap } from '../bootstrap';
|
|
4
|
+
import build from './build';
|
|
5
|
+
import { getModule } from './internal/workspace';
|
|
6
|
+
|
|
7
|
+
export default async function (name, options: any) {
|
|
8
|
+
if (await build()) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
dotenv.config();
|
|
13
|
+
|
|
14
|
+
const id = options.id ? Number(options.id) : undefined;
|
|
15
|
+
|
|
16
|
+
const module = await getModule(name);
|
|
17
|
+
|
|
18
|
+
const bootstrap = new Bootstrap(module.descriptor);
|
|
19
|
+
const session = bootstrap.useSessionId(id).live();
|
|
20
|
+
|
|
21
|
+
await session.awake(module.default);
|
|
22
|
+
}
|
package/src/cli/test.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { BacktesterStreamer } from '../adapter';
|
|
2
|
+
import { Bootstrap } from '../bootstrap';
|
|
3
|
+
import build from './build';
|
|
4
|
+
import { getModule } from './internal/workspace';
|
|
5
|
+
|
|
6
|
+
export default async function (name, options: any) {
|
|
7
|
+
if (await build()) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const module = await getModule(name);
|
|
12
|
+
|
|
13
|
+
const bootstrap = new Bootstrap(module.descriptor);
|
|
14
|
+
|
|
15
|
+
if (!module.descriptor.storage) {
|
|
16
|
+
throw new Error('Please provide a "storage" property in session descriptor.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const from = options.from
|
|
20
|
+
? Date.parse(options.from)
|
|
21
|
+
: module.descriptor.simulation.from;
|
|
22
|
+
|
|
23
|
+
if (!from) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Please set a "from" date in session descriptor or provide the date as parameter.'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const to = options.to ? Date.parse(options.to) : module.descriptor.simulation.to;
|
|
30
|
+
|
|
31
|
+
if (!to) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'Please set a "to" date in session descriptor or provide the date as parameter.'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await new Promise<void>(async resolve => {
|
|
38
|
+
const [session] = bootstrap.useBacktestPeriod(from, to).backtest({
|
|
39
|
+
onBacktestCompleted: async () => {
|
|
40
|
+
await session.dispose();
|
|
41
|
+
|
|
42
|
+
console.log('backtest completed.');
|
|
43
|
+
|
|
44
|
+
resolve();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await session.awake(module.default);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -7,7 +7,7 @@ describe('balance tests', () => {
|
|
|
7
7
|
|
|
8
8
|
expect(sut.asset.toString()).toEqual('xyz:abc');
|
|
9
9
|
expect(sut.free).toEqual(0);
|
|
10
|
-
expect(sut.
|
|
10
|
+
expect(sut.locked).toEqual(0);
|
|
11
11
|
expect(Object.keys(sut.position).length).toEqual(0);
|
|
12
12
|
});
|
|
13
13
|
|
|
@@ -18,7 +18,7 @@ describe('balance tests', () => {
|
|
|
18
18
|
|
|
19
19
|
expect(sut.asset.toString()).toEqual('xyz:abc');
|
|
20
20
|
expect(sut.free).toEqual(100);
|
|
21
|
-
expect(sut.
|
|
21
|
+
expect(sut.locked).toEqual(50);
|
|
22
22
|
expect(Object.keys(sut.position).length).toEqual(0);
|
|
23
23
|
});
|
|
24
24
|
});
|
package/src/domain/balance.ts
CHANGED
|
@@ -7,19 +7,20 @@ import { Position, PositionMode } from './position';
|
|
|
7
7
|
* Represents single asset balance in your wallet.
|
|
8
8
|
*/
|
|
9
9
|
export class Balance implements Component {
|
|
10
|
+
kind = 'balance';
|
|
10
11
|
timestamp: timestamp;
|
|
11
12
|
|
|
12
13
|
readonly maintenanceMarginRate = 1;
|
|
13
14
|
|
|
14
|
-
private
|
|
15
|
-
private
|
|
15
|
+
private available = 0;
|
|
16
|
+
private unavailable = 0;
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Returns available amount to trade.
|
|
19
20
|
*/
|
|
20
21
|
get free(): number {
|
|
21
22
|
return (
|
|
22
|
-
this.
|
|
23
|
+
this.available +
|
|
23
24
|
this.getEstimatedUnrealizedPnL('CROSS') -
|
|
24
25
|
this.getEstimatedMaintenanceMargin('CROSS')
|
|
25
26
|
);
|
|
@@ -28,8 +29,8 @@ export class Balance implements Component {
|
|
|
28
29
|
/**
|
|
29
30
|
* Return locked amount for order.
|
|
30
31
|
*/
|
|
31
|
-
get
|
|
32
|
-
return this.
|
|
32
|
+
get locked(): number {
|
|
33
|
+
return this.unavailable;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -38,7 +39,7 @@ export class Balance implements Component {
|
|
|
38
39
|
*/
|
|
39
40
|
get total(): number {
|
|
40
41
|
return (
|
|
41
|
-
this.
|
|
42
|
+
this.available + this.unavailable + this.getEstimatedUnrealizedPnL() /* +
|
|
42
43
|
this.getEstimatedMaintenanceMargin()*/
|
|
43
44
|
);
|
|
44
45
|
}
|
|
@@ -51,20 +52,20 @@ export class Balance implements Component {
|
|
|
51
52
|
constructor(public readonly asset: Asset) {}
|
|
52
53
|
|
|
53
54
|
transact(amount: number) {
|
|
54
|
-
if (this.
|
|
55
|
-
throw new Error(`invalid balance amount has: ${this.
|
|
55
|
+
if (this.available + amount < 0) {
|
|
56
|
+
throw new Error(`invalid balance amount has: ${this.available} wants: ${amount}`);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
this.
|
|
59
|
+
this.available += amount;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
set(free: number, freezed: number) {
|
|
62
63
|
if (free != null) {
|
|
63
|
-
this.
|
|
64
|
+
this.available = free;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
if (freezed != null) {
|
|
67
|
-
this.
|
|
68
|
+
this.unavailable = freezed;
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -73,21 +74,21 @@ export class Balance implements Component {
|
|
|
73
74
|
* If you place new pending order, you will lock your balance to fund order.
|
|
74
75
|
*/
|
|
75
76
|
freez(amount: number) {
|
|
76
|
-
if (this.
|
|
77
|
-
throw new Error(`insufficient funds has: ${this.
|
|
77
|
+
if (this.available < amount) {
|
|
78
|
+
throw new Error(`insufficient funds has: ${this.available} wants: ${amount}`);
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
this.
|
|
81
|
-
this.
|
|
81
|
+
this.available -= amount;
|
|
82
|
+
this.unavailable += amount;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
unfreez(amount: number) {
|
|
85
|
-
if (this.
|
|
86
|
-
throw new Error(`insufficient funds has: ${this.
|
|
86
|
+
if (this.unavailable < amount) {
|
|
87
|
+
throw new Error(`insufficient funds has: ${this.unavailable} wants: ${amount}`);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
90
|
+
this.available += amount;
|
|
91
|
+
this.unavailable -= amount;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/**
|