@quantform/core 0.3.222 → 0.3.230

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 (271) hide show
  1. package/dist/adapter/adapter-aggregate.js +3 -3
  2. package/dist/adapter/adapter-aggregate.js.map +1 -1
  3. package/dist/adapter/adapter.d.ts +4 -4
  4. package/dist/adapter/adapter.event.d.ts +1 -1
  5. package/dist/adapter/adapter.event.js +3 -2
  6. package/dist/adapter/adapter.event.js.map +1 -1
  7. package/dist/adapter/adapter.js +3 -3
  8. package/dist/adapter/backtester/backtester-adapter.d.ts +2 -2
  9. package/dist/adapter/backtester/backtester-adapter.js +3 -5
  10. package/dist/adapter/backtester/backtester-adapter.js.map +1 -1
  11. package/dist/adapter/backtester/backtester-cursor.d.ts +2 -2
  12. package/dist/adapter/backtester/backtester-cursor.js +5 -1
  13. package/dist/adapter/backtester/backtester-cursor.js.map +1 -1
  14. package/dist/adapter/backtester/backtester-cursor.spec.js +5 -5
  15. package/dist/adapter/backtester/backtester-cursor.spec.js.map +1 -1
  16. package/dist/adapter/backtester/backtester-streamer.d.ts +1 -1
  17. package/dist/{common/decimals.spec.d.ts → adapter/backtester/backtester-streamer.spec.d.ts} +0 -0
  18. package/dist/adapter/backtester/backtester-streamer.spec.js +45 -0
  19. package/dist/adapter/backtester/backtester-streamer.spec.js.map +1 -0
  20. package/dist/adapter/paper/{model/paper-model.d.ts → executor/paper-executor.d.ts} +1 -1
  21. package/dist/adapter/paper/{model/paper-model.js → executor/paper-executor.js} +4 -4
  22. package/dist/adapter/paper/executor/paper-executor.js.map +1 -0
  23. package/dist/adapter/paper/{model/paper-margin-model.d.ts → executor/paper-margin-executor.d.ts} +2 -2
  24. package/dist/adapter/paper/{model/paper-margin-model.js → executor/paper-margin-executor.js} +11 -11
  25. package/dist/adapter/paper/executor/paper-margin-executor.js.map +1 -0
  26. package/dist/adapter/paper/{model/paper-spot-model.d.ts → executor/paper-spot-executor.d.ts} +3 -3
  27. package/dist/adapter/paper/{model/paper-spot-model.js → executor/paper-spot-executor.js} +7 -7
  28. package/dist/adapter/paper/executor/paper-spot-executor.js.map +1 -0
  29. package/dist/adapter/paper/index.d.ts +2 -2
  30. package/dist/adapter/paper/index.js +2 -2
  31. package/dist/adapter/paper/index.js.map +1 -1
  32. package/dist/adapter/paper/paper-adapter.d.ts +3 -3
  33. package/dist/adapter/paper/paper-adapter.js +6 -6
  34. package/dist/adapter/paper/paper-adapter.js.map +1 -1
  35. package/dist/adapter/paper/paper-adapter.spec.js +2 -2
  36. package/dist/adapter/paper/paper-adapter.spec.js.map +1 -1
  37. package/dist/bin.d.ts +2 -2
  38. package/dist/bin.js +6 -4
  39. package/dist/bin.js.map +1 -1
  40. package/dist/domain/asset.js +1 -1
  41. package/dist/domain/balance.d.ts +1 -1
  42. package/dist/domain/balance.js +1 -3
  43. package/dist/domain/balance.js.map +1 -1
  44. package/dist/domain/candle-builder.d.ts +1 -1
  45. package/dist/domain/candle.d.ts +1 -1
  46. package/dist/domain/candle.spec.js +2 -2
  47. package/dist/domain/{commision.d.ts → commission.d.ts} +2 -2
  48. package/dist/domain/{commision.js → commission.js} +7 -7
  49. package/dist/domain/commission.js.map +1 -0
  50. package/dist/domain/component.d.ts +1 -1
  51. package/dist/domain/index.d.ts +1 -1
  52. package/dist/domain/index.js +1 -1
  53. package/dist/domain/index.js.map +1 -1
  54. package/dist/domain/instrument.d.ts +3 -3
  55. package/dist/domain/order.d.ts +1 -1
  56. package/dist/domain/orderbook.d.ts +1 -1
  57. package/dist/domain/position.d.ts +1 -1
  58. package/dist/domain/position.js +3 -3
  59. package/dist/domain/trade.d.ts +1 -1
  60. package/dist/index.d.ts +1 -2
  61. package/dist/index.js +1 -2
  62. package/dist/index.js.map +1 -1
  63. package/dist/indicator/truerange.spec.js +6 -6
  64. package/dist/session/index.d.ts +1 -0
  65. package/dist/session/index.js +1 -0
  66. package/dist/session/index.js.map +1 -1
  67. package/dist/session/session-descriptor.d.ts +2 -3
  68. package/dist/session/session-statement.d.ts +5 -0
  69. package/dist/session/session-statement.js +87 -0
  70. package/dist/session/session-statement.js.map +1 -0
  71. package/dist/session/session.d.ts +3 -3
  72. package/dist/session/session.js +14 -27
  73. package/dist/session/session.js.map +1 -1
  74. package/dist/session/session.spec.js +3 -3
  75. package/dist/session/session.spec.js.map +1 -1
  76. package/dist/{common → shared}/datetime.d.ts +0 -0
  77. package/dist/{common → shared}/datetime.js +0 -0
  78. package/dist/{common → shared}/datetime.js.map +1 -1
  79. package/dist/{common → shared}/decimals.d.ts +0 -0
  80. package/dist/{common → shared}/decimals.js +0 -0
  81. package/dist/{common → shared}/decimals.js.map +1 -1
  82. package/dist/{common/policy.spec.d.ts → shared/decimals.spec.d.ts} +0 -0
  83. package/dist/{common → shared}/decimals.spec.js +0 -0
  84. package/dist/{common → shared}/decimals.spec.js.map +1 -1
  85. package/dist/{common → shared}/index.d.ts +0 -0
  86. package/dist/{common → shared}/index.js +0 -0
  87. package/dist/{common → shared}/index.js.map +1 -1
  88. package/dist/{common → shared}/io.d.ts +0 -0
  89. package/dist/{common → shared}/io.js +0 -0
  90. package/dist/shared/io.js.map +1 -0
  91. package/dist/{common → shared}/logger.d.ts +0 -0
  92. package/dist/{common → shared}/logger.js +0 -0
  93. package/dist/{common → shared}/logger.js.map +1 -1
  94. package/dist/{common → shared}/policy.d.ts +0 -0
  95. package/dist/{common → shared}/policy.js +0 -0
  96. package/dist/{common → shared}/policy.js.map +1 -1
  97. package/dist/{common/topic.spec.d.ts → shared/policy.spec.d.ts} +0 -0
  98. package/dist/{common → shared}/policy.spec.js +0 -0
  99. package/dist/{common → shared}/policy.spec.js.map +1 -1
  100. package/dist/{common → shared}/topic.d.ts +0 -0
  101. package/dist/{common → shared}/topic.js +0 -0
  102. package/dist/{common → shared}/topic.js.map +1 -1
  103. package/dist/{common/worker.spec.d.ts → shared/topic.spec.d.ts} +0 -0
  104. package/dist/{common → shared}/topic.spec.js +0 -0
  105. package/dist/{common → shared}/topic.spec.js.map +1 -1
  106. package/dist/{common → shared}/worker.d.ts +0 -0
  107. package/dist/{common → shared}/worker.js +0 -0
  108. package/dist/{common → shared}/worker.js.map +1 -1
  109. package/dist/shared/worker.spec.d.ts +1 -0
  110. package/dist/{common → shared}/worker.spec.js +0 -0
  111. package/dist/{common → shared}/worker.spec.js.map +1 -1
  112. package/dist/storage/feed.d.ts +7 -4
  113. package/dist/storage/feed.js +21 -0
  114. package/dist/storage/feed.js.map +1 -1
  115. package/dist/storage/index.d.ts +1 -2
  116. package/dist/storage/index.js +1 -2
  117. package/dist/storage/index.js.map +1 -1
  118. package/dist/storage/measurement.d.ts +7 -9
  119. package/dist/storage/measurement.js +25 -0
  120. package/dist/storage/measurement.js.map +1 -1
  121. package/dist/storage/storage.d.ts +23 -0
  122. package/dist/storage/storage.js +48 -0
  123. package/dist/storage/storage.js.map +1 -0
  124. package/dist/store/event/store-balance.event.d.ts +1 -1
  125. package/dist/store/event/store-balance.event.js +1 -1
  126. package/dist/store/event/store-balance.event.spec.js +3 -3
  127. package/dist/store/event/store-balance.event.spec.js.map +1 -1
  128. package/dist/store/event/store-candle.event.d.ts +2 -2
  129. package/dist/store/event/store-candle.event.js +31 -3
  130. package/dist/store/event/store-candle.event.js.map +1 -1
  131. package/dist/store/event/store-candle.event.spec.d.ts +1 -0
  132. package/dist/store/event/store-candle.event.spec.js +24 -0
  133. package/dist/store/event/store-candle.event.spec.js.map +1 -0
  134. package/dist/store/event/store-instrument.event.d.ts +4 -4
  135. package/dist/store/event/store-instrument.event.js +3 -3
  136. package/dist/store/event/store-instrument.event.js.map +1 -1
  137. package/dist/store/event/store-instrument.event.spec.js +3 -3
  138. package/dist/store/event/store-instrument.event.spec.js.map +1 -1
  139. package/dist/store/event/store-order.event.d.ts +1 -1
  140. package/dist/store/event/store-order.event.js +1 -1
  141. package/dist/store/event/store-order.event.spec.js +2 -2
  142. package/dist/store/event/store-orderbook.event.d.ts +1 -1
  143. package/dist/store/event/store-orderbook.event.js +1 -1
  144. package/dist/store/event/store-position.event.d.ts +1 -1
  145. package/dist/store/event/store-position.event.js +1 -1
  146. package/dist/store/event/store-trade.event.d.ts +1 -1
  147. package/dist/store/event/store-trade.event.js +1 -1
  148. package/dist/store/event/store-trade.event.spec.js +6 -6
  149. package/dist/store/event/store-trade.event.spec.js.map +1 -1
  150. package/dist/store/event/store.event.d.ts +1 -1
  151. package/dist/store/store.d.ts +2 -2
  152. package/dist/store/store.js +1 -1
  153. package/dist/store/store.state.d.ts +1 -1
  154. package/dist/store/store.state.js.map +1 -1
  155. package/dist/tests/backtester-adapter.spec.js +7 -7
  156. package/dist/tests/backtester-adapter.spec.js.map +1 -1
  157. package/dist/tsconfig.tsbuildinfo +1 -1
  158. package/jestconfig.unit.json +2 -1
  159. package/package.json +1 -1
  160. package/src/adapter/adapter-aggregate.ts +4 -2
  161. package/src/adapter/adapter.event.ts +2 -2
  162. package/src/adapter/adapter.ts +4 -4
  163. package/src/adapter/backtester/backtester-adapter.ts +4 -6
  164. package/src/adapter/backtester/backtester-cursor.spec.ts +8 -8
  165. package/src/adapter/backtester/backtester-cursor.ts +7 -3
  166. package/src/adapter/backtester/backtester-streamer.spec.ts +53 -0
  167. package/src/adapter/backtester/backtester-streamer.ts +1 -1
  168. package/src/adapter/paper/{model/paper-model.ts → executor/paper-executor.ts} +1 -1
  169. package/src/adapter/paper/{model/paper-margin-model.ts → executor/paper-margin-executor.ts} +4 -4
  170. package/src/adapter/paper/{model/paper-spot-model.ts → executor/paper-spot-executor.ts} +5 -5
  171. package/src/adapter/paper/index.ts +2 -2
  172. package/src/adapter/paper/paper-adapter.spec.ts +4 -4
  173. package/src/adapter/paper/paper-adapter.ts +8 -8
  174. package/src/bin.ts +8 -6
  175. package/src/domain/asset.ts +2 -2
  176. package/src/domain/balance.ts +3 -5
  177. package/src/domain/candle-builder.ts +1 -1
  178. package/src/domain/candle.spec.ts +1 -1
  179. package/src/domain/candle.ts +1 -1
  180. package/src/domain/{commision.ts → commission.ts} +3 -3
  181. package/src/domain/component.ts +1 -1
  182. package/src/domain/index.ts +1 -1
  183. package/src/domain/instrument.ts +3 -3
  184. package/src/domain/order.ts +1 -1
  185. package/src/domain/orderbook.ts +1 -1
  186. package/src/domain/position.ts +2 -2
  187. package/src/domain/trade.ts +1 -1
  188. package/src/index.ts +1 -7
  189. package/src/indicator/truerange.spec.ts +1 -1
  190. package/src/session/index.ts +1 -0
  191. package/src/session/session-descriptor.ts +29 -7
  192. package/src/session/session-statement.ts +119 -0
  193. package/src/session/session.spec.ts +6 -4
  194. package/src/session/session.ts +67 -33
  195. package/src/{common → shared}/datetime.ts +0 -0
  196. package/src/{common → shared}/decimals.spec.ts +0 -0
  197. package/src/{common → shared}/decimals.ts +0 -0
  198. package/src/{common → shared}/index.ts +0 -0
  199. package/src/{common → shared}/io.ts +0 -0
  200. package/src/{common → shared}/logger.ts +0 -0
  201. package/src/{common → shared}/policy.spec.ts +0 -0
  202. package/src/{common → shared}/policy.ts +0 -0
  203. package/src/{common → shared}/topic.spec.ts +0 -0
  204. package/src/{common → shared}/topic.ts +0 -0
  205. package/src/{common → shared}/worker.spec.ts +0 -0
  206. package/src/{common → shared}/worker.ts +0 -0
  207. package/src/storage/feed.ts +32 -7
  208. package/src/storage/index.ts +1 -2
  209. package/src/storage/measurement.ts +28 -14
  210. package/src/storage/storage.ts +76 -0
  211. package/src/store/event/store-balance.event.spec.ts +3 -3
  212. package/src/store/event/store-balance.event.ts +2 -2
  213. package/src/store/event/store-candle.event.spec.ts +32 -0
  214. package/src/store/event/store-candle.event.ts +26 -2
  215. package/src/store/event/store-instrument.event.spec.ts +3 -3
  216. package/src/store/event/store-instrument.event.ts +5 -5
  217. package/src/store/event/store-order.event.spec.ts +1 -1
  218. package/src/store/event/store-order.event.ts +2 -2
  219. package/src/store/event/store-orderbook.event.ts +2 -2
  220. package/src/store/event/store-position.event.ts +2 -2
  221. package/src/store/event/store-trade.event.spec.ts +2 -2
  222. package/src/store/event/store-trade.event.ts +2 -2
  223. package/src/store/event/store.event.ts +1 -1
  224. package/src/store/store.state.ts +2 -1
  225. package/src/store/store.ts +1 -1
  226. package/src/tests/backtester-adapter.spec.ts +10 -10
  227. package/dist/adapter/paper/model/paper-margin-model.js.map +0 -1
  228. package/dist/adapter/paper/model/paper-model.js.map +0 -1
  229. package/dist/adapter/paper/model/paper-spot-model.js.map +0 -1
  230. package/dist/behaviour/behaviour.d.ts +0 -6
  231. package/dist/behaviour/behaviour.js +0 -3
  232. package/dist/behaviour/behaviour.js.map +0 -1
  233. package/dist/behaviour/combined-behaviour.d.ts +0 -14
  234. package/dist/behaviour/combined-behaviour.js +0 -26
  235. package/dist/behaviour/combined-behaviour.js.map +0 -1
  236. package/dist/behaviour/index.d.ts +0 -3
  237. package/dist/behaviour/index.js +0 -16
  238. package/dist/behaviour/index.js.map +0 -1
  239. package/dist/behaviour/statement/benchmark-statement.behaviour.d.ts +0 -15
  240. package/dist/behaviour/statement/benchmark-statement.behaviour.js +0 -41
  241. package/dist/behaviour/statement/benchmark-statement.behaviour.js.map +0 -1
  242. package/dist/behaviour/statement/equity-statement.behaviour.d.ts +0 -15
  243. package/dist/behaviour/statement/equity-statement.behaviour.js +0 -54
  244. package/dist/behaviour/statement/equity-statement.behaviour.js.map +0 -1
  245. package/dist/behaviour/statement/index.d.ts +0 -1
  246. package/dist/behaviour/statement/index.js +0 -14
  247. package/dist/behaviour/statement/index.js.map +0 -1
  248. package/dist/behaviour/statement/period-statement.behaviour.d.ts +0 -9
  249. package/dist/behaviour/statement/period-statement.behaviour.js +0 -20
  250. package/dist/behaviour/statement/period-statement.behaviour.js.map +0 -1
  251. package/dist/behaviour/statement/statement.behaviour.d.ts +0 -7
  252. package/dist/behaviour/statement/statement.behaviour.js +0 -19
  253. package/dist/behaviour/statement/statement.behaviour.js.map +0 -1
  254. package/dist/common/io.js.map +0 -1
  255. package/dist/domain/commision.js.map +0 -1
  256. package/dist/storage/feed.interceptor.d.ts +0 -14
  257. package/dist/storage/feed.interceptor.js +0 -52
  258. package/dist/storage/feed.interceptor.js.map +0 -1
  259. package/dist/storage/in-memory.feed.d.ts +0 -10
  260. package/dist/storage/in-memory.feed.js +0 -30
  261. package/dist/storage/in-memory.feed.js.map +0 -1
  262. package/src/behaviour/behaviour.ts +0 -7
  263. package/src/behaviour/combined-behaviour.ts +0 -23
  264. package/src/behaviour/index.ts +0 -3
  265. package/src/behaviour/statement/benchmark-statement.behaviour.ts +0 -53
  266. package/src/behaviour/statement/equity-statement.behaviour.ts +0 -71
  267. package/src/behaviour/statement/index.ts +0 -1
  268. package/src/behaviour/statement/period-statement.behaviour.ts +0 -26
  269. package/src/behaviour/statement/statement.behaviour.ts +0 -19
  270. package/src/storage/feed.interceptor.ts +0 -92
  271. package/src/storage/in-memory.feed.ts +0 -38
@@ -0,0 +1,119 @@
1
+ import { Session } from '.';
2
+ import { InstrumentSelector } from '../domain';
3
+ import { combineLatest, finalize, map, take, tap } from 'rxjs';
4
+ import { drawdown } from '../indicator';
5
+ import { floor, precision } from '../shared';
6
+
7
+ export function period() {
8
+ return (session: Session) => {
9
+ const period = session.useStatement('period');
10
+
11
+ return session.store.changes$.pipe(
12
+ finalize(() => (period.to = new Date(session.timestamp).toISOString())),
13
+ take(1),
14
+ tap(it => (period.from = new Date(it.timestamp).toISOString()))
15
+ );
16
+ };
17
+ }
18
+
19
+ export function benchmark(instrument: InstrumentSelector) {
20
+ return (session: Session) => {
21
+ const statement = session.useStatement('benchmark');
22
+
23
+ let balance: number, entry: number, exit: number;
24
+ let dd = 0;
25
+
26
+ return combineLatest([
27
+ session.orderbook(instrument),
28
+ session.balance(instrument.quote),
29
+ session.orderbook(instrument).pipe(
30
+ drawdown(it => it.midRate),
31
+ tap(it => (dd = it))
32
+ )
33
+ ]).pipe(
34
+ map(([orderbook, quote]) => {
35
+ const price = orderbook.midRate;
36
+
37
+ if (!balance) {
38
+ balance = quote.total;
39
+ }
40
+
41
+ if (!entry) {
42
+ entry = price;
43
+ }
44
+
45
+ exit = price;
46
+ }),
47
+ finalize(() => {
48
+ const pnl = exit / entry - 1;
49
+
50
+ statement['benchmark_entry'] = entry;
51
+ statement['benchmark_exit'] = exit;
52
+ statement['benchmark_pnl'] = floor(balance * pnl, precision(entry));
53
+ statement['benchmark_pnl_pp'] = floor(pnl * 100, 2);
54
+ statement['benchmark_drawdown_pp'] = floor(dd * 100, 2);
55
+ })
56
+ );
57
+ };
58
+ }
59
+
60
+ export function equity(instrument: InstrumentSelector) {
61
+ return (session: Session) => {
62
+ const statement = session.useStatement('benchmark');
63
+
64
+ let balance: number, min: number, max: number;
65
+ let dd = 0;
66
+ let equity = 0;
67
+
68
+ return combineLatest([
69
+ session.orderbook(instrument),
70
+ session.balance(instrument.base),
71
+ session.balance(instrument.quote),
72
+ session.positions(instrument)
73
+ ]).pipe(
74
+ tap(([, , quote]) => {
75
+ if (!balance) {
76
+ balance = quote.total;
77
+ }
78
+ }),
79
+ map(([orderbook, base, quote, positions]) =>
80
+ orderbook.instrument.quote.fixed(
81
+ quote.total +
82
+ base.total * orderbook.bestBidRate +
83
+ positions.reduce(
84
+ (agg, position) => agg + position.calculatePnL(orderbook.bestBidRate),
85
+ 0
86
+ )
87
+ )
88
+ ),
89
+ tap(it => {
90
+ if (!min) {
91
+ min = it;
92
+ } else {
93
+ min = Math.min(min, it);
94
+ }
95
+
96
+ if (!max) {
97
+ max = it;
98
+ } else {
99
+ max = Math.max(max, it);
100
+ }
101
+
102
+ equity = it;
103
+ }),
104
+ drawdown(it => it),
105
+ tap(it => (dd = it)),
106
+ finalize(() => {
107
+ const pnl = equity / balance - 1;
108
+ const scale = precision(equity);
109
+
110
+ statement['equity'] = equity;
111
+ statement['equity_min'] = min;
112
+ statement['equity_max'] = max;
113
+ statement['equity_pnl'] = floor(balance * pnl, scale);
114
+ statement['equity_pnl_pp'] = floor(pnl * 100, 2);
115
+ statement['equity_drawdown_pp'] = floor(dd * 100, 2);
116
+ })
117
+ );
118
+ };
119
+ }
@@ -1,8 +1,10 @@
1
1
  import { InstrumentPatchEvent } from '../store/event';
2
- import { Asset, Commision } from '../domain';
3
- import { now } from '../common';
2
+ import { Asset, Commission } from '../domain';
3
+ import { now } from '../shared';
4
4
  import { SessionDescriptor } from './session-descriptor';
5
5
  import { paper } from '../bin';
6
+ import { Session } from './session';
7
+ import { of } from 'rxjs';
6
8
 
7
9
  describe('session tests', () => {
8
10
  const descriptor: SessionDescriptor = {
@@ -29,14 +31,14 @@ describe('session tests', () => {
29
31
  now(),
30
32
  new Asset('de30', 'cex', 2),
31
33
  new Asset('usd', 'cex', 2),
32
- new Commision(0, 0),
34
+ new Commission(0, 0),
33
35
  ''
34
36
  ),
35
37
  new InstrumentPatchEvent(
36
38
  now(),
37
39
  new Asset('wig20', 'cex', 2),
38
40
  new Asset('pln', 'cex', 2),
39
- new Commision(0, 0),
41
+ new Commission(0, 0),
40
42
  ''
41
43
  )
42
44
  );
@@ -22,9 +22,8 @@ import {
22
22
  } from '../domain';
23
23
  import { Store } from '../store';
24
24
  import { concat, from, Observable, Subject, Subscription } from 'rxjs';
25
- import { Behaviour, CombinedBehaviour, FunctionBehaviour } from '../behaviour';
26
25
  import { AdapterAggregate } from '../adapter/adapter-aggregate';
27
- import { Worker } from '../common';
26
+ import { Worker, now } from '../shared';
28
27
  import { Trade } from '../domain/trade';
29
28
  import { SessionDescriptor } from './session-descriptor';
30
29
  import {
@@ -39,18 +38,24 @@ type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
39
38
  export class Session {
40
39
  private initialized = false;
41
40
  private subscription: Subscription;
42
- private behaviour: Behaviour;
43
41
  private worker = new Worker();
44
42
 
45
43
  get timestamp(): number {
46
44
  return this.store.snapshot.timestamp;
47
45
  }
48
46
 
47
+ readonly statement: Record<string, Record<string, any>> = {};
48
+
49
49
  constructor(
50
50
  readonly store: Store,
51
51
  readonly aggregate: AdapterAggregate,
52
52
  readonly descriptor?: SessionDescriptor
53
- ) {}
53
+ ) {
54
+ // generate session id based on time if not provided.
55
+ if (descriptor && !descriptor.id) {
56
+ descriptor.id = now();
57
+ }
58
+ }
54
59
 
55
60
  async awake(): Promise<void> {
56
61
  if (this.initialized) {
@@ -59,18 +64,11 @@ export class Session {
59
64
 
60
65
  this.initialized = true;
61
66
 
67
+ // awake all adapters and synchronize trading accounts with store.
62
68
  await this.aggregate.awake(this.descriptor != null);
63
69
 
64
- if (this.descriptor?.behaviour) {
65
- if (this.descriptor.behaviour instanceof Function) {
66
- this.behaviour = new FunctionBehaviour(this.descriptor.behaviour);
67
- } else {
68
- this.behaviour = Array.isArray(this.descriptor.behaviour)
69
- ? new CombinedBehaviour(this.descriptor.behaviour)
70
- : this.descriptor.behaviour;
71
- }
72
-
73
- this.subscription = this.behaviour.describe(this).subscribe();
70
+ if (this.descriptor.describe) {
71
+ this.subscription = this.descriptor.describe(this).subscribe();
74
72
  }
75
73
  }
76
74
 
@@ -89,24 +87,29 @@ export class Session {
89
87
  await this.worker.wait();
90
88
  }
91
89
 
92
- statement(output: Record<string, any>) {
93
- if (!this.behaviour?.statement) {
94
- return;
95
- }
96
-
97
- this.behaviour.statement(output);
90
+ useStatement(section: string): Record<string, any> {
91
+ return this.statement[section] ?? (this.statement[section] = {});
98
92
  }
99
93
 
94
+ /**
95
+ * Returns last stored measurement and setter for it in session.
96
+ * For example you can save and restore variables in same session between runs.
97
+ * Example usage:
98
+ * const [order$, setOrder] = session.measurement<Order>('order');
99
+ *
100
+ * order.pipe(tap(it => console.log(`your last order was: ${it}`)));
101
+ *
102
+ * setOrder(order);
103
+ */
100
104
  useMeasure<T extends { timestamp: number }>(
101
- params: { key: string; timestamp?: number },
105
+ params: { kind: string; timestamp?: number },
102
106
  defaultValue: T = undefined
103
107
  ): [Observable<T>, (value: Optional<T, 'timestamp'>) => void] {
104
108
  const stored$ = from(
105
109
  this.descriptor.measurement.query(this.descriptor.id, {
106
- type: params.key,
107
- timestamp: params.timestamp ?? this.timestamp,
108
- limit: 1,
109
- direction: 'BACKWARD'
110
+ to: params.timestamp ?? this.timestamp,
111
+ kind: params.kind,
112
+ count: 1
110
113
  })
111
114
  ).pipe(
112
115
  map(it =>
@@ -119,7 +122,7 @@ export class Session {
119
122
 
120
123
  const setter = (value: T) => {
121
124
  const timestamp = value.timestamp ?? this.timestamp;
122
- const measure = { timestamp, type: params.key, payload: value };
125
+ const measure = { timestamp, kind: params.kind, payload: value };
123
126
 
124
127
  this.worker.enqueue(() =>
125
128
  this.descriptor.measurement.save(this.descriptor.id, [measure])
@@ -131,17 +134,19 @@ export class Session {
131
134
  return [concat(stored$, subject$.asObservable()), setter];
132
135
  }
133
136
 
137
+ /**
138
+ * Return values for patch provided in optimization file.
139
+ * Example usage:
140
+ * const orderSize = session.useOptimizer('order.size');
141
+ */
134
142
  useOptimizer(path: string): any {
135
- const session = this;
136
- const [order$, setOrderMeasure] = session.useMeasure(
137
- { key: 'ordered' },
138
- { limt: 10, timestamp: 0 }
139
- );
140
-
141
- setOrderMeasure({ limt: 0, timestamp: 0 });
142
143
  return undefined;
143
144
  }
144
145
 
146
+ /**
147
+ * Subscribes to specific instrument. Usually forces adapter to subscribe
148
+ * for orderbook and ticker streams.
149
+ */
145
150
  async subscribe(instrument: Array<InstrumentSelector>): Promise<void> {
146
151
  const grouped = instrument
147
152
  .filter(it => it != null)
@@ -162,6 +167,11 @@ export class Session {
162
167
  }
163
168
  }
164
169
 
170
+ /**
171
+ * Opens collection of orders.
172
+ * Example:
173
+ * session.open(Order.buyMarket(instrument, 100));
174
+ */
165
175
  async open(...orders: Order[]): Promise<void> {
166
176
  await Promise.all(
167
177
  orders.map(it =>
@@ -173,6 +183,9 @@ export class Session {
173
183
  );
174
184
  }
175
185
 
186
+ /**
187
+ * Cancels specific order.
188
+ */
176
189
  cancel(order: Order): Promise<void> {
177
190
  return this.aggregate.dispatch(
178
191
  order.instrument.base.exchange,
@@ -180,6 +193,10 @@ export class Session {
180
193
  );
181
194
  }
182
195
 
196
+ /**
197
+ * Subscribes to specific instrument changes.
198
+ * When adapter awake then it will fetch collection of all available instruments.
199
+ */
183
200
  instrument(selector: InstrumentSelector): Observable<Instrument> {
184
201
  this.subscribe([selector]);
185
202
 
@@ -189,6 +206,10 @@ export class Session {
189
206
  );
190
207
  }
191
208
 
209
+ /**
210
+ * Subscribes to instruments changes.
211
+ * When adapter awake then it will fetch collection of all available instruments.
212
+ */
192
213
  instruments(): Observable<Instrument[]> {
193
214
  return this.store.changes$.pipe(
194
215
  filter(it => it instanceof Instrument),
@@ -199,6 +220,9 @@ export class Session {
199
220
  );
200
221
  }
201
222
 
223
+ /**
224
+ * Subscribes to trade/ticker changes.
225
+ */
202
226
  trade(selector?: InstrumentSelector): Observable<Trade> {
203
227
  this.subscribe([selector]);
204
228
 
@@ -212,6 +236,10 @@ export class Session {
212
236
  );
213
237
  }
214
238
 
239
+ /**
240
+ * Subscribes to orderbook changes.
241
+ * Right now you can access only best bid and best ask.
242
+ */
215
243
  orderbook(selector?: InstrumentSelector): Observable<Orderbook> {
216
244
  this.subscribe([selector]);
217
245
 
@@ -225,6 +253,9 @@ export class Session {
225
253
  );
226
254
  }
227
255
 
256
+ /**
257
+ * Subscribes to position on leveraged market.
258
+ */
228
259
  position(selector?: InstrumentSelector): Observable<Position> {
229
260
  this.subscribe([selector]);
230
261
 
@@ -238,6 +269,9 @@ export class Session {
238
269
  );
239
270
  }
240
271
 
272
+ /**
273
+ * Subscribes to positions on leveraged markets.
274
+ */
241
275
  positions(selector: InstrumentSelector): Observable<Position[]> {
242
276
  this.subscribe([selector]);
243
277
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,13 +1,38 @@
1
1
  import { StoreEvent } from '../store/event';
2
2
  import { InstrumentSelector } from '../domain';
3
- import { timestamp } from '../common';
3
+ import { Storage, StorageQueryOptions } from './storage';
4
4
 
5
- export interface Feed {
6
- read(
5
+ export class Feed {
6
+ constructor(private readonly storage: Storage) {}
7
+
8
+ index(): Promise<Array<string>> {
9
+ return this.storage.index();
10
+ }
11
+
12
+ save(instrument: InstrumentSelector, events: StoreEvent[]): Promise<void> {
13
+ return this.storage.save(
14
+ instrument.toString(),
15
+ events.map(it => ({
16
+ timestamp: it.timestamp,
17
+ kind: it.type,
18
+ json: JSON.stringify(it, (key, value) =>
19
+ key != 'timestamp' && key != 'type' && key != 'instrument' ? value : undefined
20
+ )
21
+ }))
22
+ );
23
+ }
24
+
25
+ async query(
7
26
  instrument: InstrumentSelector,
8
- from: timestamp,
9
- to: timestamp
10
- ): Promise<StoreEvent[]>;
27
+ options: StorageQueryOptions
28
+ ): Promise<StoreEvent[]> {
29
+ const rows = await this.storage.query(instrument.toString(), options);
11
30
 
12
- write(instrument: InstrumentSelector, events: StoreEvent[]): Promise<void>;
31
+ return rows.map(it => ({
32
+ timestamp: it.timestamp,
33
+ type: it.kind,
34
+ instrument,
35
+ ...JSON.parse(it.json)
36
+ }));
37
+ }
13
38
  }
@@ -1,4 +1,3 @@
1
1
  export * from './feed';
2
- export * from './in-memory.feed';
3
- export * from './feed.interceptor';
2
+ export * from './storage';
4
3
  export * from './measurement';
@@ -1,23 +1,37 @@
1
- import { timestamp } from '../common';
1
+ import { timestamp } from '../shared';
2
+ import { Storage, StorageQueryOptions } from './storage';
2
3
 
3
4
  export interface Measure {
4
5
  timestamp: timestamp;
5
- type: string;
6
+ kind: string;
6
7
  payload: any;
7
8
  }
8
9
 
9
- export interface Measurement {
10
- index(): Promise<Array<number>>;
10
+ export class Measurement {
11
+ constructor(private readonly storage: Storage) {}
11
12
 
12
- query(
13
- session: number,
14
- options: {
15
- timestamp: timestamp;
16
- type?: string;
17
- limit: number;
18
- direction: 'FORWARD' | 'BACKWARD';
19
- }
20
- ): Promise<Measure[]>;
13
+ async index(): Promise<Array<number>> {
14
+ return (await this.storage.index()).map(it => Number(it));
15
+ }
21
16
 
22
- save(session: number, measurements: Measure[]): Promise<void>;
17
+ save(session: number, measurements: Measure[]): Promise<void> {
18
+ return this.storage.save(
19
+ session.toString(),
20
+ measurements.map(it => ({
21
+ timestamp: it.timestamp,
22
+ kind: it.kind,
23
+ json: JSON.stringify(it.payload)
24
+ }))
25
+ );
26
+ }
27
+
28
+ async query(session: number, options: StorageQueryOptions): Promise<Measure[]> {
29
+ const rows = await this.storage.query(session.toString(), options);
30
+
31
+ return rows.map(it => ({
32
+ timestamp: it.timestamp,
33
+ kind: it.kind,
34
+ payload: JSON.parse(it.json)
35
+ }));
36
+ }
23
37
  }
@@ -0,0 +1,76 @@
1
+ export type StorageDocument = {
2
+ timestamp: number;
3
+ kind: string;
4
+ json: string;
5
+ };
6
+
7
+ export type StorageQueryOptions = {
8
+ from?: number;
9
+ to?: number;
10
+ kind?: string;
11
+ count: number;
12
+ };
13
+
14
+ export interface Storage {
15
+ index(): Promise<Array<string>>;
16
+
17
+ save(library: string, documents: StorageDocument[]): Promise<void>;
18
+
19
+ query(library: string, options: StorageQueryOptions): Promise<StorageDocument[]>;
20
+ }
21
+
22
+ export class InMemoryStorage implements Storage {
23
+ private tables: Record<string, StorageDocument[]> = {};
24
+
25
+ async index(): Promise<Array<string>> {
26
+ return Object.keys(this.tables);
27
+ }
28
+
29
+ async query(library: string, options: StorageQueryOptions): Promise<StorageDocument[]> {
30
+ if (!this.tables[library]) {
31
+ return [];
32
+ }
33
+
34
+ let query = this.tables[library];
35
+
36
+ if (options.from) {
37
+ query = query.filter(it => it.timestamp > options.from);
38
+ }
39
+
40
+ if (options.to) {
41
+ query = query.filter(it => it.timestamp < options.to);
42
+ }
43
+
44
+ if (options.kind) {
45
+ query = query.filter(it => it.kind == options.kind);
46
+ }
47
+
48
+ if (options.from == undefined && options.to) {
49
+ query = query.reverse();
50
+ }
51
+
52
+ if (options.count) {
53
+ query = query.slice(0, options.count);
54
+ }
55
+
56
+ return query;
57
+ }
58
+
59
+ async save(library: string, documents: StorageDocument[]): Promise<void> {
60
+ if (!this.tables[library]) {
61
+ this.tables[library] = [];
62
+ }
63
+
64
+ const buffer = this.tables[library];
65
+
66
+ for (const document of documents) {
67
+ buffer.push(document);
68
+ }
69
+
70
+ buffer.sort((lhs, rhs) => lhs.timestamp - rhs.timestamp);
71
+ }
72
+
73
+ clear() {
74
+ this.tables = {};
75
+ }
76
+ }
@@ -1,7 +1,7 @@
1
- import { Asset, Commision } from '../../domain';
1
+ import { Asset, Commission } from '../../domain';
2
2
  import { BalancePatchEvent } from '.';
3
3
  import { InstrumentPatchEvent } from './store-instrument.event';
4
- import { now } from '../../common';
4
+ import { now } from '../../shared';
5
5
  import { Store } from '..';
6
6
 
7
7
  describe('balance event tests', () => {
@@ -15,7 +15,7 @@ describe('balance event tests', () => {
15
15
  store.changes$.subscribe(it => (component = it));
16
16
 
17
17
  store.dispatch(
18
- new InstrumentPatchEvent(timestamp, base, quote, new Commision(0, 0), '')
18
+ new InstrumentPatchEvent(timestamp, base, quote, new Commission(0, 0), '')
19
19
  );
20
20
  store.dispatch(new BalancePatchEvent(base, 100, 0, timestamp));
21
21
 
@@ -1,5 +1,5 @@
1
- import { event } from '../../common/topic';
2
- import { timestamp } from '../../common';
1
+ import { event } from '../../shared/topic';
2
+ import { timestamp } from '../../shared';
3
3
  import { AssetSelector } from '../../domain/asset';
4
4
  import { Balance } from '../../domain/balance';
5
5
  import { State } from '../store.state';
@@ -0,0 +1,32 @@
1
+ import { Asset, Instrument } from '../../domain';
2
+ import { CandleEvent, CandleEventHandler } from '.';
3
+ import { State } from '../store.state';
4
+ import { now } from '../../shared';
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('candle patch event tests', () => {
13
+ test('should patch trade object', () => {
14
+ const timestamp = now();
15
+ const state = new State();
16
+
17
+ state.universe.instrument[instrument.toString()] = instrument;
18
+ state.subscription.instrument[instrument.toString()] = instrument;
19
+
20
+ const event = new CandleEvent(instrument, 1, 1, 1, 1, 1, 1, timestamp);
21
+
22
+ CandleEventHandler(event, state);
23
+
24
+ const trade = state.trade[instrument.toString()];
25
+
26
+ expect(trade.timestamp).toEqual(timestamp);
27
+ expect(trade.instrument.toString()).toEqual(instrument.toString());
28
+ expect(trade.rate).toEqual(1);
29
+ expect(trade.quantity).toEqual(1);
30
+ expect(state.timestamp).toEqual(timestamp);
31
+ });
32
+ });
@@ -1,9 +1,12 @@
1
1
  import { TradePatchEventHandler } from './store-trade.event';
2
- import { timestamp } from '../../common/datetime';
2
+ import { OrderbookPatchEventHandler } from './store-orderbook.event';
3
+ import { timestamp } from '../../shared/datetime';
3
4
  import { InstrumentSelector } from '../../domain';
4
5
  import { State } from '../store.state';
5
6
  import { StoreEvent } from './store.event';
7
+ import { event } from '../../shared/topic';
6
8
 
9
+ @event
7
10
  export class CandleEvent implements StoreEvent {
8
11
  type = 'candle';
9
12
 
@@ -20,7 +23,10 @@ export class CandleEvent implements StoreEvent {
20
23
  }
21
24
 
22
25
  export function CandleEventHandler(event: CandleEvent, state: State) {
23
- return TradePatchEventHandler(
26
+ const instrument = state.universe.instrument[event.instrument.toString()];
27
+
28
+ // patch trade object
29
+ const trade = TradePatchEventHandler(
24
30
  {
25
31
  type: 'trade-patch',
26
32
  instrument: event.instrument,
@@ -30,4 +36,22 @@ export function CandleEventHandler(event: CandleEvent, state: State) {
30
36
  },
31
37
  state
32
38
  );
39
+
40
+ // patch orderbook by assuming candle close price is mid orderbook price
41
+ const orderbook = OrderbookPatchEventHandler(
42
+ {
43
+ type: 'orderbook-patch',
44
+ instrument: event.instrument,
45
+ bestAskQuantity: event.volume * 0.5,
46
+ bestAskRate: event.close + instrument.quote.tickSize,
47
+ bestBidQuantity: event.volume * 0.5,
48
+ bestBidRate: event.close - instrument.quote.tickSize,
49
+ timestamp: event.timestamp
50
+ },
51
+ state
52
+ );
53
+
54
+ state.timestamp = event.timestamp;
55
+
56
+ return [trade, orderbook];
33
57
  }