@quantform/core 0.3.260 → 0.3.265
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 -5
- package/dist/adapter/adapter-aggregate.js +50 -21
- package/dist/adapter/adapter-aggregate.js.map +1 -1
- package/dist/adapter/adapter.d.ts +30 -7
- package/dist/adapter/adapter.js +29 -2
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-adapter.d.ts +13 -8
- package/dist/adapter/backtester/backtester-adapter.js +25 -32
- package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
- package/dist/adapter/index.d.ts +0 -1
- package/dist/adapter/index.js +0 -1
- package/dist/adapter/index.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.d.ts +10 -7
- package/dist/adapter/paper/paper-adapter.js +24 -42
- package/dist/adapter/paper/paper-adapter.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.spec.js +25 -7
- package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.js +5 -5
- package/dist/bootstrap.js.map +1 -1
- package/dist/domain/index.d.ts +3 -0
- package/dist/domain/index.js +3 -0
- package/dist/domain/index.js.map +1 -1
- package/dist/{session → domain}/session-descriptor.d.ts +2 -2
- package/dist/{session → domain}/session-descriptor.js +0 -0
- package/dist/domain/session-descriptor.js.map +1 -0
- package/dist/{session → domain}/session.d.ts +2 -2
- package/dist/{session → domain}/session.js +4 -4
- package/dist/domain/session.js.map +1 -0
- package/dist/{session → domain}/session.spec.d.ts +0 -0
- package/dist/{session → domain}/session.spec.js +0 -0
- package/dist/domain/session.spec.js.map +1 -0
- package/dist/{session/session-statement.d.ts → domain/statement.d.ts} +2 -2
- package/dist/{session/session-statement.js → domain/statement.js} +1 -1
- package/dist/domain/statement.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc.d.ts +1 -1
- package/dist/ipc.js +11 -5
- package/dist/ipc.js.map +1 -1
- package/dist/ipc.spec.js +4 -57
- package/dist/ipc.spec.js.map +1 -1
- package/dist/shared/task.d.ts +1 -1
- package/dist/store/event/store-order.event.js +5 -5
- package/dist/store/event/store-order.event.js.map +1 -1
- package/dist/tests/backtester-adapter.spec.js +7 -24
- package/dist/tests/backtester-adapter.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/adapter/adapter-aggregate.ts +55 -75
- package/src/adapter/adapter.ts +83 -5
- package/src/adapter/backtester/backtester-adapter.ts +39 -19
- package/src/adapter/index.ts +0 -1
- package/src/adapter/paper/paper-adapter.spec.ts +42 -7
- package/src/adapter/paper/paper-adapter.ts +31 -23
- package/src/bootstrap.ts +2 -2
- package/src/domain/index.ts +3 -0
- package/src/{session → domain}/session-descriptor.ts +2 -2
- package/src/{session → domain}/session.spec.ts +0 -0
- package/src/{session → domain}/session.ts +18 -6
- package/src/{session/session-statement.ts → domain/statement.ts} +1 -1
- package/src/index.ts +0 -1
- package/src/ipc.spec.ts +7 -45
- package/src/ipc.ts +7 -8
- package/src/shared/task.ts +1 -1
- package/src/store/event/store-order.event.ts +5 -5
- package/src/tests/backtester-adapter.spec.ts +6 -12
- package/src/tests/session.spec.ts +1 -1
- package/dist/adapter/adapter.event.d.ts +0 -43
- package/dist/adapter/adapter.event.js +0 -104
- package/dist/adapter/adapter.event.js.map +0 -1
- package/dist/session/index.d.ts +0 -4
- package/dist/session/index.js +0 -17
- package/dist/session/index.js.map +0 -1
- package/dist/session/session-descriptor.js.map +0 -1
- package/dist/session/session-optimizer.d.ts +0 -10
- package/dist/session/session-optimizer.js +0 -62
- package/dist/session/session-optimizer.js.map +0 -1
- package/dist/session/session-optimizer.spec.d.ts +0 -1
- package/dist/session/session-optimizer.spec.js +0 -103
- package/dist/session/session-optimizer.spec.js.map +0 -1
- package/dist/session/session-statement.js.map +0 -1
- package/dist/session/session.js.map +0 -1
- package/dist/session/session.spec.js.map +0 -1
- package/src/adapter/adapter.event.ts +0 -64
- package/src/session/index.ts +0 -4
- package/src/session/session-optimizer.spec.ts +0 -86
- package/src/session/session-optimizer.ts +0 -81
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { Store } from '../store';
|
|
2
|
-
import { Adapter, AdapterContext } from '.';
|
|
3
|
-
import { Logger } from '../shared';
|
|
4
|
-
import {
|
|
5
|
-
AdapterAccountCommand,
|
|
6
|
-
AdapterAwakeCommand,
|
|
7
|
-
AdapterDisposeCommand,
|
|
8
|
-
AdapterFeedCommand,
|
|
9
|
-
AdapterHistoryQuery,
|
|
10
|
-
AdapterOrderCancelCommand,
|
|
11
|
-
AdapterOrderOpenCommand,
|
|
12
|
-
AdapterSubscribeCommand
|
|
13
|
-
} from './adapter.event';
|
|
2
|
+
import { Adapter, AdapterContext, FeedQuery, HistoryQuery } from '.';
|
|
14
3
|
import { InstrumentSelector, Order, Candle } from '../domain';
|
|
15
4
|
import { Feed } from './../storage';
|
|
5
|
+
import { Logger } from '../shared';
|
|
16
6
|
|
|
17
7
|
/**
|
|
18
8
|
* Manages instances of all adapters provided in session descriptor.
|
|
@@ -31,16 +21,28 @@ export class AdapterAggregate {
|
|
|
31
21
|
* @returns
|
|
32
22
|
*/
|
|
33
23
|
get(adapterName: string): Adapter {
|
|
34
|
-
|
|
24
|
+
const adapter = this.adapter[adapterName];
|
|
25
|
+
|
|
26
|
+
if (!adapter) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Unknown adapter: ${adapterName}. You should provide adapter in session descriptor.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return adapter;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
/**
|
|
38
36
|
* Sets up all adapters.
|
|
39
37
|
*/
|
|
40
38
|
async awake(): Promise<void> {
|
|
41
|
-
for (const adapter
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
for (const adapter of Object.values(this.adapter)) {
|
|
40
|
+
try {
|
|
41
|
+
await adapter.awake(this.createContext(adapter));
|
|
42
|
+
await adapter.account();
|
|
43
|
+
} catch (e) {
|
|
44
|
+
Logger.error(e);
|
|
45
|
+
}
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -48,8 +50,12 @@ export class AdapterAggregate {
|
|
|
48
50
|
* Disposes all adapters.
|
|
49
51
|
*/
|
|
50
52
|
async dispose(): Promise<any> {
|
|
51
|
-
for (const adapter
|
|
52
|
-
|
|
53
|
+
for (const adapter of Object.values(this.adapter)) {
|
|
54
|
+
try {
|
|
55
|
+
await adapter.dispose();
|
|
56
|
+
} catch (e) {
|
|
57
|
+
Logger.error(e);
|
|
58
|
+
}
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -73,7 +79,11 @@ export class AdapterAggregate {
|
|
|
73
79
|
}, {});
|
|
74
80
|
|
|
75
81
|
for (const adapterName in grouped) {
|
|
76
|
-
|
|
82
|
+
try {
|
|
83
|
+
await this.get(adapterName).subscribe(grouped[adapterName]);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
Logger.error(e);
|
|
86
|
+
}
|
|
77
87
|
}
|
|
78
88
|
}
|
|
79
89
|
|
|
@@ -82,84 +92,54 @@ export class AdapterAggregate {
|
|
|
82
92
|
* @param order an order to open.
|
|
83
93
|
*/
|
|
84
94
|
open(order: Order): Promise<void> {
|
|
85
|
-
|
|
86
|
-
order.instrument.base.adapter
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
try {
|
|
96
|
+
return this.get(order.instrument.base.adapter).open(order);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
Logger.error(e);
|
|
99
|
+
}
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
/**
|
|
92
103
|
* Cancels specific order.
|
|
93
104
|
*/
|
|
94
105
|
cancel(order: Order): Promise<void> {
|
|
95
|
-
|
|
96
|
-
order.instrument.base.adapter
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
try {
|
|
107
|
+
return this.get(order.instrument.base.adapter).cancel(order);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
Logger.error(e);
|
|
110
|
+
}
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
/**
|
|
102
114
|
*
|
|
103
|
-
* @param selector Returns collection of candles for specific history.
|
|
104
|
-
* @param timeframe
|
|
105
|
-
* @param length
|
|
106
115
|
* @returns
|
|
107
116
|
*/
|
|
108
|
-
history(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
selector.base.adapter,
|
|
115
|
-
new AdapterHistoryQuery(selector, timeframe, length)
|
|
116
|
-
);
|
|
117
|
+
history(query: HistoryQuery): Promise<Candle[]> {
|
|
118
|
+
try {
|
|
119
|
+
return this.get(query.instrument.base.adapter).history(query);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
Logger.error(e);
|
|
122
|
+
}
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
/**
|
|
120
126
|
* Feeds a storage with historical instrument data.
|
|
121
|
-
* @param selector
|
|
122
|
-
* @param from
|
|
123
|
-
* @param to
|
|
124
|
-
* @param destination
|
|
125
|
-
* @param callback
|
|
126
127
|
* @returns
|
|
127
128
|
*/
|
|
128
|
-
feed(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
): Promise<void> {
|
|
135
|
-
return this.dispatch(
|
|
136
|
-
selector.base.adapter,
|
|
137
|
-
new AdapterFeedCommand(selector, from, to, destination, callback)
|
|
138
|
-
);
|
|
129
|
+
feed(query: FeedQuery): Promise<void> {
|
|
130
|
+
try {
|
|
131
|
+
return this.get(query.instrument.base.adapter).feed(query);
|
|
132
|
+
} catch (e) {
|
|
133
|
+
Logger.error(e);
|
|
134
|
+
}
|
|
139
135
|
}
|
|
140
136
|
|
|
141
137
|
/**
|
|
142
|
-
*
|
|
143
|
-
* @param
|
|
144
|
-
* @param command
|
|
138
|
+
*
|
|
139
|
+
* @param adapter
|
|
145
140
|
* @returns
|
|
146
141
|
*/
|
|
147
|
-
private
|
|
148
|
-
|
|
149
|
-
command: TCommand
|
|
150
|
-
): Promise<TResponse> {
|
|
151
|
-
const adapter = this.get(adapterName);
|
|
152
|
-
|
|
153
|
-
if (!adapter) {
|
|
154
|
-
throw new Error(
|
|
155
|
-
`Unknown adapter: ${adapterName}. You should provide adapter in session descriptor.`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
return adapter.dispatch(command, new AdapterContext(adapter, this.store));
|
|
161
|
-
} catch (e) {
|
|
162
|
-
Logger.error(e);
|
|
163
|
-
}
|
|
142
|
+
private createContext(adapter: Adapter) {
|
|
143
|
+
return new AdapterContext(adapter, this.store);
|
|
164
144
|
}
|
|
165
145
|
}
|
package/src/adapter/adapter.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { now, timestamp } from '../shared';
|
|
2
2
|
import { PaperExecutor } from './paper/executor/paper-executor';
|
|
3
3
|
import { PaperAdapter } from './paper';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { State, Store } from '../store';
|
|
5
|
+
import { InstrumentSelector, Order, Candle } from '../domain';
|
|
6
|
+
import { Feed } from '../storage';
|
|
7
|
+
import { StoreEvent } from '../store/event';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Shared context for adapter execution. Provides access to the store.
|
|
@@ -15,18 +17,94 @@ export class AdapterContext {
|
|
|
15
17
|
return this.adapter.timestamp();
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
get snapshot(): State {
|
|
21
|
+
return this.store.snapshot;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor(private readonly adapter: Adapter, private readonly store: Store) {}
|
|
25
|
+
|
|
26
|
+
dispatch(...events: StoreEvent[]) {
|
|
27
|
+
return this.store.dispatch(...events);
|
|
28
|
+
}
|
|
19
29
|
}
|
|
20
30
|
|
|
31
|
+
export type HistoryQuery = {
|
|
32
|
+
instrument: InstrumentSelector;
|
|
33
|
+
timeframe: number;
|
|
34
|
+
length: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type FeedQuery = {
|
|
38
|
+
instrument: InstrumentSelector;
|
|
39
|
+
from: timestamp;
|
|
40
|
+
to: timestamp;
|
|
41
|
+
destination: Feed;
|
|
42
|
+
callback: (timestamp: number) => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
21
45
|
/**
|
|
22
46
|
* Base adapter class, you should derive your own adapter from this class.
|
|
23
47
|
* @abstract
|
|
24
48
|
*/
|
|
25
|
-
export abstract class Adapter
|
|
49
|
+
export abstract class Adapter {
|
|
50
|
+
context: AdapterContext;
|
|
51
|
+
|
|
26
52
|
abstract name: string;
|
|
27
|
-
abstract createPaperExecutor(adapter: PaperAdapter): PaperExecutor;
|
|
28
53
|
|
|
29
54
|
timestamp(): timestamp {
|
|
30
55
|
return now();
|
|
31
56
|
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Setup an adapter.
|
|
60
|
+
* @param context
|
|
61
|
+
*/
|
|
62
|
+
async awake(context: AdapterContext): Promise<void> {
|
|
63
|
+
this.context = context;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Dispose an adapter.
|
|
68
|
+
*/
|
|
69
|
+
async dispose(): Promise<void> {}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Subscribe to collection of instruments.
|
|
73
|
+
* @param instruments
|
|
74
|
+
*/
|
|
75
|
+
subscribe(instruments: InstrumentSelector[]): Promise<void> {
|
|
76
|
+
throw new Error('method not implemented');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
*
|
|
81
|
+
*/
|
|
82
|
+
account(): Promise<void> {
|
|
83
|
+
throw new Error('method not implemented');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Opens new order.
|
|
88
|
+
* @param order an order to open.
|
|
89
|
+
*/
|
|
90
|
+
open(order: Order): Promise<void> {
|
|
91
|
+
throw new Error('method not implemented');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Cancels specific order.
|
|
96
|
+
*/
|
|
97
|
+
cancel(order: Order): Promise<void> {
|
|
98
|
+
throw new Error('method not implemented');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
history(query: HistoryQuery): Promise<Candle[]> {
|
|
102
|
+
throw new Error('method not implemented');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
feed(query: FeedQuery): Promise<void> {
|
|
106
|
+
throw new Error('method not implemented');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
abstract createPaperExecutor(adapter: PaperAdapter): PaperExecutor;
|
|
32
110
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Adapter, AdapterContext } from '..';
|
|
2
2
|
import { BacktesterStreamer } from './backtester-streamer';
|
|
3
3
|
import { PaperAdapter, PaperOptions } from '../paper';
|
|
4
|
-
import { handler } from '../../shared/topic';
|
|
5
4
|
import { timestamp } from '../../shared';
|
|
6
|
-
import { AdapterSubscribeCommand, AdapterHistoryQuery } from '../adapter.event';
|
|
7
5
|
import { InstrumentSubscriptionEvent } from '../../store/event';
|
|
6
|
+
import { InstrumentSelector, Order, Candle } from '../../domain';
|
|
7
|
+
import { PaperExecutor } from '../paper/executor/paper-executor';
|
|
8
|
+
import { FeedQuery, HistoryQuery } from '../adapter';
|
|
8
9
|
|
|
9
10
|
export class BacktesterOptions extends PaperOptions {
|
|
10
11
|
from: timestamp;
|
|
@@ -14,43 +15,62 @@ export class BacktesterOptions extends PaperOptions {
|
|
|
14
15
|
export class BacktesterAdapter extends Adapter {
|
|
15
16
|
readonly name = this.decoratedAdapter.name;
|
|
16
17
|
|
|
17
|
-
constructor(readonly decoratedAdapter: Adapter, readonly streamer: BacktesterStreamer) {
|
|
18
|
-
super();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
18
|
timestamp() {
|
|
22
19
|
return this.streamer.timestamp;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
constructor(readonly decoratedAdapter: Adapter, readonly streamer: BacktesterStreamer) {
|
|
23
|
+
super();
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
async awake(context: AdapterContext): Promise<void> {
|
|
27
|
+
await super.awake(context);
|
|
28
|
+
await this.decoratedAdapter.awake(context);
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
dispose(): Promise<void> {
|
|
32
|
+
return this.decoratedAdapter.dispose();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async subscribe(instruments: InstrumentSelector[]): Promise<void> {
|
|
36
|
+
instruments.forEach(it => {
|
|
36
37
|
this.streamer.subscribe(it);
|
|
37
38
|
});
|
|
38
39
|
|
|
39
|
-
context.
|
|
40
|
-
...
|
|
41
|
-
it => new InstrumentSubscriptionEvent(context.timestamp, it, true)
|
|
40
|
+
this.context.dispatch(
|
|
41
|
+
...instruments.map(
|
|
42
|
+
it => new InstrumentSubscriptionEvent(this.context.timestamp, it, true)
|
|
42
43
|
)
|
|
43
44
|
);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
account(): Promise<void> {
|
|
48
|
+
return this.decoratedAdapter.account();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
open(order: Order): Promise<void> {
|
|
52
|
+
return this.decoratedAdapter.open(order);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
cancel(order: Order): Promise<void> {
|
|
56
|
+
return this.decoratedAdapter.cancel(order);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async history(query: HistoryQuery): Promise<Candle[]> {
|
|
48
60
|
this.streamer.stop();
|
|
49
61
|
|
|
50
|
-
const response = await this.decoratedAdapter.
|
|
62
|
+
const response = await this.decoratedAdapter.history(query);
|
|
51
63
|
|
|
52
64
|
this.streamer.tryContinue();
|
|
53
65
|
|
|
54
66
|
return response;
|
|
55
67
|
}
|
|
68
|
+
|
|
69
|
+
feed(query: FeedQuery): Promise<void> {
|
|
70
|
+
return this.decoratedAdapter.feed(query);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
createPaperExecutor(adapter: PaperAdapter): PaperExecutor {
|
|
74
|
+
return this.decoratedAdapter.createPaperExecutor(adapter);
|
|
75
|
+
}
|
|
56
76
|
}
|
package/src/adapter/index.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { PaperSpotExecutor } from '.';
|
|
2
|
+
import { AdapterContext } from '..';
|
|
2
3
|
import { Store } from '../../store';
|
|
3
4
|
import { Adapter } from '../adapter';
|
|
4
5
|
import { PaperExecutor } from './executor/paper-executor';
|
|
5
6
|
import { PaperAdapter } from './paper-adapter';
|
|
7
|
+
import { Order, Asset, Commission, assetOf } from './../../domain';
|
|
8
|
+
import { instrumentOf } from './../../domain/instrument';
|
|
9
|
+
import { BalancePatchEvent, InstrumentPatchEvent } from '../../store/event';
|
|
6
10
|
|
|
7
11
|
class DefaultAdapter extends Adapter {
|
|
8
12
|
name = 'default';
|
|
@@ -11,24 +15,55 @@ class DefaultAdapter extends Adapter {
|
|
|
11
15
|
return 123;
|
|
12
16
|
}
|
|
13
17
|
|
|
18
|
+
async awake(context: AdapterContext): Promise<void> {
|
|
19
|
+
super.awake(context);
|
|
20
|
+
|
|
21
|
+
context.dispatch(
|
|
22
|
+
new InstrumentPatchEvent(
|
|
23
|
+
context.timestamp,
|
|
24
|
+
new Asset('a', this.name, 8),
|
|
25
|
+
new Asset('b', this.name, 4),
|
|
26
|
+
new Commission(0.1, 0.1),
|
|
27
|
+
'a-b'
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
createPaperExecutor(adapter: PaperAdapter): PaperExecutor {
|
|
15
33
|
return new PaperSpotExecutor(adapter);
|
|
16
34
|
}
|
|
17
35
|
}
|
|
18
36
|
|
|
19
37
|
describe('paper adapter tests', () => {
|
|
38
|
+
const options = {
|
|
39
|
+
balance: {
|
|
40
|
+
['default:a']: 1000,
|
|
41
|
+
['default:b']: 200
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
20
45
|
test('should return wrapped adapter name and timestamp', () => {
|
|
21
|
-
const adapter = new DefaultAdapter();
|
|
22
46
|
const store = new Store();
|
|
23
|
-
const options = {
|
|
24
|
-
balance: {
|
|
25
|
-
['binance:usdt']: 1000
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
47
|
|
|
29
|
-
const sut = new PaperAdapter(
|
|
48
|
+
const sut = new PaperAdapter(new DefaultAdapter(), store, options);
|
|
30
49
|
|
|
31
50
|
expect(sut.name).toEqual('default');
|
|
32
51
|
expect(sut.timestamp()).toEqual(123);
|
|
33
52
|
});
|
|
53
|
+
|
|
54
|
+
test('', async () => {
|
|
55
|
+
const store = new Store();
|
|
56
|
+
const adapter = new DefaultAdapter();
|
|
57
|
+
|
|
58
|
+
const sut = new PaperAdapter(adapter, store, options);
|
|
59
|
+
|
|
60
|
+
await sut.awake(new AdapterContext(sut, store));
|
|
61
|
+
await sut.account();
|
|
62
|
+
|
|
63
|
+
const order = Order.buyMarket(instrumentOf('default:a-b'), 1.0);
|
|
64
|
+
|
|
65
|
+
await sut.open(order);
|
|
66
|
+
|
|
67
|
+
expect(store.snapshot.order[order.id]).toEqual(order);
|
|
68
|
+
});
|
|
34
69
|
});
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { Adapter, AdapterContext } from '..';
|
|
2
2
|
import { Store } from '../../store';
|
|
3
3
|
import { PaperExecutor } from './executor/paper-executor';
|
|
4
|
-
import {
|
|
5
|
-
import { assetOf } from '../../domain';
|
|
4
|
+
import { assetOf, Candle, InstrumentSelector, Order } from '../../domain';
|
|
6
5
|
import { BalancePatchEvent } from '../../store/event';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
AdapterOrderOpenCommand,
|
|
10
|
-
AdapterOrderCancelCommand
|
|
11
|
-
} from '../adapter.event';
|
|
6
|
+
import { Feed } from 'src/storage';
|
|
7
|
+
import { FeedQuery, HistoryQuery } from '../adapter';
|
|
12
8
|
|
|
13
9
|
export class PaperOptions {
|
|
14
10
|
balance: { [key: string]: number };
|
|
@@ -32,16 +28,20 @@ export class PaperAdapter extends Adapter {
|
|
|
32
28
|
return this.decoratedAdapter.timestamp();
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
async awake(context: AdapterContext): Promise<void> {
|
|
32
|
+
await super.awake(context);
|
|
33
|
+
await this.decoratedAdapter.awake(context);
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
return this.decoratedAdapter.
|
|
36
|
+
dispose(): Promise<void> {
|
|
37
|
+
return this.decoratedAdapter.dispose();
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
subscribe(instruments: InstrumentSelector[]): Promise<void> {
|
|
41
|
+
return this.decoratedAdapter.subscribe(instruments);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async account(): Promise<void> {
|
|
45
45
|
let subscribed = Object.values(this.store.snapshot.subscription.asset).filter(
|
|
46
46
|
it => it.adapter == this.name
|
|
47
47
|
);
|
|
@@ -57,25 +57,33 @@ export class PaperAdapter extends Adapter {
|
|
|
57
57
|
|
|
58
58
|
subscribed = subscribed.filter(it => it.toString() != asset.toString());
|
|
59
59
|
|
|
60
|
-
this.store.dispatch(new BalancePatchEvent(asset, free, 0, context.timestamp));
|
|
60
|
+
this.store.dispatch(new BalancePatchEvent(asset, free, 0, this.context.timestamp));
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
for (const missingAsset of subscribed) {
|
|
64
|
-
this.store.dispatch(
|
|
64
|
+
this.store.dispatch(
|
|
65
|
+
new BalancePatchEvent(missingAsset, 0, 0, this.context.timestamp)
|
|
66
|
+
);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
async open(order: Order): Promise<void> {
|
|
71
|
+
this.executor.open(order);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async cancel(order: Order): Promise<void> {
|
|
75
|
+
this.executor.cancel(order);
|
|
76
|
+
}
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
history(query: HistoryQuery): Promise<Candle[]> {
|
|
79
|
+
return this.decoratedAdapter.history(query);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
feed(query: FeedQuery): Promise<void> {
|
|
83
|
+
return this.decoratedAdapter.feed(query);
|
|
84
|
+
}
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
createPaperExecutor(adapter: PaperAdapter): PaperExecutor {
|
|
87
|
+
return this.decoratedAdapter.createPaperExecutor(adapter);
|
|
80
88
|
}
|
|
81
89
|
}
|
package/src/bootstrap.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from './adapter/backtester';
|
|
6
6
|
import { AdapterAggregate } from './adapter';
|
|
7
7
|
import { PaperAdapter } from './adapter/paper';
|
|
8
|
-
import { Session, SessionDescriptor } from './
|
|
8
|
+
import { Session, SessionDescriptor } from './domain';
|
|
9
9
|
import { Store } from './store';
|
|
10
10
|
|
|
11
11
|
export class Bootstrap {
|
|
@@ -54,7 +54,7 @@ export class Bootstrap {
|
|
|
54
54
|
|
|
55
55
|
const aggregate = new AdapterAggregate(
|
|
56
56
|
this.descriptor.adapter.map(
|
|
57
|
-
it => new
|
|
57
|
+
it => new BacktesterAdapter(new PaperAdapter(it, store, backtester), streamer)
|
|
58
58
|
),
|
|
59
59
|
store
|
|
60
60
|
);
|
package/src/domain/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Adapter } from '../adapter';
|
|
2
2
|
import { Measurement } from '../storage/measurement';
|
|
3
3
|
import { Feed } from '../storage';
|
|
4
|
-
import { BacktesterOptions } from '
|
|
5
|
-
import { PaperOptions } from '
|
|
4
|
+
import { BacktesterOptions } from '../adapter/backtester';
|
|
5
|
+
import { PaperOptions } from '../adapter/paper';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Describes a single session.
|
|
File without changes
|
|
@@ -146,19 +146,29 @@ export class Session {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
|
-
* Opens
|
|
149
|
+
* Opens a new order.
|
|
150
150
|
* Example:
|
|
151
151
|
* session.open(Order.buyMarket(instrument, 100));
|
|
152
152
|
*/
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
open(order: Order): Observable<Order> {
|
|
154
|
+
return from(this.aggregate.open(order)).pipe(
|
|
155
|
+
switchMap(it =>
|
|
156
|
+
this.store.changes$.pipe(filter(it => it instanceof Order && it.id == it.id))
|
|
157
|
+
),
|
|
158
|
+
map(it => it as Order)
|
|
159
|
+
);
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
/**
|
|
158
163
|
* Cancels specific order.
|
|
159
164
|
*/
|
|
160
|
-
cancel(order: Order):
|
|
161
|
-
return this.aggregate.cancel(order)
|
|
165
|
+
cancel(order: Order): Observable<Order> {
|
|
166
|
+
return from(this.aggregate.cancel(order)).pipe(
|
|
167
|
+
switchMap(it =>
|
|
168
|
+
this.store.changes$.pipe(filter(it => it instanceof Order && it.id == it.id))
|
|
169
|
+
),
|
|
170
|
+
map(it => it as Order)
|
|
171
|
+
);
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
/**
|
|
@@ -305,7 +315,9 @@ export class Session {
|
|
|
305
315
|
return this.store.changes$.pipe(
|
|
306
316
|
startWith(this.store.snapshot.universe.instrument[selector.toString()]),
|
|
307
317
|
filter(it => it instanceof Instrument && it.toString() == selector.toString()),
|
|
308
|
-
switchMap(() =>
|
|
318
|
+
switchMap(() =>
|
|
319
|
+
from(this.aggregate.history({ instrument: selector, timeframe, length }))
|
|
320
|
+
),
|
|
309
321
|
take(1),
|
|
310
322
|
shareReplay(),
|
|
311
323
|
mergeMap(it => it)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Session } from '.';
|
|
2
|
-
import { InstrumentSelector } from '
|
|
2
|
+
import { InstrumentSelector } from '.';
|
|
3
3
|
import { combineLatest, finalize, map, take, tap } from 'rxjs';
|
|
4
4
|
import { drawdown } from '../indicator';
|
|
5
5
|
import { floor, precision } from '../shared';
|
package/src/index.ts
CHANGED