@quantform/core 0.5.15 → 0.5.23
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 +5 -4
- package/dist/adapter/adapter-aggregate.js +4 -4
- package/dist/adapter/adapter-aggregate.js.map +1 -1
- package/dist/adapter/adapter.d.ts +4 -15
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-adapter.d.ts +3 -3
- package/dist/adapter/backtester/backtester-adapter.js +4 -4
- package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
- package/dist/adapter/backtester/backtester-adapter.spec.js +7 -6
- package/dist/adapter/backtester/backtester-adapter.spec.js.map +1 -1
- package/dist/adapter/backtester/backtester-cursor.d.ts +4 -4
- package/dist/adapter/backtester/backtester-cursor.js.map +1 -1
- package/dist/adapter/backtester/backtester-cursor.spec.js +19 -17
- package/dist/adapter/backtester/backtester-cursor.spec.js.map +1 -1
- package/dist/adapter/backtester/backtester-streamer.d.ts +0 -1
- package/dist/adapter/backtester/backtester-streamer.js +4 -14
- package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
- package/dist/adapter/backtester/backtester-streamer.spec.js +12 -11
- package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -1
- package/dist/adapter/paper/engine/paper-engine.js +20 -19
- package/dist/adapter/paper/engine/paper-engine.js.map +1 -1
- package/dist/adapter/paper/engine/paper-engine.spec.js +19 -19
- package/dist/adapter/paper/engine/paper-engine.spec.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.d.ts +4 -3
- package/dist/adapter/paper/paper-adapter.js +8 -7
- package/dist/adapter/paper/paper-adapter.js.map +1 -1
- package/dist/adapter/paper/paper-adapter.spec.js +5 -4
- package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
- package/dist/cli/pull.js +5 -10
- package/dist/cli/pull.js.map +1 -1
- package/dist/domain/asset.d.ts +4 -4
- package/dist/domain/asset.js +4 -7
- package/dist/domain/asset.js.map +1 -1
- package/dist/domain/asset.spec.js +4 -4
- package/dist/domain/asset.spec.js.map +1 -1
- package/dist/domain/balance.d.ts +9 -9
- package/dist/domain/balance.js +16 -15
- package/dist/domain/balance.js.map +1 -1
- package/dist/domain/balance.spec.js +37 -36
- package/dist/domain/balance.spec.js.map +1 -1
- package/dist/domain/candle.d.ts +8 -8
- package/dist/domain/candle.js +3 -2
- package/dist/domain/candle.js.map +1 -1
- package/dist/domain/candle.operator.d.ts +3 -2
- package/dist/domain/candle.operator.js.map +1 -1
- package/dist/domain/candle.operator.spec.js +43 -42
- package/dist/domain/candle.operator.spec.js.map +1 -1
- package/dist/domain/candle.spec.js +11 -11
- package/dist/domain/candle.spec.js.map +1 -1
- package/dist/domain/commission.d.ts +10 -9
- package/dist/domain/commission.js +5 -5
- package/dist/domain/commission.js.map +1 -1
- package/dist/domain/commission.spec.js +13 -12
- package/dist/domain/commission.spec.js.map +1 -1
- package/dist/domain/error.d.ts +2 -1
- package/dist/domain/error.js +1 -1
- package/dist/domain/error.js.map +1 -1
- package/dist/domain/order.d.ts +13 -13
- package/dist/domain/order.js +10 -9
- package/dist/domain/order.js.map +1 -1
- package/dist/domain/order.operator.spec.js +6 -5
- package/dist/domain/order.operator.spec.js.map +1 -1
- package/dist/domain/order.spec.js +11 -10
- package/dist/domain/order.spec.js.map +1 -1
- package/dist/domain/orderbook.d.ts +18 -6
- package/dist/domain/orderbook.js +5 -4
- package/dist/domain/orderbook.js.map +1 -1
- package/dist/domain/position.d.ts +7 -7
- package/dist/domain/position.js +2 -2
- package/dist/domain/position.js.map +1 -1
- package/dist/domain/position.operator.d.ts +3 -2
- package/dist/domain/position.operator.js +3 -3
- package/dist/domain/position.operator.js.map +1 -1
- package/dist/domain/position.operator.spec.js +4 -3
- package/dist/domain/position.operator.spec.js.map +1 -1
- package/dist/domain/position.spec.js +10 -9
- package/dist/domain/position.spec.js.map +1 -1
- package/dist/domain/session.js +1 -1
- package/dist/domain/session.js.map +1 -1
- package/dist/domain/session.spec.js +1 -1
- package/dist/domain/session.spec.js.map +1 -1
- package/dist/domain/trade.d.ts +3 -3
- package/dist/indicator/atr.d.ts +2 -1
- package/dist/indicator/atr.js +3 -3
- package/dist/indicator/atr.js.map +1 -1
- package/dist/indicator/cross.d.ts +3 -2
- package/dist/indicator/cross.js +13 -13
- package/dist/indicator/cross.js.map +1 -1
- package/dist/indicator/cross.spec.js +22 -21
- package/dist/indicator/cross.spec.js.map +1 -1
- package/dist/indicator/donchian.d.ts +3 -2
- package/dist/indicator/donchian.js.map +1 -1
- package/dist/indicator/drawdown.d.ts +2 -1
- package/dist/indicator/drawdown.js +3 -2
- package/dist/indicator/drawdown.js.map +1 -1
- package/dist/indicator/ema.d.ts +2 -1
- package/dist/indicator/ema.js +3 -2
- package/dist/indicator/ema.js.map +1 -1
- package/dist/indicator/ema.spec.js +3 -2
- package/dist/indicator/ema.spec.js.map +1 -1
- package/dist/indicator/envelope.d.ts +4 -3
- package/dist/indicator/envelope.js +4 -4
- package/dist/indicator/envelope.js.map +1 -1
- package/dist/indicator/index.d.ts +1 -1
- package/dist/indicator/index.js +1 -1
- package/dist/indicator/index.js.map +1 -1
- package/dist/indicator/macd.d.ts +2 -1
- package/dist/indicator/macd.js +1 -1
- package/dist/indicator/macd.js.map +1 -1
- package/dist/indicator/min-max.d.ts +4 -3
- package/dist/indicator/min-max.js +5 -4
- package/dist/indicator/min-max.js.map +1 -1
- package/dist/indicator/rma.d.ts +2 -1
- package/dist/indicator/rma.js +3 -2
- package/dist/indicator/rma.js.map +1 -1
- package/dist/indicator/sma.d.ts +2 -1
- package/dist/indicator/sma.js +4 -6
- package/dist/indicator/sma.js.map +1 -1
- package/dist/indicator/sma.spec.js +3 -2
- package/dist/indicator/sma.spec.js.map +1 -1
- package/dist/indicator/swma.d.ts +2 -1
- package/dist/indicator/swma.js +5 -1
- package/dist/indicator/swma.js.map +1 -1
- package/dist/indicator/tma.d.ts +2 -1
- package/dist/indicator/tma.js.map +1 -1
- package/dist/indicator/tma.spec.js +3 -2
- package/dist/indicator/tma.spec.js.map +1 -1
- package/dist/indicator/trailing.d.ts +8 -7
- package/dist/indicator/trailing.js +12 -11
- package/dist/indicator/trailing.js.map +1 -1
- package/dist/indicator/trailing.spec.js +15 -14
- package/dist/indicator/trailing.spec.js.map +1 -1
- package/dist/indicator/true-range.d.ts +4 -0
- package/dist/indicator/true-range.js +20 -0
- package/dist/indicator/true-range.js.map +1 -0
- package/dist/indicator/{truerange.spec.d.ts → true-range.spec.d.ts} +0 -0
- package/dist/indicator/true-range.spec.js +28 -0
- package/dist/indicator/true-range.spec.js.map +1 -0
- package/dist/indicator/window.d.ts +1 -1
- package/dist/indicator/window.js +1 -1
- package/dist/indicator/window.js.map +1 -1
- package/dist/indicator/wma.d.ts +2 -1
- package/dist/indicator/wma.js +6 -5
- package/dist/indicator/wma.js.map +1 -1
- package/dist/indicator/wma.spec.js +3 -2
- package/dist/indicator/wma.spec.js.map +1 -1
- package/dist/shared/collections.d.ts +16 -0
- package/dist/shared/collections.js +83 -1
- package/dist/shared/collections.js.map +1 -1
- package/dist/shared/collections.spec.d.ts +1 -0
- package/dist/shared/collections.spec.js +28 -0
- package/dist/shared/collections.spec.js.map +1 -0
- package/dist/shared/decimals.d.ts +15 -6
- package/dist/shared/decimals.js +21 -34
- package/dist/shared/decimals.js.map +1 -1
- package/dist/shared/decimals.spec.js +17 -22
- package/dist/shared/decimals.spec.js.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/pipe.d.ts +4 -0
- package/dist/shared/pipe.js +9 -0
- package/dist/shared/pipe.js.map +1 -0
- package/dist/storage/feed.d.ts +8 -4
- package/dist/storage/feed.js +55 -18
- package/dist/storage/feed.js.map +1 -1
- package/dist/store/store-balance.event.d.ts +6 -6
- package/dist/store/store-balance.event.js +2 -2
- package/dist/store/store-balance.event.js.map +1 -1
- package/dist/store/store-balance.event.spec.js +4 -4
- package/dist/store/store-balance.event.spec.js.map +1 -1
- package/dist/store/store-instrument.event.js.map +1 -1
- package/dist/store/store-instrument.event.spec.js +1 -1
- package/dist/store/store-instrument.event.spec.js.map +1 -1
- package/dist/store/store-order.event.d.ts +3 -3
- package/dist/store/store-order.event.js.map +1 -1
- package/dist/store/store-order.event.spec.js +1 -1
- package/dist/store/store-order.event.spec.js.map +1 -1
- package/dist/store/store-orderbook.event.d.ts +4 -6
- package/dist/store/store-orderbook.event.js +13 -13
- package/dist/store/store-orderbook.event.js.map +1 -1
- package/dist/store/store-orderbook.event.spec.d.ts +1 -0
- package/dist/store/store-orderbook.event.spec.js +28 -0
- package/dist/store/store-orderbook.event.spec.js.map +1 -0
- package/dist/store/store-position.event.d.ts +4 -4
- package/dist/store/store-position.event.js +23 -11
- package/dist/store/store-position.event.js.map +1 -1
- package/dist/store/store-trade.event.d.ts +4 -4
- package/dist/store/store-trade.event.js +2 -2
- package/dist/store/store-trade.event.js.map +1 -1
- package/dist/store/store-trade.event.spec.js +8 -8
- package/dist/store/store-trade.event.spec.js.map +1 -1
- package/dist/store/store.spec.js +15 -15
- package/dist/store/store.spec.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jestconfig.json +1 -0
- package/package.json +3 -1
- package/src/adapter/adapter-aggregate.ts +16 -7
- package/src/adapter/adapter.ts +15 -18
- package/src/adapter/backtester/backtester-adapter.spec.ts +18 -9
- package/src/adapter/backtester/backtester-adapter.ts +14 -5
- package/src/adapter/backtester/backtester-cursor.spec.ts +20 -18
- package/src/adapter/backtester/backtester-cursor.ts +5 -5
- package/src/adapter/backtester/backtester-streamer.spec.ts +14 -13
- package/src/adapter/backtester/backtester-streamer.ts +5 -22
- package/src/adapter/paper/engine/paper-engine.spec.ts +30 -23
- package/src/adapter/paper/engine/paper-engine.ts +28 -21
- package/src/adapter/paper/paper-adapter.spec.ts +15 -6
- package/src/adapter/paper/paper-adapter.ts +20 -8
- package/src/cli/pull.ts +6 -5
- package/src/domain/asset.spec.ts +4 -4
- package/src/domain/asset.ts +7 -14
- package/src/domain/balance.spec.ts +37 -36
- package/src/domain/balance.ts +29 -29
- package/src/domain/candle.operator.spec.ts +43 -42
- package/src/domain/candle.operator.ts +4 -3
- package/src/domain/candle.spec.ts +13 -12
- package/src/domain/candle.ts +9 -9
- package/src/domain/commission.spec.ts +13 -12
- package/src/domain/commission.ts +13 -11
- package/src/domain/error.ts +5 -3
- package/src/domain/order.operator.spec.ts +6 -5
- package/src/domain/order.spec.ts +11 -10
- package/src/domain/order.ts +25 -22
- package/src/domain/orderbook.ts +14 -8
- package/src/domain/position.operator.spec.ts +5 -4
- package/src/domain/position.operator.ts +8 -5
- package/src/domain/position.spec.ts +10 -9
- package/src/domain/position.ts +8 -8
- package/src/domain/session.spec.ts +3 -3
- package/src/domain/session.ts +1 -3
- package/src/domain/trade.ts +3 -3
- package/src/indicator/atr.ts +6 -5
- package/src/indicator/cross.spec.ts +32 -47
- package/src/indicator/cross.ts +18 -10
- package/src/indicator/donchian.ts +5 -4
- package/src/indicator/drawdown.ts +7 -5
- package/src/indicator/ema.spec.ts +4 -3
- package/src/indicator/ema.ts +7 -6
- package/src/indicator/envelope.ts +11 -6
- package/src/indicator/index.ts +1 -1
- package/src/indicator/macd.ts +5 -4
- package/src/indicator/min-max.ts +10 -7
- package/src/indicator/rma.ts +7 -6
- package/src/indicator/sma.spec.ts +4 -3
- package/src/indicator/sma.ts +9 -8
- package/src/indicator/swma.ts +8 -4
- package/src/indicator/tma.spec.ts +4 -3
- package/src/indicator/tma.ts +4 -3
- package/src/indicator/trailing.spec.ts +29 -16
- package/src/indicator/trailing.ts +22 -16
- package/src/indicator/true-range.spec.ts +32 -0
- package/src/indicator/true-range.ts +32 -0
- package/src/indicator/window.ts +3 -3
- package/src/indicator/wma.spec.ts +4 -3
- package/src/indicator/wma.ts +8 -7
- package/src/shared/collections.spec.ts +30 -0
- package/src/shared/collections.ts +106 -0
- package/src/shared/decimals.spec.ts +18 -24
- package/src/shared/decimals.ts +22 -55
- package/src/shared/index.ts +1 -0
- package/src/shared/pipe.ts +12 -0
- package/src/storage/feed.ts +89 -34
- package/src/store/store-balance.event.spec.ts +5 -5
- package/src/store/store-balance.event.ts +6 -6
- package/src/store/store-instrument.event.spec.ts +2 -2
- package/src/store/store-instrument.event.ts +2 -2
- package/src/store/store-order.event.spec.ts +2 -2
- package/src/store/store-order.event.ts +2 -2
- package/src/store/store-orderbook.event.spec.ts +37 -0
- package/src/store/store-orderbook.event.ts +13 -13
- package/src/store/store-position.event.ts +27 -15
- package/src/store/store-trade.event.spec.ts +9 -9
- package/src/store/store-trade.event.ts +5 -5
- package/src/store/store.spec.ts +16 -16
- package/dist/indicator/truerange.d.ts +0 -3
- package/dist/indicator/truerange.js +0 -19
- package/dist/indicator/truerange.js.map +0 -1
- package/dist/indicator/truerange.spec.js +0 -28
- package/dist/indicator/truerange.spec.js.map +0 -1
- package/src/indicator/truerange.spec.ts +0 -32
- package/src/indicator/truerange.ts +0 -31
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import { from } from 'rxjs';
|
|
1
|
+
import { from, map } from 'rxjs';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { d } from '../shared';
|
|
4
|
+
import { trailingDown, trailingUp } from './trailing';
|
|
4
5
|
|
|
5
|
-
describe('
|
|
6
|
+
describe('trailingUp', () => {
|
|
6
7
|
test('should trigger uptrend once', done => {
|
|
7
8
|
let triggered = false;
|
|
8
9
|
|
|
9
10
|
from([1, 2, 3, 2, 3])
|
|
10
|
-
.pipe(
|
|
11
|
+
.pipe(
|
|
12
|
+
map(it => d(it)),
|
|
13
|
+
trailingUp(d(3), d(1), it => it)
|
|
14
|
+
)
|
|
11
15
|
.subscribe({
|
|
12
16
|
next: it => {
|
|
13
|
-
expect(it).
|
|
17
|
+
expect(it).toEqual(d(2));
|
|
14
18
|
triggered = true;
|
|
15
19
|
},
|
|
16
20
|
complete: () => {
|
|
17
|
-
expect(triggered).
|
|
21
|
+
expect(triggered).toEqual(true);
|
|
18
22
|
done();
|
|
19
23
|
}
|
|
20
24
|
});
|
|
@@ -24,33 +28,39 @@ describe('trailingup', () => {
|
|
|
24
28
|
let triggered = 0;
|
|
25
29
|
|
|
26
30
|
from([1, 2, 3, 2, 3, 2, 3])
|
|
27
|
-
.pipe(
|
|
31
|
+
.pipe(
|
|
32
|
+
map(it => d(it)),
|
|
33
|
+
trailingUp(d(3), d(1), it => it)
|
|
34
|
+
)
|
|
28
35
|
.subscribe({
|
|
29
36
|
next: it => {
|
|
30
|
-
expect(it).
|
|
37
|
+
expect(d(it)).toEqual(d(2));
|
|
31
38
|
triggered++;
|
|
32
39
|
},
|
|
33
40
|
complete: () => {
|
|
34
|
-
expect(triggered).
|
|
41
|
+
expect(triggered).toEqual(2);
|
|
35
42
|
done();
|
|
36
43
|
}
|
|
37
44
|
});
|
|
38
45
|
});
|
|
39
46
|
});
|
|
40
47
|
|
|
41
|
-
describe('
|
|
48
|
+
describe('trailingDown', () => {
|
|
42
49
|
test('should trigger downtrend once', done => {
|
|
43
50
|
let triggered = false;
|
|
44
51
|
|
|
45
52
|
from([3, 2, 1, 2, 1])
|
|
46
|
-
.pipe(
|
|
53
|
+
.pipe(
|
|
54
|
+
map(it => d(it)),
|
|
55
|
+
trailingDown(d(3), d(1), it => it)
|
|
56
|
+
)
|
|
47
57
|
.subscribe({
|
|
48
58
|
next: it => {
|
|
49
|
-
expect(it).
|
|
59
|
+
expect(it).toEqual(d(2));
|
|
50
60
|
triggered = true;
|
|
51
61
|
},
|
|
52
62
|
complete: () => {
|
|
53
|
-
expect(triggered).
|
|
63
|
+
expect(triggered).toEqual(true);
|
|
54
64
|
done();
|
|
55
65
|
}
|
|
56
66
|
});
|
|
@@ -60,14 +70,17 @@ describe('trailingdown', () => {
|
|
|
60
70
|
let triggered = 0;
|
|
61
71
|
|
|
62
72
|
from([3, 2, 1, 2, 1, 2, 1])
|
|
63
|
-
.pipe(
|
|
73
|
+
.pipe(
|
|
74
|
+
map(it => d(it)),
|
|
75
|
+
trailingDown(d(3), d(1), it => it)
|
|
76
|
+
)
|
|
64
77
|
.subscribe({
|
|
65
78
|
next: it => {
|
|
66
|
-
expect(it).
|
|
79
|
+
expect(it).toEqual(d(2));
|
|
67
80
|
triggered++;
|
|
68
81
|
},
|
|
69
82
|
complete: () => {
|
|
70
|
-
expect(triggered).
|
|
83
|
+
expect(triggered).toEqual(2);
|
|
71
84
|
done();
|
|
72
85
|
}
|
|
73
86
|
});
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { filter, Observable } from 'rxjs';
|
|
2
2
|
|
|
3
|
+
import { d, decimal } from '../shared';
|
|
4
|
+
|
|
3
5
|
export class Trailing {
|
|
4
6
|
private triggered = false;
|
|
5
|
-
private max = null;
|
|
6
|
-
private min = null;
|
|
7
|
+
private max: decimal = null;
|
|
8
|
+
private min: decimal = null;
|
|
7
9
|
|
|
8
|
-
constructor(readonly trigger:
|
|
10
|
+
constructor(readonly trigger: decimal, readonly buffer: decimal) {}
|
|
9
11
|
|
|
10
|
-
up(value:
|
|
11
|
-
if (value
|
|
12
|
+
up(value: decimal): boolean {
|
|
13
|
+
if (value.greaterThanOrEqualTo(this.trigger)) {
|
|
12
14
|
this.triggered = true;
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -16,11 +18,11 @@ export class Trailing {
|
|
|
16
18
|
if (!this.max) {
|
|
17
19
|
this.max = value;
|
|
18
20
|
} else {
|
|
19
|
-
this.max =
|
|
21
|
+
this.max = decimal.max(this.max, value);
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
if (this.triggered && value
|
|
25
|
+
if (this.triggered && value.lessThanOrEqualTo(this.max.minus(this.buffer))) {
|
|
24
26
|
this.triggered = false;
|
|
25
27
|
this.max = null;
|
|
26
28
|
|
|
@@ -30,8 +32,8 @@ export class Trailing {
|
|
|
30
32
|
return false;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
down(value:
|
|
34
|
-
if (value
|
|
35
|
+
down(value: decimal): boolean {
|
|
36
|
+
if (value.lessThanOrEqualTo(this.trigger)) {
|
|
35
37
|
this.triggered = true;
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -39,11 +41,11 @@ export class Trailing {
|
|
|
39
41
|
if (!this.min) {
|
|
40
42
|
this.min = value;
|
|
41
43
|
} else {
|
|
42
|
-
this.min =
|
|
44
|
+
this.min = decimal.min(this.min, value);
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
if (this.triggered && value
|
|
48
|
+
if (this.triggered && value.greaterThanOrEqualTo(this.min.plus(this.buffer))) {
|
|
47
49
|
this.triggered = false;
|
|
48
50
|
this.min = null;
|
|
49
51
|
|
|
@@ -54,7 +56,11 @@ export class Trailing {
|
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
export function
|
|
59
|
+
export function trailingUp<T>(
|
|
60
|
+
trigger: decimal,
|
|
61
|
+
buffer: decimal,
|
|
62
|
+
value: (it: T) => decimal
|
|
63
|
+
) {
|
|
58
64
|
return function (source: Observable<T>): Observable<T> {
|
|
59
65
|
const trailing = new Trailing(trigger, buffer);
|
|
60
66
|
|
|
@@ -62,10 +68,10 @@ export function trailingup<T>(trigger: number, buffer: number, value: (it: T) =>
|
|
|
62
68
|
};
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
export function
|
|
66
|
-
trigger:
|
|
67
|
-
buffer:
|
|
68
|
-
value: (it: T) =>
|
|
71
|
+
export function trailingDown<T>(
|
|
72
|
+
trigger: decimal,
|
|
73
|
+
buffer: decimal,
|
|
74
|
+
value: (it: T) => decimal
|
|
69
75
|
) {
|
|
70
76
|
return function (source: Observable<T>): Observable<T> {
|
|
71
77
|
const trailing = new Trailing(trigger, buffer);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { from } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
import { Candle } from '../domain';
|
|
4
|
+
import { d, decimal, now } from '../shared';
|
|
5
|
+
import { sma } from './sma';
|
|
6
|
+
import { trueRange } from './true-range';
|
|
7
|
+
|
|
8
|
+
describe('trueRange', () => {
|
|
9
|
+
test('should return expected value', done => {
|
|
10
|
+
let value: decimal;
|
|
11
|
+
|
|
12
|
+
//12 Dec 2020 00:00
|
|
13
|
+
from([
|
|
14
|
+
new Candle(now(), d(18127.81), d(18149.75), d(18100.0), d(18136.17)),
|
|
15
|
+
new Candle(now(), d(18136.17), d(18136.18), d(18089.75), d(18092.15)),
|
|
16
|
+
new Candle(now(), d(18092.15), d(18120.03), d(18081.0), d(18082.39)),
|
|
17
|
+
new Candle(now(), d(18082.39), d(18087.04), d(18041.0), d(18065.4)),
|
|
18
|
+
new Candle(now(), d(18066.11), d(18079.15), d(18041.0), d(18046.47))
|
|
19
|
+
])
|
|
20
|
+
.pipe(
|
|
21
|
+
trueRange(it => it),
|
|
22
|
+
sma(4, ([, it]) => it)
|
|
23
|
+
)
|
|
24
|
+
.subscribe({
|
|
25
|
+
next: ([, it]) => (value = it),
|
|
26
|
+
complete: () => {
|
|
27
|
+
expect(value.toDecimalPlaces(2)).toEqual(d(42.41));
|
|
28
|
+
done();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { map, Observable, share } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
import { Candle } from '../domain';
|
|
4
|
+
import { decimal } from '../shared';
|
|
5
|
+
|
|
6
|
+
export function trueRange<T>(fn: (it: T) => Candle) {
|
|
7
|
+
return function (source: Observable<T>): Observable<[T, decimal]> {
|
|
8
|
+
let previous;
|
|
9
|
+
|
|
10
|
+
return source.pipe(
|
|
11
|
+
map(it => {
|
|
12
|
+
const current = fn(it);
|
|
13
|
+
|
|
14
|
+
const value =
|
|
15
|
+
previous == null
|
|
16
|
+
? current.high.minus(current.low)
|
|
17
|
+
: decimal.max(
|
|
18
|
+
decimal.max(
|
|
19
|
+
current.high.minus(current.low),
|
|
20
|
+
decimal.abs(current.high.minus(previous.close))
|
|
21
|
+
),
|
|
22
|
+
decimal.abs(current.low.minus(previous.close))
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const tuple: [T, decimal] = [it, value];
|
|
26
|
+
|
|
27
|
+
return tuple;
|
|
28
|
+
}),
|
|
29
|
+
share()
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
}
|
package/src/indicator/window.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { map, Observable, share } from 'rxjs';
|
|
|
3
3
|
import { RingBuffer } from './ring-buffer';
|
|
4
4
|
|
|
5
5
|
export function window<T, Y>(length: number, fn: (value: T) => Y) {
|
|
6
|
-
return function (source: Observable<T>): Observable<[T, RingBuffer<Y>, Y, Y]> {
|
|
6
|
+
return function (source: Observable<T>): Observable<[T, RingBuffer<Y>, Y, Y?]> {
|
|
7
7
|
const buffer = new RingBuffer<Y>(length);
|
|
8
8
|
|
|
9
9
|
return source.pipe(
|
|
10
10
|
map(it => {
|
|
11
|
-
let removed =
|
|
11
|
+
let removed = undefined;
|
|
12
12
|
|
|
13
13
|
if (buffer.isFull) {
|
|
14
14
|
removed = buffer.peek();
|
|
@@ -18,7 +18,7 @@ export function window<T, Y>(length: number, fn: (value: T) => Y) {
|
|
|
18
18
|
|
|
19
19
|
buffer.enqueue(value);
|
|
20
20
|
|
|
21
|
-
const tuple: [T, RingBuffer<Y>, Y, Y] = [it, buffer, value, removed];
|
|
21
|
+
const tuple: [T, RingBuffer<Y>, Y, Y?] = [it, buffer, value, removed];
|
|
22
22
|
|
|
23
23
|
return tuple;
|
|
24
24
|
}),
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
|
|
3
|
+
import { d, decimal } from '../shared';
|
|
3
4
|
import { wma } from './wma';
|
|
4
5
|
|
|
5
6
|
describe('wma', () => {
|
|
6
7
|
test('should return expected value', done => {
|
|
7
|
-
let value;
|
|
8
|
+
let value: decimal;
|
|
8
9
|
//12 Dec 2020 00:00
|
|
9
|
-
from([18136.17, 18092.15, 18082.39, 18065.4, 18046.47])
|
|
10
|
+
from([d(18136.17), d(18092.15), d(18082.39), d(18065.4), d(18046.47)])
|
|
10
11
|
.pipe(wma(5, it => it))
|
|
11
12
|
.subscribe({
|
|
12
13
|
next: ([, it]) => (value = it),
|
|
13
14
|
complete: () => {
|
|
14
|
-
expect(
|
|
15
|
+
expect(value.toDecimalPlaces(2)).toEqual(d(18070.77));
|
|
15
16
|
done();
|
|
16
17
|
}
|
|
17
18
|
});
|
package/src/indicator/wma.ts
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import { filter, map, Observable, share } from 'rxjs';
|
|
2
2
|
|
|
3
|
+
import { d, decimal } from '../shared';
|
|
3
4
|
import { window } from './window';
|
|
4
5
|
|
|
5
|
-
export function wma<T>(length: number, fn: (it: T) =>
|
|
6
|
-
return function (source: Observable<T>): Observable<[T,
|
|
6
|
+
export function wma<T>(length: number, fn: (it: T) => decimal) {
|
|
7
|
+
return function (source: Observable<T>): Observable<[T, decimal]> {
|
|
7
8
|
return source.pipe(
|
|
8
9
|
window(length, fn),
|
|
9
10
|
filter(([, buffer]) => buffer.isFull),
|
|
10
11
|
map(([it, buffer]) => {
|
|
11
|
-
let norm = 0.0;
|
|
12
|
-
let sum = 0.0;
|
|
12
|
+
let norm = d(0.0);
|
|
13
|
+
let sum = d(0.0);
|
|
13
14
|
|
|
14
15
|
for (let i = 0; i < buffer.capacity; i++) {
|
|
15
16
|
const weight = (buffer.capacity - i) * buffer.capacity;
|
|
16
17
|
|
|
17
|
-
norm = norm
|
|
18
|
-
sum = sum
|
|
18
|
+
norm = norm.add(weight);
|
|
19
|
+
sum = sum.add(buffer.at(buffer.capacity - (i + 1)).mul(weight));
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const tuple: [T,
|
|
22
|
+
const tuple: [T, decimal] = [it, sum.div(norm)];
|
|
22
23
|
|
|
23
24
|
return tuple;
|
|
24
25
|
}),
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Liquidity, LiquidityAskComparer } from '..';
|
|
2
|
+
import { d, PriorityList } from '.';
|
|
3
|
+
|
|
4
|
+
describe('PriorityList', () => {
|
|
5
|
+
test('should patch a best bid and ask', () => {
|
|
6
|
+
const input = [
|
|
7
|
+
{ rate: d(2), quantity: d(2) },
|
|
8
|
+
{ rate: d(2), quantity: d(3) },
|
|
9
|
+
{ rate: d(5), quantity: d(1) },
|
|
10
|
+
{ rate: d(5), quantity: d(2) },
|
|
11
|
+
{ rate: d(3), quantity: d(1) },
|
|
12
|
+
{ rate: d(1), quantity: d(8) },
|
|
13
|
+
{ rate: d(1), quantity: d(0) },
|
|
14
|
+
{ rate: d(3), quantity: d(0) },
|
|
15
|
+
{ rate: d(5), quantity: d(0) }
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const sut = new PriorityList<Liquidity>(LiquidityAskComparer, 'rate');
|
|
19
|
+
|
|
20
|
+
input.forEach(it => (it.quantity.greaterThan(0) ? sut.enqueue(it) : sut.dequeue(it)));
|
|
21
|
+
|
|
22
|
+
const volume = sut.reduce((it, agg) => agg.add(it.quantity.mul(it.rate)), d.Zero);
|
|
23
|
+
|
|
24
|
+
expect(sut.head.rate).toEqual(d(2));
|
|
25
|
+
expect(sut.head.quantity).toEqual(d(3));
|
|
26
|
+
expect(volume).toEqual(d(6));
|
|
27
|
+
expect(sut.getByKey(2).quantity).toEqual(d(3));
|
|
28
|
+
expect(sut.getByKey(3)).toEqual(undefined);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -33,3 +33,109 @@ export class Set<T extends { id: string }> {
|
|
|
33
33
|
return this.array;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
export class PriorityList<T extends { next: T }> {
|
|
38
|
+
private valueByKey: Record<any, T> = {};
|
|
39
|
+
|
|
40
|
+
head: T;
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
private readonly comparer: (lhs: Omit<T, 'next'>, rhs: Omit<T, 'next'>) => number,
|
|
44
|
+
private readonly key: keyof Omit<T, 'next'>
|
|
45
|
+
) {}
|
|
46
|
+
|
|
47
|
+
getByKey(key: any): T {
|
|
48
|
+
return this.valueByKey[key];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private make(value: Omit<T, 'next'>, next: T = undefined): T {
|
|
52
|
+
const node = { ...value, next } as T;
|
|
53
|
+
|
|
54
|
+
this.valueByKey[node[this.key].toString()] = node;
|
|
55
|
+
|
|
56
|
+
return node;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
enqueue(value: Omit<T, 'next'>) {
|
|
60
|
+
if (!this.head) {
|
|
61
|
+
this.head = this.make(value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.comparer(this.head, value) > 0) {
|
|
65
|
+
this.head = this.make(value, this.head);
|
|
66
|
+
} else if (this.comparer(this.head, value) == 0) {
|
|
67
|
+
this.head = this.make(value, this.head.next);
|
|
68
|
+
} else {
|
|
69
|
+
this.visit(it => {
|
|
70
|
+
if (it.next) {
|
|
71
|
+
if (this.comparer(it.next, value) == 0) {
|
|
72
|
+
it.next = this.make(value, it.next.next);
|
|
73
|
+
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (this.comparer(it.next, value) > 0) {
|
|
78
|
+
it.next = this.make(value, it.next);
|
|
79
|
+
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return true;
|
|
84
|
+
} else {
|
|
85
|
+
it.next = this.make(value);
|
|
86
|
+
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
dequeue(value: Omit<T, 'next'>) {
|
|
94
|
+
if (!this.head) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this.comparer(this.head, value) == 0) {
|
|
99
|
+
this.head = this.head.next;
|
|
100
|
+
|
|
101
|
+
delete this.valueByKey[value[this.key].toString()];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.visit(it => {
|
|
105
|
+
if (it.next && this.comparer(it.next, value) == 0) {
|
|
106
|
+
it.next = it.next.next;
|
|
107
|
+
|
|
108
|
+
delete this.valueByKey[value[this.key].toString()];
|
|
109
|
+
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
clear() {
|
|
116
|
+
this.head = undefined;
|
|
117
|
+
this.valueByKey = {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
visit(fn: (value: T) => boolean) {
|
|
121
|
+
let top = this.head;
|
|
122
|
+
|
|
123
|
+
while (top) {
|
|
124
|
+
if (!fn(top)) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
top = top.next;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
reduce<K>(fn: (value: T, aggregate: K) => K, initValue: K) {
|
|
133
|
+
this.visit(it => {
|
|
134
|
+
initValue = fn(it, initValue);
|
|
135
|
+
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return initValue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,35 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d } from './decimals';
|
|
2
2
|
|
|
3
3
|
describe('decimals', () => {
|
|
4
4
|
test('get precision tests', () => {
|
|
5
|
-
expect(
|
|
6
|
-
expect(
|
|
7
|
-
expect(
|
|
8
|
-
expect(
|
|
9
|
-
expect(
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('fixed tests', () => {
|
|
13
|
-
expect(fixed(0.01, 2)).toEqual(0.01);
|
|
14
|
-
expect(fixed(0.011, 2)).toEqual(0.01);
|
|
15
|
-
expect(fixed(0.019, 2)).toEqual(0.01);
|
|
16
|
-
expect(fixed(0.019, 0)).toEqual(0);
|
|
17
|
-
expect(fixed(0.00000058, 8)).toEqual(0.00000058);
|
|
5
|
+
expect(d(0.01).decimalPlaces()).toEqual(2);
|
|
6
|
+
expect(d(0.0054).decimalPlaces()).toEqual(4);
|
|
7
|
+
expect(d(0.9).decimalPlaces()).toEqual(1);
|
|
8
|
+
expect(d(0.0).decimalPlaces()).toEqual(0);
|
|
9
|
+
expect(d(2).decimalPlaces()).toEqual(0);
|
|
18
10
|
});
|
|
19
11
|
|
|
20
12
|
test('floor tests', () => {
|
|
21
|
-
expect(
|
|
22
|
-
expect(
|
|
23
|
-
expect(
|
|
24
|
-
expect(
|
|
25
|
-
expect(
|
|
13
|
+
expect(d(0.01).toFloor(2)).toEqual(d(0.01));
|
|
14
|
+
expect(d(0.000000019).toFloor(8)).toEqual(d(0.00000001));
|
|
15
|
+
expect(d(0.011).toFloor(2)).toEqual(d(0.01));
|
|
16
|
+
expect(d(0.019).toFloor(2)).toEqual(d(0.01));
|
|
17
|
+
expect(d(0.019).toFloor(0)).toEqual(d.Zero);
|
|
18
|
+
expect(d(0.01).toFloor(2)).toEqual(d(0.01));
|
|
19
|
+
expect(d(0.00000058).toFloor(8)).toEqual(d(0.00000058));
|
|
26
20
|
});
|
|
27
21
|
|
|
28
22
|
test('ceil tests', () => {
|
|
29
|
-
expect(
|
|
30
|
-
expect(
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
33
|
-
expect(
|
|
23
|
+
expect(d(0.01).toCeil(2)).toEqual(d(0.01));
|
|
24
|
+
expect(d(0.000000019).toCeil(8)).toEqual(d(0.00000002));
|
|
25
|
+
expect(d(0.011).toCeil(2)).toEqual(d(0.02));
|
|
26
|
+
expect(d(0.019).toCeil(2)).toEqual(d(0.02));
|
|
27
|
+
expect(d(0.019).toCeil(0)).toEqual(d(1));
|
|
34
28
|
});
|
|
35
29
|
});
|
package/src/shared/decimals.ts
CHANGED
|
@@ -1,79 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
* calculate the number of decimal places of a number
|
|
3
|
-
* @param value
|
|
4
|
-
* @returns a precision number of the value
|
|
5
|
-
*/
|
|
6
|
-
export function precision(value: number) {
|
|
7
|
-
if (!isFinite(value)) {
|
|
8
|
-
return 0;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let e = 1;
|
|
12
|
-
let p = 0;
|
|
1
|
+
import { Decimal } from 'decimal.js';
|
|
13
2
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
declare module 'decimal.js' {
|
|
4
|
+
interface Decimal {
|
|
5
|
+
toFloor(decimalPlaces: number): Decimal;
|
|
6
|
+
toCeil(decimalPlaces: number): Decimal;
|
|
17
7
|
}
|
|
18
|
-
|
|
19
|
-
return p;
|
|
20
8
|
}
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* @param precision
|
|
26
|
-
* @returns rounded down number
|
|
27
|
-
*/
|
|
28
|
-
export function floor(number: number, precision: number): number {
|
|
29
|
-
const fixed = Math.pow(10, precision);
|
|
10
|
+
Decimal.prototype.toFloor = function (decimalPlaces: number) {
|
|
11
|
+
return this.toDecimalPlaces(decimalPlaces, Decimal.ROUND_FLOOR);
|
|
12
|
+
};
|
|
30
13
|
|
|
31
|
-
|
|
32
|
-
|
|
14
|
+
Decimal.prototype.toCeil = function (decimalPlaces: number) {
|
|
15
|
+
return this.toDecimalPlaces(decimalPlaces, Decimal.ROUND_CEIL);
|
|
16
|
+
};
|
|
33
17
|
|
|
34
|
-
|
|
35
|
-
* round up the number to the given precision
|
|
36
|
-
* @param number
|
|
37
|
-
* @param precision
|
|
38
|
-
* @returns rounded up number
|
|
39
|
-
*/
|
|
40
|
-
export function ceil(number: number, precision: number): number {
|
|
41
|
-
const fixed = Math.pow(10, precision);
|
|
18
|
+
export class decimal extends Decimal {}
|
|
42
19
|
|
|
43
|
-
|
|
20
|
+
export function d(value: Decimal.Value) {
|
|
21
|
+
return new decimal(value);
|
|
44
22
|
}
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
* round the number to the given precision
|
|
48
|
-
* @param number
|
|
49
|
-
* @param precision
|
|
50
|
-
* @returns rounded number
|
|
51
|
-
*/
|
|
52
|
-
export function fixed(number: number, precision: number): number {
|
|
53
|
-
return (
|
|
54
|
-
Math.floor((number + Number.EPSILON) * Math.pow(10, precision)) /
|
|
55
|
-
Math.pow(10, precision)
|
|
56
|
-
);
|
|
57
|
-
}
|
|
24
|
+
d.Zero = new decimal(0);
|
|
58
25
|
|
|
59
|
-
export function weightedMean(values:
|
|
26
|
+
export function weightedMean(values: decimal[], weights: decimal[]): decimal {
|
|
60
27
|
const result = values
|
|
61
28
|
.map((value, i) => {
|
|
62
29
|
const weight = weights[i];
|
|
63
|
-
const sum = value
|
|
30
|
+
const sum = value.mul(weight);
|
|
64
31
|
return [sum, weight];
|
|
65
32
|
})
|
|
66
|
-
.reduce((p, c) => [p[0]
|
|
33
|
+
.reduce((p, c) => [p[0].add(c[0]), p[1].add(c[1])], [d.Zero, d.Zero]);
|
|
67
34
|
|
|
68
35
|
if (!result[1]) {
|
|
69
|
-
return
|
|
36
|
+
return d.Zero;
|
|
70
37
|
}
|
|
71
38
|
|
|
72
|
-
return result[0]
|
|
39
|
+
return result[0].div(result[1]);
|
|
73
40
|
}
|
|
74
41
|
|
|
75
|
-
export function pnl(entryRate:
|
|
76
|
-
return (
|
|
42
|
+
export function pnl(entryRate: decimal, exitRate: decimal, amount: decimal) {
|
|
43
|
+
return exitRate.div(entryRate).minus(1).mul(amount);
|
|
77
44
|
}
|
|
78
45
|
|
|
79
46
|
/**
|
package/src/shared/index.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { filter, map, Observable } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
// tslint:disable-next-line: no-any
|
|
4
|
+
type Constructor<T> = new (...args: any[]) => T;
|
|
5
|
+
|
|
6
|
+
export function ofType<T extends K, K>(type: Constructor<T>) {
|
|
7
|
+
return (input: Observable<K>) =>
|
|
8
|
+
input.pipe(
|
|
9
|
+
filter(it => it instanceof type),
|
|
10
|
+
map(it => it as T)
|
|
11
|
+
);
|
|
12
|
+
}
|