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
package/spec/errors.json CHANGED
@@ -2605,6 +2605,99 @@
2605
2605
  "refines": null,
2606
2606
  "test": "tests/engine/verify.test.ts"
2607
2607
  },
2608
+ {
2609
+ "code": "OS6020",
2610
+ "title": "The report window holds no bars",
2611
+ "severity": "error",
2612
+ "stage": "host",
2613
+ "since": 1,
2614
+ "message": "The window from {from} to {to} holds none of the {count} bars supplied.",
2615
+ "placeholders": {
2616
+ "from": "the first moment the window covers, or the first bar supplied where the host named none",
2617
+ "to": "the last moment it covers",
2618
+ "count": "how many bars were supplied"
2619
+ },
2620
+ "cause": "A report is about the bars inside the window the host chose, and a window holding none of them has nothing to report: no equity point, no trade and no summary. The bars outside it are warmup, which execute and whose orders are real, so an empty window is not an empty run, and reporting one as a flat curve would tell a reader that nothing happened when something did.",
2621
+ "fix": "Widen the window until it covers bars, or supply the bars it covers. Both bounds are inclusive and are compared against the times of the bars supplied rather than against a calendar, so a window that falls inside a gap in the data is empty however wide it looks.",
2622
+ "autofix": false,
2623
+ "example": {
2624
+ "kind": "transcript",
2625
+ "before": "bars supplied: 1240, first day 1, last day 1240\nreport window: day 1300 to day 1400, bars inside 0",
2626
+ "after": "bars supplied: 1240, first day 1, last day 1240\nreport window: day 1100 to day 1240, bars inside 141"
2627
+ },
2628
+ "spec": "language.md 7.1",
2629
+ "refines": null,
2630
+ "test": "tests/backtest/range.test.ts"
2631
+ },
2632
+ {
2633
+ "code": "OS6021",
2634
+ "title": "A run setting cannot be applied as stated",
2635
+ "severity": "error",
2636
+ "stage": "host",
2637
+ "since": 1,
2638
+ "message": "{setting} cannot be applied: {problem}.",
2639
+ "placeholders": {
2640
+ "setting": "the setting that cannot be applied, such as the charge schedule or the comparison tolerance",
2641
+ "problem": "what makes it unusable, such as a charge line levied on a line declared after it"
2642
+ },
2643
+ "cause": "A run states the settings it is carried out under before its first bar, and a setting that cannot be carried out is refused there rather than quietly producing a figure. A charge line levied on lines not declared before it has no single evaluation order, so two engines would charge two different amounts and both would be defensible. A slippage stated in ticks with no tick size to measure a tick in would charge nothing at all, which is a backtest that lies in the strategy's favour. A comparison tolerance carrying a bound and no reason is a failed comparison somebody switched off.",
2644
+ "fix": "State the setting so it can be carried out: declare a charge line after every line it is levied on, supply the tick size a slippage in ticks is measured in, or write down the reason a tolerance needs a bound. Nothing has been computed at the point this is refused, so correcting the setting and running again costs one run.",
2645
+ "autofix": false,
2646
+ "example": {
2647
+ "kind": "transcript",
2648
+ "before": "charge lines: tax on levy, levy\nslippage: 2 ticks, tick size not supplied",
2649
+ "after": "charge lines: levy, tax on levy\nslippage: 2 ticks, tick size 0.05"
2650
+ },
2651
+ "spec": "stdlib.md 17.1",
2652
+ "refines": null,
2653
+ "test": "tests/accounting/schedules.test.ts"
2654
+ },
2655
+ {
2656
+ "code": "OS6022",
2657
+ "title": "The bars are not the bars the record was made from",
2658
+ "severity": "error",
2659
+ "stage": "host",
2660
+ "since": 1,
2661
+ "message": "The bars supplied hash to {found}, and the record was made from {expected}.",
2662
+ "placeholders": {
2663
+ "found": "the hash of the bars supplied now",
2664
+ "expected": "the hash the record names"
2665
+ },
2666
+ "cause": "A record names the bars it was made from by a hash over their canonical form, so a replay can prove it is replaying the same run rather than producing a different study under the same name. Bars are revised: a feed corrects a print, a session is extended, a split is applied to history. A replay over revised bars that reported the original figures would be the most convincing wrong answer this system can produce.",
2667
+ "fix": "Replay against the bars the record names. Where the revision is the point, make a second record over the revised bars and compare the two runs, rather than overwriting one run with the other under one name.",
2668
+ "autofix": false,
2669
+ "example": {
2670
+ "kind": "transcript",
2671
+ "before": "record: bars 1240, hash 9f2c4e...\nsupplied: bars 1240, hash 4ab70d...",
2672
+ "after": "record: bars 1240, hash 9f2c4e...\nsupplied: bars 1240, hash 9f2c4e..."
2673
+ },
2674
+ "spec": "conformance.md 3",
2675
+ "refines": null,
2676
+ "test": "tests/backtest/record.test.ts"
2677
+ },
2678
+ {
2679
+ "code": "OS6023",
2680
+ "title": "Two cost models are stated at once",
2681
+ "severity": "error",
2682
+ "stage": "host",
2683
+ "since": 1,
2684
+ "message": "A charge schedule was supplied, and the declaration states a commission of {commission} in {commissionType}.",
2685
+ "placeholders": {
2686
+ "commission": "the commission the declaration states",
2687
+ "commissionType": "the unit that commission is stated in"
2688
+ },
2689
+ "cause": "The declaration's commission is the script's own statement of what trading costs and a supplied schedule is the platform's, and the two describe the same money. Applied together they charge it twice; applied one at a time they charge whichever an engine happened to prefer, which is a rule nobody wrote down and a figure nobody can explain afterwards. So exactly one of the two is stated for a run, and stating both is refused before the first bar rather than reconciled behind the reader.",
2690
+ "fix": "Supply the schedule and leave the declaration's commission at its default of zero, or state the commission in the declaration and supply no schedule. A schedule is the one of the two that can carry a floor, a cap, a charge levied on a charge, and a cost that falls on one side of the trade only.",
2691
+ "autofix": false,
2692
+ "example": {
2693
+ "kind": "transcript",
2694
+ "before": "declaration: commission 20, per trade\nhost: charge schedule supplied, 4 lines",
2695
+ "after": "declaration: commission 0, per trade\nhost: charge schedule supplied, 4 lines"
2696
+ },
2697
+ "spec": "language.md 13.3; stdlib.md 17.1",
2698
+ "refines": null,
2699
+ "test": "tests/backtest/settings.test.ts"
2700
+ },
2608
2701
  {
2609
2702
  "code": "OS7001",
2610
2703
  "title": "Only a strategy can do that",
@@ -2984,6 +3077,53 @@
2984
3077
  "refines": null,
2985
3078
  "test": "tests/engine/closing.test.ts"
2986
3079
  },
3080
+ {
3081
+ "code": "OS7018",
3082
+ "title": "A frame names an order this strategy did not place",
3083
+ "severity": "error",
3084
+ "stage": "host",
3085
+ "since": 1,
3086
+ "message": "The frame names intent {intent}, and this strategy holds no such order.",
3087
+ "placeholders": {
3088
+ "intent": "the intent id the frame named"
3089
+ },
3090
+ "cause": "Step 1 of the fold locates the row a frame is about, and a frame naming no row cannot be folded into anything. It is a fact about the host rather than about the strategy: a destination answering for an order another strategy placed, or answering for a run that has already ended. The frame changes nothing and is reported, because a fold that passed over it in silence would leave a host's mistake invisible to the only party who can correct it.",
3091
+ "fix": "Answer with the intent id the engine sent. A destination's own reference is carried in the frame's reference field, where the engine records it and never parses it, and it is not what an answer is addressed by.",
3092
+ "autofix": false,
3093
+ "example": {
3094
+ "kind": "transcript",
3095
+ "before": "engine sent: intent 7, intent 8\nframe: intent 11, status filled, filled qty 1",
3096
+ "after": "engine sent: intent 7, intent 8\nframe: intent 8, status filled, filled qty 1"
3097
+ },
3098
+ "spec": "stdlib.md 17.8, 17.14",
3099
+ "refines": null,
3100
+ "test": null,
3101
+ "deferred": "Nothing raises this yet. The fold refuses the frame and records the refusal as a word on the outcome it hands back, and a word is not a catalogue code, so nothing reports it against the run and a host answering for orders it was never handed stays invisible. Raised when the outcome of a fold carries a code and a bar carries a channel for a diagnostic that does not stop it, stdlib.md 17.8."
3102
+ },
3103
+ {
3104
+ "code": "OS7019",
3105
+ "title": "A fill was reported with no price",
3106
+ "severity": "error",
3107
+ "stage": "host",
3108
+ "since": 1,
3109
+ "message": "The frame reports {qty} filled for intent {intent}, and no average fill price.",
3110
+ "placeholders": {
3111
+ "qty": "the cumulative filled quantity the frame reports",
3112
+ "intent": "the intent id the frame named"
3113
+ },
3114
+ "cause": "Step 3 of the fold takes the destination's own average over the cumulative quantity, because the engine never averages two averages of its own. A frame reporting more filled than the row holds and no price to take is a fill that can be marked against nothing: no position average, no realised profit, no equity point. It is refused whole rather than folded for its quantity alone, because a position holding a size and no price is worse than no position at all.",
3115
+ "fix": "Report the average fill price the destination computed over the cumulative quantity, on every frame that reports a quantity greater than the last one. A frame carrying no new quantity needs no price.",
3116
+ "autofix": false,
3117
+ "example": {
3118
+ "kind": "transcript",
3119
+ "before": "frame: intent 8, status filled, filled qty 3, average fill price absent",
3120
+ "after": "frame: intent 8, status filled, filled qty 3, average fill price 104.25"
3121
+ },
3122
+ "spec": "stdlib.md 17.8, 17.14",
3123
+ "refines": null,
3124
+ "test": null,
3125
+ "deferred": "Nothing raises this yet. The fold refuses the frame and records the refusal as a word on the outcome it hands back, and a word is not a catalogue code, so a destination reporting a fill that can be marked against nothing is refused in silence. Raised when the outcome of a fold carries a code and a bar carries a channel for a diagnostic that does not stop it, stdlib.md 17.8."
3126
+ },
2987
3127
  {
2988
3128
  "code": "OS8001",
2989
3129
  "title": "A stateful call inside a branch",
@@ -100,6 +100,23 @@ export interface ChartTableOptions {
100
100
  readonly borderWidth: number;
101
101
  /** The backdrop behind the whole grid, which is the grid's own `bgColor`. */
102
102
  readonly background?: string;
103
+ /**
104
+ * Column widths in media pixels, one per column.
105
+ *
106
+ * The chart's own default is one flat width for every column, which is a
107
+ * width chosen without seeing the text. A grid whose first column says
108
+ * "Moving averages" and whose second says "RSI: 27.22" has two columns that
109
+ * want different room, and one number cannot give it to both.
110
+ */
111
+ readonly cellWidth?: readonly number[];
112
+ /**
113
+ * `'auto'` to shrink a cell's type until its text fits the cell it is in.
114
+ *
115
+ * The chart measures the text it is about to draw, which nothing upstream of
116
+ * it can do, so this is what makes a column width that is slightly wrong
117
+ * merely slightly loose instead of two cells written over each other.
118
+ */
119
+ readonly fontSize?: number | 'auto';
103
120
  }
104
121
 
105
122
  /** A grid of cells pinned to a corner, and the options it is drawn with. */
@@ -118,10 +118,98 @@ function oneTable(
118
118
  position: CORNERS[position] ?? 'top-right',
119
119
  borderWidth: numberField(declared.options.borderWidth, lookup, 0),
120
120
  ...(bgColour === undefined ? {} : { background: cssColour(bgColour) }),
121
+ ...(cols > 0 ? { cellWidth: columnWidths(grid, cols) } : {}),
122
+ // The chart measures the text it is about to draw and shrinks it to fit,
123
+ // which is the half of this that cannot be done from here.
124
+ fontSize: 'auto',
121
125
  },
122
126
  };
123
127
  }
124
128
 
129
+ /**
130
+ * How wide each column has to be, in media pixels.
131
+ *
132
+ * The chart's default is one width for every column, chosen without seeing the
133
+ * text, and a grid whose header says "Moving averages" over cells saying
134
+ * "RSI: 27.22" has one column bleeding into the next. Nothing downstream can
135
+ * choose better, because by the time the chart has the grid it has lost which
136
+ * cells belong together; and nothing upstream can, because the script declares
137
+ * a size and writes text, not a layout.
138
+ *
139
+ * **This is an estimate, and it is allowed to be.** A real width needs the font
140
+ * the chart will draw with, and only the chart has it. So the width is counted
141
+ * off the characters and the chart's own `fontSize: 'auto'` corrects whatever
142
+ * this gets wrong: too narrow and the type shrinks a little, too wide and the
143
+ * column is a little loose. Either is a table that reads. The failure this
144
+ * replaces was neither.
145
+ *
146
+ * Delete it when the chart can size a column from its own measurements. It
147
+ * exists because that is not a thing it can be asked to do yet.
148
+ */
149
+ function columnWidths(grid: readonly (readonly ChartCell[])[], cols: number): number[] {
150
+ const widths: number[] = [];
151
+ for (let col = 0; col < cols; col += 1) {
152
+ let widest = 0;
153
+ for (const row of grid) {
154
+ const text = row[col]?.text ?? '';
155
+ if (text.length > 0) widest = Math.max(widest, textWidth(text));
156
+ }
157
+ widths.push(Math.min(MAX_COLUMN, Math.max(MIN_COLUMN, Math.ceil(widest) + CELL_PADDING * 2)));
158
+ }
159
+ return widths;
160
+ }
161
+
162
+ /**
163
+ * Roughly how wide a string is at the type size the chart starts from.
164
+ *
165
+ * Counted rather than measured, and counted in two weights rather than one: a
166
+ * column of "Histogram: -7.01" is half punctuation and narrow digits, and
167
+ * treating every character as an em-and-a-bit makes that column half again as
168
+ * wide as it needs to be while "Moving averages" stays too narrow. Two weights
169
+ * is not typography, but it is enough to tell those two apart.
170
+ */
171
+ function textWidth(text: string): number {
172
+ let width = 0;
173
+ for (const character of text) {
174
+ width += NARROW.has(character) ? BASE_SIZE * NARROW_EM : BASE_SIZE * WIDE_EM;
175
+ }
176
+ return width;
177
+ }
178
+
179
+ /** The characters that take noticeably less than an average advance. */
180
+ const NARROW = new Set([...' .,:;!|ijlt1IJfr()[]{}-']);
181
+
182
+ /**
183
+ * The type size the widths are counted at.
184
+ *
185
+ * The chart's own default row is 18 media pixels and it draws at 62% of the
186
+ * row, so this is the size a cell gets before anything shrinks it. Counting at
187
+ * a larger size would reserve room the text never uses.
188
+ */
189
+ const BASE_SIZE = 11;
190
+ const NARROW_EM = 0.32;
191
+ const WIDE_EM = 0.56;
192
+
193
+ /** What the chart insets a cell's text by, on each side. */
194
+ const CELL_PADDING = 4;
195
+
196
+ /**
197
+ * The narrowest a column is drawn.
198
+ *
199
+ * A column of one-character cells sized to its content is a sliver, and a grid
200
+ * of slivers reads as a rendering fault rather than as a narrow column.
201
+ */
202
+ const MIN_COLUMN = 56;
203
+
204
+ /**
205
+ * The widest a column is drawn.
206
+ *
207
+ * A cell holding a sentence would otherwise push the grid past the pane it is
208
+ * pinned inside, taking the chart with it. Past this the type shrinks instead,
209
+ * which is the chart's job and it is better at it.
210
+ */
211
+ const MAX_COLUMN = 220;
212
+
125
213
  /**
126
214
  * One written cell, or none.
127
215
  *
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Laying a script out again, as an editor command.
3
+ *
4
+ * `format` is the tier's, and the guarantee it carries is the one that makes a
5
+ * format button safe to put in front of somebody holding a position: laying a
6
+ * script out again cannot change what it computes. Every example and every gate
7
+ * script in the repository is formatted, both texts compiled, and the compiled
8
+ * programs compared field for field; and every call checks itself against the
9
+ * lexer, so a rule that is wrong returns the source untouched rather than a
10
+ * changed program.
11
+ *
12
+ * The whole document is replaced rather than a minimal edit computed. It is the
13
+ * honest version: a diff would have to be trusted to be equivalent to the
14
+ * replacement, and a formatter that moved a line somewhere unintended would do
15
+ * it silently. The cursor is kept where it was in the new text, clamped, because
16
+ * a format that sends the caret to the top of the file is a format nobody
17
+ * presses twice.
18
+ *
19
+ * **A document with two character line endings comes back with one character
20
+ * ones**, because that is what `language.md` 3.1 normalises a file to before
21
+ * anything reads it and what every offset in this tier indexes. It is the one
22
+ * change to a file's bytes that formatting makes without being asked, and it is
23
+ * recorded in `spec/editor-narrowings.json`.
24
+ */
25
+ import { format } from '../../editor/index.js';
26
+ import type { EditorView } from './contract.js';
27
+ import { documentOf, normalisedOffset } from './positions.js';
28
+
29
+ /**
30
+ * Lays the document out again, and says whether anything changed.
31
+ *
32
+ * ```ts
33
+ * keymap.of([{ key: "Shift-Alt-f", run: formatDocument }])
34
+ * ```
35
+ *
36
+ * False where the document is already laid out, which is what an editor command
37
+ * returns when it has nothing to do: the key press then falls through to
38
+ * whatever else is bound to it rather than being swallowed.
39
+ */
40
+ export function formatDocument(view: EditorView): boolean {
41
+ const raw = view.state.doc.toString();
42
+ const held = documentOf(raw);
43
+ const laid = format(held.text);
44
+ if (laid === raw) return false;
45
+
46
+ const at = normalisedOffset(held, view.state.selection.main.head);
47
+ view.dispatch({
48
+ changes: { from: 0, to: view.state.doc.length, insert: laid },
49
+ selection: { anchor: Math.min(at, laid.length) },
50
+ scrollIntoView: true,
51
+ });
52
+ return true;
53
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Completions, as the editor's completion source.
3
+ *
4
+ * The whole of the work is done by `complete`, which reads the standard library
5
+ * manifest, the checker's bindings and the error catalogue. What is here is the
6
+ * translation: the editor wants a range to replace, a list of rows, and a note
7
+ * about whether the list is already filtered.
8
+ *
9
+ * **The list is already filtered**, by the word the writer has typed, compared
10
+ * exactly, because the language's names are case sensitive. So `filter` is false
11
+ * and the editor is told not to filter it again: its own matcher is fuzzy and
12
+ * case insensitive, and running it over a list that has already answered the
13
+ * question would put `EMA` in front of somebody as though it compiled.
14
+ *
15
+ * **A planned call is shown and marked.** `complete` carries the catalogue's own
16
+ * OS2020 sentence on a planned row, so that is what goes in the row's expanded
17
+ * note, and the row is pushed to the bottom of the list with the editor's own
18
+ * boost. A host that would rather not show them at all drops them with one
19
+ * field, which is why they are marked rather than filtered here.
20
+ */
21
+ import { complete } from '../../editor/index.js';
22
+ import type { Completion } from '../../editor/index.js';
23
+ import type {
24
+ EditorCompletion,
25
+ EditorCompletionContext,
26
+ EditorCompletionResult,
27
+ } from './contract.js';
28
+ import { COMPLETION_TYPES } from './tokens.js';
29
+
30
+ /** As far down the list as the editor will move a row. */
31
+ const PLANNED_BOOST = -99;
32
+
33
+ function rowOf(one: Completion): EditorCompletion {
34
+ const info = one.refusal ?? one.summary;
35
+ const type = COMPLETION_TYPES[one.kind];
36
+ return {
37
+ label: one.label,
38
+ ...(type === undefined ? {} : { type }),
39
+ detail: one.detail,
40
+ ...(info === undefined ? {} : { info }),
41
+ ...(one.insert === one.label ? {} : { apply: one.insert }),
42
+ ...(one.planned ? { boost: PLANNED_BOOST } : {}),
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Every completion for the position the editor is asking about.
48
+ *
49
+ * ```ts
50
+ * autocompletion({ override: [openscriptCompletion] })
51
+ * ```
52
+ *
53
+ * Nothing comes back for an empty list rather than an empty list, because the
54
+ * editor reads the two differently: an empty result closes the panel and null
55
+ * lets another source answer, and a host with a snippet source of its own should
56
+ * keep it.
57
+ */
58
+ export function openscriptCompletion(
59
+ context: EditorCompletionContext,
60
+ ): EditorCompletionResult | null {
61
+ const rows = complete(context.state.doc.toString(), context.pos);
62
+ // The first row carries the range every row replaces, so an empty list and a
63
+ // list whose first row is missing are one answer: nothing. Two guards for that
64
+ // would be one guard and a line no test can reach.
65
+ const first = rows[0];
66
+ if (first === undefined) return null;
67
+
68
+ return {
69
+ from: first.replace.offset,
70
+ to: first.replace.offset + first.replace.length,
71
+ options: rows.map(rowOf),
72
+ filter: false,
73
+ };
74
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * The editor component's own shapes, as this adapter targets them.
3
+ *
4
+ * **Why they are declared here rather than imported.** The editor component is a
5
+ * peer dependency: a host that wants a text area installs it, and a host that
6
+ * wants only the language must not be made to. This repository therefore does
7
+ * not install it, cannot type check against it, and would fail its own layering
8
+ * check the moment a file here imported it. It is the same decision the chart
9
+ * adapter took, for the same reason, and `src/adapters/charts/contract.ts` says
10
+ * it at length.
11
+ *
12
+ * So the shapes this produces values for are declared below and the host's own
13
+ * build is where the two meet. Each one is a one line check that costs a host
14
+ * nothing and fails to compile if this file has drifted:
15
+ *
16
+ * ```ts
17
+ * import type { StreamParser } from "<the editor's language package>";
18
+ * import type { CompletionSource } from "<the editor's completion package>";
19
+ * import type { Diagnostic } from "<the editor's lint package>";
20
+ *
21
+ * const parser: StreamParser<unknown> = openscriptStream;
22
+ * const source: CompletionSource = openscriptCompletion;
23
+ * const linter: (view: EditorView) => Diagnostic[] = openscriptLint;
24
+ * ```
25
+ *
26
+ * **That line catches a value of the wrong shape and not an argument of the
27
+ * wrong shape.** A method's parameters are compared in both directions, so a
28
+ * hook declared here as taking something the editor never passes still assigns.
29
+ * That is the failure the chart adapter has actually made, so every member below
30
+ * takes the editor's own argument, spelled as the editor spells it, and every
31
+ * test drives each one with the argument the editor passes rather than with one
32
+ * of its own.
33
+ *
34
+ * **A style name is not checked by any of those lines.** The names this adapter
35
+ * returns from `token` are looked up by the editor in its own table of
36
+ * highlighting tags at run time, and an unknown one is a warning on a console
37
+ * rather than a compile error. That is why the whole mapping is one exported
38
+ * record a host can replace in a line, and why it is recorded in
39
+ * `spec/editor-narrowings.json` as the one thing here a type cannot hold.
40
+ */
41
+
42
+ /**
43
+ * One line of the document, as the editor's tokenizer sees it.
44
+ *
45
+ * The editor hands a tokenizer a line at a time and expects `pos` to have moved
46
+ * forward when `token` returns. Only the members this adapter reads are
47
+ * declared: a narrower shape is still assignable from the editor's wider one.
48
+ */
49
+ export interface EditorStringStream {
50
+ /** The line's text, without its line ending. */
51
+ readonly string: string;
52
+ /** Where the tokenizer has got to, which this adapter moves. */
53
+ pos: number;
54
+ /** Where the current token began. */
55
+ readonly start: number;
56
+ /** Whether `pos` is at the end of the line. */
57
+ eol(): boolean;
58
+ /** Whether `pos` is at the start of the line. */
59
+ sol(): boolean;
60
+ }
61
+
62
+ /**
63
+ * A tokenizer the editor drives one line at a time.
64
+ *
65
+ * `token` returns a style name, or null for text with no style of its own. The
66
+ * state is the tokenizer's to define, and the editor copies it at line
67
+ * boundaries so that it can restart mid document, which is why `copyState` is
68
+ * here.
69
+ */
70
+ export interface EditorStreamParser<State> {
71
+ readonly name?: string;
72
+ startState?(indentUnit: number): State;
73
+ token(stream: EditorStringStream, state: State): string | null;
74
+ copyState?(state: State): State;
75
+ blankLine?(state: State, indentUnit: number): void;
76
+ readonly languageData?: Readonly<Record<string, unknown>>;
77
+ }
78
+
79
+ /** The document, which the editor holds as a rope rather than as a string. */
80
+ export interface EditorText {
81
+ readonly length: number;
82
+ toString(): string;
83
+ }
84
+
85
+ /** The part of the editor's state this adapter reads. */
86
+ export interface EditorState {
87
+ readonly doc: EditorText;
88
+ readonly selection: {
89
+ readonly main: { readonly head: number; readonly from: number; readonly to: number };
90
+ };
91
+ }
92
+
93
+ /** The part of the editor's view this adapter reads and dispatches to. */
94
+ export interface EditorView {
95
+ readonly state: EditorState;
96
+ dispatch(transaction: {
97
+ readonly changes: { readonly from: number; readonly to: number; readonly insert: string };
98
+ readonly selection?: { readonly anchor: number };
99
+ readonly scrollIntoView?: boolean;
100
+ }): void;
101
+ }
102
+
103
+ /** What the editor hands a completion source. */
104
+ export interface EditorCompletionContext {
105
+ readonly state: EditorState;
106
+ readonly pos: number;
107
+ /** Whether the writer asked for completions rather than merely typing. */
108
+ readonly explicit: boolean;
109
+ }
110
+
111
+ /** One row of the editor's completion list. */
112
+ export interface EditorCompletion {
113
+ readonly label: string;
114
+ /** The editor's own name for the icon beside a row. */
115
+ readonly type?: string;
116
+ readonly detail?: string;
117
+ readonly info?: string;
118
+ /** The text to insert, where it differs from the label. */
119
+ readonly apply?: string;
120
+ /** Moves a row up or down the list, from -99 to 99. */
121
+ readonly boost?: number;
122
+ }
123
+
124
+ export interface EditorCompletionResult {
125
+ readonly from: number;
126
+ readonly to?: number;
127
+ readonly options: readonly EditorCompletion[];
128
+ /** False when the list is already filtered, which this adapter's is. */
129
+ readonly filter?: boolean;
130
+ }
131
+
132
+ /** One squiggle in the editor's lint panel and gutter. */
133
+ export interface EditorDiagnostic {
134
+ readonly from: number;
135
+ readonly to: number;
136
+ readonly severity: 'hint' | 'info' | 'warning' | 'error';
137
+ readonly message: string;
138
+ /** Where the diagnostic came from, shown beside the message. */
139
+ readonly source?: string;
140
+ readonly actions?: readonly {
141
+ readonly name: string;
142
+ apply(view: EditorView, from: number, to: number): void;
143
+ }[];
144
+ }
145
+
146
+ /** What the editor shows over the text, and the element it shows. */
147
+ export interface EditorTooltipView {
148
+ readonly dom: unknown;
149
+ }
150
+
151
+ export interface EditorTooltip {
152
+ readonly pos: number;
153
+ readonly end?: number;
154
+ readonly above?: boolean;
155
+ create(view: EditorView): EditorTooltipView;
156
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The editor adapter: the language intelligence, wired into a text component.
3
+ *
4
+ * This and the chart adapter are the only modules in the repository allowed to
5
+ * know two worlds at once, and it is the piece a platform with its own editor
6
+ * replaces rather than the piece it patches. Everything under `src/editor` stays
7
+ * ignorant of any component, and the layering check is what keeps it that way.
8
+ *
9
+ * **The editor component is a peer dependency, and nothing here imports it.** A
10
+ * package that pulled a text component into everyone's install would defeat the
11
+ * point of a language a platform can take on its own, so the shapes this
12
+ * produces are declared in `contract.ts` and the host's own build is where the
13
+ * two are compared, in one line per piece. See that file for the lines. The
14
+ * entry point resolves and every function here runs in an install that holds no
15
+ * editor at all, which `scripts/check-entry-points.mjs` proves on every build.
16
+ *
17
+ * Six functions in, five pieces out:
18
+ *
19
+ * openscriptStream highlighting, a line at a time
20
+ * openscriptCompletion the completion list
21
+ * openscriptLint errors as you type, with their fixes
22
+ * openscriptHoverTooltip what the thing under the pointer is
23
+ * openscriptSignatureTooltip the call being written
24
+ * formatDocument lay the script out again
25
+ *
26
+ * The two tooltips take the markup as a parameter and have no default for it, so
27
+ * nothing in this package names a browser global at any tier. A host that wants
28
+ * none of this calls the six functions itself and draws its own. That is not the fallback, it is the supported path: what a host gives up
29
+ * by taking these instead is recorded in `spec/editor-narrowings.json`, which is
30
+ * three lines long and worth reading before choosing.
31
+ */
32
+ export type {
33
+ EditorCompletion,
34
+ EditorCompletionContext,
35
+ EditorCompletionResult,
36
+ EditorDiagnostic,
37
+ EditorState,
38
+ EditorStreamParser,
39
+ EditorStringStream,
40
+ EditorText,
41
+ EditorTooltip,
42
+ EditorTooltipView,
43
+ EditorView,
44
+ } from './contract.js';
45
+
46
+ export { COMPLETION_TYPES, HIGHLIGHT_TOKENS } from './tokens.js';
47
+
48
+ export type { StreamState } from './stream.js';
49
+ export { openscriptStream } from './stream.js';
50
+
51
+ export { openscriptCompletion } from './completion.js';
52
+
53
+ export { diagnosticsFor, openscriptLint } from './lint.js';
54
+
55
+ export type { Renderer } from './tooltips.js';
56
+ export {
57
+ hoverLines,
58
+ openscriptHoverTooltip,
59
+ openscriptSignatureTooltip,
60
+ signatureLines,
61
+ } from './tooltips.js';
62
+
63
+ export { formatDocument } from './commands.js';
64
+
65
+ export type { Document } from './positions.js';
66
+ export { documentOf, normalisedOffset } from './positions.js';