@quantform/core 0.7.22 → 0.7.24
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/lib/app.d.ts +12 -0
- package/lib/app.d.ts.map +1 -0
- package/lib/app.js +35 -0
- package/lib/cli/build.d.ts.map +1 -1
- package/lib/cli/build.js +8 -18
- package/lib/cli/index.js +3 -12
- package/lib/cli/internal/script.d.ts +1 -1
- package/lib/cli/internal/script.d.ts.map +1 -1
- package/lib/cli/internal/script.js +8 -28
- package/lib/cli/live.js +10 -22
- package/lib/cli/paper.js +10 -22
- package/lib/cli/replay.d.ts.map +1 -1
- package/lib/cli/replay.js +15 -26
- package/lib/index.d.ts +1 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -6
- package/lib/make-test-module.d.ts.map +1 -1
- package/lib/make-test-module.js +18 -20
- package/lib/module.d.ts.map +1 -1
- package/lib/module.js +9 -24
- package/lib/module.spec.js +8 -17
- package/lib/replay/use-replay-options.d.ts +5 -4
- package/lib/replay/use-replay-options.d.ts.map +1 -1
- package/lib/replay/use-replay-options.js +5 -1
- package/lib/replay/use-replay-scheduler.d.ts +7 -6
- package/lib/replay/use-replay-scheduler.d.ts.map +1 -1
- package/lib/replay/use-replay-scheduler.js +64 -70
- package/lib/replay/use-replay-storage-buffer.d.ts +3 -2
- package/lib/replay/use-replay-storage-buffer.d.ts.map +1 -1
- package/lib/replay/use-replay-storage-buffer.js +12 -23
- package/lib/replay/use-replay-storage-cursor.d.ts +9 -8
- package/lib/replay/use-replay-storage-cursor.d.ts.map +1 -1
- package/lib/replay/use-replay-storage-cursor.js +17 -25
- package/lib/replay/use-replay-storage.d.ts +5 -4
- package/lib/replay/use-replay-storage.d.ts.map +1 -1
- package/lib/replay/use-replay-storage.js +32 -39
- package/lib/session/use-session-storage.d.ts +1 -1
- package/lib/session/use-session-storage.d.ts.map +1 -1
- package/lib/session/use-session-storage.js +8 -5
- package/lib/shared/environment.js +1 -1
- package/lib/shared/index.d.ts +0 -1
- package/lib/shared/index.d.ts.map +1 -1
- package/lib/shared/index.js +0 -1
- package/lib/simulator/use-simulator.spec.js +17 -30
- package/lib/storage/in-memory/in-memory-storage.d.ts.map +1 -1
- package/lib/storage/in-memory/in-memory-storage.factory.js +2 -2
- package/lib/storage/in-memory/in-memory-storage.js +56 -55
- package/lib/storage/in-memory/in-memory-storage.spec.js +90 -100
- package/lib/storage/storage.d.ts +9 -8
- package/lib/storage/storage.d.ts.map +1 -1
- package/lib/storage/storage.js +8 -2
- package/lib/storage/use-cache.d.ts +1 -1
- package/lib/storage/use-cache.d.ts.map +1 -1
- package/lib/storage/use-cache.js +5 -5
- package/lib/storage/use-cache.spec.js +14 -25
- package/lib/storage/use-storage.d.ts +1 -1
- package/lib/storage/use-storage.d.ts.map +1 -1
- package/lib/storage/use-storage.js +9 -6
- package/lib/use-execution-mode.js +2 -2
- package/lib/use-hash.spec.js +3 -6
- package/lib/use-logger.d.ts +1 -1
- package/lib/use-logger.d.ts.map +1 -1
- package/lib/use-logger.js +10 -7
- package/lib/use-memo.spec.js +14 -25
- package/lib/use-socket.d.ts +3 -2
- package/lib/use-socket.d.ts.map +1 -1
- package/lib/use-socket.js +2 -2
- package/lib/use-timestamp.d.ts +14 -1
- package/lib/use-timestamp.d.ts.map +1 -1
- package/lib/use-timestamp.js +30 -3
- package/lib/with-request.d.ts +2 -1
- package/lib/with-request.d.ts.map +1 -1
- package/lib/with-request.js +4 -13
- package/package.json +6 -10
- package/src/app.ts +52 -0
- package/src/cli/build.ts +11 -6
- package/src/cli/internal/script.ts +25 -54
- package/src/cli/replay.ts +13 -2
- package/src/index.ts +1 -6
- package/src/make-test-module.ts +12 -2
- package/src/module.ts +0 -3
- package/src/replay/use-replay-options.ts +7 -3
- package/src/replay/use-replay-scheduler.ts +75 -67
- package/src/replay/use-replay-storage-buffer.ts +2 -1
- package/src/replay/use-replay-storage-cursor.ts +33 -28
- package/src/replay/use-replay-storage.ts +36 -27
- package/src/session/use-session-storage.ts +7 -5
- package/src/shared/index.ts +0 -1
- package/src/storage/in-memory/in-memory-storage.spec.ts +55 -54
- package/src/storage/in-memory/in-memory-storage.ts +24 -7
- package/src/storage/storage.ts +16 -7
- package/src/storage/use-cache.ts +4 -4
- package/src/storage/use-storage.ts +8 -6
- package/src/use-hash.spec.ts +3 -6
- package/src/use-logger.ts +9 -6
- package/src/use-socket.ts +5 -5
- package/src/use-timestamp.ts +41 -3
- package/src/with-request.ts +3 -3
- package/lib/asset/asset.d.ts +0 -41
- package/lib/asset/asset.d.ts.map +0 -1
- package/lib/asset/asset.js +0 -76
- package/lib/asset/asset.spec.d.ts +0 -2
- package/lib/asset/asset.spec.d.ts.map +0 -1
- package/lib/asset/asset.spec.js +0 -54
- package/lib/asset/index.d.ts +0 -2
- package/lib/asset/index.d.ts.map +0 -1
- package/lib/asset/index.js +0 -17
- package/lib/component/distinct-until-timesamp-changed.d.ts +0 -5
- package/lib/component/distinct-until-timesamp-changed.d.ts.map +0 -1
- package/lib/component/distinct-until-timesamp-changed.js +0 -9
- package/lib/component/error.d.ts +0 -17
- package/lib/component/error.d.ts.map +0 -1
- package/lib/component/error.js +0 -33
- package/lib/component/index.d.ts +0 -8
- package/lib/component/index.d.ts.map +0 -1
- package/lib/component/index.js +0 -23
- package/lib/component/ohlc-operator.d.ts +0 -11
- package/lib/component/ohlc-operator.d.ts.map +0 -1
- package/lib/component/ohlc-operator.js +0 -69
- package/lib/component/ohlc-operator.spec.d.ts +0 -2
- package/lib/component/ohlc-operator.spec.d.ts.map +0 -1
- package/lib/component/ohlc-operator.spec.js +0 -110
- package/lib/component/ohlc.d.ts +0 -12
- package/lib/component/ohlc.d.ts.map +0 -1
- package/lib/component/ohlc.js +0 -20
- package/lib/component/ohlc.spec.d.ts +0 -2
- package/lib/component/ohlc.spec.d.ts.map +0 -1
- package/lib/component/ohlc.spec.js +0 -25
- package/lib/component/timeframe.d.ts +0 -15
- package/lib/component/timeframe.d.ts.map +0 -1
- package/lib/component/timeframe.js +0 -21
- package/lib/core.d.ts +0 -3
- package/lib/core.d.ts.map +0 -1
- package/lib/core.js +0 -17
- package/lib/instrument/commission/commission.d.ts +0 -16
- package/lib/instrument/commission/commission.d.ts.map +0 -1
- package/lib/instrument/commission/commission.js +0 -28
- package/lib/instrument/commission/commission.spec.d.ts +0 -2
- package/lib/instrument/commission/commission.spec.d.ts.map +0 -1
- package/lib/instrument/commission/commission.spec.js +0 -30
- package/lib/instrument/index.d.ts +0 -3
- package/lib/instrument/index.d.ts.map +0 -1
- package/lib/instrument/index.js +0 -18
- package/lib/instrument/instrument.d.ts +0 -28
- package/lib/instrument/instrument.d.ts.map +0 -1
- package/lib/instrument/instrument.js +0 -53
- package/lib/instrument/instrument.spec.d.ts +0 -2
- package/lib/instrument/instrument.spec.d.ts.map +0 -1
- package/lib/instrument/instrument.spec.js +0 -51
- package/lib/operators.d.ts +0 -5
- package/lib/operators.d.ts.map +0 -1
- package/lib/operators.js +0 -16
- package/lib/shared/datetime.d.ts +0 -3
- package/lib/shared/datetime.d.ts.map +0 -1
- package/lib/shared/datetime.js +0 -7
- package/lib/strategy.d.ts +0 -15
- package/lib/strategy.d.ts.map +0 -1
- package/lib/strategy.js +0 -26
- package/lib/strategy.spec.d.ts +0 -2
- package/lib/strategy.spec.d.ts.map +0 -1
- package/lib/strategy.spec.js +0 -34
- package/lib/when-socket.d.ts +0 -8
- package/lib/when-socket.d.ts.map +0 -1
- package/lib/when-socket.js +0 -53
- package/lib/with-memo.d.ts +0 -5
- package/lib/with-memo.d.ts.map +0 -1
- package/lib/with-memo.js +0 -20
- package/lib/with-memo.spec.d.ts +0 -2
- package/lib/with-memo.spec.d.ts.map +0 -1
- package/lib/with-memo.spec.js +0 -47
- package/src/asset/asset.spec.ts +0 -70
- package/src/asset/asset.ts +0 -89
- package/src/asset/index.ts +0 -1
- package/src/component/distinct-until-timesamp-changed.ts +0 -11
- package/src/component/error.ts +0 -32
- package/src/component/index.ts +0 -7
- package/src/component/ohlc-operator.spec.ts +0 -125
- package/src/component/ohlc-operator.ts +0 -122
- package/src/component/ohlc.spec.ts +0 -30
- package/src/component/ohlc.ts +0 -18
- package/src/component/timeframe.ts +0 -17
- package/src/core.ts +0 -16
- package/src/instrument/commission/commission.spec.ts +0 -35
- package/src/instrument/commission/commission.ts +0 -27
- package/src/instrument/index.ts +0 -2
- package/src/instrument/instrument.spec.ts +0 -76
- package/src/instrument/instrument.ts +0 -65
- package/src/operators.ts +0 -18
- package/src/shared/datetime.ts +0 -5
- package/src/strategy.spec.ts +0 -42
- package/src/strategy.ts +0 -36
- package/src/when-socket.ts +0 -61
- package/src/with-memo.spec.ts +0 -46
- package/src/with-memo.ts +0 -33
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
concat,
|
|
3
|
-
filter,
|
|
4
|
-
last,
|
|
5
|
-
map,
|
|
6
|
-
mergeMap,
|
|
7
|
-
Observable,
|
|
8
|
-
OperatorFunction,
|
|
9
|
-
share,
|
|
10
|
-
skipLast,
|
|
11
|
-
switchMap
|
|
12
|
-
} from 'rxjs';
|
|
13
|
-
|
|
14
|
-
import { Ohlc, tf } from '@lib/component';
|
|
15
|
-
import { decimal } from '@lib/shared';
|
|
16
|
-
|
|
17
|
-
function aggregate(
|
|
18
|
-
candle: Ohlc | undefined,
|
|
19
|
-
timeframe: number,
|
|
20
|
-
value: decimal,
|
|
21
|
-
timestamp: number
|
|
22
|
-
) {
|
|
23
|
-
const frame = tf(timestamp, timeframe);
|
|
24
|
-
|
|
25
|
-
if (!candle) {
|
|
26
|
-
return new Ohlc(frame, value, value, value, value);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (candle.timestamp === frame) {
|
|
30
|
-
candle.apply(value);
|
|
31
|
-
return undefined;
|
|
32
|
-
} else {
|
|
33
|
-
return new Ohlc(frame, candle.close, value, value, value);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function ohlc<T extends { timestamp: number }>(
|
|
38
|
-
timeframe: number,
|
|
39
|
-
fn: (x: T) => decimal,
|
|
40
|
-
candleToStartWith?: Ohlc
|
|
41
|
-
) {
|
|
42
|
-
return function (source: Observable<T>): Observable<Ohlc> {
|
|
43
|
-
let candle = candleToStartWith;
|
|
44
|
-
|
|
45
|
-
return source.pipe(
|
|
46
|
-
map(it => {
|
|
47
|
-
const newCandle = aggregate(candle, timeframe, fn(it), it.timestamp);
|
|
48
|
-
if (newCandle) {
|
|
49
|
-
const prevCandle = candle;
|
|
50
|
-
candle = newCandle;
|
|
51
|
-
|
|
52
|
-
if (candleToStartWith && candleToStartWith.timestamp < newCandle.timestamp) {
|
|
53
|
-
candleToStartWith = undefined;
|
|
54
|
-
|
|
55
|
-
if (prevCandle) {
|
|
56
|
-
return [prevCandle, candle];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return [candle];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (candleToStartWith) {
|
|
64
|
-
candleToStartWith = undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (candle) {
|
|
68
|
-
return [candle];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return [];
|
|
72
|
-
}),
|
|
73
|
-
mergeMap(it => it),
|
|
74
|
-
share()
|
|
75
|
-
);
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function mergeOhlc<T extends { timestamp: number }>(
|
|
80
|
-
timeframe: number,
|
|
81
|
-
fn: (x: T) => decimal,
|
|
82
|
-
history$: Observable<Ohlc>
|
|
83
|
-
) {
|
|
84
|
-
return function (source$: Observable<T>): Observable<Ohlc> {
|
|
85
|
-
return concat(
|
|
86
|
-
history$.pipe(skipLast(1)),
|
|
87
|
-
history$.pipe(
|
|
88
|
-
last(),
|
|
89
|
-
switchMap(lastHistoricalCandle =>
|
|
90
|
-
source$.pipe(ohlc(timeframe, fn, lastHistoricalCandle))
|
|
91
|
-
),
|
|
92
|
-
share()
|
|
93
|
-
)
|
|
94
|
-
);
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function ohlcCompleted(): (source: Observable<Ohlc>) => Observable<Ohlc> {
|
|
99
|
-
let currCandle: Ohlc;
|
|
100
|
-
|
|
101
|
-
return (source: Observable<Ohlc>) =>
|
|
102
|
-
source.pipe(
|
|
103
|
-
map(it => {
|
|
104
|
-
if (!currCandle) {
|
|
105
|
-
currCandle = it;
|
|
106
|
-
|
|
107
|
-
return undefined;
|
|
108
|
-
} else {
|
|
109
|
-
if (currCandle.timestamp !== it.timestamp) {
|
|
110
|
-
const prevCandle = currCandle;
|
|
111
|
-
currCandle = it;
|
|
112
|
-
|
|
113
|
-
return prevCandle;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
}),
|
|
119
|
-
filter(it => it !== undefined) as OperatorFunction<Ohlc | undefined, Ohlc>,
|
|
120
|
-
share()
|
|
121
|
-
);
|
|
122
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Ohlc } from '@lib/component';
|
|
2
|
-
import { d, now } from '@lib/shared';
|
|
3
|
-
|
|
4
|
-
describe('Ohlc', () => {
|
|
5
|
-
test('should construct a ohlc', () => {
|
|
6
|
-
const timestamp = now();
|
|
7
|
-
|
|
8
|
-
const sut = new Ohlc(timestamp, d(2), d(4), d(1), d(3));
|
|
9
|
-
|
|
10
|
-
expect(sut.timestamp).toEqual(timestamp);
|
|
11
|
-
expect(sut.open).toEqual(d(2));
|
|
12
|
-
expect(sut.high).toEqual(d(4));
|
|
13
|
-
expect(sut.low).toEqual(d(1));
|
|
14
|
-
expect(sut.close).toEqual(d(3));
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('should modify a ohlc', () => {
|
|
18
|
-
const timestamp = now();
|
|
19
|
-
|
|
20
|
-
const sut = new Ohlc(timestamp, d(2), d(4), d(1), d(3));
|
|
21
|
-
|
|
22
|
-
sut.apply(d(10));
|
|
23
|
-
|
|
24
|
-
expect(sut.timestamp).toEqual(timestamp);
|
|
25
|
-
expect(sut.open).toEqual(d(2));
|
|
26
|
-
expect(sut.high).toEqual(d(10));
|
|
27
|
-
expect(sut.low).toEqual(d(1));
|
|
28
|
-
expect(sut.close).toEqual(d(10));
|
|
29
|
-
});
|
|
30
|
-
});
|
package/src/component/ohlc.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { decimal, timestamp } from '@lib/shared';
|
|
2
|
-
|
|
3
|
-
export class Ohlc {
|
|
4
|
-
constructor(
|
|
5
|
-
public timestamp: timestamp,
|
|
6
|
-
public open: decimal,
|
|
7
|
-
public high: decimal,
|
|
8
|
-
public low: decimal,
|
|
9
|
-
public close: decimal,
|
|
10
|
-
public volume?: decimal
|
|
11
|
-
) {}
|
|
12
|
-
|
|
13
|
-
apply(value: decimal) {
|
|
14
|
-
this.high = decimal.max(this.high, value);
|
|
15
|
-
this.low = decimal.min(this.low, value);
|
|
16
|
-
this.close = value;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export class Timeframe {
|
|
2
|
-
static S1 = 1000;
|
|
3
|
-
static M1 = Timeframe.S1 * 60;
|
|
4
|
-
static M5 = Timeframe.M1 * 5;
|
|
5
|
-
static M15 = Timeframe.M5 * 3;
|
|
6
|
-
static M30 = Timeframe.M15 * 2;
|
|
7
|
-
static H1 = Timeframe.M30 * 2;
|
|
8
|
-
static H4 = Timeframe.H1 * 4;
|
|
9
|
-
static H6 = Timeframe.H1 * 6;
|
|
10
|
-
static H12 = Timeframe.H6 * 2;
|
|
11
|
-
static D1 = Timeframe.H12 * 2;
|
|
12
|
-
static W1 = Timeframe.D1 * 7;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function tf(timestamp: number, timeframe: number): number {
|
|
16
|
-
return timestamp - (timestamp % timeframe);
|
|
17
|
-
}
|
package/src/core.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Dependency } from '@lib/module';
|
|
2
|
-
import { useMemo } from '@lib/use-memo';
|
|
3
|
-
|
|
4
|
-
import { InMemoryStorageFactory } from './storage';
|
|
5
|
-
import { useStorageFactory } from './storage/use-storage-factory';
|
|
6
|
-
import { useExecutionMode } from './use-execution-mode';
|
|
7
|
-
import { ConsoleLoggerFactory, logger } from './use-logger';
|
|
8
|
-
|
|
9
|
-
export function core(): Dependency[] {
|
|
10
|
-
return [
|
|
11
|
-
useMemo.options(),
|
|
12
|
-
logger(new ConsoleLoggerFactory()),
|
|
13
|
-
useExecutionMode.paperOptions({ recording: false }),
|
|
14
|
-
useStorageFactory.options(new InMemoryStorageFactory())
|
|
15
|
-
];
|
|
16
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { d } from '@lib/shared';
|
|
2
|
-
|
|
3
|
-
import { Commission, commissionPercentOf } from './commission';
|
|
4
|
-
|
|
5
|
-
describe(Commission.name, () => {
|
|
6
|
-
test('should construct a Commission', () => {
|
|
7
|
-
const sut = commissionPercentOf({
|
|
8
|
-
maker: d(0.1),
|
|
9
|
-
taker: d(0.2)
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
expect(sut.makerRate).toEqual(d(0.001));
|
|
13
|
-
expect(sut.takerRate).toEqual(d(0.002));
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('should calculate a maker fee', () => {
|
|
17
|
-
const sut = commissionPercentOf({
|
|
18
|
-
maker: d(0.1),
|
|
19
|
-
taker: d(0.2)
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
expect(sut.calculateMakerFee(d(2000))).toEqual(d(2));
|
|
23
|
-
expect(sut.applyMakerFee(d(2000))).toEqual(d(1998));
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('should calculate a taker fee', () => {
|
|
27
|
-
const sut = commissionPercentOf({
|
|
28
|
-
maker: d(0.1),
|
|
29
|
-
taker: d(0.2)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
expect(sut.calculateTakerFee(d(2000))).toEqual(d(4));
|
|
33
|
-
expect(sut.applyTakerFee(d(2000))).toEqual(d(1996));
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { d, decimal } from '@lib/shared';
|
|
2
|
-
|
|
3
|
-
export class Commission {
|
|
4
|
-
static readonly Zero = commissionPercentOf({ maker: d.Zero, taker: d.Zero });
|
|
5
|
-
|
|
6
|
-
constructor(readonly makerRate: decimal, readonly takerRate: decimal) {}
|
|
7
|
-
|
|
8
|
-
calculateMakerFee(value: decimal) {
|
|
9
|
-
return value.mul(this.makerRate);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
calculateTakerFee(value: decimal) {
|
|
13
|
-
return value.mul(this.takerRate);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
applyMakerFee(value: decimal): decimal {
|
|
17
|
-
return value.minus(this.calculateMakerFee(value));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
applyTakerFee(value: decimal): decimal {
|
|
21
|
-
return value.minus(this.calculateTakerFee(value));
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function commissionPercentOf(fees: { maker: decimal; taker: decimal }) {
|
|
26
|
-
return new Commission(fees.maker.div(100.0), fees.taker.div(100.0));
|
|
27
|
-
}
|
package/src/instrument/index.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Asset,
|
|
3
|
-
assetOf,
|
|
4
|
-
commissionPercentOf,
|
|
5
|
-
Instrument,
|
|
6
|
-
instrumentOf,
|
|
7
|
-
InstrumentSelector
|
|
8
|
-
} from '@lib/component';
|
|
9
|
-
import { d } from '@lib/shared';
|
|
10
|
-
|
|
11
|
-
describe(Instrument.name, () => {
|
|
12
|
-
test('should construct a instrument', () => {
|
|
13
|
-
const sut = new Instrument(
|
|
14
|
-
0,
|
|
15
|
-
new Asset('abc', 'xyz', 4),
|
|
16
|
-
new Asset('def', 'xyz', 4),
|
|
17
|
-
'abc-def',
|
|
18
|
-
commissionPercentOf({
|
|
19
|
-
maker: d.Zero,
|
|
20
|
-
taker: d.Zero
|
|
21
|
-
})
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
expect(sut.base.name).toEqual('abc');
|
|
25
|
-
expect(sut.base.adapterName).toEqual('xyz');
|
|
26
|
-
expect(sut.quote.name).toEqual('def');
|
|
27
|
-
expect(sut.quote.adapterName).toEqual('xyz');
|
|
28
|
-
expect(sut.id).toEqual('xyz:abc-def');
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe(InstrumentSelector.name, () => {
|
|
33
|
-
test('should construct a instrument selector', () => {
|
|
34
|
-
const sut = instrumentOf('xyz:abc-def');
|
|
35
|
-
|
|
36
|
-
expect(sut.base.name).toEqual('abc');
|
|
37
|
-
expect(sut.base.adapterName).toEqual('xyz');
|
|
38
|
-
expect(sut.quote.name).toEqual('def');
|
|
39
|
-
expect(sut.quote.adapterName).toEqual('xyz');
|
|
40
|
-
expect(sut.id).toEqual('xyz:abc-def');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('should construct a instrument selector capital case', () => {
|
|
44
|
-
const sut = instrumentOf('XYZ:ABC-DEF');
|
|
45
|
-
|
|
46
|
-
expect(sut.base.name).toEqual('abc');
|
|
47
|
-
expect(sut.base.adapterName).toEqual('xyz');
|
|
48
|
-
expect(sut.quote.name).toEqual('def');
|
|
49
|
-
expect(sut.quote.adapterName).toEqual('xyz');
|
|
50
|
-
expect(sut.id).toEqual('xyz:abc-def');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('should throw invalid format message for missing separator', () => {
|
|
54
|
-
const fn = () => instrumentOf('xyzabc-def');
|
|
55
|
-
|
|
56
|
-
expect(fn).toThrowError();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('should throw invalid format message for multiple separators', () => {
|
|
60
|
-
const fn = () => instrumentOf('xyz:abc:-def');
|
|
61
|
-
|
|
62
|
-
expect(fn).toThrowError();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('should throw invalid format message for missing pair name', () => {
|
|
66
|
-
const fn = () => instrumentOf('xyz:');
|
|
67
|
-
|
|
68
|
-
expect(fn).toThrowError();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('should throw invalid format message for missing adapter name', () => {
|
|
72
|
-
const fn = () => assetOf(':abc-def');
|
|
73
|
-
|
|
74
|
-
expect(fn).toThrowError();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { Asset, AssetSelector, AssetSelectorSeparator } from '@lib/asset';
|
|
2
|
-
import { AdapterMismatchError, InvalidInstrumentSelectorError } from '@lib/component';
|
|
3
|
-
|
|
4
|
-
import { Commission } from './commission/commission';
|
|
5
|
-
|
|
6
|
-
export const InstrumentSelectorSeparator = '-';
|
|
7
|
-
|
|
8
|
-
export class MissingInstrumentError extends Error {
|
|
9
|
-
constructor(instrument: InstrumentSelector) {
|
|
10
|
-
super(`Missing instrument: ${instrument}`);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class InstrumentSelector {
|
|
15
|
-
readonly id: string;
|
|
16
|
-
readonly base: AssetSelector;
|
|
17
|
-
readonly quote: AssetSelector;
|
|
18
|
-
|
|
19
|
-
constructor(base: string, quote: string, adapter: string) {
|
|
20
|
-
this.base = new AssetSelector(base.toLowerCase(), adapter.toLowerCase());
|
|
21
|
-
this.quote = new AssetSelector(quote.toLowerCase(), adapter.toLowerCase());
|
|
22
|
-
|
|
23
|
-
this.id = `${this.base.id}${InstrumentSelectorSeparator}${this.quote.name}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
toString() {
|
|
27
|
-
return this.id;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Represents trading market which is made up by two trading assets (base and quoted).
|
|
33
|
-
*/
|
|
34
|
-
export class Instrument extends InstrumentSelector {
|
|
35
|
-
readonly cross: Instrument | undefined;
|
|
36
|
-
leverage: number | undefined = undefined;
|
|
37
|
-
|
|
38
|
-
constructor(
|
|
39
|
-
public timestamp: number,
|
|
40
|
-
override readonly base: Asset,
|
|
41
|
-
override readonly quote: Asset,
|
|
42
|
-
readonly raw: string,
|
|
43
|
-
public commission: Commission
|
|
44
|
-
) {
|
|
45
|
-
super(base.name, quote.name, base.adapterName);
|
|
46
|
-
|
|
47
|
-
if (base.adapterName != quote.adapterName) {
|
|
48
|
-
throw new AdapterMismatchError();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function instrumentOf(selector: string): InstrumentSelector {
|
|
54
|
-
const [adapterName, asset, ...rest] = selector.split(AssetSelectorSeparator);
|
|
55
|
-
if (!adapterName || !asset || rest.length) {
|
|
56
|
-
throw new InvalidInstrumentSelectorError(selector);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const [baseAssetName, quoteAssetName] = asset.split(InstrumentSelectorSeparator);
|
|
60
|
-
if (!baseAssetName || !quoteAssetName) {
|
|
61
|
-
throw new InvalidInstrumentSelectorError(selector);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return new InstrumentSelector(baseAssetName, quoteAssetName, adapterName);
|
|
65
|
-
}
|
package/src/operators.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { filter, map, Observable } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
export function asReadonly<T>() {
|
|
4
|
-
return (input: Observable<T>) => input.pipe(map(it => it as Readonly<T>));
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function defined<T>() {
|
|
8
|
-
return (observable: Observable<T | undefined | null>) =>
|
|
9
|
-
observable.pipe(filter(it => it !== undefined && it !== null));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function exclude<T, S extends symbol>(s: S) {
|
|
13
|
-
return (observable: Observable<T | S>) =>
|
|
14
|
-
observable.pipe(
|
|
15
|
-
filter(it => it !== s),
|
|
16
|
-
map(it => it as Exclude<T, typeof s>)
|
|
17
|
-
);
|
|
18
|
-
}
|
package/src/shared/datetime.ts
DELETED
package/src/strategy.spec.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { of } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
import { Dependency } from './module';
|
|
4
|
-
import { after, before, behavior, strategy } from './strategy';
|
|
5
|
-
|
|
6
|
-
describe(strategy.name, () => {
|
|
7
|
-
let fixtures: ReturnType<typeof getFixtures>;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
fixtures = getFixtures();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('happy path', () => {
|
|
14
|
-
const sut = strategy(() => {
|
|
15
|
-
before(() => of('before'));
|
|
16
|
-
behavior(() => of('behavior 1'));
|
|
17
|
-
behavior(() => of('behavior 2'));
|
|
18
|
-
after(() => of('after'));
|
|
19
|
-
|
|
20
|
-
return fixtures.given.dependencies();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
expect(sut.dependencies).toEqual(
|
|
24
|
-
expect.arrayContaining(fixtures.given.dependencies())
|
|
25
|
-
);
|
|
26
|
-
expect(sut.description).toEqual({
|
|
27
|
-
before: [expect.any(Function)],
|
|
28
|
-
behavior: [expect.any(Function), expect.any(Function)],
|
|
29
|
-
after: [expect.any(Function)]
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
function getFixtures() {
|
|
35
|
-
return {
|
|
36
|
-
given: {
|
|
37
|
-
dependencies(): Dependency[] {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
package/src/strategy.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { Observable, of } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
import { Dependency } from './module';
|
|
4
|
-
|
|
5
|
-
export type Descriptor = () => Observable<unknown>;
|
|
6
|
-
|
|
7
|
-
export let before: (descriptor: Descriptor) => void;
|
|
8
|
-
export let behavior: (descriptor: Descriptor) => void;
|
|
9
|
-
export let after: (descriptor: Descriptor) => void;
|
|
10
|
-
|
|
11
|
-
export function strategy(descriptor: () => Dependency[]) {
|
|
12
|
-
const description = {
|
|
13
|
-
before: Array.of<Descriptor>(),
|
|
14
|
-
behavior: Array.of<Descriptor>(),
|
|
15
|
-
after: Array.of<Descriptor>()
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
before = descriptor => description.before.push(descriptor);
|
|
19
|
-
behavior = descriptor => description.behavior.push(descriptor);
|
|
20
|
-
after = descriptor => description.after.push(descriptor);
|
|
21
|
-
|
|
22
|
-
const dependencies = descriptor();
|
|
23
|
-
|
|
24
|
-
if (!description.before.length) {
|
|
25
|
-
description.before.push(() => of(true));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!description.after.length) {
|
|
29
|
-
description.after.push(() => of(true));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
dependencies,
|
|
34
|
-
description
|
|
35
|
-
};
|
|
36
|
-
}
|
package/src/when-socket.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
import { WebSocket } from 'ws';
|
|
3
|
-
|
|
4
|
-
import { useLogger } from './use-logger';
|
|
5
|
-
import { useTimestamp } from './use-timestamp';
|
|
6
|
-
|
|
7
|
-
export function whenSocket(
|
|
8
|
-
url: string,
|
|
9
|
-
options: { pingInterval?: number } = { pingInterval: 5000 }
|
|
10
|
-
): [Observable<{ timestamp: number; payload: unknown }>, (message: unknown) => void] {
|
|
11
|
-
const { debug } = useLogger('whenSocket');
|
|
12
|
-
|
|
13
|
-
const message = new Observable<{ timestamp: number; payload: unknown }>(stream => {
|
|
14
|
-
const socket = new WebSocket(url);
|
|
15
|
-
let isAlive = false;
|
|
16
|
-
let interval: NodeJS.Timeout | undefined;
|
|
17
|
-
|
|
18
|
-
socket.onmessage = it =>
|
|
19
|
-
stream.next({ timestamp: useTimestamp(), payload: JSON.parse(it.data as string) });
|
|
20
|
-
socket.onerror = it => {
|
|
21
|
-
clearInterval(interval);
|
|
22
|
-
debug('errored', url);
|
|
23
|
-
stream.error(it);
|
|
24
|
-
};
|
|
25
|
-
socket.onclose = () => {
|
|
26
|
-
debug('closed', url);
|
|
27
|
-
clearInterval(interval);
|
|
28
|
-
stream.error();
|
|
29
|
-
};
|
|
30
|
-
socket.onopen = () => {
|
|
31
|
-
debug('opened', url);
|
|
32
|
-
isAlive = true;
|
|
33
|
-
interval = setInterval(() => {
|
|
34
|
-
if (isAlive) {
|
|
35
|
-
isAlive = false;
|
|
36
|
-
|
|
37
|
-
socket.ping();
|
|
38
|
-
} else {
|
|
39
|
-
socket.terminate();
|
|
40
|
-
clearInterval(interval);
|
|
41
|
-
}
|
|
42
|
-
}, options.pingInterval);
|
|
43
|
-
|
|
44
|
-
socket.on('pong', () => {
|
|
45
|
-
isAlive = true;
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
socket.on('ping', () => {
|
|
49
|
-
isAlive = true;
|
|
50
|
-
socket.pong();
|
|
51
|
-
});
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return () => {
|
|
55
|
-
socket.terminate();
|
|
56
|
-
clearInterval(interval);
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return [message, (message: unknown) => JSON.stringify(message)];
|
|
61
|
-
}
|
package/src/with-memo.spec.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { makeTestModule } from '@lib/make-test-module';
|
|
2
|
-
|
|
3
|
-
import { instrumentOf, InstrumentSelector } from './instrument';
|
|
4
|
-
import { withMemo } from './with-memo';
|
|
5
|
-
|
|
6
|
-
describe(withMemo.name, () => {
|
|
7
|
-
let fixtures: Awaited<ReturnType<typeof getFixtures>>;
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
fixtures = await getFixtures();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('memorize same values for same arguments', () => {
|
|
14
|
-
const { act, getValue } = fixtures;
|
|
15
|
-
|
|
16
|
-
act(() => {
|
|
17
|
-
const value1 = getValue(instrumentOf('binance:btc-usdt'));
|
|
18
|
-
const value2 = getValue(instrumentOf('binance:btc-usdt'));
|
|
19
|
-
|
|
20
|
-
expect(Object.is(value1, value2)).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('memorize different values for different arguments', () => {
|
|
25
|
-
const { act, getValue } = fixtures;
|
|
26
|
-
|
|
27
|
-
act(() => {
|
|
28
|
-
const value1 = getValue(instrumentOf('binance:btc-usdt'));
|
|
29
|
-
const value2 = getValue(instrumentOf('binance:btc-busd'));
|
|
30
|
-
|
|
31
|
-
expect(value1).toEqual('binance:btc-usdt');
|
|
32
|
-
expect(value2).toEqual('binance:btc-busd');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
async function getFixtures() {
|
|
38
|
-
const { act } = await makeTestModule([]);
|
|
39
|
-
|
|
40
|
-
const getValue = withMemo((instrument: InstrumentSelector) => instrument.id);
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
act,
|
|
44
|
-
getValue: act(() => (instrument: InstrumentSelector) => getValue(instrument))
|
|
45
|
-
};
|
|
46
|
-
}
|
package/src/with-memo.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { isObservable, Observable, shareReplay } from 'rxjs';
|
|
2
|
-
import { v4 } from 'uuid';
|
|
3
|
-
|
|
4
|
-
import { throwWithContext } from './module';
|
|
5
|
-
import { asReadonly } from './operators';
|
|
6
|
-
import { dependency } from './use-hash';
|
|
7
|
-
import { useMemo } from './use-memo';
|
|
8
|
-
|
|
9
|
-
export function withMemo<T extends Array<dependency>, U>(
|
|
10
|
-
fn: (...args: T) => U
|
|
11
|
-
): (...args: T) => U;
|
|
12
|
-
export function withMemo<T extends Array<dependency>, U>(
|
|
13
|
-
fn: (...args: T) => Observable<U>
|
|
14
|
-
): (...args: T) => Observable<U>;
|
|
15
|
-
|
|
16
|
-
export function withMemo<T extends Array<dependency>, U>(
|
|
17
|
-
fn: (...args: T) => U | Observable<U>
|
|
18
|
-
): (...args: T) => U | Observable<U> {
|
|
19
|
-
throwWithContext();
|
|
20
|
-
|
|
21
|
-
const uniqueId = v4();
|
|
22
|
-
|
|
23
|
-
return (...args: T): U | Observable<U> =>
|
|
24
|
-
useMemo(() => {
|
|
25
|
-
const value = fn(...args);
|
|
26
|
-
|
|
27
|
-
if (isObservable(value)) {
|
|
28
|
-
return value.pipe(asReadonly(), shareReplay(1));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return value;
|
|
32
|
-
}, [uniqueId, ...args]);
|
|
33
|
-
}
|