@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.
Files changed (282) hide show
  1. package/dist/adapter/adapter-aggregate.d.ts +5 -4
  2. package/dist/adapter/adapter-aggregate.js +4 -4
  3. package/dist/adapter/adapter-aggregate.js.map +1 -1
  4. package/dist/adapter/adapter.d.ts +4 -15
  5. package/dist/adapter/adapter.js.map +1 -1
  6. package/dist/adapter/backtester/backtester-adapter.d.ts +3 -3
  7. package/dist/adapter/backtester/backtester-adapter.js +4 -4
  8. package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
  9. package/dist/adapter/backtester/backtester-adapter.spec.js +7 -6
  10. package/dist/adapter/backtester/backtester-adapter.spec.js.map +1 -1
  11. package/dist/adapter/backtester/backtester-cursor.d.ts +4 -4
  12. package/dist/adapter/backtester/backtester-cursor.js.map +1 -1
  13. package/dist/adapter/backtester/backtester-cursor.spec.js +19 -17
  14. package/dist/adapter/backtester/backtester-cursor.spec.js.map +1 -1
  15. package/dist/adapter/backtester/backtester-streamer.d.ts +0 -1
  16. package/dist/adapter/backtester/backtester-streamer.js +4 -14
  17. package/dist/adapter/backtester/backtester-streamer.js.map +1 -1
  18. package/dist/adapter/backtester/backtester-streamer.spec.js +12 -11
  19. package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -1
  20. package/dist/adapter/paper/engine/paper-engine.js +20 -19
  21. package/dist/adapter/paper/engine/paper-engine.js.map +1 -1
  22. package/dist/adapter/paper/engine/paper-engine.spec.js +19 -19
  23. package/dist/adapter/paper/engine/paper-engine.spec.js.map +1 -1
  24. package/dist/adapter/paper/paper-adapter.d.ts +4 -3
  25. package/dist/adapter/paper/paper-adapter.js +8 -7
  26. package/dist/adapter/paper/paper-adapter.js.map +1 -1
  27. package/dist/adapter/paper/paper-adapter.spec.js +5 -4
  28. package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
  29. package/dist/cli/pull.js +5 -10
  30. package/dist/cli/pull.js.map +1 -1
  31. package/dist/domain/asset.d.ts +4 -4
  32. package/dist/domain/asset.js +4 -7
  33. package/dist/domain/asset.js.map +1 -1
  34. package/dist/domain/asset.spec.js +4 -4
  35. package/dist/domain/asset.spec.js.map +1 -1
  36. package/dist/domain/balance.d.ts +9 -9
  37. package/dist/domain/balance.js +16 -15
  38. package/dist/domain/balance.js.map +1 -1
  39. package/dist/domain/balance.spec.js +37 -36
  40. package/dist/domain/balance.spec.js.map +1 -1
  41. package/dist/domain/candle.d.ts +8 -8
  42. package/dist/domain/candle.js +3 -2
  43. package/dist/domain/candle.js.map +1 -1
  44. package/dist/domain/candle.operator.d.ts +3 -2
  45. package/dist/domain/candle.operator.js.map +1 -1
  46. package/dist/domain/candle.operator.spec.js +43 -42
  47. package/dist/domain/candle.operator.spec.js.map +1 -1
  48. package/dist/domain/candle.spec.js +11 -11
  49. package/dist/domain/candle.spec.js.map +1 -1
  50. package/dist/domain/commission.d.ts +10 -9
  51. package/dist/domain/commission.js +5 -5
  52. package/dist/domain/commission.js.map +1 -1
  53. package/dist/domain/commission.spec.js +13 -12
  54. package/dist/domain/commission.spec.js.map +1 -1
  55. package/dist/domain/error.d.ts +2 -1
  56. package/dist/domain/error.js +1 -1
  57. package/dist/domain/error.js.map +1 -1
  58. package/dist/domain/order.d.ts +13 -13
  59. package/dist/domain/order.js +10 -9
  60. package/dist/domain/order.js.map +1 -1
  61. package/dist/domain/order.operator.spec.js +6 -5
  62. package/dist/domain/order.operator.spec.js.map +1 -1
  63. package/dist/domain/order.spec.js +11 -10
  64. package/dist/domain/order.spec.js.map +1 -1
  65. package/dist/domain/orderbook.d.ts +18 -6
  66. package/dist/domain/orderbook.js +5 -4
  67. package/dist/domain/orderbook.js.map +1 -1
  68. package/dist/domain/position.d.ts +7 -7
  69. package/dist/domain/position.js +2 -2
  70. package/dist/domain/position.js.map +1 -1
  71. package/dist/domain/position.operator.d.ts +3 -2
  72. package/dist/domain/position.operator.js +3 -3
  73. package/dist/domain/position.operator.js.map +1 -1
  74. package/dist/domain/position.operator.spec.js +4 -3
  75. package/dist/domain/position.operator.spec.js.map +1 -1
  76. package/dist/domain/position.spec.js +10 -9
  77. package/dist/domain/position.spec.js.map +1 -1
  78. package/dist/domain/session.js +1 -1
  79. package/dist/domain/session.js.map +1 -1
  80. package/dist/domain/session.spec.js +1 -1
  81. package/dist/domain/session.spec.js.map +1 -1
  82. package/dist/domain/trade.d.ts +3 -3
  83. package/dist/indicator/atr.d.ts +2 -1
  84. package/dist/indicator/atr.js +3 -3
  85. package/dist/indicator/atr.js.map +1 -1
  86. package/dist/indicator/cross.d.ts +3 -2
  87. package/dist/indicator/cross.js +13 -13
  88. package/dist/indicator/cross.js.map +1 -1
  89. package/dist/indicator/cross.spec.js +22 -21
  90. package/dist/indicator/cross.spec.js.map +1 -1
  91. package/dist/indicator/donchian.d.ts +3 -2
  92. package/dist/indicator/donchian.js.map +1 -1
  93. package/dist/indicator/drawdown.d.ts +2 -1
  94. package/dist/indicator/drawdown.js +3 -2
  95. package/dist/indicator/drawdown.js.map +1 -1
  96. package/dist/indicator/ema.d.ts +2 -1
  97. package/dist/indicator/ema.js +3 -2
  98. package/dist/indicator/ema.js.map +1 -1
  99. package/dist/indicator/ema.spec.js +3 -2
  100. package/dist/indicator/ema.spec.js.map +1 -1
  101. package/dist/indicator/envelope.d.ts +4 -3
  102. package/dist/indicator/envelope.js +4 -4
  103. package/dist/indicator/envelope.js.map +1 -1
  104. package/dist/indicator/index.d.ts +1 -1
  105. package/dist/indicator/index.js +1 -1
  106. package/dist/indicator/index.js.map +1 -1
  107. package/dist/indicator/macd.d.ts +2 -1
  108. package/dist/indicator/macd.js +1 -1
  109. package/dist/indicator/macd.js.map +1 -1
  110. package/dist/indicator/min-max.d.ts +4 -3
  111. package/dist/indicator/min-max.js +5 -4
  112. package/dist/indicator/min-max.js.map +1 -1
  113. package/dist/indicator/rma.d.ts +2 -1
  114. package/dist/indicator/rma.js +3 -2
  115. package/dist/indicator/rma.js.map +1 -1
  116. package/dist/indicator/sma.d.ts +2 -1
  117. package/dist/indicator/sma.js +4 -6
  118. package/dist/indicator/sma.js.map +1 -1
  119. package/dist/indicator/sma.spec.js +3 -2
  120. package/dist/indicator/sma.spec.js.map +1 -1
  121. package/dist/indicator/swma.d.ts +2 -1
  122. package/dist/indicator/swma.js +5 -1
  123. package/dist/indicator/swma.js.map +1 -1
  124. package/dist/indicator/tma.d.ts +2 -1
  125. package/dist/indicator/tma.js.map +1 -1
  126. package/dist/indicator/tma.spec.js +3 -2
  127. package/dist/indicator/tma.spec.js.map +1 -1
  128. package/dist/indicator/trailing.d.ts +8 -7
  129. package/dist/indicator/trailing.js +12 -11
  130. package/dist/indicator/trailing.js.map +1 -1
  131. package/dist/indicator/trailing.spec.js +15 -14
  132. package/dist/indicator/trailing.spec.js.map +1 -1
  133. package/dist/indicator/true-range.d.ts +4 -0
  134. package/dist/indicator/true-range.js +20 -0
  135. package/dist/indicator/true-range.js.map +1 -0
  136. package/dist/indicator/{truerange.spec.d.ts → true-range.spec.d.ts} +0 -0
  137. package/dist/indicator/true-range.spec.js +28 -0
  138. package/dist/indicator/true-range.spec.js.map +1 -0
  139. package/dist/indicator/window.d.ts +1 -1
  140. package/dist/indicator/window.js +1 -1
  141. package/dist/indicator/window.js.map +1 -1
  142. package/dist/indicator/wma.d.ts +2 -1
  143. package/dist/indicator/wma.js +6 -5
  144. package/dist/indicator/wma.js.map +1 -1
  145. package/dist/indicator/wma.spec.js +3 -2
  146. package/dist/indicator/wma.spec.js.map +1 -1
  147. package/dist/shared/collections.d.ts +16 -0
  148. package/dist/shared/collections.js +83 -1
  149. package/dist/shared/collections.js.map +1 -1
  150. package/dist/shared/collections.spec.d.ts +1 -0
  151. package/dist/shared/collections.spec.js +28 -0
  152. package/dist/shared/collections.spec.js.map +1 -0
  153. package/dist/shared/decimals.d.ts +15 -6
  154. package/dist/shared/decimals.js +21 -34
  155. package/dist/shared/decimals.js.map +1 -1
  156. package/dist/shared/decimals.spec.js +17 -22
  157. package/dist/shared/decimals.spec.js.map +1 -1
  158. package/dist/shared/index.d.ts +1 -0
  159. package/dist/shared/index.js +1 -0
  160. package/dist/shared/index.js.map +1 -1
  161. package/dist/shared/pipe.d.ts +4 -0
  162. package/dist/shared/pipe.js +9 -0
  163. package/dist/shared/pipe.js.map +1 -0
  164. package/dist/storage/feed.d.ts +8 -4
  165. package/dist/storage/feed.js +55 -18
  166. package/dist/storage/feed.js.map +1 -1
  167. package/dist/store/store-balance.event.d.ts +6 -6
  168. package/dist/store/store-balance.event.js +2 -2
  169. package/dist/store/store-balance.event.js.map +1 -1
  170. package/dist/store/store-balance.event.spec.js +4 -4
  171. package/dist/store/store-balance.event.spec.js.map +1 -1
  172. package/dist/store/store-instrument.event.js.map +1 -1
  173. package/dist/store/store-instrument.event.spec.js +1 -1
  174. package/dist/store/store-instrument.event.spec.js.map +1 -1
  175. package/dist/store/store-order.event.d.ts +3 -3
  176. package/dist/store/store-order.event.js.map +1 -1
  177. package/dist/store/store-order.event.spec.js +1 -1
  178. package/dist/store/store-order.event.spec.js.map +1 -1
  179. package/dist/store/store-orderbook.event.d.ts +4 -6
  180. package/dist/store/store-orderbook.event.js +13 -13
  181. package/dist/store/store-orderbook.event.js.map +1 -1
  182. package/dist/store/store-orderbook.event.spec.d.ts +1 -0
  183. package/dist/store/store-orderbook.event.spec.js +28 -0
  184. package/dist/store/store-orderbook.event.spec.js.map +1 -0
  185. package/dist/store/store-position.event.d.ts +4 -4
  186. package/dist/store/store-position.event.js +23 -11
  187. package/dist/store/store-position.event.js.map +1 -1
  188. package/dist/store/store-trade.event.d.ts +4 -4
  189. package/dist/store/store-trade.event.js +2 -2
  190. package/dist/store/store-trade.event.js.map +1 -1
  191. package/dist/store/store-trade.event.spec.js +8 -8
  192. package/dist/store/store-trade.event.spec.js.map +1 -1
  193. package/dist/store/store.spec.js +15 -15
  194. package/dist/store/store.spec.js.map +1 -1
  195. package/dist/tsconfig.tsbuildinfo +1 -1
  196. package/jestconfig.json +1 -0
  197. package/package.json +3 -1
  198. package/src/adapter/adapter-aggregate.ts +16 -7
  199. package/src/adapter/adapter.ts +15 -18
  200. package/src/adapter/backtester/backtester-adapter.spec.ts +18 -9
  201. package/src/adapter/backtester/backtester-adapter.ts +14 -5
  202. package/src/adapter/backtester/backtester-cursor.spec.ts +20 -18
  203. package/src/adapter/backtester/backtester-cursor.ts +5 -5
  204. package/src/adapter/backtester/backtester-streamer.spec.ts +14 -13
  205. package/src/adapter/backtester/backtester-streamer.ts +5 -22
  206. package/src/adapter/paper/engine/paper-engine.spec.ts +30 -23
  207. package/src/adapter/paper/engine/paper-engine.ts +28 -21
  208. package/src/adapter/paper/paper-adapter.spec.ts +15 -6
  209. package/src/adapter/paper/paper-adapter.ts +20 -8
  210. package/src/cli/pull.ts +6 -5
  211. package/src/domain/asset.spec.ts +4 -4
  212. package/src/domain/asset.ts +7 -14
  213. package/src/domain/balance.spec.ts +37 -36
  214. package/src/domain/balance.ts +29 -29
  215. package/src/domain/candle.operator.spec.ts +43 -42
  216. package/src/domain/candle.operator.ts +4 -3
  217. package/src/domain/candle.spec.ts +13 -12
  218. package/src/domain/candle.ts +9 -9
  219. package/src/domain/commission.spec.ts +13 -12
  220. package/src/domain/commission.ts +13 -11
  221. package/src/domain/error.ts +5 -3
  222. package/src/domain/order.operator.spec.ts +6 -5
  223. package/src/domain/order.spec.ts +11 -10
  224. package/src/domain/order.ts +25 -22
  225. package/src/domain/orderbook.ts +14 -8
  226. package/src/domain/position.operator.spec.ts +5 -4
  227. package/src/domain/position.operator.ts +8 -5
  228. package/src/domain/position.spec.ts +10 -9
  229. package/src/domain/position.ts +8 -8
  230. package/src/domain/session.spec.ts +3 -3
  231. package/src/domain/session.ts +1 -3
  232. package/src/domain/trade.ts +3 -3
  233. package/src/indicator/atr.ts +6 -5
  234. package/src/indicator/cross.spec.ts +32 -47
  235. package/src/indicator/cross.ts +18 -10
  236. package/src/indicator/donchian.ts +5 -4
  237. package/src/indicator/drawdown.ts +7 -5
  238. package/src/indicator/ema.spec.ts +4 -3
  239. package/src/indicator/ema.ts +7 -6
  240. package/src/indicator/envelope.ts +11 -6
  241. package/src/indicator/index.ts +1 -1
  242. package/src/indicator/macd.ts +5 -4
  243. package/src/indicator/min-max.ts +10 -7
  244. package/src/indicator/rma.ts +7 -6
  245. package/src/indicator/sma.spec.ts +4 -3
  246. package/src/indicator/sma.ts +9 -8
  247. package/src/indicator/swma.ts +8 -4
  248. package/src/indicator/tma.spec.ts +4 -3
  249. package/src/indicator/tma.ts +4 -3
  250. package/src/indicator/trailing.spec.ts +29 -16
  251. package/src/indicator/trailing.ts +22 -16
  252. package/src/indicator/true-range.spec.ts +32 -0
  253. package/src/indicator/true-range.ts +32 -0
  254. package/src/indicator/window.ts +3 -3
  255. package/src/indicator/wma.spec.ts +4 -3
  256. package/src/indicator/wma.ts +8 -7
  257. package/src/shared/collections.spec.ts +30 -0
  258. package/src/shared/collections.ts +106 -0
  259. package/src/shared/decimals.spec.ts +18 -24
  260. package/src/shared/decimals.ts +22 -55
  261. package/src/shared/index.ts +1 -0
  262. package/src/shared/pipe.ts +12 -0
  263. package/src/storage/feed.ts +89 -34
  264. package/src/store/store-balance.event.spec.ts +5 -5
  265. package/src/store/store-balance.event.ts +6 -6
  266. package/src/store/store-instrument.event.spec.ts +2 -2
  267. package/src/store/store-instrument.event.ts +2 -2
  268. package/src/store/store-order.event.spec.ts +2 -2
  269. package/src/store/store-order.event.ts +2 -2
  270. package/src/store/store-orderbook.event.spec.ts +37 -0
  271. package/src/store/store-orderbook.event.ts +13 -13
  272. package/src/store/store-position.event.ts +27 -15
  273. package/src/store/store-trade.event.spec.ts +9 -9
  274. package/src/store/store-trade.event.ts +5 -5
  275. package/src/store/store.spec.ts +16 -16
  276. package/dist/indicator/truerange.d.ts +0 -3
  277. package/dist/indicator/truerange.js +0 -19
  278. package/dist/indicator/truerange.js.map +0 -1
  279. package/dist/indicator/truerange.spec.js +0 -28
  280. package/dist/indicator/truerange.spec.js.map +0 -1
  281. package/src/indicator/truerange.spec.ts +0 -32
  282. package/src/indicator/truerange.ts +0 -31
@@ -1,6 +1,9 @@
1
- import { Candle, InstrumentSelector } from '../domain';
2
- import { StoreEvent } from '../store';
3
- import { Storage, StorageQueryOptions } from './storage';
1
+ import { InstrumentSelector } from '../domain';
2
+ import { d } from '../shared';
3
+ import { OrderbookPatchEvent, TradePatchEvent } from '../store';
4
+ import { Storage, StorageDocument, StorageQueryOptions } from './storage';
5
+
6
+ export type StorageEvent = TradePatchEvent | OrderbookPatchEvent;
4
7
 
5
8
  /**
6
9
  * Represents a storage supposed to store historical data.
@@ -18,25 +21,29 @@ export class Feed {
18
21
 
19
22
  /**
20
23
  *
21
- * @param instrument
22
- * @param candles
24
+ * @param events
23
25
  * @returns
24
26
  */
25
- save(instrument: InstrumentSelector, candles: Candle[]): Promise<void> {
26
- return this.storage.save(
27
- instrument.toString(),
28
- candles.map(it => ({
29
- timestamp: it.timestamp,
30
- kind: 'candle',
31
- json: JSON.stringify({
32
- o: it.open,
33
- h: it.high,
34
- l: it.low,
35
- c: it.close,
36
- v: it.volume
37
- })
38
- }))
39
- );
27
+ async save(events: StorageEvent[]): Promise<void> {
28
+ const grouped = events.reduce((aggregate, it) => {
29
+ const document = this.serializeEvent(it);
30
+
31
+ if (!document) {
32
+ return aggregate;
33
+ }
34
+
35
+ if (aggregate[it.instrument.id]) {
36
+ aggregate[it.instrument.id].push(document);
37
+ } else {
38
+ aggregate[it.instrument.id] = [document];
39
+ }
40
+
41
+ return aggregate;
42
+ }, {} as Record<string, StorageDocument[]>);
43
+
44
+ for (const instrument in grouped) {
45
+ await this.storage.save(instrument, grouped[instrument]);
46
+ }
40
47
  }
41
48
 
42
49
  /**
@@ -48,20 +55,68 @@ export class Feed {
48
55
  async query(
49
56
  instrument: InstrumentSelector,
50
57
  options: StorageQueryOptions
51
- ): Promise<Candle[]> {
52
- const rows = await this.storage.query(instrument.id, options);
53
-
54
- return rows.map(it => {
55
- const payload = JSON.parse(it.json);
56
-
57
- return new Candle(
58
- it.timestamp,
59
- payload.o,
60
- payload.h,
61
- payload.l,
62
- payload.c,
63
- payload.v
58
+ ): Promise<StorageEvent[]> {
59
+ const documents = await this.storage.query(instrument.id, options);
60
+
61
+ return documents.map(it => this.deserializeEvent(it, instrument));
62
+ }
63
+
64
+ /**
65
+ * Converts a StorageEvent to a persisted StorageDocument.
66
+ */
67
+ protected serializeEvent(event: StorageEvent): StorageDocument | undefined {
68
+ if (event instanceof OrderbookPatchEvent) {
69
+ return {
70
+ timestamp: event.timestamp,
71
+ kind: 'orderbook',
72
+ json: JSON.stringify({
73
+ ar: event.ask.rate.toString(),
74
+ ab: event.ask.quantity.toString(),
75
+ br: event.bid.rate.toString(),
76
+ bb: event.bid.quantity.toString()
77
+ })
78
+ };
79
+ }
80
+
81
+ if (event instanceof TradePatchEvent) {
82
+ return {
83
+ timestamp: event.timestamp,
84
+ kind: 'trade',
85
+ json: JSON.stringify({
86
+ r: event.rate.toString(),
87
+ q: event.quantity.toString()
88
+ })
89
+ };
90
+ }
91
+
92
+ return undefined;
93
+ }
94
+
95
+ /**
96
+ * Converts a persisted StorageDocument to a StorageEvent.
97
+ */
98
+ protected deserializeEvent(
99
+ document: StorageDocument,
100
+ instrument: InstrumentSelector
101
+ ): StorageEvent {
102
+ const payload = JSON.parse(document.json);
103
+
104
+ if (document.kind === 'trade') {
105
+ return new TradePatchEvent(
106
+ instrument,
107
+ d(payload.r),
108
+ d(payload.q),
109
+ document.timestamp
110
+ );
111
+ }
112
+
113
+ if (document.kind === 'orderbook') {
114
+ return new OrderbookPatchEvent(
115
+ instrument,
116
+ { rate: d(payload.ar), quantity: d(payload.aq), next: undefined },
117
+ { rate: d(payload.bb), quantity: d(payload.bq), next: undefined },
118
+ document.timestamp
64
119
  );
65
- });
120
+ }
66
121
  }
67
122
  }
@@ -1,5 +1,5 @@
1
1
  import { Asset, Commission } from '../domain';
2
- import { now } from '../shared';
2
+ import { d, now } from '../shared';
3
3
  import { Store } from '.';
4
4
  import { BalancePatchEvent } from './store-balance.event';
5
5
  import { InstrumentPatchEvent } from './store-instrument.event';
@@ -15,15 +15,15 @@ describe('BalancePatchEvent', () => {
15
15
  store.changes$.subscribe(it => (component = it));
16
16
 
17
17
  store.dispatch(
18
- new InstrumentPatchEvent(timestamp, base, quote, new Commission(0, 0), '')
18
+ new InstrumentPatchEvent(timestamp, base, quote, new Commission(d.Zero, d.Zero), '')
19
19
  );
20
- store.dispatch(new BalancePatchEvent(base, 100, 0, timestamp));
20
+ store.dispatch(new BalancePatchEvent(base, d(100), d.Zero, timestamp));
21
21
 
22
22
  const balance = store.snapshot.balance.get(base.id);
23
23
 
24
24
  expect(balance).toEqual(component);
25
- expect(balance.free).toEqual(100);
26
- expect(balance.locked).toEqual(0);
25
+ expect(balance.free).toEqual(d(100));
26
+ expect(balance.locked).toEqual(d.Zero);
27
27
  expect(balance.timestamp).toEqual(timestamp);
28
28
  expect(store.snapshot.timestamp).toEqual(timestamp);
29
29
  });
@@ -1,5 +1,5 @@
1
1
  import { AssetSelector, Balance, InstrumentSelector } from '../domain';
2
- import { timestamp } from '../shared';
2
+ import { decimal, timestamp } from '../shared';
3
3
  import { StoreEvent } from './store.event';
4
4
  import { State, StateChangeTracker } from './store-state';
5
5
 
@@ -9,8 +9,8 @@ import { State, StateChangeTracker } from './store-state';
9
9
  export class BalancePatchEvent implements StoreEvent {
10
10
  constructor(
11
11
  readonly asset: AssetSelector,
12
- readonly free: number,
13
- readonly freezed: number,
12
+ readonly free: decimal,
13
+ readonly freezed: decimal,
14
14
  readonly timestamp: timestamp
15
15
  ) {}
16
16
 
@@ -37,7 +37,7 @@ export class BalancePatchEvent implements StoreEvent {
37
37
  export class BalanceTransactEvent implements StoreEvent {
38
38
  constructor(
39
39
  readonly asset: AssetSelector,
40
- readonly amount: number,
40
+ readonly amount: decimal,
41
41
  readonly timestamp: timestamp
42
42
  ) {}
43
43
 
@@ -76,14 +76,14 @@ export class BalanceLockOrderEvent implements StoreEvent {
76
76
 
77
77
  state.timestamp = this.timestamp;
78
78
 
79
- if (balanceToLock.base > 0) {
79
+ if (balanceToLock.base.greaterThan(0)) {
80
80
  base.timestamp = this.timestamp;
81
81
  base.lock(this.orderId, balanceToLock.base);
82
82
 
83
83
  changes.commit(base);
84
84
  }
85
85
 
86
- if (balanceToLock.quote > 0) {
86
+ if (balanceToLock.quote.greaterThan(0)) {
87
87
  quote.timestamp = this.timestamp;
88
88
  quote.lock(this.orderId, balanceToLock.quote);
89
89
 
@@ -1,6 +1,6 @@
1
1
  import { Store } from '..';
2
2
  import { Asset, Commission } from '../domain';
3
- import { now } from '../shared';
3
+ import { d, now } from '../shared';
4
4
  import { InstrumentPatchEvent } from './store-instrument.event';
5
5
 
6
6
  describe('InstrumentPatchEvent', () => {
@@ -11,7 +11,7 @@ describe('InstrumentPatchEvent', () => {
11
11
  const quote = new Asset('usd', 'cex', 2);
12
12
 
13
13
  store.dispatch(
14
- new InstrumentPatchEvent(timestamp, base, quote, new Commission(0, 0), '')
14
+ new InstrumentPatchEvent(timestamp, base, quote, new Commission(d.Zero, d.Zero), '')
15
15
  );
16
16
 
17
17
  const { universe } = store.snapshot;
@@ -1,8 +1,8 @@
1
- import { Asset, Commission, InstrumentSelector, Instrument } from '../domain';
1
+ import { Asset, Commission, Instrument, InstrumentSelector } from '../domain';
2
2
  import { timestamp } from '../shared';
3
3
  import { State, StateChangeTracker } from '../store';
4
- import { InnerSet } from './store-state';
5
4
  import { StoreEvent } from './store.event';
5
+ import { InnerSet } from './store-state';
6
6
 
7
7
  export class InstrumentPatchEvent implements StoreEvent {
8
8
  constructor(
@@ -1,5 +1,5 @@
1
1
  import { Asset, Instrument, Order } from '../domain';
2
- import { now } from '../shared';
2
+ import { d, now } from '../shared';
3
3
  import { Store } from '../store';
4
4
  import { OrderLoadEvent } from './store-order.event';
5
5
 
@@ -13,7 +13,7 @@ describe('OrderLoadEvent', () => {
13
13
  test('should load order to store', () => {
14
14
  const timestamp = now();
15
15
  const store = new Store();
16
- const order = Order.market(instrument, 1.0);
16
+ const order = Order.market(instrument, d(1.0));
17
17
 
18
18
  order.state = 'PENDING';
19
19
 
@@ -1,5 +1,5 @@
1
1
  import { InstrumentSelector, Order } from '../domain';
2
- import { timestamp } from '../shared';
2
+ import { decimal, timestamp } from '../shared';
3
3
  import { StoreEvent } from './store.event';
4
4
  import { InnerSet, State, StateChangeTracker } from './store-state';
5
5
 
@@ -71,7 +71,7 @@ export class OrderFilledEvent implements StoreEvent {
71
71
  constructor(
72
72
  readonly id: string,
73
73
  readonly instrument: InstrumentSelector,
74
- readonly averageExecutionRate: number,
74
+ readonly averageExecutionRate: decimal,
75
75
  readonly timestamp: timestamp
76
76
  ) {}
77
77
 
@@ -0,0 +1,37 @@
1
+ import { Store } from '..';
2
+ import { Asset, Instrument, Liquidity } from '../domain';
3
+ import { d, now } from '../shared';
4
+ import { OrderbookPatchEvent } from '.';
5
+
6
+ const instrument = new Instrument(
7
+ new Asset('btc', 'binance', 8),
8
+ new Asset('usdt', 'binance', 2),
9
+ 'binance:btc-usdt'
10
+ );
11
+
12
+ describe('OrderbookPatchEvent', () => {
13
+ let store: Store;
14
+
15
+ beforeEach(() => {
16
+ store = new Store();
17
+ store.snapshot.universe.instrument.upsert(instrument);
18
+ store.snapshot.subscription.instrument.upsert(instrument);
19
+ });
20
+
21
+ test('should set a best bid and ask', () => {
22
+ const timestamp = now();
23
+
24
+ const ask: Liquidity = { rate: d(2), quantity: d(2), next: undefined };
25
+ const bid: Liquidity = { rate: d(1), quantity: d(1), next: undefined };
26
+
27
+ store.dispatch(new OrderbookPatchEvent(instrument, ask, bid, timestamp));
28
+
29
+ const orderbook = store.snapshot.orderbook.get(instrument.id);
30
+
31
+ expect(orderbook.timestamp).toEqual(timestamp);
32
+ expect(orderbook.instrument.id).toEqual(orderbook.instrument.id);
33
+ expect(orderbook.asks).toEqual(ask);
34
+ expect(orderbook.bids).toEqual(bid);
35
+ expect(store.snapshot.timestamp).toEqual(timestamp);
36
+ });
37
+ });
@@ -1,4 +1,4 @@
1
- import { InstrumentSelector, Orderbook } from '../domain';
1
+ import { InstrumentSelector, Liquidity, Orderbook } from '../domain';
2
2
  import { timestamp } from '../shared';
3
3
  import { StoreEvent } from './store.event';
4
4
  import { State, StateChangeTracker } from './store-state';
@@ -6,10 +6,8 @@ import { State, StateChangeTracker } from './store-state';
6
6
  export class OrderbookPatchEvent implements StoreEvent {
7
7
  constructor(
8
8
  readonly instrument: InstrumentSelector,
9
- readonly bestAskRate: number,
10
- readonly bestAskQuantity: number,
11
- readonly bestBidRate: number,
12
- readonly bestBidQuantity: number,
9
+ readonly ask: Liquidity,
10
+ readonly bid: Liquidity,
13
11
  readonly timestamp: timestamp
14
12
  ) {}
15
13
 
@@ -26,10 +24,8 @@ export class OrderbookPatchEvent implements StoreEvent {
26
24
  state.timestamp = this.timestamp;
27
25
 
28
26
  orderbook.timestamp = this.timestamp;
29
- orderbook.bestAskRate = orderbook.instrument.quote.fixed(this.bestAskRate);
30
- orderbook.bestAskQuantity = orderbook.instrument.base.fixed(this.bestAskQuantity);
31
- orderbook.bestBidRate = orderbook.instrument.quote.fixed(this.bestBidRate);
32
- orderbook.bestBidQuantity = orderbook.instrument.base.fixed(this.bestBidQuantity);
27
+ orderbook.asks = this.ask;
28
+ orderbook.bids = this.bid;
33
29
 
34
30
  const quote = state.balance.get(orderbook.instrument.quote.id);
35
31
 
@@ -39,13 +35,17 @@ export class OrderbookPatchEvent implements StoreEvent {
39
35
  continue;
40
36
  }
41
37
 
42
- const rate = position.size >= 0 ? orderbook.bestBidRate : orderbook.bestAskRate;
38
+ const rate = position.size.greaterThanOrEqualTo(0)
39
+ ? orderbook.bids.rate
40
+ : orderbook.asks.rate;
43
41
 
44
- position.calculateEstimatedUnrealizedPnL(rate);
42
+ if (rate) {
43
+ position.calculateEstimatedUnrealizedPnL(rate);
44
+ }
45
45
  }
46
46
 
47
- if (quote.total < 0) {
48
- throw new Error('liquidated');
47
+ if (quote.total.lessThan(0)) {
48
+ throw new Error('You have been liquidated.');
49
49
  }
50
50
  }
51
51
 
@@ -1,5 +1,5 @@
1
1
  import { Instrument, Position, PositionMode } from '../domain';
2
- import { timestamp } from '../shared';
2
+ import { decimal, timestamp } from '../shared';
3
3
  import { StoreEvent } from './store.event';
4
4
  import { State, StateChangeTracker } from './store-state';
5
5
 
@@ -21,10 +21,13 @@ export class PositionLoadEvent implements StoreEvent {
21
21
  balance.position[this.position.id] = this.position;
22
22
 
23
23
  if (orderbook) {
24
- const rate =
25
- this.position.size >= 0 ? orderbook.bestBidRate : orderbook.bestAskRate;
24
+ const rate = this.position.size.greaterThanOrEqualTo(0)
25
+ ? orderbook.bids.rate
26
+ : orderbook.asks.rate;
26
27
 
27
- this.position.calculateEstimatedUnrealizedPnL(rate);
28
+ if (rate) {
29
+ this.position.calculateEstimatedUnrealizedPnL(rate);
30
+ }
28
31
  }
29
32
  }
30
33
  }
@@ -33,13 +36,14 @@ export class PositionPatchEvent implements StoreEvent {
33
36
  constructor(
34
37
  readonly id: string,
35
38
  readonly instrument: Instrument,
36
- readonly rate: number,
37
- readonly size: number,
39
+ readonly rate: decimal,
40
+ readonly size: decimal,
38
41
  readonly leverage: number,
39
42
  readonly mode: PositionMode,
40
43
  readonly timestamp: timestamp
41
44
  ) {}
42
45
 
46
+ // eslint-disable-next-line complexity
43
47
  handle(state: State, changes: StateChangeTracker): void {
44
48
  if (!state.subscription.instrument.get(this.instrument.id)) {
45
49
  throw new Error(`Trying to patch unsubscribed instrument: ${this.instrument.id}`);
@@ -50,18 +54,22 @@ export class PositionPatchEvent implements StoreEvent {
50
54
 
51
55
  let position = balance.position[this.id];
52
56
 
53
- if (this.size == 0) {
57
+ if (this.size.equals(0)) {
54
58
  if (position) {
55
- position.averageExecutionRate = this.instrument.quote.fixed(this.rate);
56
- position.size = this.instrument.base.fixed(this.size);
59
+ position.averageExecutionRate = this.instrument.quote.floor(this.rate);
60
+ position.size = this.instrument.base.floor(this.size);
57
61
  position.leverage = this.leverage;
58
62
 
59
63
  delete balance.position[this.id];
60
64
 
61
65
  if (orderbook) {
62
- const rate = position.size >= 0 ? orderbook.bestBidRate : orderbook.bestAskRate;
66
+ const rate = position.size.greaterThanOrEqualTo(0)
67
+ ? orderbook.bids.rate
68
+ : orderbook.asks.rate;
63
69
 
64
- position.calculateEstimatedUnrealizedPnL(rate);
70
+ if (rate) {
71
+ position.calculateEstimatedUnrealizedPnL(rate);
72
+ }
65
73
  }
66
74
 
67
75
  changes.commit(position);
@@ -69,9 +77,9 @@ export class PositionPatchEvent implements StoreEvent {
69
77
  }
70
78
  }
71
79
 
72
- const size = (position.size = this.instrument.base.fixed(this.size));
80
+ const size = (position.size = this.instrument.base.floor(this.size));
73
81
  const averageExecutionRate = (position.averageExecutionRate =
74
- this.instrument.quote.fixed(this.rate));
82
+ this.instrument.quote.floor(this.rate));
75
83
 
76
84
  if (!position) {
77
85
  position = new Position(
@@ -91,9 +99,13 @@ export class PositionPatchEvent implements StoreEvent {
91
99
  }
92
100
 
93
101
  if (orderbook) {
94
- const rate = position.size >= 0 ? orderbook.bestBidRate : orderbook.bestAskRate;
102
+ const rate = position.size.greaterThanOrEqualTo(0)
103
+ ? orderbook.bids.rate
104
+ : orderbook.asks.rate;
95
105
 
96
- position.calculateEstimatedUnrealizedPnL(rate);
106
+ if (rate) {
107
+ position.calculateEstimatedUnrealizedPnL(rate);
108
+ }
97
109
  }
98
110
 
99
111
  changes.commit(position);
@@ -1,6 +1,6 @@
1
1
  import { Store } from '..';
2
2
  import { Asset, Instrument } from '../domain';
3
- import { now } from '../shared';
3
+ import { d, now } from '../shared';
4
4
  import { TradePatchEvent } from '.';
5
5
 
6
6
  const instrument = new Instrument(
@@ -17,14 +17,14 @@ describe('TradePatchEvent', () => {
17
17
  store.snapshot.universe.instrument.upsert(instrument);
18
18
  store.snapshot.subscription.instrument.upsert(instrument);
19
19
 
20
- store.dispatch(new TradePatchEvent(instrument, 1000, 0.1, timestamp));
20
+ store.dispatch(new TradePatchEvent(instrument, d(1000), d(0.1), timestamp));
21
21
 
22
22
  const trade = store.snapshot.trade.get(instrument.id);
23
23
 
24
24
  expect(trade.timestamp).toEqual(timestamp);
25
25
  expect(trade.instrument.id).toEqual(trade.instrument.id);
26
- expect(trade.rate).toEqual(1000);
27
- expect(trade.quantity).toEqual(0.1);
26
+ expect(trade.rate).toEqual(d(1000));
27
+ expect(trade.quantity).toEqual(d(0.1));
28
28
  expect(store.snapshot.timestamp).toEqual(timestamp);
29
29
  });
30
30
 
@@ -35,16 +35,16 @@ describe('TradePatchEvent', () => {
35
35
  store.snapshot.universe.instrument.upsert(instrument);
36
36
  store.snapshot.subscription.instrument.upsert(instrument);
37
37
 
38
- store.dispatch(new TradePatchEvent(instrument, 1000, 0.1, timestamp));
38
+ store.dispatch(new TradePatchEvent(instrument, d(1000), d(0.1), timestamp));
39
39
 
40
40
  const trade = store.snapshot.trade.get(instrument.id);
41
41
 
42
- store.dispatch(new TradePatchEvent(instrument, 2000, 0.2, timestamp));
42
+ store.dispatch(new TradePatchEvent(instrument, d(2000), d(0.2), timestamp));
43
43
 
44
44
  expect(trade.timestamp).toEqual(timestamp);
45
45
  expect(trade.instrument.id).toEqual(instrument.id);
46
- expect(trade.rate).toEqual(2000);
47
- expect(trade.quantity).toEqual(0.2);
46
+ expect(trade.rate).toEqual(d(2000));
47
+ expect(trade.quantity).toEqual(d(0.2));
48
48
  expect(store.snapshot.timestamp).toEqual(timestamp);
49
49
  });
50
50
 
@@ -52,7 +52,7 @@ describe('TradePatchEvent', () => {
52
52
  const store = new Store();
53
53
 
54
54
  const fn = () => {
55
- store.dispatch(new TradePatchEvent(instrument, 1000, 0.1, now()));
55
+ store.dispatch(new TradePatchEvent(instrument, d(1000), d(0.1), now()));
56
56
  };
57
57
 
58
58
  expect(fn).toThrow(Error);
@@ -1,5 +1,5 @@
1
1
  import { InstrumentSelector, Trade } from '../domain';
2
- import { timestamp } from '../shared';
2
+ import { decimal, timestamp } from '../shared';
3
3
  import { StoreEvent } from './store.event';
4
4
  import { State, StateChangeTracker } from './store-state';
5
5
 
@@ -10,8 +10,8 @@ import { State, StateChangeTracker } from './store-state';
10
10
  export class TradePatchEvent implements StoreEvent {
11
11
  constructor(
12
12
  readonly instrument: InstrumentSelector,
13
- readonly rate: number,
14
- readonly quantity: number,
13
+ readonly rate: decimal,
14
+ readonly quantity: decimal,
15
15
  readonly timestamp: timestamp
16
16
  ) {}
17
17
 
@@ -28,8 +28,8 @@ export class TradePatchEvent implements StoreEvent {
28
28
  state.timestamp = this.timestamp;
29
29
 
30
30
  trade.timestamp = this.timestamp;
31
- trade.rate = trade.instrument.quote.fixed(this.rate);
32
- trade.quantity = trade.instrument.base.fixed(this.quantity);
31
+ trade.rate = trade.instrument.quote.floor(this.rate);
32
+ trade.quantity = trade.instrument.base.floor(this.quantity);
33
33
 
34
34
  changes.commit(trade);
35
35
  }
@@ -1,7 +1,7 @@
1
1
  import { withLatestFrom } from 'rxjs';
2
2
 
3
3
  import { Asset, balance, Instrument, Order, order } from '../domain';
4
- import { now } from '../shared';
4
+ import { d, now } from '../shared';
5
5
  import { Store } from './store';
6
6
  import { BalanceTransactEvent } from './store-balance.event';
7
7
  import {
@@ -35,7 +35,7 @@ describe('Store', () => {
35
35
  }
36
36
  });
37
37
 
38
- store.dispatch(new OrderLoadEvent(Order.market(instrument, 10), now()));
38
+ store.dispatch(new OrderLoadEvent(Order.market(instrument, d(10)), now()));
39
39
 
40
40
  expect(hasUpdatedOrder).toBe(false);
41
41
  });
@@ -49,7 +49,7 @@ describe('Store', () => {
49
49
  }
50
50
  });
51
51
 
52
- store.dispatch(new OrderNewEvent(Order.market(instrument, 10), now()));
52
+ store.dispatch(new OrderNewEvent(Order.market(instrument, d(10)), now()));
53
53
 
54
54
  expect(hasUpdatedOrder).toBe(true);
55
55
  });
@@ -63,7 +63,7 @@ describe('Store', () => {
63
63
  }
64
64
  });
65
65
 
66
- const buyOrder = Order.market(instrument, 10);
66
+ const buyOrder = Order.market(instrument, d(10));
67
67
 
68
68
  store.dispatch(new OrderNewEvent(buyOrder, now()));
69
69
  store.dispatch(new OrderPendingEvent(buyOrder.id, instrument, now()));
@@ -81,14 +81,14 @@ describe('Store', () => {
81
81
  }
82
82
  });
83
83
 
84
- const buyOrder = Order.market(instrument, 10);
84
+ const buyOrder = Order.market(instrument, d(10));
85
85
 
86
86
  store.dispatch(new OrderNewEvent(buyOrder, now()));
87
87
  store.dispatch(new OrderPendingEvent(buyOrder.id, instrument, now()));
88
- store.dispatch(new OrderFilledEvent(buyOrder.id, instrument, 44, now()));
88
+ store.dispatch(new OrderFilledEvent(buyOrder.id, instrument, d(44), now()));
89
89
 
90
90
  expect(buyOrder.state).toBe('FILLED');
91
- expect(buyOrder.averageExecutionRate).toBe(44);
91
+ expect(buyOrder.averageExecutionRate).toEqual(d(44));
92
92
  expect(states.length).toBe(0);
93
93
  });
94
94
 
@@ -101,7 +101,7 @@ describe('Store', () => {
101
101
  }
102
102
  });
103
103
 
104
- const buyOrder = Order.market(instrument, 10);
104
+ const buyOrder = Order.market(instrument, d(10));
105
105
 
106
106
  store.dispatch(new OrderNewEvent(buyOrder, now()));
107
107
  store.dispatch(new OrderPendingEvent(buyOrder.id, instrument, now()));
@@ -123,19 +123,19 @@ describe('Store', () => {
123
123
  )
124
124
  .subscribe({
125
125
  next: ([balance, order]) => {
126
- expect(balance.free).toBe(10);
127
- expect(order.state).toBe('PENDING');
126
+ expect(balance.free).toEqual(d(10));
127
+ expect(order.state).toEqual('PENDING');
128
128
 
129
129
  done();
130
130
  }
131
131
  });
132
132
 
133
- const buyOrder = Order.market(instrument, 10);
133
+ const buyOrder = Order.market(instrument, d(10));
134
134
 
135
135
  store.dispatch(
136
136
  new OrderNewEvent(buyOrder, now()),
137
137
  new OrderPendingEvent(buyOrder.id, instrument, now()),
138
- new BalanceTransactEvent(instrument.quote, 10, now())
138
+ new BalanceTransactEvent(instrument.quote, d(10), now())
139
139
  );
140
140
  });
141
141
 
@@ -149,7 +149,7 @@ describe('Store', () => {
149
149
  next: it => {
150
150
  counter--;
151
151
 
152
- expect(it.free).toBe(10);
152
+ expect(it.free).toEqual(d(10));
153
153
 
154
154
  if (!counter) {
155
155
  done();
@@ -161,7 +161,7 @@ describe('Store', () => {
161
161
  next: it => {
162
162
  counter--;
163
163
 
164
- expect(it.state).toBe('PENDING');
164
+ expect(it.state).toEqual('PENDING');
165
165
 
166
166
  if (!counter) {
167
167
  done();
@@ -169,12 +169,12 @@ describe('Store', () => {
169
169
  }
170
170
  });
171
171
 
172
- const buyOrder = Order.market(instrument, 10);
172
+ const buyOrder = Order.market(instrument, d(10));
173
173
 
174
174
  store.dispatch(
175
175
  new OrderNewEvent(buyOrder, now()),
176
176
  new OrderPendingEvent(buyOrder.id, instrument, now()),
177
- new BalanceTransactEvent(instrument.quote, 10, now())
177
+ new BalanceTransactEvent(instrument.quote, d(10), now())
178
178
  );
179
179
  });
180
180
  });
@@ -1,3 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- import { Candle } from '../domain';
3
- export declare function truerange<T>(fn: (it: T) => Candle): (source: Observable<T>) => Observable<[T, number]>;