openalgo-script 0.2.0 → 0.5.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 (357) hide show
  1. package/CHANGELOG.md +1198 -0
  2. package/README.md +127 -40
  3. package/dist/adapters/charts/driving.d.ts +50 -0
  4. package/dist/adapters/charts/driving.d.ts.map +1 -0
  5. package/dist/adapters/charts/driving.js +57 -0
  6. package/dist/adapters/charts/driving.js.map +1 -0
  7. package/dist/adapters/charts/run.d.ts +20 -0
  8. package/dist/adapters/charts/run.d.ts.map +1 -1
  9. package/dist/adapters/charts/run.js +83 -16
  10. package/dist/adapters/charts/run.js.map +1 -1
  11. package/dist/adapters/charts/surfaces.d.ts +17 -0
  12. package/dist/adapters/charts/surfaces.d.ts.map +1 -1
  13. package/dist/adapters/charts/tables.js +82 -0
  14. package/dist/adapters/charts/tables.js.map +1 -1
  15. package/dist/adapters/charts/venue.d.ts +73 -0
  16. package/dist/adapters/charts/venue.d.ts.map +1 -0
  17. package/dist/adapters/charts/venue.js +104 -0
  18. package/dist/adapters/charts/venue.js.map +1 -0
  19. package/dist/adapters/codemirror/commands.d.ts +14 -0
  20. package/dist/adapters/codemirror/commands.d.ts.map +1 -0
  21. package/dist/adapters/codemirror/commands.js +52 -0
  22. package/dist/adapters/codemirror/commands.js.map +1 -0
  23. package/dist/adapters/codemirror/completion.d.ts +15 -0
  24. package/dist/adapters/codemirror/completion.d.ts.map +1 -0
  25. package/dist/adapters/codemirror/completion.js +64 -0
  26. package/dist/adapters/codemirror/completion.js.map +1 -0
  27. package/dist/adapters/codemirror/contract.d.ts +156 -0
  28. package/dist/adapters/codemirror/contract.d.ts.map +1 -0
  29. package/dist/adapters/codemirror/contract.js +42 -0
  30. package/dist/adapters/codemirror/contract.js.map +1 -0
  31. package/dist/adapters/codemirror/index.d.ts +43 -0
  32. package/dist/adapters/codemirror/index.d.ts.map +1 -0
  33. package/dist/adapters/codemirror/index.js +8 -0
  34. package/dist/adapters/codemirror/index.js.map +1 -0
  35. package/dist/adapters/codemirror/lint.d.ts +17 -0
  36. package/dist/adapters/codemirror/lint.d.ts.map +1 -0
  37. package/dist/adapters/codemirror/lint.js +62 -0
  38. package/dist/adapters/codemirror/lint.js.map +1 -0
  39. package/dist/adapters/codemirror/positions.d.ts +16 -0
  40. package/dist/adapters/codemirror/positions.d.ts.map +1 -0
  41. package/dist/adapters/codemirror/positions.js +65 -0
  42. package/dist/adapters/codemirror/positions.js.map +1 -0
  43. package/dist/adapters/codemirror/stream.d.ts +23 -0
  44. package/dist/adapters/codemirror/stream.d.ts.map +1 -0
  45. package/dist/adapters/codemirror/stream.js +70 -0
  46. package/dist/adapters/codemirror/stream.js.map +1 -0
  47. package/dist/adapters/codemirror/tokens.d.ts +44 -0
  48. package/dist/adapters/codemirror/tokens.d.ts.map +1 -0
  49. package/dist/adapters/codemirror/tokens.js +34 -0
  50. package/dist/adapters/codemirror/tokens.js.map +1 -0
  51. package/dist/adapters/codemirror/tooltips.d.ts +35 -0
  52. package/dist/adapters/codemirror/tooltips.d.ts.map +1 -0
  53. package/dist/adapters/codemirror/tooltips.js +108 -0
  54. package/dist/adapters/codemirror/tooltips.js.map +1 -0
  55. package/dist/core/accounting/analysis.d.ts +111 -0
  56. package/dist/core/accounting/analysis.d.ts.map +1 -0
  57. package/dist/core/accounting/analysis.js +123 -0
  58. package/dist/core/accounting/analysis.js.map +1 -0
  59. package/dist/core/accounting/charges.d.ts +136 -0
  60. package/dist/core/accounting/charges.d.ts.map +1 -0
  61. package/dist/core/accounting/charges.js +362 -0
  62. package/dist/core/accounting/charges.js.map +1 -0
  63. package/dist/core/accounting/equity.d.ts +191 -0
  64. package/dist/core/accounting/equity.d.ts.map +1 -0
  65. package/dist/core/accounting/equity.js +167 -0
  66. package/dist/core/accounting/equity.js.map +1 -0
  67. package/dist/core/accounting/index.d.ts +46 -0
  68. package/dist/core/accounting/index.d.ts.map +1 -0
  69. package/dist/core/accounting/index.js +37 -0
  70. package/dist/core/accounting/index.js.map +1 -0
  71. package/dist/core/accounting/markers.d.ts +43 -0
  72. package/dist/core/accounting/markers.d.ts.map +1 -0
  73. package/dist/core/accounting/markers.js +64 -0
  74. package/dist/core/accounting/markers.js.map +1 -0
  75. package/dist/core/accounting/monthly.d.ts +52 -0
  76. package/dist/core/accounting/monthly.d.ts.map +1 -0
  77. package/dist/core/accounting/monthly.js +99 -0
  78. package/dist/core/accounting/monthly.js.map +1 -0
  79. package/dist/core/accounting/report.d.ts +41 -0
  80. package/dist/core/accounting/report.d.ts.map +1 -0
  81. package/dist/core/accounting/report.js +65 -0
  82. package/dist/core/accounting/report.js.map +1 -0
  83. package/dist/core/accounting/shapes.d.ts +70 -0
  84. package/dist/core/accounting/shapes.d.ts.map +1 -0
  85. package/dist/core/accounting/shapes.js +21 -0
  86. package/dist/core/accounting/shapes.js.map +1 -0
  87. package/dist/core/accounting/statistics.d.ts +87 -0
  88. package/dist/core/accounting/statistics.d.ts.map +1 -0
  89. package/dist/core/accounting/statistics.js +268 -0
  90. package/dist/core/accounting/statistics.js.map +1 -0
  91. package/dist/core/accounting/trades.d.ts +118 -0
  92. package/dist/core/accounting/trades.d.ts.map +1 -0
  93. package/dist/core/accounting/trades.js +186 -0
  94. package/dist/core/accounting/trades.js.map +1 -0
  95. package/dist/core/backtest/case.d.ts +60 -0
  96. package/dist/core/backtest/case.d.ts.map +1 -0
  97. package/dist/core/backtest/case.js +319 -0
  98. package/dist/core/backtest/case.js.map +1 -0
  99. package/dist/core/backtest/compare.d.ts +38 -0
  100. package/dist/core/backtest/compare.d.ts.map +1 -0
  101. package/dist/core/backtest/compare.js +191 -0
  102. package/dist/core/backtest/compare.js.map +1 -0
  103. package/dist/core/backtest/declaration.d.ts +52 -0
  104. package/dist/core/backtest/declaration.d.ts.map +1 -0
  105. package/dist/core/backtest/declaration.js +48 -0
  106. package/dist/core/backtest/declaration.js.map +1 -0
  107. package/dist/core/backtest/deliver.d.ts +93 -0
  108. package/dist/core/backtest/deliver.d.ts.map +1 -0
  109. package/dist/core/backtest/deliver.js +94 -0
  110. package/dist/core/backtest/deliver.js.map +1 -0
  111. package/dist/core/backtest/drive.d.ts +87 -0
  112. package/dist/core/backtest/drive.d.ts.map +1 -0
  113. package/dist/core/backtest/drive.js +312 -0
  114. package/dist/core/backtest/drive.js.map +1 -0
  115. package/dist/core/backtest/index.d.ts +58 -0
  116. package/dist/core/backtest/index.d.ts.map +1 -0
  117. package/dist/core/backtest/index.js +47 -0
  118. package/dist/core/backtest/index.js.map +1 -0
  119. package/dist/core/backtest/range.d.ts +84 -0
  120. package/dist/core/backtest/range.d.ts.map +1 -0
  121. package/dist/core/backtest/range.js +90 -0
  122. package/dist/core/backtest/range.js.map +1 -0
  123. package/dist/core/backtest/record.d.ts +299 -0
  124. package/dist/core/backtest/record.d.ts.map +1 -0
  125. package/dist/core/backtest/record.js +243 -0
  126. package/dist/core/backtest/record.js.map +1 -0
  127. package/dist/core/backtest/replay.d.ts +48 -0
  128. package/dist/core/backtest/replay.d.ts.map +1 -0
  129. package/dist/core/backtest/replay.js +137 -0
  130. package/dist/core/backtest/replay.js.map +1 -0
  131. package/dist/core/backtest/resting.d.ts +62 -0
  132. package/dist/core/backtest/resting.d.ts.map +1 -0
  133. package/dist/core/backtest/resting.js +59 -0
  134. package/dist/core/backtest/resting.js.map +1 -0
  135. package/dist/core/backtest/settings.d.ts +117 -0
  136. package/dist/core/backtest/settings.d.ts.map +1 -0
  137. package/dist/core/backtest/settings.js +207 -0
  138. package/dist/core/backtest/settings.js.map +1 -0
  139. package/dist/core/backtest/simulate.d.ts +258 -0
  140. package/dist/core/backtest/simulate.d.ts.map +1 -0
  141. package/dist/core/backtest/simulate.js +335 -0
  142. package/dist/core/backtest/simulate.js.map +1 -0
  143. package/dist/core/catalogue/catalogue.generated.d.ts +66 -0
  144. package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -1
  145. package/dist/core/catalogue/catalogue.generated.js +6 -0
  146. package/dist/core/catalogue/catalogue.generated.js.map +1 -1
  147. package/dist/core/catalogue/values.generated.d.ts +24 -0
  148. package/dist/core/catalogue/values.generated.d.ts.map +1 -1
  149. package/dist/core/check/index.d.ts +2 -1
  150. package/dist/core/check/index.d.ts.map +1 -1
  151. package/dist/core/check/index.js +1 -1
  152. package/dist/core/check/index.js.map +1 -1
  153. package/dist/core/check/library-orders.js +2 -2
  154. package/dist/core/check/library-orders.js.map +1 -1
  155. package/dist/core/check/library-prose.generated.d.ts +16 -0
  156. package/dist/core/check/library-prose.generated.d.ts.map +1 -0
  157. package/dist/core/check/library-prose.generated.js +353 -0
  158. package/dist/core/check/library-prose.generated.js.map +1 -0
  159. package/dist/core/check/surface.d.ts +24 -0
  160. package/dist/core/check/surface.d.ts.map +1 -1
  161. package/dist/core/check/surface.js +29 -0
  162. package/dist/core/check/surface.js.map +1 -1
  163. package/dist/core/emit/canonical.d.ts +22 -8
  164. package/dist/core/emit/canonical.d.ts.map +1 -1
  165. package/dist/core/emit/canonical.js +67 -6
  166. package/dist/core/emit/canonical.js.map +1 -1
  167. package/dist/core/emit/defaults.d.ts +35 -16
  168. package/dist/core/emit/defaults.d.ts.map +1 -1
  169. package/dist/core/emit/defaults.js +66 -0
  170. package/dist/core/emit/defaults.js.map +1 -1
  171. package/dist/core/emit/index.d.ts +2 -0
  172. package/dist/core/emit/index.d.ts.map +1 -1
  173. package/dist/core/emit/index.js +2 -0
  174. package/dist/core/emit/index.js.map +1 -1
  175. package/dist/core/engine/arithmetic.d.ts +6 -25
  176. package/dist/core/engine/arithmetic.d.ts.map +1 -1
  177. package/dist/core/engine/arithmetic.js +48 -3
  178. package/dist/core/engine/arithmetic.js.map +1 -1
  179. package/dist/core/engine/index.d.ts +2 -2
  180. package/dist/core/engine/index.d.ts.map +1 -1
  181. package/dist/core/engine/index.js +2 -2
  182. package/dist/core/engine/index.js.map +1 -1
  183. package/dist/core/engine/library/arrays.d.ts.map +1 -1
  184. package/dist/core/engine/library/arrays.js +8 -2
  185. package/dist/core/engine/library/arrays.js.map +1 -1
  186. package/dist/core/engine/library/code-points.d.ts +40 -0
  187. package/dist/core/engine/library/code-points.d.ts.map +1 -0
  188. package/dist/core/engine/library/code-points.js +74 -0
  189. package/dist/core/engine/library/code-points.js.map +1 -0
  190. package/dist/core/engine/library/index.d.ts +5 -0
  191. package/dist/core/engine/library/index.d.ts.map +1 -1
  192. package/dist/core/engine/library/index.js +5 -0
  193. package/dist/core/engine/library/index.js.map +1 -1
  194. package/dist/core/engine/library/text.d.ts.map +1 -1
  195. package/dist/core/engine/library/text.js +51 -34
  196. package/dist/core/engine/library/text.js.map +1 -1
  197. package/dist/core/engine/load.d.ts +35 -1
  198. package/dist/core/engine/load.d.ts.map +1 -1
  199. package/dist/core/engine/load.js +63 -0
  200. package/dist/core/engine/load.js.map +1 -1
  201. package/dist/core/engine/verify-tables.d.ts.map +1 -1
  202. package/dist/core/engine/verify-tables.js +41 -0
  203. package/dist/core/engine/verify-tables.js.map +1 -1
  204. package/dist/core/index.d.ts +43 -4
  205. package/dist/core/index.d.ts.map +1 -1
  206. package/dist/core/index.js +23 -2
  207. package/dist/core/index.js.map +1 -1
  208. package/dist/core/stdlib/index.d.ts +1 -1
  209. package/dist/core/stdlib/index.d.ts.map +1 -1
  210. package/dist/core/stdlib/index.js +1 -1
  211. package/dist/core/stdlib/index.js.map +1 -1
  212. package/dist/core/stdlib/maths/index.d.ts +1 -1
  213. package/dist/core/stdlib/maths/index.d.ts.map +1 -1
  214. package/dist/core/stdlib/maths/index.js +1 -1
  215. package/dist/core/stdlib/maths/index.js.map +1 -1
  216. package/dist/core/stdlib/maths/rounding.d.ts +5 -0
  217. package/dist/core/stdlib/maths/rounding.d.ts.map +1 -1
  218. package/dist/core/stdlib/maths/rounding.js +19 -1
  219. package/dist/core/stdlib/maths/rounding.js.map +1 -1
  220. package/dist/core/version/version.generated.d.ts +1 -1
  221. package/dist/core/version/version.generated.js +1 -1
  222. package/dist/editor/complete.d.ts +40 -0
  223. package/dist/editor/complete.d.ts.map +1 -0
  224. package/dist/editor/complete.js +206 -0
  225. package/dist/editor/complete.js.map +1 -0
  226. package/dist/editor/diagnose.d.ts +18 -0
  227. package/dist/editor/diagnose.d.ts.map +1 -0
  228. package/dist/editor/diagnose.js +70 -0
  229. package/dist/editor/diagnose.js.map +1 -0
  230. package/dist/editor/format.d.ts +11 -0
  231. package/dist/editor/format.d.ts.map +1 -0
  232. package/dist/editor/format.js +50 -0
  233. package/dist/editor/format.js.map +1 -0
  234. package/dist/editor/highlight.d.ts +28 -0
  235. package/dist/editor/highlight.d.ts.map +1 -0
  236. package/dist/editor/highlight.js +65 -0
  237. package/dist/editor/highlight.js.map +1 -0
  238. package/dist/editor/hover.d.ts +40 -0
  239. package/dist/editor/hover.d.ts.map +1 -0
  240. package/dist/editor/hover.js +147 -0
  241. package/dist/editor/hover.js.map +1 -0
  242. package/dist/editor/index.d.ts +60 -0
  243. package/dist/editor/index.d.ts.map +1 -0
  244. package/dist/editor/index.js +7 -0
  245. package/dist/editor/index.js.map +1 -0
  246. package/dist/editor/kinds.d.ts +27 -0
  247. package/dist/editor/kinds.d.ts.map +1 -0
  248. package/dist/editor/kinds.js +118 -0
  249. package/dist/editor/kinds.js.map +1 -0
  250. package/dist/editor/layout.d.ts +47 -0
  251. package/dist/editor/layout.d.ts.map +1 -0
  252. package/dist/editor/layout.js +135 -0
  253. package/dist/editor/layout.js.map +1 -0
  254. package/dist/editor/manifest.d.ts +47 -0
  255. package/dist/editor/manifest.d.ts.map +1 -0
  256. package/dist/editor/manifest.js +93 -0
  257. package/dist/editor/manifest.js.map +1 -0
  258. package/dist/editor/reading.d.ts +41 -0
  259. package/dist/editor/reading.d.ts.map +1 -0
  260. package/dist/editor/reading.js +51 -0
  261. package/dist/editor/reading.js.map +1 -0
  262. package/dist/editor/scan.d.ts +26 -0
  263. package/dist/editor/scan.d.ts.map +1 -0
  264. package/dist/editor/scan.js +139 -0
  265. package/dist/editor/scan.js.map +1 -0
  266. package/dist/editor/scope.d.ts +19 -0
  267. package/dist/editor/scope.d.ts.map +1 -0
  268. package/dist/editor/scope.js +99 -0
  269. package/dist/editor/scope.js.map +1 -0
  270. package/dist/editor/signature.d.ts +38 -0
  271. package/dist/editor/signature.d.ts.map +1 -0
  272. package/dist/editor/signature.js +112 -0
  273. package/dist/editor/signature.js.map +1 -0
  274. package/dist/editor/site.d.ts +46 -0
  275. package/dist/editor/site.d.ts.map +1 -0
  276. package/dist/editor/site.js +184 -0
  277. package/dist/editor/site.js.map +1 -0
  278. package/dist/editor/spacing.d.ts +29 -0
  279. package/dist/editor/spacing.d.ts.map +1 -0
  280. package/dist/editor/spacing.js +116 -0
  281. package/dist/editor/spacing.js.map +1 -0
  282. package/package.json +42 -3
  283. package/spec/README.md +2 -1
  284. package/spec/errors.json +141 -1
  285. package/src/adapters/charts/driving.ts +109 -0
  286. package/src/adapters/charts/run.ts +120 -28
  287. package/src/adapters/charts/surfaces.ts +17 -0
  288. package/src/adapters/charts/tables.ts +88 -0
  289. package/src/adapters/charts/venue.ts +132 -0
  290. package/src/adapters/codemirror/commands.ts +53 -0
  291. package/src/adapters/codemirror/completion.ts +74 -0
  292. package/src/adapters/codemirror/contract.ts +156 -0
  293. package/src/adapters/codemirror/index.ts +66 -0
  294. package/src/adapters/codemirror/lint.ts +66 -0
  295. package/src/adapters/codemirror/positions.ts +79 -0
  296. package/src/adapters/codemirror/stream.ts +87 -0
  297. package/src/adapters/codemirror/tokens.ts +64 -0
  298. package/src/adapters/codemirror/tooltips.ts +113 -0
  299. package/src/core/accounting/analysis.ts +188 -0
  300. package/src/core/accounting/charges.ts +452 -0
  301. package/src/core/accounting/equity.ts +314 -0
  302. package/src/core/accounting/index.ts +51 -0
  303. package/src/core/accounting/markers.ts +95 -0
  304. package/src/core/accounting/monthly.ts +137 -0
  305. package/src/core/accounting/report.ts +94 -0
  306. package/src/core/accounting/shapes.ts +73 -0
  307. package/src/core/accounting/statistics.ts +387 -0
  308. package/src/core/accounting/trades.ts +313 -0
  309. package/src/core/backtest/case.ts +395 -0
  310. package/src/core/backtest/compare.ts +246 -0
  311. package/src/core/backtest/declaration.ts +97 -0
  312. package/src/core/backtest/deliver.ts +161 -0
  313. package/src/core/backtest/drive.ts +468 -0
  314. package/src/core/backtest/index.ts +64 -0
  315. package/src/core/backtest/range.ts +137 -0
  316. package/src/core/backtest/record.ts +470 -0
  317. package/src/core/backtest/replay.ts +168 -0
  318. package/src/core/backtest/resting.ts +125 -0
  319. package/src/core/backtest/settings.ts +280 -0
  320. package/src/core/backtest/simulate.ts +499 -0
  321. package/src/core/catalogue/catalogue.generated.ts +6 -0
  322. package/src/core/catalogue/values.generated.ts +6 -0
  323. package/src/core/check/index.ts +3 -0
  324. package/src/core/check/library-orders.ts +2 -2
  325. package/src/core/check/library-prose.generated.ts +367 -0
  326. package/src/core/check/surface.ts +34 -0
  327. package/src/core/emit/canonical.ts +67 -9
  328. package/src/core/emit/defaults.ts +70 -0
  329. package/src/core/emit/index.ts +2 -0
  330. package/src/core/engine/arithmetic.ts +23 -3
  331. package/src/core/engine/index.ts +2 -2
  332. package/src/core/engine/library/arrays.ts +8 -2
  333. package/src/core/engine/library/code-points.ts +73 -0
  334. package/src/core/engine/library/index.ts +6 -0
  335. package/src/core/engine/library/text.ts +53 -35
  336. package/src/core/engine/load.ts +84 -2
  337. package/src/core/engine/verify-tables.ts +43 -0
  338. package/src/core/index.ts +100 -2
  339. package/src/core/stdlib/index.ts +1 -0
  340. package/src/core/stdlib/maths/index.ts +1 -0
  341. package/src/core/stdlib/maths/rounding.ts +23 -1
  342. package/src/core/version/version.generated.ts +1 -1
  343. package/src/editor/complete.ts +266 -0
  344. package/src/editor/diagnose.ts +71 -0
  345. package/src/editor/format.ts +85 -0
  346. package/src/editor/highlight.ts +110 -0
  347. package/src/editor/hover.ts +199 -0
  348. package/src/editor/index.ts +65 -0
  349. package/src/editor/kinds.ts +157 -0
  350. package/src/editor/layout.ts +189 -0
  351. package/src/editor/manifest.ts +119 -0
  352. package/src/editor/reading.ts +69 -0
  353. package/src/editor/scan.ts +162 -0
  354. package/src/editor/scope.ts +122 -0
  355. package/src/editor/signature.ts +150 -0
  356. package/src/editor/site.ts +233 -0
  357. package/src/editor/spacing.ts +132 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * The run record: a whole run as a plain JSON document.
3
+ *
4
+ * **The record is the conformance case.** That is the one decision this module
5
+ * exists to hold, and everything in it follows from it: no object reference, no
6
+ * function, no dependence on this implementation's internals, and nothing that
7
+ * only this engine can read. A record carries the script revision, the compiled
8
+ * program, the bars, the settings the host chose, the frames the destination
9
+ * answered in delivery order, the fills the engine folded in fold order, the
10
+ * ledger the run ended with, the diagnostics and the report.
11
+ *
12
+ * **The bars are inline or referenced, and both carry the same hash.** Inline is
13
+ * the conformance form, because a case has to hold every byte of its own input.
14
+ * Referenced is the operational form, because the smallest deployment has a one
15
+ * megabyte request body and nothing that grows with history may travel in one.
16
+ * A replay whose bars do not hash to the record's hash is OS6022 rather than a
17
+ * silently different study.
18
+ *
19
+ * **The frames name an intent by ordinal and never by an engine's own id**,
20
+ * because a case cannot know what id another engine minted. The same reason the
21
+ * ledger rows here are flat words rather than this engine's own types.
22
+ *
23
+ * The record is written through the existing canonical encoding, so there is one
24
+ * canonical writer in the repository and key order and number form are fixed for
25
+ * everybody.
26
+ */
27
+ import type { RecordedFill, Report } from '../accounting/index.js';
28
+ import type { Diagnostic } from '../diagnostics/index.js';
29
+ import type { CompiledProgram, SourceStamp } from '../emit/index.js';
30
+ import type { Instrument, LedgerRow } from '../engine/index.js';
31
+ import type { BacktestSettings } from './settings.js';
32
+ /** One bar as a record holds it. */
33
+ export interface RecordedBar {
34
+ readonly time: number | null;
35
+ readonly open: number | null;
36
+ readonly high: number | null;
37
+ readonly low: number | null;
38
+ readonly close: number | null;
39
+ readonly volume: number | null;
40
+ readonly oi: number | null;
41
+ }
42
+ /**
43
+ * The bars, held or pointed at.
44
+ *
45
+ * Both forms carry the same hash, computed the same way over the canonical
46
+ * tuple of each bar, so a referenced record can be turned into an inline one by
47
+ * whoever holds the bars and the result is the same case.
48
+ */
49
+ export type BarsInRecord = {
50
+ readonly form: 'inline';
51
+ readonly hash: string;
52
+ readonly count: number;
53
+ readonly rows: readonly RecordedBar[];
54
+ } | {
55
+ readonly form: 'referenced';
56
+ readonly hash: string;
57
+ readonly count: number;
58
+ readonly firstTime: number | null;
59
+ readonly lastTime: number | null;
60
+ };
61
+ /** The columns of `conformance.md`'s frames.csv, in its order and with its names. */
62
+ export interface RecordedFrame {
63
+ readonly afterBar: number;
64
+ /** An ordinal, 1-based, never an engine's own id. */
65
+ readonly intent: number;
66
+ readonly status: string;
67
+ readonly filledQty: number;
68
+ readonly avgFillPrice: number | null;
69
+ readonly orderRef: string | null;
70
+ readonly text: string | null;
71
+ /**
72
+ * The destination's own instant for this frame, or null where it stated none.
73
+ *
74
+ * **One field of the ledger is folded from it.** `host-interface.md` 7.2
75
+ * gives a frame a `time` and `stdlib.md` 17.7 moves `updatedAt` to it, so a
76
+ * record that dropped it wrote a ledger no engine reading the case back could
77
+ * fold to: handed frames with no instant, that engine leaves every
78
+ * `updatedAt` at `placedAt`, and the two disagree on exactly the rows whose
79
+ * destination answered later than the bar that placed the order.
80
+ *
81
+ * Absent rather than substituted, because 7.2 lets a destination state none
82
+ * and a row whose frame stated none keeps the instant it had.
83
+ */
84
+ readonly time: number | null;
85
+ }
86
+ /** A ledger row of `stdlib.md` 17.7, flattened: the `orders` channel of expected.json. */
87
+ export interface RecordedOrder {
88
+ readonly intent: number;
89
+ readonly orderRef: string;
90
+ readonly tag: string;
91
+ readonly leg: string;
92
+ readonly positionRef: number;
93
+ readonly symbol: string | null;
94
+ readonly exchange: string | null;
95
+ readonly product: string;
96
+ readonly side: string;
97
+ readonly qty: number | null;
98
+ readonly qtyType: string;
99
+ readonly type: string | null;
100
+ readonly price: number | null;
101
+ readonly trigger: number | null;
102
+ readonly status: string;
103
+ readonly filledQty: number;
104
+ readonly avgFillPrice: number | null;
105
+ readonly rejection: string | null;
106
+ readonly placedAt: number | null;
107
+ readonly updatedAt: number | null;
108
+ readonly units: number | null;
109
+ }
110
+ /** A diagnostic as a record holds it: the code, the span and the bar. */
111
+ export interface RecordedDiagnostic {
112
+ readonly code: string;
113
+ readonly line: number;
114
+ readonly column: number;
115
+ readonly severity: string;
116
+ readonly barIndex: number | null;
117
+ }
118
+ /** One run, whole, and readable by an engine that is not this one. */
119
+ export interface RunRecord {
120
+ readonly recordVersion: number;
121
+ readonly engine: {
122
+ readonly name: string;
123
+ readonly version: string;
124
+ };
125
+ readonly languageVersion: string;
126
+ /** Already plain data. */
127
+ readonly program: CompiledProgram;
128
+ readonly programHash: string;
129
+ /** Hash, lines, file: the script revision. */
130
+ readonly source: SourceStamp;
131
+ /**
132
+ * The source text itself, or null on a record written before version 2.
133
+ *
134
+ * **A record is the conformance case, and a case has to hold `script.os`.**
135
+ * `source` above identifies the script and cannot reproduce it: a hash is a
136
+ * fingerprint, so it settles whether two files are the same and yields
137
+ * neither of them. Without the text a stored record could be replayed months
138
+ * later and still not be handed to anybody else to run, which is the whole
139
+ * claim the suite exists to test.
140
+ *
141
+ * It lives here and not on the compiled program on purpose. A program is
142
+ * executable data that no engine needs the source to run, and it is the
143
+ * versioned artefact adopters depend on; putting the text there would send a
144
+ * script everywhere its program travels and widen the format every engine
145
+ * has to read. The record is the thing that wants to be self-contained.
146
+ */
147
+ readonly sourceText: string | null;
148
+ readonly settings: BacktestSettings;
149
+ /**
150
+ * The instrument record of `host-interface.md` 4.1, as the engine was handed
151
+ * it, or null on a record written before version 3.
152
+ *
153
+ * **A case holds `instrument.json`, and `conformance.md` section 2 says that
154
+ * file is this record.** The contract in `settings` is the money layer's
155
+ * snapshot of the same instrument and holds six of the twelve facts; the
156
+ * ones a script reads and the money never does, the interval, the timezone,
157
+ * the session and the volume flag, were handed to the engine and written
158
+ * down nowhere. A case harvested from such a record either omitted a fact
159
+ * the page requires or stated one the run never had, and either way the
160
+ * second engine ran a different study than the one the expected output
161
+ * came from.
162
+ *
163
+ * It is the whole record and not the six facts beside the contract, because
164
+ * what is recorded is what the engine read at load, verbatim, and a reader
165
+ * should not have to compose it.
166
+ */
167
+ readonly instrument: Instrument | null;
168
+ readonly bars: BarsInRecord;
169
+ /** What the destination answered, in delivery order. */
170
+ readonly frames: readonly RecordedFrame[];
171
+ /** What the engine folded, in fold order. */
172
+ readonly fills: readonly RecordedFill[];
173
+ /** The ledger at the end, oldest first. */
174
+ readonly orders: readonly RecordedOrder[];
175
+ readonly diagnostics: readonly RecordedDiagnostic[];
176
+ readonly report: Report;
177
+ }
178
+ /**
179
+ * The revision of this document's own shape.
180
+ *
181
+ * A reader is handed a record and has to know what it is looking at before it
182
+ * reads a field, so the number is first in the document and is written by this
183
+ * file alone. It moves when a channel is added or a meaning changes, never when
184
+ * a figure in a report does.
185
+ */
186
+ export declare const RECORD_VERSION = 4;
187
+ /** The parts a run hands over, each already in the shape the record holds. */
188
+ export interface RecordParts {
189
+ readonly program: CompiledProgram;
190
+ /**
191
+ * The script's own text, so the record can become a conformance case.
192
+ *
193
+ * Optional because a caller that has only a compiled program cannot invent
194
+ * it, and a run is still worth recording without it. `caseFilesFrom` is the
195
+ * one thing that then cannot be served, and it says so rather than writing a
196
+ * case with a hole in it.
197
+ */
198
+ readonly sourceText?: string;
199
+ readonly settings: BacktestSettings;
200
+ /** The instrument record the engine was handed, `host-interface.md` 4.1. */
201
+ readonly instrument: Instrument;
202
+ readonly bars: readonly RecordedBar[];
203
+ /**
204
+ * Whether the bars travel in the record or are pointed at.
205
+ *
206
+ * Inline is the conformance form, because a case holds every byte of its own
207
+ * input. Referenced is the operational form, because nothing that grows with
208
+ * history may travel in a request body. Both carry the same hash over the
209
+ * same canonical tuples, so one becomes the other without becoming a
210
+ * different case.
211
+ */
212
+ readonly form: 'inline' | 'referenced';
213
+ readonly frames: readonly RecordedFrame[];
214
+ readonly fills: readonly RecordedFill[];
215
+ readonly orders: readonly RecordedOrder[];
216
+ readonly diagnostics: readonly RecordedDiagnostic[];
217
+ readonly report: Report;
218
+ }
219
+ /**
220
+ * One run, as the document another engine is handed.
221
+ *
222
+ * Nothing is computed here. Every channel arrives folded, because what a record
223
+ * says has to be what the run did rather than what a second fold of the same
224
+ * inputs came to: a record that recomputed its own report would agree with
225
+ * itself whatever the engine had actually done.
226
+ */
227
+ export declare function recordOf(parts: RecordParts): RunRecord;
228
+ /**
229
+ * The bars, held or pointed at, and the same hash either way.
230
+ *
231
+ * The referenced form keeps the first and last times so that a reader can say
232
+ * which bars are wanted without holding them, and the count so that a set of
233
+ * the wrong length is known to be wrong before it is hashed.
234
+ */
235
+ export declare function barsIn(bars: readonly RecordedBar[], form: 'inline' | 'referenced'): BarsInRecord;
236
+ /**
237
+ * The hash a record names its bars by.
238
+ *
239
+ * Over the canonical tuple of each bar, in the order they were supplied, and
240
+ * through the canonical encoding the compiled program is hashed with, so there
241
+ * is one canonical writer in the repository rather than two that agree until
242
+ * one of them is changed. A tuple rather than an object, because the field
243
+ * order is then the specification's and not a sort's.
244
+ */
245
+ export declare function barsHash(bars: readonly RecordedBar[]): string;
246
+ /**
247
+ * The record as the bytes another engine reads.
248
+ *
249
+ * Through the canonical encoding, so key order is fixed, every number is in the
250
+ * shortest decimal form that reads back as itself, and two runs of the same
251
+ * record produce the same bytes on every machine. That is what makes the
252
+ * reproducibility test a byte comparison rather than a tour of the fields.
253
+ */
254
+ export declare function recordToJson(record: RunRecord): string;
255
+ /**
256
+ * The run, without who produced it.
257
+ *
258
+ * **`engine` is provenance, and provenance is not part of the run.** The claim
259
+ * a rerun makes is that the run is a function of the record, and that claim has
260
+ * to survive the engine it runs on being upgraded: a record stored under
261
+ * `0.3.0` and rerun under `0.4.0` produces the same trades, the same fills and
262
+ * the same money, and a document carrying the version stamp inside the bytes
263
+ * being compared says it does not.
264
+ *
265
+ * It was compared as whole bytes until the first version bump would have broken
266
+ * it, which is a test that passes for exactly as long as nothing changes and
267
+ * then fails for the one reason that is not a defect. So the stamp stays in the
268
+ * record, because knowing which engine wrote a run is worth keeping, and the
269
+ * comparison is made over everything else.
270
+ */
271
+ export declare function runBytes(record: RunRecord): string;
272
+ /**
273
+ * A record read back from those bytes.
274
+ *
275
+ * A parse and not a validation: what comes back is the document as it was
276
+ * written, and a document this engine did not write is the caller's to trust or
277
+ * not. The one thing asserted is the revision, because reading a later
278
+ * revision's fields under this one's rules is how a record silently becomes a
279
+ * different run.
280
+ */
281
+ export declare function recordFromJson(text: string): RunRecord | null;
282
+ /**
283
+ * One ledger row, flattened into the words a case file prints.
284
+ *
285
+ * The engine's own types do not cross: a second engine has its own, and a
286
+ * channel written in this one's would be a case only this one could read. The
287
+ * intent is an ordinal for the same reason, because no engine can know what id
288
+ * another minted.
289
+ *
290
+ * `qtyType` comes from the intent rather than from the row, because the unit a
291
+ * quantity is counted in is per order and not per leg: an order the engine
292
+ * sized itself is in units whatever the declaration counts in, and a row that
293
+ * printed the declaration's unit for it would state a quantity in a unit it was
294
+ * never measured in.
295
+ */
296
+ export declare function orderIn(row: LedgerRow, intent: number, qtyType: string): RecordedOrder;
297
+ /** One diagnostic, as a record holds it: the code, the span and the bar. */
298
+ export declare function diagnosticIn(diagnostic: Diagnostic, barIndex: number | null): RecordedDiagnostic;
299
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../../src/core/backtest/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ import { canonicalNumber, canonicalise, programHash, sha256, sourceHash } from '../emit/index.js';
2
+ import { VERSION } from '../version/index.js';
3
+ /**
4
+ * The revision of this document's own shape.
5
+ *
6
+ * A reader is handed a record and has to know what it is looking at before it
7
+ * reads a field, so the number is first in the document and is written by this
8
+ * file alone. It moves when a channel is added or a meaning changes, never when
9
+ * a figure in a report does.
10
+ */
11
+ export const RECORD_VERSION = 4;
12
+ /**
13
+ * The revision each later channel arrived in.
14
+ *
15
+ * A record written before a channel existed reads with that channel absent,
16
+ * and this table is what `recordFromJson` reads it from: one row per channel
17
+ * added since version 1, so the rule for an old record is stated once and
18
+ * grows by a line when the next channel does.
19
+ *
20
+ * `frameTime` is a field of a row rather than a channel of the document, and it
21
+ * is a row here for the same reason the other two are: what a reader has to
22
+ * know is which revision it arrived in. Where the absence is written differs,
23
+ * and that is the reader's business below, not this table's.
24
+ */
25
+ const ADDED_IN = { sourceText: 2, instrument: 3, frameTime: 4 };
26
+ /** What this engine calls itself in a record it wrote. */
27
+ const ENGINE_NAME = 'openscript';
28
+ /**
29
+ * One run, as the document another engine is handed.
30
+ *
31
+ * Nothing is computed here. Every channel arrives folded, because what a record
32
+ * says has to be what the run did rather than what a second fold of the same
33
+ * inputs came to: a record that recomputed its own report would agree with
34
+ * itself whatever the engine had actually done.
35
+ */
36
+ export function recordOf(parts) {
37
+ return {
38
+ recordVersion: RECORD_VERSION,
39
+ engine: { name: ENGINE_NAME, version: VERSION },
40
+ languageVersion: canonicalNumber(parts.program.openscript.language),
41
+ program: parts.program,
42
+ programHash: programHash(parts.program),
43
+ source: parts.program.source,
44
+ sourceText: textFor(parts),
45
+ settings: parts.settings,
46
+ instrument: parts.instrument,
47
+ bars: barsIn(parts.bars, parts.form),
48
+ frames: parts.frames,
49
+ fills: parts.fills,
50
+ orders: parts.orders,
51
+ diagnostics: parts.diagnostics,
52
+ report: parts.report,
53
+ };
54
+ }
55
+ /**
56
+ * The source text, checked against the hash the program already carries.
57
+ *
58
+ * A check rather than a promise, and it costs one hash of a few kilobytes. The
59
+ * failure it exists for is quiet: a caller that passes the text of a different
60
+ * revision than the one it compiled produces a case whose script does not make
61
+ * its own expected output, and the engine being tested gets the blame for a
62
+ * disagreement that was in the case all along.
63
+ */
64
+ function textFor(parts) {
65
+ const text = parts.sourceText;
66
+ if (text === undefined)
67
+ return null;
68
+ if (sourceHash(text) !== parts.program.source.hash) {
69
+ throw new Error('openscript: the source text does not hash to the source hash the program carries');
70
+ }
71
+ return text;
72
+ }
73
+ /**
74
+ * The bars, held or pointed at, and the same hash either way.
75
+ *
76
+ * The referenced form keeps the first and last times so that a reader can say
77
+ * which bars are wanted without holding them, and the count so that a set of
78
+ * the wrong length is known to be wrong before it is hashed.
79
+ */
80
+ export function barsIn(bars, form) {
81
+ const hash = barsHash(bars);
82
+ if (form === 'inline') {
83
+ return { form: 'inline', hash, count: bars.length, rows: bars };
84
+ }
85
+ return {
86
+ form: 'referenced',
87
+ hash,
88
+ count: bars.length,
89
+ firstTime: bars[0]?.time ?? null,
90
+ lastTime: bars[bars.length - 1]?.time ?? null,
91
+ };
92
+ }
93
+ /**
94
+ * The hash a record names its bars by.
95
+ *
96
+ * Over the canonical tuple of each bar, in the order they were supplied, and
97
+ * through the canonical encoding the compiled program is hashed with, so there
98
+ * is one canonical writer in the repository rather than two that agree until
99
+ * one of them is changed. A tuple rather than an object, because the field
100
+ * order is then the specification's and not a sort's.
101
+ */
102
+ export function barsHash(bars) {
103
+ const tuples = bars.map((bar) => [
104
+ bar.time,
105
+ bar.open,
106
+ bar.high,
107
+ bar.low,
108
+ bar.close,
109
+ bar.volume,
110
+ bar.oi,
111
+ ]);
112
+ return 'sha256:' + sha256(canonicalise(tuples));
113
+ }
114
+ /**
115
+ * The record as the bytes another engine reads.
116
+ *
117
+ * Through the canonical encoding, so key order is fixed, every number is in the
118
+ * shortest decimal form that reads back as itself, and two runs of the same
119
+ * record produce the same bytes on every machine. That is what makes the
120
+ * reproducibility test a byte comparison rather than a tour of the fields.
121
+ */
122
+ export function recordToJson(record) {
123
+ return canonicalise(record);
124
+ }
125
+ /**
126
+ * The run, without who produced it.
127
+ *
128
+ * **`engine` is provenance, and provenance is not part of the run.** The claim
129
+ * a rerun makes is that the run is a function of the record, and that claim has
130
+ * to survive the engine it runs on being upgraded: a record stored under
131
+ * `0.3.0` and rerun under `0.4.0` produces the same trades, the same fills and
132
+ * the same money, and a document carrying the version stamp inside the bytes
133
+ * being compared says it does not.
134
+ *
135
+ * It was compared as whole bytes until the first version bump would have broken
136
+ * it, which is a test that passes for exactly as long as nothing changes and
137
+ * then fails for the one reason that is not a defect. So the stamp stays in the
138
+ * record, because knowing which engine wrote a run is worth keeping, and the
139
+ * comparison is made over everything else.
140
+ */
141
+ export function runBytes(record) {
142
+ const { engine: _engine, ...run } = record;
143
+ return canonicalise(run);
144
+ }
145
+ /**
146
+ * A record read back from those bytes.
147
+ *
148
+ * A parse and not a validation: what comes back is the document as it was
149
+ * written, and a document this engine did not write is the caller's to trust or
150
+ * not. The one thing asserted is the revision, because reading a later
151
+ * revision's fields under this one's rules is how a record silently becomes a
152
+ * different run.
153
+ */
154
+ export function recordFromJson(text) {
155
+ const parsed = JSON.parse(text);
156
+ if (parsed === null || typeof parsed !== 'object')
157
+ return null;
158
+ const record = parsed;
159
+ const version = record.recordVersion;
160
+ if (typeof version !== 'number' || version < 1 || version > RECORD_VERSION)
161
+ return null;
162
+ // An earlier revision is readable and a later one is not, and the asymmetry
163
+ // is the point. A later revision may mean something by a field this one
164
+ // thinks it knows, which is how a record silently becomes a different run. An
165
+ // earlier one only ever has fewer: every channel it carries means here what
166
+ // it meant there, and the ones added since are absent rather than wrong. So a
167
+ // run stored months ago still reads, which is the whole of what it was stored
168
+ // for, and a channel it never carried reads as absent.
169
+ if (version === RECORD_VERSION)
170
+ return record;
171
+ return {
172
+ ...record,
173
+ sourceText: version >= ADDED_IN.sourceText ? record.sourceText : null,
174
+ instrument: version >= ADDED_IN.instrument ? record.instrument : null,
175
+ frames: version >= ADDED_IN.frameTime ? record.frames : timeless(record.frames),
176
+ };
177
+ }
178
+ /**
179
+ * The frames of a record written before a frame carried an instant.
180
+ *
181
+ * The absence is written on every frame rather than on the record, because the
182
+ * channel is a field of a row: a reader that left the field undefined would
183
+ * hand the case projection an undefined where the column's absent spelling
184
+ * belongs, and the file would read `undefined` back to the next engine. The
185
+ * frames are left as they are when they are not an array, because this is a
186
+ * parse and not a validation and a document this engine did not write is the
187
+ * caller's to trust or not.
188
+ */
189
+ function timeless(frames) {
190
+ if (!Array.isArray(frames))
191
+ return frames;
192
+ return frames.map((frame) => ({ ...frame, time: null }));
193
+ }
194
+ /**
195
+ * One ledger row, flattened into the words a case file prints.
196
+ *
197
+ * The engine's own types do not cross: a second engine has its own, and a
198
+ * channel written in this one's would be a case only this one could read. The
199
+ * intent is an ordinal for the same reason, because no engine can know what id
200
+ * another minted.
201
+ *
202
+ * `qtyType` comes from the intent rather than from the row, because the unit a
203
+ * quantity is counted in is per order and not per leg: an order the engine
204
+ * sized itself is in units whatever the declaration counts in, and a row that
205
+ * printed the declaration's unit for it would state a quantity in a unit it was
206
+ * never measured in.
207
+ */
208
+ export function orderIn(row, intent, qtyType) {
209
+ return {
210
+ intent,
211
+ orderRef: row.orderRef,
212
+ tag: row.tag,
213
+ leg: row.leg,
214
+ positionRef: row.positionRef,
215
+ symbol: row.instrument.symbol,
216
+ exchange: row.instrument.exchange,
217
+ product: row.product,
218
+ side: row.side,
219
+ qty: row.qty,
220
+ qtyType,
221
+ type: row.type,
222
+ price: row.price,
223
+ trigger: row.trigger,
224
+ status: row.status,
225
+ filledQty: row.filledQty,
226
+ avgFillPrice: row.avgFillPrice,
227
+ rejection: row.rejection === '' ? null : row.rejection,
228
+ placedAt: row.placedAt,
229
+ updatedAt: row.updatedAt,
230
+ units: row.units,
231
+ };
232
+ }
233
+ /** One diagnostic, as a record holds it: the code, the span and the bar. */
234
+ export function diagnosticIn(diagnostic, barIndex) {
235
+ return {
236
+ code: diagnostic.code,
237
+ line: diagnostic.span.line,
238
+ column: diagnostic.span.column,
239
+ severity: diagnostic.severity,
240
+ barIndex,
241
+ };
242
+ }
243
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../../src/core/backtest/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { Report } from '../accounting/index.js';
2
+ import type { Diagnostic } from '../diagnostics/index.js';
3
+ import type { BacktestResult } from './drive.js';
4
+ import type { RecordedBar, RunRecord } from './record.js';
5
+ /** The report a record folds to, or why it could not be folded. */
6
+ export type ReplayResult = {
7
+ readonly ok: true;
8
+ readonly report: Report;
9
+ } | {
10
+ readonly ok: false;
11
+ readonly diagnostic: Diagnostic;
12
+ };
13
+ /**
14
+ * The record's own report, computed again from its own fills.
15
+ *
16
+ * The bars are the record's where it carries them and the caller's where it
17
+ * points at them, and either way they are hashed against what the record names
18
+ * before a figure is computed from them.
19
+ */
20
+ export declare function replay(record: RunRecord, bars?: readonly RecordedBar[] | null): ReplayResult;
21
+ /**
22
+ * The same run, executed again.
23
+ *
24
+ * Everything the run depended on is in the record: the program, the bars, the
25
+ * settings and the inputs. So a rerun takes no argument the record does not
26
+ * already hold, except the bars a referenced record points at rather than
27
+ * carries, and the record it produces is comparable with the original field for
28
+ * field and byte for byte.
29
+ */
30
+ export declare function rerun(record: RunRecord, bars?: readonly RecordedBar[] | null): BacktestResult;
31
+ type BarsResult = {
32
+ readonly ok: true;
33
+ readonly bars: readonly RecordedBar[];
34
+ } | {
35
+ readonly ok: false;
36
+ readonly diagnostic: Diagnostic;
37
+ };
38
+ /**
39
+ * The bars a record was made from, proved to be those bars.
40
+ *
41
+ * A record carrying its own bars is still checked against any that are supplied
42
+ * beside them, because supplying a different set is exactly the mistake worth
43
+ * catching: the caller believes they are replaying this run and they are
44
+ * studying another one.
45
+ */
46
+ export declare function barsOf(record: RunRecord, supplied: readonly RecordedBar[] | null): BarsResult;
47
+ export {};
48
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../../src/core/backtest/replay.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAkB,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE1D,mEAAmE;AACnE,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,SAAS,WAAW,EAAE,GAAG,IAAW,GAAG,YAAY,CAoBlG;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,SAAS,WAAW,EAAE,GAAG,IAAW,GAAG,cAAc,CAcnG;AAED,KAAK,UAAU,GACX;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAE5D;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,GAAG,UAAU,CAY7F"}