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,262 @@
1
+ /**
2
+ * The loop: a compiled program, some bars, and the record of what happened.
3
+ *
4
+ * **Everything is collected as it arrives.** Each bar hands back what it
5
+ * applied and what the frames delivered before it did to the ledger, and that
6
+ * is what the record is built from. Nothing is read back at the end except one
7
+ * snapshot of the ledger, and that snapshot is copied rather than kept:
8
+ * `engine.orders()` hands back the ledger's own array, which the fold writes
9
+ * into and a refused bar truncates, so a driver holding on to it would report
10
+ * whatever the array happened to say last rather than what the run did.
11
+ *
12
+ * **Every bar supplied executes and only the ones inside the window are
13
+ * reported.** A bar before the window is warmup: it runs, its orders are real,
14
+ * and a position opened on it is carried in. What it does not get is an equity
15
+ * point. `range.ts` decides which is which and refuses a window holding no bar
16
+ * at all, before the first bar rather than after the last.
17
+ *
18
+ * **The bar count handed over is the total.** An engine told that each bar is
19
+ * the only bar reports `bar.isLast` true on every one of them, and a strategy
20
+ * that flattens on the last bar then flattens on all of them.
21
+ *
22
+ * **Frames are delivered at the boundary and folded there.** What the venue
23
+ * answered after bar k reaches the engine before bar k + 1 executes, which is
24
+ * where `host-interface.md` 7.4 puts the intake, and the outcome of that fold
25
+ * is reported on bar k + 1. A frame answered after the last bar has no boundary
26
+ * left to fold at: it is not delivered, it is not recorded as delivered, and
27
+ * the order it was about is in the ledger at whatever it last said.
28
+ */
29
+ import { reportOf, scheduleFromDeclaration } from '../accounting/index.js';
30
+ import { load } from '../engine/index.js';
31
+ import { declarationOf } from './declaration.js';
32
+ import { marksFor, windowFor } from './range.js';
33
+ import { diagnosticIn, orderIn, recordOf } from './record.js';
34
+ import { Simulator } from './simulate.js';
35
+ import { checkSettings } from './settings.js';
36
+ /**
37
+ * One run, from a compiled program to a record of it.
38
+ *
39
+ * A refusal before the first bar comes back as a refusal and not as a record:
40
+ * a program that would not load, a setting that cannot be applied and a window
41
+ * holding no bars are all runs that did not happen, and a record of one would
42
+ * be a document asserting figures nothing computed. A failure during a bar is
43
+ * the other way round: the run happened, it stopped, and the record carries
44
+ * both what it did and the diagnostic that stopped it.
45
+ */
46
+ export function backtest(program, bars, settings, options = {}) {
47
+ const framed = windowFor(bars, settings.range);
48
+ if (!framed.ok)
49
+ return { ok: false, diagnostic: framed.diagnostic };
50
+ // The venue is built after the program is loaded, because the bar a market
51
+ // order is priced at is the declaration's and the declaration is resolved at
52
+ // load. The route is wired first and reaches it through this binding, which
53
+ // is what lets one host serve both halves.
54
+ let venue;
55
+ const loaded = load(program, {
56
+ settings: settings.inputs,
57
+ host: hostFor(settings, (effect, bar) => venue?.route(effect, bar)),
58
+ });
59
+ if (!loaded.ok)
60
+ return { ok: false, diagnostic: loaded.diagnostic };
61
+ const engine = loaded.engine;
62
+ const declared = declarationOf(engine.program, loaded.inputs);
63
+ const problem = checkSettings(settings, declared);
64
+ if (problem !== null)
65
+ return { ok: false, diagnostic: problem };
66
+ const schedule = scheduleFor(settings, declared);
67
+ venue = new Simulator({
68
+ bars,
69
+ contract: settings.contract,
70
+ fill: settings.fill,
71
+ slippageTicks: schedule.slippageTicks,
72
+ fillOn: declared.fillOn,
73
+ qtyType: declared.qtyType,
74
+ });
75
+ const run = walk(engine, venue, bars, framed.covered);
76
+ const ordinals = ordinalsOf(venue.intents);
77
+ const marks = marksFor(bars, framed.covered);
78
+ return {
79
+ ok: true,
80
+ record: recordOf({
81
+ program: engine.program,
82
+ settings,
83
+ bars,
84
+ form: options.form ?? 'inline',
85
+ frames: run.frames.map((one) => framedAs(one.frame, one.afterBar, ordinals)),
86
+ fills: run.fills,
87
+ orders: ordersOf(engine.orders(), venue.intents, ordinals),
88
+ diagnostics: run.diagnostics,
89
+ report: reportOf(run.fills, marks, schedule, settings.contract, declared.capital),
90
+ }),
91
+ };
92
+ }
93
+ /**
94
+ * The bars, in order, with the venue answering between them.
95
+ *
96
+ * The one ordering rule: deliver, then append, then collect what the append
97
+ * reported, then ask the venue what this bar did. A driver that asked the venue
98
+ * before appending would price a fill against a bar the strategy had not seen.
99
+ */
100
+ function walk(engine, venue, bars, covered) {
101
+ const fills = [];
102
+ const frames = [];
103
+ const diagnostics = [];
104
+ const intents = new Map();
105
+ const refs = new Map();
106
+ const sizes = new Map();
107
+ let pending = [];
108
+ let seq = 0;
109
+ for (let index = 0; index < covered.total; index += 1) {
110
+ const bar = bars[index];
111
+ if (bar === undefined)
112
+ continue;
113
+ for (const one of pending) {
114
+ const ref = one.frame.orderRef;
115
+ if (typeof ref === 'string')
116
+ refs.set(one.frame.intentId, ref);
117
+ engine.deliver(one.frame);
118
+ frames.push(one);
119
+ }
120
+ pending = [];
121
+ const result = engine.append({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close,
122
+ volume: bar.volume, oi: bar.oi }, { isConfirmed: true, isRealtime: false }, covered.total);
123
+ for (const intent of intentsIn(result))
124
+ intents.set(intent.intentId, intent);
125
+ seq = settle(result, index, bar, { intents, refs, sizes }, fills, seq);
126
+ if (result.diagnostic !== undefined) {
127
+ diagnostics.push(diagnosticIn(result.diagnostic, index));
128
+ break;
129
+ }
130
+ pending = venue.framesFor(index).map((frame) => ({ frame, afterBar: index }));
131
+ }
132
+ return { fills, frames, diagnostics };
133
+ }
134
+ /**
135
+ * The fills one bar's fold settled.
136
+ *
137
+ * **The position sizes either side of step 6 are kept here and not read back
138
+ * from the engine**, because the engine reports a leg's net and a fill settles
139
+ * against one position reference: the two are different numbers whenever a leg
140
+ * holds more than one position, which is every flip. They are folded in the
141
+ * order the frames were folded in, from the same deltas, so they are the same
142
+ * arithmetic the position book did rather than a second reading of its result.
143
+ * When `FrameOutcome` carries them, these two lines come from the engine and
144
+ * this fold keeps only the sequence number.
145
+ *
146
+ * A refused frame settles nothing and is not a fill. Today the fold reports the
147
+ * refusal as a word rather than a code, so nothing here can write it into the
148
+ * record as a diagnostic; that is OS7018 and OS7019, and both are deferred.
149
+ */
150
+ function settle(result, index, bar, books, fills, from) {
151
+ let seq = from;
152
+ for (const outcome of result.frames) {
153
+ if (outcome.refused !== undefined)
154
+ continue;
155
+ if (outcome.delta <= 0 || outcome.price === null)
156
+ continue;
157
+ const intent = books.intents.get(outcome.intentId);
158
+ if (intent === undefined || intent.side === null)
159
+ continue;
160
+ const before = books.sizes.get(intent.positionRef) ?? 0;
161
+ const after = before + (intent.side === 'sell' ? -outcome.delta : outcome.delta);
162
+ books.sizes.set(intent.positionRef, after);
163
+ seq += 1;
164
+ fills.push({
165
+ seq,
166
+ intentId: outcome.intentId,
167
+ orderRef: books.refs.get(outcome.intentId) ?? '',
168
+ tag: intent.tag,
169
+ positionRef: intent.positionRef,
170
+ side: intent.side,
171
+ units: outcome.delta,
172
+ price: outcome.price,
173
+ // The bar the fold happened at, which is the bar the position exists
174
+ // from. Where the fill itself traded is the bar before it or this bar's
175
+ // own open, and neither is a bar the strategy could have acted on it in.
176
+ barIndex: index,
177
+ barTime: bar.time,
178
+ refSizeBefore: before,
179
+ refSizeAfter: after,
180
+ });
181
+ }
182
+ return seq;
183
+ }
184
+ /** Every intent one bar handed over, in the order the bar applied them. */
185
+ function intentsIn(result) {
186
+ const out = [];
187
+ for (const effect of result.effects)
188
+ for (const intent of effect.intents)
189
+ out.push(intent);
190
+ return out;
191
+ }
192
+ /**
193
+ * The ordinal of every intent, which is how a case names one.
194
+ *
195
+ * One, two, three in the order the run placed them, because no engine can know
196
+ * the id another minted and a case that named one would only ever be readable
197
+ * by the engine that wrote it.
198
+ */
199
+ function ordinalsOf(intents) {
200
+ const out = new Map();
201
+ for (const intent of intents) {
202
+ if (!out.has(intent.intentId))
203
+ out.set(intent.intentId, out.size + 1);
204
+ }
205
+ return out;
206
+ }
207
+ /** One delivered frame, in the columns a case file prints. */
208
+ function framedAs(frame, afterBar, ordinals) {
209
+ return {
210
+ afterBar,
211
+ intent: ordinals.get(frame.intentId) ?? 0,
212
+ status: frame.status,
213
+ filledQty: frame.filledQty,
214
+ avgFillPrice: frame.avgFillPrice ?? null,
215
+ orderRef: frame.orderRef ?? null,
216
+ text: frame.text ?? null,
217
+ };
218
+ }
219
+ /** The ledger at the end, copied out of the engine's own array. */
220
+ function ordersOf(rows, intents, ordinals) {
221
+ return rows.map((row) => {
222
+ const intent = intents.find((one) => one.intentId === row.intentId);
223
+ return orderIn(row, ordinals.get(row.intentId) ?? 0, intent?.qtyType ?? '');
224
+ });
225
+ }
226
+ /**
227
+ * The cost model the run is carried out under.
228
+ *
229
+ * The host's schedule where it supplied one, and otherwise the declaration's
230
+ * own commission as the one kind of schedule the money layer evaluates. Both
231
+ * cannot be stated at once: that is OS6023 and it is refused before this is
232
+ * asked.
233
+ */
234
+ function scheduleFor(settings, declared) {
235
+ if (settings.costs !== null)
236
+ return settings.costs;
237
+ return scheduleFromDeclaration(declared.commission, declared.commissionType, declared.slippage, settings.contract.currency, settings.contract.digits);
238
+ }
239
+ /**
240
+ * The host a backtest is: an instrument record, a clock and a destination.
241
+ *
242
+ * Duty 3 is not served. A backtest holds the chart's own bars and nothing else,
243
+ * so a program that reads another instrument declares a capability this host
244
+ * does not have and is refused at load, by name, rather than drawing a line
245
+ * with nothing in it.
246
+ */
247
+ function hostFor(settings, route) {
248
+ const instrument = {
249
+ ...(settings.contract.symbol === null ? {} : { symbol: settings.contract.symbol }),
250
+ ...(settings.contract.exchange === null ? {} : { exchange: settings.contract.exchange }),
251
+ ...(settings.contract.tickSize === null ? {} : { tickSize: settings.contract.tickSize }),
252
+ ...(settings.contract.lotSize === null ? {} : { lotSize: settings.contract.lotSize }),
253
+ pointValue: settings.contract.pointValue,
254
+ currency: settings.contract.currency,
255
+ };
256
+ return {
257
+ instrument,
258
+ ...(settings.now === null ? {} : { now: settings.now }),
259
+ route,
260
+ };
261
+ }
262
+ //# sourceMappingURL=drive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive.js","sourceRoot":"","sources":["../../../src/core/backtest/drive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The backtest: a program, some bars, and a record of what happened.
3
+ *
4
+ * **A run produces a record, and the record is the product.** Not a number
5
+ * printed at the end and not a chart: a document that carries the program, the
6
+ * bars, the settings the host chose, every frame the destination answered,
7
+ * every fill the engine folded, the ledger at the end and the report. A second
8
+ * engine is handed that document as a conformance case, and this engine is
9
+ * handed it again months later to prove that an upgrade did not change a result.
10
+ *
11
+ * **The cost model lives in this module and not in the engine.** `stdlib.md`
12
+ * 17.1 puts slippage and commission on the destination, and the engine folds the
13
+ * price it is told; a backtest is a destination, so this is where a price is
14
+ * worsened by a tick and a fill is charged. The engine never adjusts a price and
15
+ * this module never folds one.
16
+ *
17
+ * `backtest` drives one, `replay` folds a stored one's money again from its own
18
+ * fills, and `rerun` executes a stored one again and is held to producing the
19
+ * same bytes. `recordToJson` and `recordFromJson` are the document itself.
20
+ * `compareRuns` puts two records beside each other and says whether the gap
21
+ * between them clears the noise. `caseFilesFrom` is named by the design and is
22
+ * not here yet: it will return text and write nothing, because core does no
23
+ * I/O, and it needs a channel the record does not carry today. `conformance.md`
24
+ * section 2 requires a case to hold `script.os`, the source text, and a record
25
+ * carries only the source's hash, its line count and its file name.
26
+ */
27
+ export { backtest } from './drive.js';
28
+ export type { BacktestResult, DriveOptions } from './drive.js';
29
+ export { declarationOf } from './declaration.js';
30
+ export type { RunDeclaration } from './declaration.js';
31
+ export { marksFor, windowFor } from './range.js';
32
+ export type { ReportWindow, WindowResult } from './range.js';
33
+ export { replay, rerun } from './replay.js';
34
+ export type { ReplayResult } from './replay.js';
35
+ export { testResting } from './resting.js';
36
+ export type { RestOutcome, RestingOrder } from './resting.js';
37
+ export { Simulator } from './simulate.js';
38
+ export type { SimulatorOptions } from './simulate.js';
39
+ export { DEFAULT_FILL, EXACT, WHOLE_RANGE, checkSettings, settingsFor } from './settings.js';
40
+ export { RECORD_VERSION, barsHash, recordFromJson, recordOf, recordToJson, runBytes } from './record.js';
41
+ export type { RecordParts } from './record.js';
42
+ export type { BacktestSettings, DateRange, FillPolicy, Tolerance } from './settings.js';
43
+ export type { BarsInRecord, RecordedBar, RecordedDiagnostic, RecordedFrame, RecordedOrder, RunRecord, } from './record.js';
44
+ export { compareRuns } from './compare.js';
45
+ export type { RunComparison } from './compare.js';
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/backtest/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACzG,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxF,YAAY,EACV,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,SAAS,GACV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The backtest: a program, some bars, and a record of what happened.
3
+ *
4
+ * **A run produces a record, and the record is the product.** Not a number
5
+ * printed at the end and not a chart: a document that carries the program, the
6
+ * bars, the settings the host chose, every frame the destination answered,
7
+ * every fill the engine folded, the ledger at the end and the report. A second
8
+ * engine is handed that document as a conformance case, and this engine is
9
+ * handed it again months later to prove that an upgrade did not change a result.
10
+ *
11
+ * **The cost model lives in this module and not in the engine.** `stdlib.md`
12
+ * 17.1 puts slippage and commission on the destination, and the engine folds the
13
+ * price it is told; a backtest is a destination, so this is where a price is
14
+ * worsened by a tick and a fill is charged. The engine never adjusts a price and
15
+ * this module never folds one.
16
+ *
17
+ * `backtest` drives one, `replay` folds a stored one's money again from its own
18
+ * fills, and `rerun` executes a stored one again and is held to producing the
19
+ * same bytes. `recordToJson` and `recordFromJson` are the document itself.
20
+ * `compareRuns` puts two records beside each other and says whether the gap
21
+ * between them clears the noise. `caseFilesFrom` is named by the design and is
22
+ * not here yet: it will return text and write nothing, because core does no
23
+ * I/O, and it needs a channel the record does not carry today. `conformance.md`
24
+ * section 2 requires a case to hold `script.os`, the source text, and a record
25
+ * carries only the source's hash, its line count and its file name.
26
+ */
27
+ export { backtest } from './drive.js';
28
+ export { declarationOf } from './declaration.js';
29
+ export { marksFor, windowFor } from './range.js';
30
+ export { replay, rerun } from './replay.js';
31
+ export { testResting } from './resting.js';
32
+ export { Simulator } from './simulate.js';
33
+ export { DEFAULT_FILL, EXACT, WHOLE_RANGE, checkSettings, settingsFor } from './settings.js';
34
+ export { RECORD_VERSION, barsHash, recordFromJson, recordOf, recordToJson, runBytes } from './record.js';
35
+ export { compareRuns } from './compare.js';
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/backtest/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAWzG,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Which bars a report is about, and which merely got it there.
3
+ *
4
+ * **Every bar supplied executes, and only some of them are reported.** That is
5
+ * the whole of this file and it is the fact a driver written without it gets
6
+ * wrong. A study of the last year needs the indicators warm before the year
7
+ * starts, so bars before the window are handed to the engine exactly as the
8
+ * reported ones are: they execute, their orders are real, and a position opened
9
+ * on one of them is carried into the window rather than appearing from nowhere
10
+ * at its first bar. What they are not is reported: no equity point, and no
11
+ * calendar month of their own.
12
+ *
13
+ * **A window holding no bar at all is refused**, OS6020, rather than reported
14
+ * as a flat curve. A flat curve is what a strategy that did nothing also
15
+ * produces, and only this can tell the two apart.
16
+ *
17
+ * **Both bounds are inclusive and are compared against the times of the bars
18
+ * supplied**, never against a calendar. A window that falls inside a gap in the
19
+ * data is empty however wide it looks, which is the truthful answer: there are
20
+ * no bars in it.
21
+ *
22
+ * The other fact a driver gets wrong is not a window at all and is written here
23
+ * because this is where a driver looks: the bar count handed to the engine is
24
+ * the total it will be given, not the number given so far. An engine told that
25
+ * each bar is the only bar reports `bar.isLast` true on every one of them, and
26
+ * a strategy that flattens on the last bar flattens on all of them.
27
+ */
28
+ import type { BarMark } from '../accounting/index.js';
29
+ import type { Diagnostic } from '../diagnostics/index.js';
30
+ import type { DateRange } from './settings.js';
31
+ import type { RecordedBar } from './record.js';
32
+ /** Which of the bars supplied the report is about. */
33
+ export interface ReportWindow {
34
+ /** The first reported bar, by index into the bars supplied. */
35
+ readonly first: number;
36
+ /** The last reported bar, inclusive. */
37
+ readonly last: number;
38
+ /** How many bars run before the first reported one. */
39
+ readonly warmup: number;
40
+ /** Every bar supplied, which is what the engine is told a run holds. */
41
+ readonly total: number;
42
+ }
43
+ export type WindowResult = {
44
+ readonly ok: true;
45
+ readonly covered: ReportWindow;
46
+ } | {
47
+ readonly ok: false;
48
+ readonly diagnostic: Diagnostic;
49
+ };
50
+ /**
51
+ * The window a range names over the bars supplied.
52
+ *
53
+ * A bar with no time is inside an unstated bound and outside a stated one,
54
+ * because a bound is a comparison and there is nothing to compare it with. A
55
+ * run over bars none of which carry a time is therefore reported whole when the
56
+ * host named no range, which is what a host handing over a bare series means,
57
+ * and refused the moment it names one, which is what a host asking for a window
58
+ * it cannot be given deserves to be told.
59
+ */
60
+ export declare function windowFor(bars: readonly RecordedBar[], range: DateRange): WindowResult;
61
+ /** Whether one bar's own time is inside the window, both bounds inclusive. */
62
+ export declare function inside(time: number | null, range: DateRange): boolean;
63
+ /**
64
+ * Whether a bar is reported, which is the question the marks are built from.
65
+ *
66
+ * Asked of an index rather than of a time, because the window is decided once
67
+ * and a bar asked twice has to be answered the same way both times.
68
+ */
69
+ export declare function inReport(index: number, covered: ReportWindow): boolean;
70
+ /**
71
+ * The bars as the report marks against them: the close, and whether it counts.
72
+ *
73
+ * One mark per bar supplied, warmup included, because the money layer sweeps
74
+ * the warmup rather than being handed a shortened list: a trade opened before
75
+ * the window is already in the fold at the window's first point, with its
76
+ * charges paid and its position marked. A mark says which of the two it is and
77
+ * the fold decides what to do about it.
78
+ *
79
+ * Built here rather than in the driver because a replay marks the same bars the
80
+ * same way, and two places deciding what a warmup bar is would be two reports
81
+ * of one run.
82
+ */
83
+ export declare function marksFor(bars: readonly RecordedBar[], covered: ReportWindow): readonly BarMark[];
84
+ //# sourceMappingURL=range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range.d.ts","sourceRoot":"","sources":["../../../src/core/backtest/range.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAE5D;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,GAAG,YAAY,CAsBtF;AAED,8EAA8E;AAC9E,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAMrE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAEtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,SAAS,OAAO,EAAE,CAOhG"}
@@ -0,0 +1,90 @@
1
+ import { diagnosticFor } from '../diagnostics/index.js';
2
+ /**
3
+ * The window a range names over the bars supplied.
4
+ *
5
+ * A bar with no time is inside an unstated bound and outside a stated one,
6
+ * because a bound is a comparison and there is nothing to compare it with. A
7
+ * run over bars none of which carry a time is therefore reported whole when the
8
+ * host named no range, which is what a host handing over a bare series means,
9
+ * and refused the moment it names one, which is what a host asking for a window
10
+ * it cannot be given deserves to be told.
11
+ */
12
+ export function windowFor(bars, range) {
13
+ let first = -1;
14
+ let last = -1;
15
+ for (let index = 0; index < bars.length; index += 1) {
16
+ const bar = bars[index];
17
+ if (bar === undefined || !inside(bar.time, range))
18
+ continue;
19
+ if (first < 0)
20
+ first = index;
21
+ last = index;
22
+ }
23
+ if (first < 0) {
24
+ return {
25
+ ok: false,
26
+ diagnostic: diagnosticFor('OS6020', NO_POSITION, {
27
+ from: bound(range.from, bars[0]?.time ?? null, 'the first bar supplied'),
28
+ to: bound(range.to, bars[bars.length - 1]?.time ?? null, 'the last bar supplied'),
29
+ count: bars.length,
30
+ }),
31
+ };
32
+ }
33
+ return { ok: true, covered: { first, last, warmup: first, total: bars.length } };
34
+ }
35
+ /** Whether one bar's own time is inside the window, both bounds inclusive. */
36
+ export function inside(time, range) {
37
+ if (range.from === null && range.to === null)
38
+ return true;
39
+ if (time === null)
40
+ return false;
41
+ if (range.from !== null && time < range.from)
42
+ return false;
43
+ if (range.to !== null && time > range.to)
44
+ return false;
45
+ return true;
46
+ }
47
+ /**
48
+ * Whether a bar is reported, which is the question the marks are built from.
49
+ *
50
+ * Asked of an index rather than of a time, because the window is decided once
51
+ * and a bar asked twice has to be answered the same way both times.
52
+ */
53
+ export function inReport(index, covered) {
54
+ return index >= covered.first && index <= covered.last;
55
+ }
56
+ /**
57
+ * The bars as the report marks against them: the close, and whether it counts.
58
+ *
59
+ * One mark per bar supplied, warmup included, because the money layer sweeps
60
+ * the warmup rather than being handed a shortened list: a trade opened before
61
+ * the window is already in the fold at the window's first point, with its
62
+ * charges paid and its position marked. A mark says which of the two it is and
63
+ * the fold decides what to do about it.
64
+ *
65
+ * Built here rather than in the driver because a replay marks the same bars the
66
+ * same way, and two places deciding what a warmup bar is would be two reports
67
+ * of one run.
68
+ */
69
+ export function marksFor(bars, covered) {
70
+ return bars.map((bar, index) => ({
71
+ barIndex: index,
72
+ time: bar.time,
73
+ close: bar.close,
74
+ inReport: inReport(index, covered),
75
+ }));
76
+ }
77
+ /** What a refusal calls a bound the host left for the bars to decide. */
78
+ function bound(stated, fallback, word) {
79
+ if (stated !== null)
80
+ return stated;
81
+ return fallback === null ? word : fallback;
82
+ }
83
+ /**
84
+ * Where a refusal about the window points.
85
+ *
86
+ * Nowhere in the script, for the same reason every other run setting points
87
+ * nowhere: the window is the host's choice and the script did not make it.
88
+ */
89
+ const NO_POSITION = { offset: 0, length: 0, line: 0, column: 0 };
90
+ //# sourceMappingURL=range.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range.js","sourceRoot":"","sources":["../../../src/core/backtest/range.ts"],"names":[],"mappings":"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"}