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,155 @@
1
+ /**
2
+ * Whether this trade was still held at the close of this bar.
3
+ *
4
+ * The boundaries are the whole of the rule and both are decisions. A trade is
5
+ * held from the close of the bar it opened on, because an entry that settled
6
+ * during a bar is a position that bar ended holding. It is not held at the
7
+ * close of the bar it closed on, because that bar ended flat. So a trade
8
+ * opened and closed inside one bar is held at no close at all, which is what a
9
+ * run that was flat at every mark should report.
10
+ *
11
+ * Every figure in this module that asks which trades are open asks it here, so
12
+ * the curve, the exposure and the count of bars in the market cannot come to
13
+ * three different answers about one bar.
14
+ */
15
+ export function openOnBar(trade, barIndex) {
16
+ if (barIndex < trade.openedOnBar)
17
+ return false;
18
+ return trade.closedOnBar === null || barIndex < trade.closedOnBar;
19
+ }
20
+ /**
21
+ * A ratio against a basis that may not be there to divide by.
22
+ *
23
+ * Capital of zero and a peak of zero are both reachable, and both turn an
24
+ * honest division into a value JSON cannot carry: a report whose worst figure
25
+ * comes back `null` from a round trip is worse than one that says zero. A basis
26
+ * that is not positive has no ratio to state, so the figure beside it, which is
27
+ * money and is always true, is the one a reader is left with.
28
+ */
29
+ export function ratioOf(value, basis) {
30
+ return basis > 0 ? value / basis : 0;
31
+ }
32
+ /**
33
+ * The curve, one point per report bar, in the order the bars arrived.
34
+ *
35
+ * Warmup bars are swept and not reported: their orders were real, so a trade
36
+ * opened during the warmup is already in the fold at the first point, with its
37
+ * charges already paid and its position already marked. A curve that began its
38
+ * fold at the first report bar would lose both, and would lose them silently.
39
+ *
40
+ * The running peak starts at the capital rather than at the first point, so a
41
+ * run that is down from its first bar is in drawdown at its first bar. Starting
42
+ * it at the first point would report every run as having begun at its high.
43
+ */
44
+ export function equityOver(trades, marks, contract, capital) {
45
+ const points = [];
46
+ let held = [];
47
+ let next = 0;
48
+ let realised = 0;
49
+ let charges = 0;
50
+ let peak = capital;
51
+ let mark = null;
52
+ for (const bar of marks) {
53
+ // Opened by this bar, charges and all. The pointer is why the trades have
54
+ // to arrive in the order they opened.
55
+ while (next < trades.length) {
56
+ const opening = trades[next];
57
+ if (opening === undefined || opening.openedOnBar > bar.barIndex)
58
+ break;
59
+ charges += opening.charges;
60
+ held.push(opening);
61
+ next += 1;
62
+ }
63
+ // Closed by this bar, gross and all. A trade that opened and closed inside
64
+ // one bar is taken on and given up here in that order, so its cost and its
65
+ // gross are both in this point and its position is in none.
66
+ let closedHere = false;
67
+ for (const trade of held) {
68
+ if (closedBy(trade, bar.barIndex)) {
69
+ realised += trade.grossProfit;
70
+ closedHere = true;
71
+ }
72
+ }
73
+ if (closedHere)
74
+ held = held.filter((trade) => !closedBy(trade, bar.barIndex));
75
+ // A close the host did not have leaves the previous mark standing. It is
76
+ // carried across the warmup boundary too, so the first report bar of a run
77
+ // whose close is absent is marked at the last price there was.
78
+ if (bar.close !== null)
79
+ mark = bar.close;
80
+ if (!bar.inReport)
81
+ continue;
82
+ let openProfit = 0;
83
+ let exposure = 0;
84
+ for (const trade of held) {
85
+ // Before the first close there has ever been, a trade is marked at its
86
+ // own entry: no profit, and the position still visible in the exposure.
87
+ const at = mark ?? trade.entryPrice;
88
+ const direction = trade.side === 'long' ? 1 : -1;
89
+ openProfit += direction * (at - trade.entryPrice) * trade.units * contract.pointValue;
90
+ exposure += Math.abs(trade.units * at * contract.pointValue);
91
+ }
92
+ const cash = capital + realised - charges;
93
+ const equity = cash + openProfit;
94
+ if (equity > peak)
95
+ peak = equity;
96
+ const drawdown = equity - peak;
97
+ points.push({
98
+ barIndex: bar.barIndex,
99
+ time: bar.time,
100
+ realised,
101
+ charges,
102
+ openProfit,
103
+ cash,
104
+ equity,
105
+ exposure,
106
+ drawdown,
107
+ drawdownPercent: ratioOf(drawdown, peak),
108
+ });
109
+ }
110
+ return points;
111
+ }
112
+ /**
113
+ * How many of these bars ended with something held.
114
+ *
115
+ * Swept rather than searched: a sorted list of the bars trades opened on, a
116
+ * sorted list of the bars they closed on, and the running difference between
117
+ * how many of each have gone by. That is the boundary rule `openOnBar` states,
118
+ * arrived at from the other side, and the two are asserted to agree over a
119
+ * generated corpus rather than trusted to. A count that disagrees with the
120
+ * curve beside it about which bars were in the market is exactly the kind of
121
+ * defect a reader finds by adding two of the printed figures up.
122
+ */
123
+ export function barsInMarketOver(trades, equity) {
124
+ const opens = trades.map((trade) => trade.openedOnBar).sort(ascending);
125
+ const closes = trades
126
+ .filter((trade) => trade.closedOnBar !== null)
127
+ .map((trade) => trade.closedOnBar ?? 0)
128
+ .sort(ascending);
129
+ let opened = 0;
130
+ let closed = 0;
131
+ let live = 0;
132
+ let bars = 0;
133
+ for (const point of equity) {
134
+ while (opened < opens.length && (opens[opened] ?? 0) <= point.barIndex) {
135
+ live += 1;
136
+ opened += 1;
137
+ }
138
+ while (closed < closes.length && (closes[closed] ?? 0) <= point.barIndex) {
139
+ live -= 1;
140
+ closed += 1;
141
+ }
142
+ if (live > 0)
143
+ bars += 1;
144
+ }
145
+ return bars;
146
+ }
147
+ /** Whether this bar is the bar the trade closed on, or one after it. */
148
+ function closedBy(trade, barIndex) {
149
+ return trade.closedOnBar !== null && trade.closedOnBar <= barIndex;
150
+ }
151
+ /** Smallest first, said once, because a sort without a comparison sorts text. */
152
+ function ascending(left, right) {
153
+ return left - right;
154
+ }
155
+ //# sourceMappingURL=equity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"equity.js","sourceRoot":"","sources":["../../../src/core/accounting/equity.ts"],"names":[],"mappings":"AA6GA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,KAAY,EAAE,QAAgB;IACtD,IAAI,QAAQ,GAAG,KAAK,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,KAAK,CAAC,WAAW,KAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC;AACpE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,KAAa;IAClD,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CACxB,MAAwB,EACxB,KAAyB,EACzB,QAAkB,EAClB,OAAc;IAEd,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,IAAI,GAAY,EAAE,CAAC;IACvB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,IAAI,GAAG,OAAO,CAAC;IACnB,IAAI,IAAI,GAAkB,IAAI,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,0EAA0E;QAC1E,sCAAsC;QACtC,OAAO,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC,QAAQ;gBAAE,MAAM;YACvE,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACnB,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;QAED,2EAA2E;QAC3E,2EAA2E;QAC3E,4DAA4D;QAC5D,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,QAAQ,IAAI,KAAK,CAAC,WAAW,CAAC;gBAC9B,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;QACH,CAAC;QACD,IAAI,UAAU;YAAE,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;QAE9E,yEAAyE;QACzE,2EAA2E;QAC3E,+DAA+D;QAC/D,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI;YAAE,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC;QACzC,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAE5B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,uEAAuE;YACvE,wEAAwE;YACxE,MAAM,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;YACpC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,UAAU,IAAI,SAAS,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC;YACtF,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;QACjC,IAAI,MAAM,GAAG,IAAI;YAAE,IAAI,GAAG,MAAM,CAAC;QACjC,MAAM,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC;YACV,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ;YACR,OAAO;YACP,UAAU;YACV,IAAI;YACJ,MAAM;YACN,QAAQ;YACR,QAAQ;YACR,eAAe,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;SACzC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAwB,EACxB,MAA8B;IAE9B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,MAAM;SAClB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC;SAC7C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;SACtC,IAAI,CAAC,SAAS,CAAC,CAAC;IAEnB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACvE,IAAI,IAAI,CAAC,CAAC;YACV,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;QACD,OAAO,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACzE,IAAI,IAAI,CAAC,CAAC;YACV,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;QACD,IAAI,IAAI,GAAG,CAAC;YAAE,IAAI,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wEAAwE;AACxE,SAAS,QAAQ,CAAC,KAAY,EAAE,QAAgB;IAC9C,OAAO,KAAK,CAAC,WAAW,KAAK,IAAI,IAAI,KAAK,CAAC,WAAW,IAAI,QAAQ,CAAC;AACrE,CAAC;AAED,iFAAiF;AACjF,SAAS,SAAS,CAAC,IAAY,EAAE,KAAa;IAC5C,OAAO,IAAI,GAAG,KAAK,CAAC;AACtB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The money: what a run made, what it cost, and what that is worth knowing.
3
+ *
4
+ * **This module imports no engine and no emitter, and it never will.** It is
5
+ * arithmetic over portable data: a list of settled fills, a list of bar closes,
6
+ * a charge schedule and the contract the run was carried out under. Two things
7
+ * follow from that and both of them are the reason for it.
8
+ *
9
+ * A stored record can be reported again with no engine present, which is what
10
+ * makes a run record a conformance case rather than a souvenir. And the engine
11
+ * can call this, when the day comes that a script may read its own equity,
12
+ * without a cycle and without a second implementation of any of these formulas
13
+ * sitting inside the execution path disagreeing with this one.
14
+ *
15
+ * The door names what a caller may use. The fold itself, the cost basis, the
16
+ * round trip state machine and the statistics stay behind it: they are one
17
+ * algorithm with one caller.
18
+ *
19
+ * Behaviour lands beside these shapes, each call in the file its type is in.
20
+ * The cost model is here: `chargeFor` is what one fill came to,
21
+ * `scheduleFromDeclaration` is the declaration's own commission as the one kind
22
+ * of schedule this module evaluates, and `scheduleProblem` is what a schedule
23
+ * is refused for before the first bar. The round trips, the equity curve, the
24
+ * statistics and the report land beside their own shapes the same way.
25
+ *
26
+ * The shapes were agreed before any of it computed, because they are what the
27
+ * engine, the backtest driver and a second engine all have to agree about.
28
+ */
29
+ export { chargeFor, scheduleFromDeclaration, scheduleProblem } from './charges.js';
30
+ export type { BarMark, Contract, Money, RecordedFill } from './shapes.js';
31
+ export type { ChargeBase, ChargeBreakdown, ChargeLine, ChargeSchedule, ChargeSide, } from './charges.js';
32
+ export { tradesOf } from './trades.js';
33
+ export type { Trade } from './trades.js';
34
+ export { equityOver } from './equity.js';
35
+ export type { EquityPoint } from './equity.js';
36
+ export { monthlyOver } from './monthly.js';
37
+ export type { MonthlyReturn } from './monthly.js';
38
+ export { markersOf } from './markers.js';
39
+ export type { TradeMarker } from './markers.js';
40
+ export { summaryOf } from './statistics.js';
41
+ export type { Summary } from './statistics.js';
42
+ export { reportOf } from './report.js';
43
+ export type { Report } from './report.js';
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACnF,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EACV,UAAU,EACV,eAAe,EACf,UAAU,EACV,cAAc,EACd,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The money: what a run made, what it cost, and what that is worth knowing.
3
+ *
4
+ * **This module imports no engine and no emitter, and it never will.** It is
5
+ * arithmetic over portable data: a list of settled fills, a list of bar closes,
6
+ * a charge schedule and the contract the run was carried out under. Two things
7
+ * follow from that and both of them are the reason for it.
8
+ *
9
+ * A stored record can be reported again with no engine present, which is what
10
+ * makes a run record a conformance case rather than a souvenir. And the engine
11
+ * can call this, when the day comes that a script may read its own equity,
12
+ * without a cycle and without a second implementation of any of these formulas
13
+ * sitting inside the execution path disagreeing with this one.
14
+ *
15
+ * The door names what a caller may use. The fold itself, the cost basis, the
16
+ * round trip state machine and the statistics stay behind it: they are one
17
+ * algorithm with one caller.
18
+ *
19
+ * Behaviour lands beside these shapes, each call in the file its type is in.
20
+ * The cost model is here: `chargeFor` is what one fill came to,
21
+ * `scheduleFromDeclaration` is the declaration's own commission as the one kind
22
+ * of schedule this module evaluates, and `scheduleProblem` is what a schedule
23
+ * is refused for before the first bar. The round trips, the equity curve, the
24
+ * statistics and the report land beside their own shapes the same way.
25
+ *
26
+ * The shapes were agreed before any of it computed, because they are what the
27
+ * engine, the backtest driver and a second engine all have to agree about.
28
+ */
29
+ export { chargeFor, scheduleFromDeclaration, scheduleProblem } from './charges.js';
30
+ export { tradesOf } from './trades.js';
31
+ export { equityOver } from './equity.js';
32
+ export { monthlyOver } from './monthly.js';
33
+ export { markersOf } from './markers.js';
34
+ export { summaryOf } from './statistics.js';
35
+ export { reportOf } from './report.js';
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/accounting/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AASnF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * One marker per entry and exit fill, which is the chart's whole input.
3
+ *
4
+ * A chart draws a trade as two marks and a line between them, and everything it
5
+ * needs to do that is in the fills: the bar, the trade the fill belongs to, the
6
+ * side, the units, the price and the tag the order carried. So the markers are
7
+ * produced here, by the module that already knows which fill belongs to which
8
+ * trade, and no chart is needed to produce them and none is imported to.
9
+ */
10
+ import type { RecordedFill } from './shapes.js';
11
+ import type { Trade } from './trades.js';
12
+ /** One entry or exit fill, addressed as a chart addresses it. */
13
+ export interface TradeMarker {
14
+ readonly barIndex: number;
15
+ readonly time: number | null;
16
+ readonly tradeIndex: number;
17
+ readonly kind: 'entry' | 'exit';
18
+ readonly side: 'buy' | 'sell';
19
+ readonly units: number;
20
+ readonly price: number;
21
+ readonly tag: string;
22
+ }
23
+ /**
24
+ * One marker per entry and exit fill, against the trades those fills made up.
25
+ *
26
+ * **The fill is read with the same rule the trade list read it with.**
27
+ * `closedBy` and `openedBy` are imported from `trades.ts` rather than restated,
28
+ * because a marker that called an exit an entry would draw the chart the report
29
+ * contradicts. A fill that carried a reference through zero is both: it closes
30
+ * the trade the reference held and opens the next one, so it produces two
31
+ * markers, in that order, which is the order the trade list folded it in.
32
+ *
33
+ * **A trade is found by its reference and by the order it opened**, never by
34
+ * the bar it opened on: a reference that went to zero and was entered again on
35
+ * the same bar is two trades, and addressing them by bar would put both
36
+ * markers on the first. The trades are walked once per reference, oldest first,
37
+ * which is the order `tradesOf` built them in.
38
+ *
39
+ * The count is an identity rather than a claim: the markers of one trade are
40
+ * its `entries` plus its `exits`, and a test asserts it over the same fills.
41
+ */
42
+ export declare function markersOf(fills: readonly RecordedFill[], trades: readonly Trade[]): readonly TradeMarker[];
43
+ //# sourceMappingURL=markers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,MAAM,EAAE,SAAS,KAAK,EAAE,GACvB,SAAS,WAAW,EAAE,CA6BxB"}
@@ -0,0 +1,64 @@
1
+ import { closedBy, openedBy } from './trades.js';
2
+ /**
3
+ * One marker per entry and exit fill, against the trades those fills made up.
4
+ *
5
+ * **The fill is read with the same rule the trade list read it with.**
6
+ * `closedBy` and `openedBy` are imported from `trades.ts` rather than restated,
7
+ * because a marker that called an exit an entry would draw the chart the report
8
+ * contradicts. A fill that carried a reference through zero is both: it closes
9
+ * the trade the reference held and opens the next one, so it produces two
10
+ * markers, in that order, which is the order the trade list folded it in.
11
+ *
12
+ * **A trade is found by its reference and by the order it opened**, never by
13
+ * the bar it opened on: a reference that went to zero and was entered again on
14
+ * the same bar is two trades, and addressing them by bar would put both
15
+ * markers on the first. The trades are walked once per reference, oldest first,
16
+ * which is the order `tradesOf` built them in.
17
+ *
18
+ * The count is an identity rather than a claim: the markers of one trade are
19
+ * its `entries` plus its `exits`, and a test asserts it over the same fills.
20
+ */
21
+ export function markersOf(fills, trades) {
22
+ const ordered = fills.slice().sort((a, b) => a.seq - b.seq);
23
+ const queues = new Map();
24
+ for (const trade of trades) {
25
+ const held = queues.get(trade.positionRef) ?? [];
26
+ held.push(trade);
27
+ queues.set(trade.positionRef, held);
28
+ }
29
+ const out = [];
30
+ for (const fill of ordered) {
31
+ const queue = queues.get(fill.positionRef) ?? [];
32
+ const closing = closedBy(fill.refSizeBefore, fill.refSizeAfter);
33
+ const opening = openedBy(fill.refSizeBefore, fill.refSizeAfter);
34
+ if (closing > 0) {
35
+ const held = queue[0];
36
+ if (held !== undefined)
37
+ out.push(markerFor(fill, held.index, 'exit', closing));
38
+ // The trade the reference held is finished by a fill that closed the
39
+ // whole of it, and the next trade on that reference is the one the fills
40
+ // after this belong to.
41
+ if (held !== undefined && (opening > 0 || fill.refSizeAfter === 0))
42
+ queue.shift();
43
+ }
44
+ if (opening > 0) {
45
+ const held = queue[0];
46
+ if (held !== undefined)
47
+ out.push(markerFor(fill, held.index, 'entry', opening));
48
+ }
49
+ }
50
+ return out;
51
+ }
52
+ function markerFor(fill, tradeIndex, kind, units) {
53
+ return {
54
+ barIndex: fill.barIndex,
55
+ time: fill.barTime,
56
+ tradeIndex,
57
+ kind,
58
+ side: fill.side,
59
+ units,
60
+ price: fill.price,
61
+ tag: fill.tag,
62
+ };
63
+ }
64
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../../src/core/accounting/markers.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAejD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS,CACvB,KAA8B,EAC9B,MAAwB;IAExB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAEhE,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,KAAK,SAAS;gBAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YAC/E,qEAAqE;YACrE,yEAAyE;YACzE,wBAAwB;YACxB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC,CAAC;gBAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACpF,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,KAAK,SAAS;gBAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAChB,IAAkB,EAClB,UAAkB,EAClB,IAAsB,EACtB,KAAa;IAEb,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,OAAO;QAClB,UAAU;QACV,IAAI;QACJ,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The month by month table, bucketed in UTC from bar times.
3
+ *
4
+ * **UTC, and it says so on the type.** A calendar month in the instrument's own
5
+ * timezone would need the session machinery and the instrument's calendar, and
6
+ * a table that silently buckets a trade into the wrong month is worse than one
7
+ * that states the basis it used. No timezone library, and no pretence of the
8
+ * instrument's calendar.
9
+ */
10
+ import type { EquityPoint } from './equity.js';
11
+ import type { Money } from './shapes.js';
12
+ import type { Trade } from './trades.js';
13
+ /** One calendar month of the run, in UTC. */
14
+ export interface MonthlyReturn {
15
+ readonly year: number;
16
+ /** 1 to 12, UTC. */
17
+ readonly month: number;
18
+ readonly netProfit: Money;
19
+ /** Against the equity at the month's first bar. */
20
+ readonly returnPercent: number;
21
+ readonly trades: number;
22
+ }
23
+ /**
24
+ * The months a run passed through, in the order it passed through them.
25
+ *
26
+ * **A month holds the change in equity across the closes inside it.** The first
27
+ * point of the curve has nothing before it to be compared with, so it opens the
28
+ * table rather than contributing to it, and every later point contributes what
29
+ * it moved from the point before. The consequence is an identity rather than a
30
+ * claim: the months add up to the equity at the last point less the equity at
31
+ * the first, and nothing that happened during the warmup is attributed to a
32
+ * month it did not happen in.
33
+ *
34
+ * `returnPercent` is that change over the equity at the month's first bar,
35
+ * which is the basis this table states and the reason the figure is a fraction
36
+ * rather than a fraction times a hundred: `drawdownPercent` beside it is
37
+ * `drawdown / peak`, and one report with two conventions is a report a reader
38
+ * has to check every figure of.
39
+ *
40
+ * `trades` counts the trades that closed inside the month, by the time they
41
+ * closed. An open trade is in no month, because the month it will be counted in
42
+ * is not decided yet.
43
+ *
44
+ * **A point with no time is in no month.** A bucket is a calendar fact and a
45
+ * bar with no time states no calendar, so such a point carries its change into
46
+ * the month in force rather than opening one, and where none is in force it is
47
+ * outside the table altogether. That is the honest reading: bucketing it by the
48
+ * month that happened to come before would put money in a month on the strength
49
+ * of nothing.
50
+ */
51
+ export declare function monthlyOver(equity: readonly EquityPoint[], trades: readonly Trade[]): readonly MonthlyReturn[];
52
+ //# sourceMappingURL=monthly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monthly.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/monthly.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oBAAoB;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,MAAM,EAAE,SAAS,KAAK,EAAE,GACvB,SAAS,aAAa,EAAE,CA2C1B"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The months a run passed through, in the order it passed through them.
3
+ *
4
+ * **A month holds the change in equity across the closes inside it.** The first
5
+ * point of the curve has nothing before it to be compared with, so it opens the
6
+ * table rather than contributing to it, and every later point contributes what
7
+ * it moved from the point before. The consequence is an identity rather than a
8
+ * claim: the months add up to the equity at the last point less the equity at
9
+ * the first, and nothing that happened during the warmup is attributed to a
10
+ * month it did not happen in.
11
+ *
12
+ * `returnPercent` is that change over the equity at the month's first bar,
13
+ * which is the basis this table states and the reason the figure is a fraction
14
+ * rather than a fraction times a hundred: `drawdownPercent` beside it is
15
+ * `drawdown / peak`, and one report with two conventions is a report a reader
16
+ * has to check every figure of.
17
+ *
18
+ * `trades` counts the trades that closed inside the month, by the time they
19
+ * closed. An open trade is in no month, because the month it will be counted in
20
+ * is not decided yet.
21
+ *
22
+ * **A point with no time is in no month.** A bucket is a calendar fact and a
23
+ * bar with no time states no calendar, so such a point carries its change into
24
+ * the month in force rather than opening one, and where none is in force it is
25
+ * outside the table altogether. That is the honest reading: bucketing it by the
26
+ * month that happened to come before would put money in a month on the strength
27
+ * of nothing.
28
+ */
29
+ export function monthlyOver(equity, trades) {
30
+ const buckets = [];
31
+ let previous;
32
+ let current;
33
+ for (const point of equity) {
34
+ const at = monthOf(point.time);
35
+ if (at !== null && (current === undefined || current.year !== at.year || current.month !== at.month)) {
36
+ // The equity this month started from, which is where the previous month
37
+ // left the account and not where this one's first bar closed. Taking the
38
+ // opening point's own equity put the month's first move into the
39
+ // numerator and into the denominator at once: a February that doubled a
40
+ // thousand pounds reported fifty percent, because the gain was divided by
41
+ // the two thousand it had already produced.
42
+ current = {
43
+ year: at.year,
44
+ month: at.month,
45
+ netProfit: 0,
46
+ basis: previous === undefined ? point.equity : previous.equity,
47
+ trades: 0,
48
+ };
49
+ buckets.push(current);
50
+ }
51
+ if (previous !== undefined && current !== undefined) {
52
+ current.netProfit += point.equity - previous.equity;
53
+ }
54
+ previous = point;
55
+ }
56
+ for (const trade of trades) {
57
+ const at = monthOf(trade.closedAt);
58
+ if (at === null)
59
+ continue;
60
+ const bucket = buckets.find((one) => one.year === at.year && one.month === at.month);
61
+ if (bucket !== undefined)
62
+ bucket.trades += 1;
63
+ }
64
+ return buckets.map((one) => ({
65
+ year: one.year,
66
+ month: one.month,
67
+ netProfit: one.netProfit,
68
+ returnPercent: one.basis > 0 ? one.netProfit / one.basis : 0,
69
+ trades: one.trades,
70
+ }));
71
+ }
72
+ /**
73
+ * The calendar month an instant falls in, in UTC and in no other zone.
74
+ *
75
+ * Decomposed rather than formatted, so no locale, no zone table and no library
76
+ * is involved: the same instant produces the same month on every machine this
77
+ * ever runs on.
78
+ */
79
+ function monthOf(time) {
80
+ if (time === null || !Number.isFinite(time) || Math.abs(time) > LATEST_INSTANT)
81
+ return null;
82
+ const at = new Date(time);
83
+ return { year: at.getUTCFullYear(), month: at.getUTCMonth() + 1 };
84
+ }
85
+ /**
86
+ * The furthest either way an instant can be and still name a month.
87
+ *
88
+ * A finite number outside it decomposes to NaN rather than throwing, and a NaN
89
+ * year never equals the next one, so every point opened a bucket of its own and
90
+ * a fifty thousand bar run produced fifty thousand rows of NaN. The canonical
91
+ * writer then threw a bare error with no code on them, out of core, on a path a
92
+ * host could not tell from an internal fault. The mistake that gets here is
93
+ * ordinary: bar times supplied in nanoseconds rather than milliseconds.
94
+ *
95
+ * A point whose time names no month contributes to no month, which is what an
96
+ * absent time already did.
97
+ */
98
+ const LATEST_INSTANT = 8.64e15;
99
+ //# sourceMappingURL=monthly.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monthly.js","sourceRoot":"","sources":["../../../src/core/accounting/monthly.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,WAAW,CACzB,MAA8B,EAC9B,MAAwB;IAExB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,QAAiC,CAAC;IACtC,IAAI,OAA2B,CAAC;IAEhC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACrG,wEAAwE;YACxE,yEAAyE;YACzE,iEAAiE;YACjE,wEAAwE;YACxE,0EAA0E;YAC1E,4CAA4C;YAC5C,OAAO,GAAG;gBACR,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,SAAS,EAAE,CAAC;gBACZ,KAAK,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM;gBAC9D,MAAM,EAAE,CAAC;aACV,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YACpD,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QACtD,CAAC;QACD,QAAQ,GAAG,KAAK,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,EAAE,KAAK,IAAI;YAAE,SAAS;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;QACrF,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC3B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,aAAa,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CAAC,CAAC,CAAC;AACN,CAAC;AAYD;;;;;;GAMG;AACH,SAAS,OAAO,CAAC,IAAmB;IAClC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,cAAc;QAAE,OAAO,IAAI,CAAC;IAC5F,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,cAAc,GAAG,OAAO,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { ChargeSchedule } from './charges.js';
2
+ import type { EquityPoint } from './equity.js';
3
+ import type { MonthlyReturn } from './monthly.js';
4
+ import type { TradeMarker } from './markers.js';
5
+ import type { BarMark, Contract, Money, RecordedFill } from './shapes.js';
6
+ import type { Summary } from './statistics.js';
7
+ import type { Trade } from './trades.js';
8
+ /** Everything a run is reported as, and nothing a chart has to compute. */
9
+ export interface Report {
10
+ readonly summary: Summary;
11
+ readonly trades: readonly Trade[];
12
+ readonly equity: readonly EquityPoint[];
13
+ readonly monthly: readonly MonthlyReturn[];
14
+ readonly markers: readonly TradeMarker[];
15
+ }
16
+ /**
17
+ * The whole report, folded from the fills, the bar closes and the schedule.
18
+ *
19
+ * **One pass, in one order, and the order is the result.** The fills are put in
20
+ * `seq` order once, here, and every fold below reads that same list: the
21
+ * charges are computed in it, the trades are built from it, the curve is marked
22
+ * along it and the markers come off it. A second ordering anywhere would be a
23
+ * report that is right to eleven digits and different in the twelfth on
24
+ * somebody else's engine, which is the failure this module is shaped to make
25
+ * impossible rather than unlikely.
26
+ *
27
+ * **A charge belongs to the fill that incurred it.** `chargeFor` rounds one
28
+ * fill's total once, and nothing here rounds it again or re-sums the collection
29
+ * in another order, so `sum(trade.charges)` and `summary.charges` are the same
30
+ * money and a test asserts it rather than a page claiming it.
31
+ *
32
+ * A run carried out under no schedule at all is charged nothing, which is a
33
+ * study of the strategy before costs and is a thing worth being able to ask
34
+ * for. It is not a default: `scheduleFromDeclaration` is what a run under the
35
+ * declaration's own commission uses, and the caller states which it wants.
36
+ */
37
+ export declare function reportOf(fills: readonly RecordedFill[], marks: readonly BarMark[], schedule: ChargeSchedule | null, contract: Contract, capital: Money): Report;
38
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/core/accounting/report.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,2EAA2E;AAC3E,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,KAAK,GACb,MAAM,CAiBR"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The report: what a run came to, folded from its fills and its bar closes.
3
+ *
4
+ * **The evaluation order is fixed here and nowhere else**, because a report
5
+ * that is right to eleven digits and different in the twelfth fails a
6
+ * conformance comparison months later on somebody else's engine, and the cause
7
+ * is always a line nobody thought was arithmetic. Fills in `seq` order, charge
8
+ * lines in declaration order, one rounding per fill total, no collection
9
+ * re-summed in another order, no dependence on a map's iteration order, no wall
10
+ * clock and no random number generator anywhere in this module.
11
+ *
12
+ * **What the report is not is read by a script.** The money entries of the
13
+ * `pos` namespace stay planned and go on refusing at the call. The moment a
14
+ * script can read its own equity mid-run, the money layer joins the execution
15
+ * path, and therefore joins the conformance surface, and every formula in this
16
+ * module has to be agreed by a second engine before a script may branch on it.
17
+ * This module computes the money after the fact from a record; whether a script
18
+ * may read it is a later decision. That this module imports no engine is the
19
+ * structural half of keeping that decision open: when it is taken, the engine
20
+ * calls this, and there is no second implementation to disagree with.
21
+ */
22
+ import { chargeFor } from './charges.js';
23
+ import { equityOver } from './equity.js';
24
+ import { monthlyOver } from './monthly.js';
25
+ import { markersOf } from './markers.js';
26
+ import { summaryOf } from './statistics.js';
27
+ import { tradesOf } from './trades.js';
28
+ /**
29
+ * The whole report, folded from the fills, the bar closes and the schedule.
30
+ *
31
+ * **One pass, in one order, and the order is the result.** The fills are put in
32
+ * `seq` order once, here, and every fold below reads that same list: the
33
+ * charges are computed in it, the trades are built from it, the curve is marked
34
+ * along it and the markers come off it. A second ordering anywhere would be a
35
+ * report that is right to eleven digits and different in the twelfth on
36
+ * somebody else's engine, which is the failure this module is shaped to make
37
+ * impossible rather than unlikely.
38
+ *
39
+ * **A charge belongs to the fill that incurred it.** `chargeFor` rounds one
40
+ * fill's total once, and nothing here rounds it again or re-sums the collection
41
+ * in another order, so `sum(trade.charges)` and `summary.charges` are the same
42
+ * money and a test asserts it rather than a page claiming it.
43
+ *
44
+ * A run carried out under no schedule at all is charged nothing, which is a
45
+ * study of the strategy before costs and is a thing worth being able to ask
46
+ * for. It is not a default: `scheduleFromDeclaration` is what a run under the
47
+ * declaration's own commission uses, and the caller states which it wants.
48
+ */
49
+ export function reportOf(fills, marks, schedule, contract, capital) {
50
+ const ordered = fills.slice().sort((a, b) => a.seq - b.seq);
51
+ const charges = ordered.map((fill) => schedule === null ? 0 : chargeFor(schedule, fill, contract).total);
52
+ const trades = tradesOf(ordered, charges, marks, contract);
53
+ const equity = equityOver(trades, marks, contract, capital);
54
+ const summary = summaryOf(trades, equity, contract, capital);
55
+ return {
56
+ summary,
57
+ trades,
58
+ equity,
59
+ monthly: monthlyOver(equity, trades),
60
+ markers: markersOf(ordered, trades),
61
+ };
62
+ }
63
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/core/accounting/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAYvC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,QAAQ,CACtB,KAA8B,EAC9B,KAAyB,EACzB,QAA+B,EAC/B,QAAkB,EAClB,OAAc;IAEd,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,KAAK,CAClE,CAAC;IAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE7D,OAAO;QACL,OAAO;QACP,MAAM;QACN,MAAM;QACN,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;QACpC,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;KACpC,CAAC;AACJ,CAAC"}