openalgo-script 0.2.0 → 0.4.0

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 (276) hide show
  1. package/CHANGELOG.md +269 -0
  2. package/README.md +65 -31
  3. package/dist/adapters/charts/surfaces.d.ts +17 -0
  4. package/dist/adapters/charts/surfaces.d.ts.map +1 -1
  5. package/dist/adapters/charts/tables.js +82 -0
  6. package/dist/adapters/charts/tables.js.map +1 -1
  7. package/dist/adapters/codemirror/commands.d.ts +14 -0
  8. package/dist/adapters/codemirror/commands.d.ts.map +1 -0
  9. package/dist/adapters/codemirror/commands.js +52 -0
  10. package/dist/adapters/codemirror/commands.js.map +1 -0
  11. package/dist/adapters/codemirror/completion.d.ts +15 -0
  12. package/dist/adapters/codemirror/completion.d.ts.map +1 -0
  13. package/dist/adapters/codemirror/completion.js +64 -0
  14. package/dist/adapters/codemirror/completion.js.map +1 -0
  15. package/dist/adapters/codemirror/contract.d.ts +156 -0
  16. package/dist/adapters/codemirror/contract.d.ts.map +1 -0
  17. package/dist/adapters/codemirror/contract.js +42 -0
  18. package/dist/adapters/codemirror/contract.js.map +1 -0
  19. package/dist/adapters/codemirror/index.d.ts +43 -0
  20. package/dist/adapters/codemirror/index.d.ts.map +1 -0
  21. package/dist/adapters/codemirror/index.js +8 -0
  22. package/dist/adapters/codemirror/index.js.map +1 -0
  23. package/dist/adapters/codemirror/lint.d.ts +17 -0
  24. package/dist/adapters/codemirror/lint.d.ts.map +1 -0
  25. package/dist/adapters/codemirror/lint.js +62 -0
  26. package/dist/adapters/codemirror/lint.js.map +1 -0
  27. package/dist/adapters/codemirror/positions.d.ts +16 -0
  28. package/dist/adapters/codemirror/positions.d.ts.map +1 -0
  29. package/dist/adapters/codemirror/positions.js +65 -0
  30. package/dist/adapters/codemirror/positions.js.map +1 -0
  31. package/dist/adapters/codemirror/stream.d.ts +23 -0
  32. package/dist/adapters/codemirror/stream.d.ts.map +1 -0
  33. package/dist/adapters/codemirror/stream.js +70 -0
  34. package/dist/adapters/codemirror/stream.js.map +1 -0
  35. package/dist/adapters/codemirror/tokens.d.ts +44 -0
  36. package/dist/adapters/codemirror/tokens.d.ts.map +1 -0
  37. package/dist/adapters/codemirror/tokens.js +34 -0
  38. package/dist/adapters/codemirror/tokens.js.map +1 -0
  39. package/dist/adapters/codemirror/tooltips.d.ts +35 -0
  40. package/dist/adapters/codemirror/tooltips.d.ts.map +1 -0
  41. package/dist/adapters/codemirror/tooltips.js +108 -0
  42. package/dist/adapters/codemirror/tooltips.js.map +1 -0
  43. package/dist/core/accounting/charges.d.ts +136 -0
  44. package/dist/core/accounting/charges.d.ts.map +1 -0
  45. package/dist/core/accounting/charges.js +362 -0
  46. package/dist/core/accounting/charges.js.map +1 -0
  47. package/dist/core/accounting/equity.d.ts +158 -0
  48. package/dist/core/accounting/equity.d.ts.map +1 -0
  49. package/dist/core/accounting/equity.js +155 -0
  50. package/dist/core/accounting/equity.js.map +1 -0
  51. package/dist/core/accounting/index.d.ts +44 -0
  52. package/dist/core/accounting/index.d.ts.map +1 -0
  53. package/dist/core/accounting/index.js +36 -0
  54. package/dist/core/accounting/index.js.map +1 -0
  55. package/dist/core/accounting/markers.d.ts +43 -0
  56. package/dist/core/accounting/markers.d.ts.map +1 -0
  57. package/dist/core/accounting/markers.js +64 -0
  58. package/dist/core/accounting/markers.js.map +1 -0
  59. package/dist/core/accounting/monthly.d.ts +52 -0
  60. package/dist/core/accounting/monthly.d.ts.map +1 -0
  61. package/dist/core/accounting/monthly.js +99 -0
  62. package/dist/core/accounting/monthly.js.map +1 -0
  63. package/dist/core/accounting/report.d.ts +38 -0
  64. package/dist/core/accounting/report.d.ts.map +1 -0
  65. package/dist/core/accounting/report.js +63 -0
  66. package/dist/core/accounting/report.js.map +1 -0
  67. package/dist/core/accounting/shapes.d.ts +70 -0
  68. package/dist/core/accounting/shapes.d.ts.map +1 -0
  69. package/dist/core/accounting/shapes.js +21 -0
  70. package/dist/core/accounting/shapes.js.map +1 -0
  71. package/dist/core/accounting/statistics.d.ts +75 -0
  72. package/dist/core/accounting/statistics.d.ts.map +1 -0
  73. package/dist/core/accounting/statistics.js +246 -0
  74. package/dist/core/accounting/statistics.js.map +1 -0
  75. package/dist/core/accounting/trades.d.ts +118 -0
  76. package/dist/core/accounting/trades.d.ts.map +1 -0
  77. package/dist/core/accounting/trades.js +186 -0
  78. package/dist/core/accounting/trades.js.map +1 -0
  79. package/dist/core/backtest/compare.d.ts +38 -0
  80. package/dist/core/backtest/compare.d.ts.map +1 -0
  81. package/dist/core/backtest/compare.js +189 -0
  82. package/dist/core/backtest/compare.js.map +1 -0
  83. package/dist/core/backtest/declaration.d.ts +52 -0
  84. package/dist/core/backtest/declaration.d.ts.map +1 -0
  85. package/dist/core/backtest/declaration.js +48 -0
  86. package/dist/core/backtest/declaration.js.map +1 -0
  87. package/dist/core/backtest/drive.d.ts +29 -0
  88. package/dist/core/backtest/drive.d.ts.map +1 -0
  89. package/dist/core/backtest/drive.js +262 -0
  90. package/dist/core/backtest/drive.js.map +1 -0
  91. package/dist/core/backtest/index.d.ts +46 -0
  92. package/dist/core/backtest/index.d.ts.map +1 -0
  93. package/dist/core/backtest/index.js +36 -0
  94. package/dist/core/backtest/index.js.map +1 -0
  95. package/dist/core/backtest/range.d.ts +84 -0
  96. package/dist/core/backtest/range.d.ts.map +1 -0
  97. package/dist/core/backtest/range.js +90 -0
  98. package/dist/core/backtest/range.js.map +1 -0
  99. package/dist/core/backtest/record.d.ts +238 -0
  100. package/dist/core/backtest/record.d.ts.map +1 -0
  101. package/dist/core/backtest/record.js +176 -0
  102. package/dist/core/backtest/record.js.map +1 -0
  103. package/dist/core/backtest/replay.d.ts +48 -0
  104. package/dist/core/backtest/replay.d.ts.map +1 -0
  105. package/dist/core/backtest/replay.js +127 -0
  106. package/dist/core/backtest/replay.js.map +1 -0
  107. package/dist/core/backtest/resting.d.ts +62 -0
  108. package/dist/core/backtest/resting.d.ts.map +1 -0
  109. package/dist/core/backtest/resting.js +59 -0
  110. package/dist/core/backtest/resting.js.map +1 -0
  111. package/dist/core/backtest/settings.d.ts +117 -0
  112. package/dist/core/backtest/settings.d.ts.map +1 -0
  113. package/dist/core/backtest/settings.js +207 -0
  114. package/dist/core/backtest/settings.js.map +1 -0
  115. package/dist/core/backtest/simulate.d.ts +146 -0
  116. package/dist/core/backtest/simulate.d.ts.map +1 -0
  117. package/dist/core/backtest/simulate.js +217 -0
  118. package/dist/core/backtest/simulate.js.map +1 -0
  119. package/dist/core/catalogue/catalogue.generated.d.ts +66 -0
  120. package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
  121. package/dist/core/catalogue/catalogue.generated.js +6 -0
  122. package/dist/core/catalogue/catalogue.generated.js.map +1 -1
  123. package/dist/core/catalogue/values.generated.d.ts +24 -0
  124. package/dist/core/catalogue/values.generated.d.ts.map +1 -1
  125. package/dist/core/check/index.d.ts +2 -1
  126. package/dist/core/check/index.d.ts.map +1 -1
  127. package/dist/core/check/index.js +1 -1
  128. package/dist/core/check/index.js.map +1 -1
  129. package/dist/core/check/library-prose.generated.d.ts +16 -0
  130. package/dist/core/check/library-prose.generated.d.ts.map +1 -0
  131. package/dist/core/check/library-prose.generated.js +353 -0
  132. package/dist/core/check/library-prose.generated.js.map +1 -0
  133. package/dist/core/check/surface.d.ts +24 -0
  134. package/dist/core/check/surface.d.ts.map +1 -1
  135. package/dist/core/check/surface.js +29 -0
  136. package/dist/core/check/surface.js.map +1 -1
  137. package/dist/core/emit/defaults.d.ts +35 -16
  138. package/dist/core/emit/defaults.d.ts.map +1 -1
  139. package/dist/core/emit/defaults.js +66 -0
  140. package/dist/core/emit/defaults.js.map +1 -1
  141. package/dist/core/emit/index.d.ts +2 -0
  142. package/dist/core/emit/index.d.ts.map +1 -1
  143. package/dist/core/emit/index.js +2 -0
  144. package/dist/core/emit/index.js.map +1 -1
  145. package/dist/core/engine/index.d.ts +1 -1
  146. package/dist/core/engine/index.d.ts.map +1 -1
  147. package/dist/core/engine/index.js +1 -1
  148. package/dist/core/engine/index.js.map +1 -1
  149. package/dist/core/engine/load.d.ts +15 -1
  150. package/dist/core/engine/load.d.ts.map +1 -1
  151. package/dist/core/engine/load.js +1 -0
  152. package/dist/core/engine/load.js.map +1 -1
  153. package/dist/core/index.d.ts +37 -3
  154. package/dist/core/index.d.ts.map +1 -1
  155. package/dist/core/index.js +17 -1
  156. package/dist/core/index.js.map +1 -1
  157. package/dist/core/version/version.generated.d.ts +1 -1
  158. package/dist/core/version/version.generated.js +1 -1
  159. package/dist/editor/complete.d.ts +40 -0
  160. package/dist/editor/complete.d.ts.map +1 -0
  161. package/dist/editor/complete.js +206 -0
  162. package/dist/editor/complete.js.map +1 -0
  163. package/dist/editor/diagnose.d.ts +18 -0
  164. package/dist/editor/diagnose.d.ts.map +1 -0
  165. package/dist/editor/diagnose.js +70 -0
  166. package/dist/editor/diagnose.js.map +1 -0
  167. package/dist/editor/format.d.ts +11 -0
  168. package/dist/editor/format.d.ts.map +1 -0
  169. package/dist/editor/format.js +50 -0
  170. package/dist/editor/format.js.map +1 -0
  171. package/dist/editor/highlight.d.ts +28 -0
  172. package/dist/editor/highlight.d.ts.map +1 -0
  173. package/dist/editor/highlight.js +65 -0
  174. package/dist/editor/highlight.js.map +1 -0
  175. package/dist/editor/hover.d.ts +40 -0
  176. package/dist/editor/hover.d.ts.map +1 -0
  177. package/dist/editor/hover.js +147 -0
  178. package/dist/editor/hover.js.map +1 -0
  179. package/dist/editor/index.d.ts +60 -0
  180. package/dist/editor/index.d.ts.map +1 -0
  181. package/dist/editor/index.js +7 -0
  182. package/dist/editor/index.js.map +1 -0
  183. package/dist/editor/kinds.d.ts +27 -0
  184. package/dist/editor/kinds.d.ts.map +1 -0
  185. package/dist/editor/kinds.js +118 -0
  186. package/dist/editor/kinds.js.map +1 -0
  187. package/dist/editor/layout.d.ts +47 -0
  188. package/dist/editor/layout.d.ts.map +1 -0
  189. package/dist/editor/layout.js +135 -0
  190. package/dist/editor/layout.js.map +1 -0
  191. package/dist/editor/manifest.d.ts +47 -0
  192. package/dist/editor/manifest.d.ts.map +1 -0
  193. package/dist/editor/manifest.js +93 -0
  194. package/dist/editor/manifest.js.map +1 -0
  195. package/dist/editor/reading.d.ts +41 -0
  196. package/dist/editor/reading.d.ts.map +1 -0
  197. package/dist/editor/reading.js +51 -0
  198. package/dist/editor/reading.js.map +1 -0
  199. package/dist/editor/scan.d.ts +26 -0
  200. package/dist/editor/scan.d.ts.map +1 -0
  201. package/dist/editor/scan.js +139 -0
  202. package/dist/editor/scan.js.map +1 -0
  203. package/dist/editor/scope.d.ts +19 -0
  204. package/dist/editor/scope.d.ts.map +1 -0
  205. package/dist/editor/scope.js +99 -0
  206. package/dist/editor/scope.js.map +1 -0
  207. package/dist/editor/signature.d.ts +38 -0
  208. package/dist/editor/signature.d.ts.map +1 -0
  209. package/dist/editor/signature.js +112 -0
  210. package/dist/editor/signature.js.map +1 -0
  211. package/dist/editor/site.d.ts +46 -0
  212. package/dist/editor/site.d.ts.map +1 -0
  213. package/dist/editor/site.js +184 -0
  214. package/dist/editor/site.js.map +1 -0
  215. package/dist/editor/spacing.d.ts +29 -0
  216. package/dist/editor/spacing.d.ts.map +1 -0
  217. package/dist/editor/spacing.js +116 -0
  218. package/dist/editor/spacing.js.map +1 -0
  219. package/package.json +30 -3
  220. package/spec/errors.json +140 -0
  221. package/src/adapters/charts/surfaces.ts +17 -0
  222. package/src/adapters/charts/tables.ts +88 -0
  223. package/src/adapters/codemirror/commands.ts +53 -0
  224. package/src/adapters/codemirror/completion.ts +74 -0
  225. package/src/adapters/codemirror/contract.ts +156 -0
  226. package/src/adapters/codemirror/index.ts +66 -0
  227. package/src/adapters/codemirror/lint.ts +66 -0
  228. package/src/adapters/codemirror/positions.ts +79 -0
  229. package/src/adapters/codemirror/stream.ts +87 -0
  230. package/src/adapters/codemirror/tokens.ts +64 -0
  231. package/src/adapters/codemirror/tooltips.ts +113 -0
  232. package/src/core/accounting/charges.ts +452 -0
  233. package/src/core/accounting/equity.ts +276 -0
  234. package/src/core/accounting/index.ts +49 -0
  235. package/src/core/accounting/markers.ts +95 -0
  236. package/src/core/accounting/monthly.ts +137 -0
  237. package/src/core/accounting/report.ts +89 -0
  238. package/src/core/accounting/shapes.ts +73 -0
  239. package/src/core/accounting/statistics.ts +350 -0
  240. package/src/core/accounting/trades.ts +313 -0
  241. package/src/core/backtest/compare.ts +244 -0
  242. package/src/core/backtest/declaration.ts +97 -0
  243. package/src/core/backtest/drive.ts +364 -0
  244. package/src/core/backtest/index.ts +52 -0
  245. package/src/core/backtest/range.ts +137 -0
  246. package/src/core/backtest/record.ts +341 -0
  247. package/src/core/backtest/replay.ts +158 -0
  248. package/src/core/backtest/resting.ts +125 -0
  249. package/src/core/backtest/settings.ts +280 -0
  250. package/src/core/backtest/simulate.ts +304 -0
  251. package/src/core/catalogue/catalogue.generated.ts +6 -0
  252. package/src/core/catalogue/values.generated.ts +6 -0
  253. package/src/core/check/index.ts +3 -0
  254. package/src/core/check/library-prose.generated.ts +367 -0
  255. package/src/core/check/surface.ts +34 -0
  256. package/src/core/emit/defaults.ts +70 -0
  257. package/src/core/emit/index.ts +2 -0
  258. package/src/core/engine/index.ts +1 -1
  259. package/src/core/engine/load.ts +16 -2
  260. package/src/core/index.ts +85 -1
  261. package/src/core/version/version.generated.ts +1 -1
  262. package/src/editor/complete.ts +266 -0
  263. package/src/editor/diagnose.ts +71 -0
  264. package/src/editor/format.ts +85 -0
  265. package/src/editor/highlight.ts +110 -0
  266. package/src/editor/hover.ts +199 -0
  267. package/src/editor/index.ts +65 -0
  268. package/src/editor/kinds.ts +157 -0
  269. package/src/editor/layout.ts +189 -0
  270. package/src/editor/manifest.ts +119 -0
  271. package/src/editor/reading.ts +69 -0
  272. package/src/editor/scan.ts +162 -0
  273. package/src/editor/scope.ts +122 -0
  274. package/src/editor/signature.ts +150 -0
  275. package/src/editor/site.ts +233 -0
  276. package/src/editor/spacing.ts +132 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The atoms the money is folded from: a fill, a contract and a bar's close.
3
+ *
4
+ * **Everything in this module is portable data.** No class, no function, no
5
+ * object reference, no absent field, no map and no date: a shape here is what
6
+ * `JSON.parse` gives back, so a report can be computed here, stored by a
7
+ * platform, sent to another process and recomputed there without this
8
+ * implementation being present. That is not a convenience. A run record is the
9
+ * conformance case a second engine is handed, and a case that can only be read
10
+ * by the engine that wrote it proves nothing about either.
11
+ *
12
+ * **A fill is the only thing money is folded from.** Not a position, not a
13
+ * ledger row, not a running total the engine happened to be holding: the fills
14
+ * the engine settled, in the order it settled them, each naming the position
15
+ * reference it moved and the size of that reference either side of the
16
+ * settlement. Every figure in a report is a function of that list and of the
17
+ * bars it is marked against, which is what makes a report reproducible from a
18
+ * record with no engine in the room.
19
+ */
20
+ /**
21
+ * Money, in the contract's own currency.
22
+ *
23
+ * A number rather than a type of its own, because a type of its own would be a
24
+ * class and a class does not survive `JSON.parse`. The rounding is stated once,
25
+ * on the contract, and applied once per fill total.
26
+ */
27
+ export type Money = number;
28
+ /**
29
+ * The instrument facts a run was carried out under, as the host stated them.
30
+ *
31
+ * A snapshot rather than a reference. An instrument's lot size and tick size
32
+ * change, and a report recomputed months later under today's facts would be a
33
+ * different study wearing the same name, so the facts travel with the run.
34
+ */
35
+ export interface Contract {
36
+ readonly symbol: string | null;
37
+ readonly exchange: string | null;
38
+ readonly currency: string;
39
+ readonly tickSize: number | null;
40
+ readonly lotSize: number | null;
41
+ /** Money per 1.0 of price per unit; 1 when the host states none. */
42
+ readonly pointValue: number;
43
+ /** Money rounding digits, half to even, once per fill total. */
44
+ readonly digits: number;
45
+ }
46
+ /** One settled fill. Every money figure is folded from these and nothing else. */
47
+ export interface RecordedFill {
48
+ readonly seq: number;
49
+ readonly intentId: number;
50
+ readonly orderRef: string;
51
+ readonly tag: string;
52
+ readonly positionRef: number;
53
+ readonly side: 'buy' | 'sell';
54
+ /** Positive, this fill's own quantity. */
55
+ readonly units: number;
56
+ readonly price: number;
57
+ readonly barIndex: number;
58
+ readonly barTime: number | null;
59
+ readonly refSizeBefore: number;
60
+ readonly refSizeAfter: number;
61
+ }
62
+ /** One bar as the report marks against it. Close only: 17.4 marks to the close. */
63
+ export interface BarMark {
64
+ readonly barIndex: number;
65
+ readonly time: number | null;
66
+ readonly close: number | null;
67
+ /** False for a warmup bar. */
68
+ readonly inReport: boolean;
69
+ }
70
+ //# sourceMappingURL=shapes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC;AAE3B;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,mFAAmF;AACnF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8BAA8B;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The atoms the money is folded from: a fill, a contract and a bar's close.
3
+ *
4
+ * **Everything in this module is portable data.** No class, no function, no
5
+ * object reference, no absent field, no map and no date: a shape here is what
6
+ * `JSON.parse` gives back, so a report can be computed here, stored by a
7
+ * platform, sent to another process and recomputed there without this
8
+ * implementation being present. That is not a convenience. A run record is the
9
+ * conformance case a second engine is handed, and a case that can only be read
10
+ * by the engine that wrote it proves nothing about either.
11
+ *
12
+ * **A fill is the only thing money is folded from.** Not a position, not a
13
+ * ledger row, not a running total the engine happened to be holding: the fills
14
+ * the engine settled, in the order it settled them, each naming the position
15
+ * reference it moved and the size of that reference either side of the
16
+ * settlement. Every figure in a report is a function of that list and of the
17
+ * bars it is marked against, which is what makes a report reproducible from a
18
+ * record with no engine in the room.
19
+ */
20
+ export {};
21
+ //# sourceMappingURL=shapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shapes.js","sourceRoot":"","sources":["../../../src/core/accounting/shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
@@ -0,0 +1,75 @@
1
+ import type { EquityPoint } from './equity.js';
2
+ import type { Contract, Money } from './shapes.js';
3
+ import type { Trade } from './trades.js';
4
+ /** What the whole run came to. */
5
+ export interface Summary {
6
+ readonly capital: Money;
7
+ readonly currency: string;
8
+ readonly netProfit: Money;
9
+ /** Sum of winning trades, before charges. */
10
+ readonly grossProfit: Money;
11
+ /**
12
+ * The losing trades' gross, as a magnitude, and it can come out at or below
13
+ * zero.
14
+ *
15
+ * A trade wins or loses on its net after charges and contributes its gross
16
+ * here, so a trade whose gross was positive and whose charges took it under
17
+ * lands in the losses carrying a positive gross, which lowers this figure and
18
+ * with few enough trades beside it takes it to zero or past it. `tallyOf`
19
+ * says why that is preferred to counting one trade as a loser in one figure
20
+ * and a winner in another. It is written here because it was documented as a
21
+ * positive magnitude and is not one, and a reader dividing by it was handed a
22
+ * negative profit factor with nothing saying that could happen.
23
+ */
24
+ readonly grossLoss: Money;
25
+ readonly charges: Money;
26
+ /** `netProfit / capital`, a fraction and not a figure times a hundred. */
27
+ readonly returnPercent: number;
28
+ /** Closed only. */
29
+ readonly tradeCount: number;
30
+ readonly openTradeCount: number;
31
+ readonly wins: number;
32
+ readonly losses: number;
33
+ /** Exactly zero net. */
34
+ readonly scratches: number;
35
+ /** wins / (wins + losses), null when none closed. */
36
+ readonly winRate: number | null;
37
+ readonly averageWin: Money;
38
+ /** Positive magnitude. */
39
+ readonly averageLoss: Money;
40
+ /** Money per closed trade. */
41
+ readonly expectancy: Money;
42
+ readonly expectancyStandardError: Money;
43
+ /**
44
+ * Gross profit over gross loss, or null where there is no ratio to take.
45
+ *
46
+ * Null when the gross loss is not above zero: a run with no losing trade has
47
+ * nothing to divide by, and one whose losses cost less in gross than their
48
+ * charges has a denominator at or below zero. A profit factor is a
49
+ * non-negative ratio everywhere it is used, so a negative one is not a
50
+ * surprising value, it is a number nobody can act on. It used to be
51
+ * reported: two trades, one charged into a loss on a positive gross, gave a
52
+ * profit factor of -0.5.
53
+ */
54
+ readonly profitFactor: number | null;
55
+ /** Zero or negative, the same sign the curve states it with. */
56
+ readonly maxDrawdown: Money;
57
+ /** The deepest point's own fraction, not the worst fraction of any point. */
58
+ readonly maxDrawdownPercent: number;
59
+ /** A bar time, never an index. */
60
+ readonly maxDrawdownAt: number | null;
61
+ readonly longestDrawdownBars: number;
62
+ readonly averageBarsHeld: number | null;
63
+ readonly barsInMarket: number;
64
+ readonly barCount: number;
65
+ }
66
+ /**
67
+ * The whole run in one shape, folded from its trades and its own curve.
68
+ *
69
+ * The curve is passed in rather than recomputed, because a summary that folded
70
+ * its own would be a second equity curve with a second set of rounding, and the
71
+ * first disagreement between them would be a drawdown figure that no point in
72
+ * the reported curve ever reached.
73
+ */
74
+ export declare function summaryOf(trades: readonly Trade[], equity: readonly EquityPoint[], contract: Contract, capital: Money): Summary;
75
+ //# sourceMappingURL=statistics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/statistics.ts"],"names":[],"mappings":"AA6DA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,kCAAkC;AAClC,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,mBAAmB;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wBAAwB;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,uBAAuB,EAAE,KAAK,CAAC;IACxC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,kCAAkC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AA2BD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,KAAK,GACb,OAAO,CAuCT"}
@@ -0,0 +1,246 @@
1
+ /**
2
+ * The summary, and the two figures in it that decide whether a run means
3
+ * anything.
4
+ *
5
+ * **Win rate is over closed trades, on net profit after charges**, and a trade
6
+ * whose net is exactly zero is a scratch counted in neither half. It is null
7
+ * where nothing closed rather than zero, because zero is a number a reader
8
+ * compares against and "nothing has closed yet" is not a losing run.
9
+ *
10
+ * **Expectancy is money per closed trade**, and it has two spellings that must
11
+ * agree: the win rate against the average win and the average loss, and the net
12
+ * profit over the trade count. Two spellings of one figure that disagree is how
13
+ * a report loses its reader, so one of them is the computation and the other is
14
+ * a test, and the test says which sequence of roundings it allows for.
15
+ *
16
+ * **And its standard error is what answers the question a comparison asks.**
17
+ * The sample standard deviation of per-trade net over the square root of the
18
+ * trade count is what turns "this run made more" into "this run made more than
19
+ * the noise", and without it a difference of two percent over eleven trades
20
+ * reads like a result.
21
+ *
22
+ * Bar times rather than bar indices, wherever a figure addresses a bar: loading
23
+ * more history shifts every index, so a report that addresses a bar by index
24
+ * changes when the warmup changes.
25
+ *
26
+ * ## Which trades a figure is counted over, which is three different answers
27
+ *
28
+ * A summary folds a list holding closed trades and open ones together, and
29
+ * almost every defect this file can have is a figure counted over the wrong
30
+ * half of it.
31
+ *
32
+ * - **Net profit, and everything derived from it**, is over closed trades. An
33
+ * open trade's net is its charges so far with no gross against them, so
34
+ * counting it would report a run holding a winner as having lost money.
35
+ * - **Charges are over every trade**, open ones included, because the money
36
+ * left the account whether or not the position came back. This is the figure
37
+ * the equity curve's last point carries, and the two are asserted equal.
38
+ * - **The drawdown figures are over the curve** and not over the trades at all.
39
+ * A drawdown is a thing equity did between two trades as often as during one.
40
+ *
41
+ * ## The cases where a statistic is not a number
42
+ *
43
+ * Every one of them is a division, and every one of them is answered here
44
+ * rather than left to arrive as a value JSON turns into `null`:
45
+ *
46
+ * - **Nothing closed.** The win rate is null, which is a different claim from
47
+ * zero. Expectancy and its error are zero, because the type is money and
48
+ * money is not nullable, and `tradeCount` beside them is the field that says
49
+ * whether they mean anything.
50
+ * - **One closed trade.** A sample of one has no spread, so the standard error
51
+ * is zero. Zero here means not measurable, never measured: anything dividing
52
+ * by it checks the trade count first.
53
+ * - **Every closed trade a scratch.** There is no denominator for a win rate,
54
+ * so it is null for the same reason as nothing closed.
55
+ * - **Nothing lost.** The profit factor is null rather than an infinity, which
56
+ * is the same decision `spec/conformance.md` takes about absence: a value
57
+ * that does not survive being written down is not a value a report may hold.
58
+ * - **Everything lost.** The profit factor is zero, expectancy is negative, the
59
+ * average win is zero, and none of the four is a division by zero.
60
+ */
61
+ import { barsInMarketOver, ratioOf } from './equity.js';
62
+ /**
63
+ * The whole run in one shape, folded from its trades and its own curve.
64
+ *
65
+ * The curve is passed in rather than recomputed, because a summary that folded
66
+ * its own would be a second equity curve with a second set of rounding, and the
67
+ * first disagreement between them would be a drawdown figure that no point in
68
+ * the reported curve ever reached.
69
+ */
70
+ export function summaryOf(trades, equity, contract, capital) {
71
+ const tally = tallyOf(trades);
72
+ const depth = depthOf(equity);
73
+ const decided = tally.wins + tally.losses;
74
+ // Net over the closed count, and nothing else, because this is the figure the
75
+ // other spelling is checked against. The win rate spelling divides by the
76
+ // decided trades instead, so the two are the same number exactly when no
77
+ // trade scratched, and `tests/accounting/statistics.test.ts` asserts both the
78
+ // agreement and the one case that parts them.
79
+ const expectancy = tally.tradeCount === 0 ? 0 : tally.netProfit / tally.tradeCount;
80
+ return {
81
+ capital,
82
+ currency: contract.currency,
83
+ netProfit: tally.netProfit,
84
+ grossProfit: tally.grossProfit,
85
+ grossLoss: tally.grossLoss,
86
+ charges: tally.charges,
87
+ returnPercent: ratioOf(tally.netProfit, capital),
88
+ tradeCount: tally.tradeCount,
89
+ openTradeCount: tally.openTradeCount,
90
+ wins: tally.wins,
91
+ losses: tally.losses,
92
+ scratches: tally.scratches,
93
+ winRate: decided === 0 ? null : tally.wins / decided,
94
+ averageWin: tally.wins === 0 ? 0 : tally.winTotal / tally.wins,
95
+ averageLoss: tally.losses === 0 ? 0 : -tally.lossTotal / tally.losses,
96
+ expectancy,
97
+ expectancyStandardError: standardErrorOf(trades, expectancy, tally.tradeCount),
98
+ profitFactor: tally.grossLoss > 0 ? tally.grossProfit / tally.grossLoss : null,
99
+ maxDrawdown: depth.maxDrawdown,
100
+ maxDrawdownPercent: depth.maxDrawdownPercent,
101
+ maxDrawdownAt: depth.maxDrawdownAt,
102
+ longestDrawdownBars: depth.longestDrawdownBars,
103
+ averageBarsHeld: tally.heldCount === 0 ? null : tally.heldTotal / tally.heldCount,
104
+ barsInMarket: barsInMarketOver(trades, equity),
105
+ barCount: equity.length,
106
+ };
107
+ }
108
+ /**
109
+ * One pass over the trades, in the order they are given.
110
+ *
111
+ * A trade wins or loses on its net after charges, and its gross is what it
112
+ * contributes to the gross figures: "the sum of the winning trades, before
113
+ * charges" is two statements and this is where they meet. The one arrangement
114
+ * that reads oddly is a trade whose gross was positive and whose charges took
115
+ * it under, which lands in the losses and takes its positive gross with it,
116
+ * lowering the gross loss. That is on purpose. The alternative is a trade
117
+ * counted as a loser in one figure and a winner in another, and a profit factor
118
+ * whose two halves are counted over different sets is worse than one whose
119
+ * magnitude is odd on a trade that barely moved.
120
+ *
121
+ * What is not on purpose, and is why `profitFactor` is null rather than a ratio
122
+ * whenever this figure is not above zero: enough of those trades and the gross
123
+ * loss reaches zero or goes under, and dividing by it reported a profit factor
124
+ * of minus a half. A statistic being odd is a thing a reader can weigh. A
125
+ * statistic being negative where every use of it is a non-negative ratio is a
126
+ * number nobody can act on.
127
+ */
128
+ function tallyOf(trades) {
129
+ let netProfit = 0;
130
+ let grossProfit = 0;
131
+ let grossLoss = 0;
132
+ let charges = 0;
133
+ let tradeCount = 0;
134
+ let openTradeCount = 0;
135
+ let wins = 0;
136
+ let losses = 0;
137
+ let scratches = 0;
138
+ let winTotal = 0;
139
+ let lossTotal = 0;
140
+ let heldTotal = 0;
141
+ let heldCount = 0;
142
+ for (const trade of trades) {
143
+ charges += trade.charges;
144
+ if (trade.isOpen) {
145
+ openTradeCount += 1;
146
+ continue;
147
+ }
148
+ tradeCount += 1;
149
+ netProfit += trade.netProfit;
150
+ if (trade.barsHeld !== null) {
151
+ heldTotal += trade.barsHeld;
152
+ heldCount += 1;
153
+ }
154
+ if (trade.netProfit > 0) {
155
+ wins += 1;
156
+ winTotal += trade.netProfit;
157
+ grossProfit += trade.grossProfit;
158
+ }
159
+ else if (trade.netProfit < 0) {
160
+ losses += 1;
161
+ lossTotal += trade.netProfit;
162
+ grossLoss -= trade.grossProfit;
163
+ }
164
+ else {
165
+ scratches += 1;
166
+ }
167
+ }
168
+ return {
169
+ netProfit,
170
+ grossProfit,
171
+ grossLoss,
172
+ charges,
173
+ tradeCount,
174
+ openTradeCount,
175
+ wins,
176
+ losses,
177
+ scratches,
178
+ winTotal,
179
+ lossTotal,
180
+ heldTotal,
181
+ heldCount,
182
+ };
183
+ }
184
+ /**
185
+ * The deepest the curve went, named as one point rather than as three figures.
186
+ *
187
+ * The money, the fraction and the time all come from the same point, and the
188
+ * point is the deepest in money with the earliest one winning a tie. Taking the
189
+ * worst fraction from one bar and the worst money from another would describe a
190
+ * moment the run never had, and a reader comparing the two figures would find
191
+ * them inconsistent with every point in the curve they were drawn from.
192
+ *
193
+ * `longestDrawdownBars` is the longest run of consecutive bars under a peak: it
194
+ * starts at the first bar below one and ends at the bar before the recovery, so
195
+ * a run still under water at the last bar counts to the end. It is often the
196
+ * figure that actually stops a trader, and it is not the total number of bars
197
+ * spent under water, which is a different and much larger number.
198
+ */
199
+ function depthOf(equity) {
200
+ let maxDrawdown = 0;
201
+ let maxDrawdownPercent = 0;
202
+ let maxDrawdownAt = null;
203
+ let longestDrawdownBars = 0;
204
+ let under = 0;
205
+ for (const point of equity) {
206
+ if (point.drawdown < maxDrawdown) {
207
+ maxDrawdown = point.drawdown;
208
+ maxDrawdownPercent = point.drawdownPercent;
209
+ maxDrawdownAt = point.time;
210
+ }
211
+ if (point.drawdown < 0) {
212
+ under += 1;
213
+ if (under > longestDrawdownBars)
214
+ longestDrawdownBars = under;
215
+ }
216
+ else {
217
+ under = 0;
218
+ }
219
+ }
220
+ return { maxDrawdown, maxDrawdownPercent, maxDrawdownAt, longestDrawdownBars };
221
+ }
222
+ /**
223
+ * The standard error of the expectancy: the sample deviation over the root of
224
+ * the count.
225
+ *
226
+ * The sample deviation, with the count less one under it, and not the
227
+ * population one. The trades a run took are a sample of the trades the strategy
228
+ * would take, which is the whole reason this figure is here, and the population
229
+ * spelling understates the spread by exactly the amount that matters on the
230
+ * short runs where the question is asked.
231
+ *
232
+ * Fewer than two closed trades has no spread to measure and gives zero.
233
+ */
234
+ function standardErrorOf(trades, expectancy, tradeCount) {
235
+ if (tradeCount < 2)
236
+ return 0;
237
+ let squares = 0;
238
+ for (const trade of trades) {
239
+ if (trade.isOpen)
240
+ continue;
241
+ const away = trade.netProfit - expectancy;
242
+ squares += away * away;
243
+ }
244
+ return Math.sqrt(squares / (tradeCount - 1) / tradeCount);
245
+ }
246
+ //# sourceMappingURL=statistics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.js","sourceRoot":"","sources":["../../../src/core/accounting/statistics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * What a trade is, which is the definition every statistic is counted over.
3
+ *
4
+ * **A trade is one position reference, from the fill that first takes it away
5
+ * from zero to the fill that returns it to zero.** Nothing else is invented,
6
+ * because the engine already mints a reference per position, no order crosses
7
+ * zero, and every fill names the reference it settled however late it arrives.
8
+ *
9
+ * Everything awkward falls out of that rather than needing a rule of its own. A
10
+ * pyramided entry is more entry fills on one trade. A partial close is an exit
11
+ * fill that does not close the trade. A flip is two references and therefore
12
+ * two trades, which is what a reversing order already sends. A reference still
13
+ * holding something at the last bar is an open trade: it is in the list with
14
+ * `isOpen` true, it is counted in equity, and it is counted in no win rate.
15
+ *
16
+ * The definition is written in the specification before it is written here,
17
+ * because a rule decided in code is a rule two engines decide differently.
18
+ *
19
+ * ## What the fold reads, and what it refuses to work out for itself
20
+ *
21
+ * A fill carries the signed size of its reference either side of the
22
+ * settlement, so the fold reads what the position book did rather than
23
+ * recomputing it from quantities and sides. That is the whole of how a partial
24
+ * close, a pyramided entry and a reversal tell themselves apart: the move from
25
+ * one size to the other says how much of the fill closed what was held and how
26
+ * much of it opened something, and nothing else has to be inferred.
27
+ *
28
+ * It also settles the one case the definition does not cover on its face. A
29
+ * destination that fills more than the order asked takes a reference through
30
+ * zero rather than to it, and the position book reads the remainder as a
31
+ * position in the other direction opened at that fill's price. So the fold does
32
+ * the same: the fill closes the trade that was held and opens a second one on
33
+ * the same reference, at its own price and on its own bar. A trade that ran
34
+ * from a size to the other side of zero would report a quantity nothing was
35
+ * ever entered at.
36
+ *
37
+ * **Reducing a position does not move its average**, which is the position
38
+ * book's rule read here rather than a second one: a reducing fill adds to the
39
+ * exits and touches neither the entry quantity nor the entry cost. Every figure
40
+ * measured from the entry, the entry price a reader compares against and the
41
+ * excursion at each bar close, is measured from the price the units were
42
+ * actually bought at.
43
+ *
44
+ * **A charge lands whole on one trade and is never split.** It was rounded once
45
+ * for the fill that incurred it, and splitting it between the trade a crossing
46
+ * fill closed and the trade it opened would round it again and put a residue
47
+ * somewhere. So it is attributed to the trade the fill closed where it closed
48
+ * one, and to the trade it opened otherwise, which makes the charges of the
49
+ * trades add up to the charges of the fills exactly rather than nearly.
50
+ *
51
+ * **Gross profit is over the units that have left.** For a closed trade that is
52
+ * every unit it entered, which is the formula the report is specified with. For
53
+ * one still open it is what its exits have realised so far, which is a figure
54
+ * that is true rather than a zero standing in for money the run has already
55
+ * made.
56
+ */
57
+ import type { BarMark, Contract, Money, RecordedFill } from './shapes.js';
58
+ /** One round trip on one position reference. */
59
+ export interface Trade {
60
+ /** 1-based, in the order the trade opened. */
61
+ readonly index: number;
62
+ readonly positionRef: number;
63
+ readonly side: 'long' | 'short';
64
+ readonly openedOnBar: number;
65
+ readonly openedAt: number | null;
66
+ readonly closedOnBar: number | null;
67
+ readonly closedAt: number | null;
68
+ readonly barsHeld: number | null;
69
+ /** Total units entered. */
70
+ readonly units: number;
71
+ /** Quantity weighted over the entry fills. */
72
+ readonly entryPrice: number;
73
+ /** Quantity weighted over the exit fills. */
74
+ readonly exitPrice: number | null;
75
+ readonly entries: number;
76
+ readonly exits: number;
77
+ readonly grossProfit: Money;
78
+ readonly charges: Money;
79
+ readonly netProfit: Money;
80
+ /**
81
+ * Excursion at bar closes while open. Favourable is zero or better, adverse
82
+ * zero or worse, and both are zero for a trade no bar closed on.
83
+ */
84
+ readonly maxFavourable: Money;
85
+ readonly maxAdverse: Money;
86
+ readonly isOpen: boolean;
87
+ }
88
+ /**
89
+ * The round trips a run's fills make up, in the order they opened.
90
+ *
91
+ * `charges[index]` is the money `fills[index]` was charged, rounded once by
92
+ * whoever computed it, so the two travel as one thing and nothing here rounds
93
+ * anything a second time. A caller with no cost model supplies no charges at
94
+ * all and every trade's charges are zero.
95
+ *
96
+ * The fills are read in `seq` order whatever order they are handed in, because
97
+ * `seq` is the order the engine folded them and a report that depended on the
98
+ * order a caller happened to be holding them in would not be reproducible. The
99
+ * marks are read in bar order for the same reason, and a bar is marked after
100
+ * every fill up to it has been folded, because a fill happens during its bar
101
+ * and the close comes after.
102
+ */
103
+ export declare function tradesOf(fills: readonly RecordedFill[], charges: readonly Money[], marks: readonly BarMark[], contract: Contract): readonly Trade[];
104
+ /**
105
+ * How much of a move from one size to another closed what was held.
106
+ *
107
+ * Exported to the module and not through its door: `markers.ts` asks the same
108
+ * question of the same fills, and the rule for what a move closed and what it
109
+ * opened is one rule. Two folds may read a fill differently and produce a
110
+ * marker for an entry the trade list calls an exit, so they read it here.
111
+ *
112
+ * A move to the other side of zero closed all of it, which is the case a
113
+ * destination that overfilled produces and the one this has to get right.
114
+ */
115
+ export declare function closedBy(before: number, after: number): number;
116
+ /** And how much of it opened something, which is the rest of the same move. */
117
+ export declare function openedBy(before: number, after: number): number;
118
+ //# sourceMappingURL=trades.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trades.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/trades.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1E,gDAAgD;AAChD,MAAM,WAAW,KAAK;IACpB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,KAAK,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,OAAO,EAAE,SAAS,KAAK,EAAE,EACzB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,QAAQ,EAAE,QAAQ,GACjB,SAAS,KAAK,EAAE,CAuBlB;AA0CD;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9D"}