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,313 @@
1
+ /**
2
+ * What a trade is, which is the definition every statistic is counted over.
3
+ *
4
+ * **A trade is one position reference, from the fill that first takes it away
5
+ * from zero to the fill that returns it to zero.** Nothing else is invented,
6
+ * because the engine already mints a reference per position, no order crosses
7
+ * zero, and every fill names the reference it settled however late it arrives.
8
+ *
9
+ * Everything awkward falls out of that rather than needing a rule of its own. A
10
+ * pyramided entry is more entry fills on one trade. A partial close is an exit
11
+ * fill that does not close the trade. A flip is two references and therefore
12
+ * two trades, which is what a reversing order already sends. A reference still
13
+ * holding something at the last bar is an open trade: it is in the list with
14
+ * `isOpen` true, it is counted in equity, and it is counted in no win rate.
15
+ *
16
+ * The definition is written in the specification before it is written here,
17
+ * because a rule decided in code is a rule two engines decide differently.
18
+ *
19
+ * ## What the fold reads, and what it refuses to work out for itself
20
+ *
21
+ * A fill carries the signed size of its reference either side of the
22
+ * settlement, so the fold reads what the position book did rather than
23
+ * recomputing it from quantities and sides. That is the whole of how a partial
24
+ * close, a pyramided entry and a reversal tell themselves apart: the move from
25
+ * one size to the other says how much of the fill closed what was held and how
26
+ * much of it opened something, and nothing else has to be inferred.
27
+ *
28
+ * It also settles the one case the definition does not cover on its face. A
29
+ * destination that fills more than the order asked takes a reference through
30
+ * zero rather than to it, and the position book reads the remainder as a
31
+ * position in the other direction opened at that fill's price. So the fold does
32
+ * the same: the fill closes the trade that was held and opens a second one on
33
+ * the same reference, at its own price and on its own bar. A trade that ran
34
+ * from a size to the other side of zero would report a quantity nothing was
35
+ * ever entered at.
36
+ *
37
+ * **Reducing a position does not move its average**, which is the position
38
+ * book's rule read here rather than a second one: a reducing fill adds to the
39
+ * exits and touches neither the entry quantity nor the entry cost. Every figure
40
+ * measured from the entry, the entry price a reader compares against and the
41
+ * excursion at each bar close, is measured from the price the units were
42
+ * actually bought at.
43
+ *
44
+ * **A charge lands whole on one trade and is never split.** It was rounded once
45
+ * for the fill that incurred it, and splitting it between the trade a crossing
46
+ * fill closed and the trade it opened would round it again and put a residue
47
+ * somewhere. So it is attributed to the trade the fill closed where it closed
48
+ * one, and to the trade it opened otherwise, which makes the charges of the
49
+ * trades add up to the charges of the fills exactly rather than nearly.
50
+ *
51
+ * **Gross profit is over the units that have left.** For a closed trade that is
52
+ * every unit it entered, which is the formula the report is specified with. For
53
+ * one still open it is what its exits have realised so far, which is a figure
54
+ * that is true rather than a zero standing in for money the run has already
55
+ * made.
56
+ */
57
+ import type { BarMark, Contract, Money, RecordedFill } from './shapes.js';
58
+
59
+ /** One round trip on one position reference. */
60
+ export interface Trade {
61
+ /** 1-based, in the order the trade opened. */
62
+ readonly index: number;
63
+ readonly positionRef: number;
64
+ readonly side: 'long' | 'short';
65
+ readonly openedOnBar: number;
66
+ readonly openedAt: number | null;
67
+ readonly closedOnBar: number | null;
68
+ readonly closedAt: number | null;
69
+ readonly barsHeld: number | null;
70
+ /** Total units entered. */
71
+ readonly units: number;
72
+ /** Quantity weighted over the entry fills. */
73
+ readonly entryPrice: number;
74
+ /** Quantity weighted over the exit fills. */
75
+ readonly exitPrice: number | null;
76
+ readonly entries: number;
77
+ readonly exits: number;
78
+ readonly grossProfit: Money;
79
+ readonly charges: Money;
80
+ readonly netProfit: Money;
81
+ /**
82
+ * Excursion at bar closes while open. Favourable is zero or better, adverse
83
+ * zero or worse, and both are zero for a trade no bar closed on.
84
+ */
85
+ readonly maxFavourable: Money;
86
+ readonly maxAdverse: Money;
87
+ readonly isOpen: boolean;
88
+ }
89
+
90
+ /**
91
+ * The round trips a run's fills make up, in the order they opened.
92
+ *
93
+ * `charges[index]` is the money `fills[index]` was charged, rounded once by
94
+ * whoever computed it, so the two travel as one thing and nothing here rounds
95
+ * anything a second time. A caller with no cost model supplies no charges at
96
+ * all and every trade's charges are zero.
97
+ *
98
+ * The fills are read in `seq` order whatever order they are handed in, because
99
+ * `seq` is the order the engine folded them and a report that depended on the
100
+ * order a caller happened to be holding them in would not be reproducible. The
101
+ * marks are read in bar order for the same reason, and a bar is marked after
102
+ * every fill up to it has been folded, because a fill happens during its bar
103
+ * and the close comes after.
104
+ */
105
+ export function tradesOf(
106
+ fills: readonly RecordedFill[],
107
+ charges: readonly Money[],
108
+ marks: readonly BarMark[],
109
+ contract: Contract,
110
+ ): readonly Trade[] {
111
+ const settled = inSeqOrder(fills, charges);
112
+ const built: Building[] = [];
113
+ const live: Building[] = [];
114
+
115
+ let next = 0;
116
+ for (const bar of inBarOrder(marks)) {
117
+ while (next < settled.length) {
118
+ const one = settled[next];
119
+ if (one === undefined || one.fill.barIndex > bar.barIndex) break;
120
+ fold(one, built, live);
121
+ next += 1;
122
+ }
123
+ // A bar with no close is not a price anything can be marked at. It marks
124
+ // nothing rather than marking zero, which would read as a total loss.
125
+ if (bar.close !== null) markTo(live, bar.close, contract.pointValue);
126
+ }
127
+ for (; next < settled.length; next += 1) {
128
+ const one = settled[next];
129
+ if (one !== undefined) fold(one, built, live);
130
+ }
131
+
132
+ return built.map((trade) => finish(trade, contract));
133
+ }
134
+
135
+ /** A fill and what it cost, kept together so neither can be reordered alone. */
136
+ interface Settled {
137
+ readonly fill: RecordedFill;
138
+ readonly charge: Money;
139
+ }
140
+
141
+ /** A trade while it is still being folded: the totals a round trip is made of. */
142
+ interface Building {
143
+ readonly index: number;
144
+ readonly positionRef: number;
145
+ readonly side: 'long' | 'short';
146
+ readonly openedOnBar: number;
147
+ readonly openedAt: number | null;
148
+ closedOnBar: number | null;
149
+ closedAt: number | null;
150
+ entryUnits: number;
151
+ entryCost: number;
152
+ exitUnits: number;
153
+ exitCost: number;
154
+ entries: number;
155
+ exits: number;
156
+ charges: Money;
157
+ /** Signed, and what the reference holds for this trade right now. */
158
+ size: number;
159
+ maxFavourable: Money;
160
+ maxAdverse: Money;
161
+ }
162
+
163
+ function inSeqOrder(
164
+ fills: readonly RecordedFill[],
165
+ charges: readonly Money[],
166
+ ): readonly Settled[] {
167
+ const paired = fills.map((fill, index) => ({ fill, charge: charges[index] ?? 0 }));
168
+ return paired.sort((a, b) => a.fill.seq - b.fill.seq);
169
+ }
170
+
171
+ function inBarOrder(marks: readonly BarMark[]): readonly BarMark[] {
172
+ return marks.slice().sort((a, b) => a.barIndex - b.barIndex);
173
+ }
174
+
175
+ /**
176
+ * How much of a move from one size to another closed what was held.
177
+ *
178
+ * Exported to the module and not through its door: `markers.ts` asks the same
179
+ * question of the same fills, and the rule for what a move closed and what it
180
+ * opened is one rule. Two folds may read a fill differently and produce a
181
+ * marker for an entry the trade list calls an exit, so they read it here.
182
+ *
183
+ * A move to the other side of zero closed all of it, which is the case a
184
+ * destination that overfilled produces and the one this has to get right.
185
+ */
186
+ export function closedBy(before: number, after: number): number {
187
+ if (before === 0) return 0;
188
+ const same = Math.sign(after) === Math.sign(before);
189
+ return same ? Math.max(0, Math.abs(before) - Math.abs(after)) : Math.abs(before);
190
+ }
191
+
192
+ /** And how much of it opened something, which is the rest of the same move. */
193
+ export function openedBy(before: number, after: number): number {
194
+ if (after === 0) return 0;
195
+ if (before === 0 || Math.sign(after) !== Math.sign(before)) return Math.abs(after);
196
+ return Math.max(0, Math.abs(after) - Math.abs(before));
197
+ }
198
+
199
+ /** One fill against the trades its reference holds. */
200
+ function fold(one: Settled, built: Building[], live: Building[]): void {
201
+ const fill = one.fill;
202
+ const closing = closedBy(fill.refSizeBefore, fill.refSizeAfter);
203
+ const opening = openedBy(fill.refSizeBefore, fill.refSizeAfter);
204
+ const at = live.findIndex((trade) => trade.positionRef === fill.positionRef);
205
+ const held = at < 0 ? null : (live[at] ?? null);
206
+ let paid = false;
207
+
208
+ if (held !== null && closing > 0) {
209
+ held.exitUnits += closing;
210
+ held.exitCost += closing * fill.price;
211
+ held.exits += 1;
212
+ held.charges += one.charge;
213
+ paid = true;
214
+ // What the reference left this trade holding: nothing at all when the fill
215
+ // carried it through zero, since the other side of zero is the next trade.
216
+ held.size = opening > 0 ? 0 : fill.refSizeAfter;
217
+ if (held.size === 0) {
218
+ held.closedOnBar = fill.barIndex;
219
+ held.closedAt = fill.barTime;
220
+ live.splice(at, 1);
221
+ }
222
+ }
223
+
224
+ if (opening > 0) {
225
+ // A fill that closed something opens a trade of its own rather than adding
226
+ // to the one it just finished.
227
+ const adding = closing > 0 ? null : held;
228
+ if (adding === null) {
229
+ const fresh = begin(fill, opening, built.length + 1);
230
+ built.push(fresh);
231
+ live.push(fresh);
232
+ if (!paid) fresh.charges += one.charge;
233
+ } else {
234
+ adding.entryUnits += opening;
235
+ adding.entryCost += opening * fill.price;
236
+ adding.entries += 1;
237
+ adding.size = fill.refSizeAfter;
238
+ if (!paid) adding.charges += one.charge;
239
+ }
240
+ return;
241
+ }
242
+
243
+ // A fill that moved nothing still cost something, and it cost it on account
244
+ // of the trade its reference is holding.
245
+ if (!paid && held !== null) held.charges += one.charge;
246
+ }
247
+
248
+ function begin(fill: RecordedFill, units: number, index: number): Building {
249
+ return {
250
+ index,
251
+ positionRef: fill.positionRef,
252
+ side: fill.refSizeAfter > 0 ? 'long' : 'short',
253
+ openedOnBar: fill.barIndex,
254
+ openedAt: fill.barTime,
255
+ closedOnBar: null,
256
+ closedAt: null,
257
+ entryUnits: units,
258
+ entryCost: units * fill.price,
259
+ exitUnits: 0,
260
+ exitCost: 0,
261
+ entries: 1,
262
+ exits: 0,
263
+ charges: 0,
264
+ size: fill.refSizeAfter,
265
+ maxFavourable: 0,
266
+ maxAdverse: 0,
267
+ };
268
+ }
269
+
270
+ /** Every open trade against one bar's close. */
271
+ function markTo(live: readonly Building[], close: number, pointValue: number): void {
272
+ for (const trade of live) {
273
+ const entry = averageOf(trade.entryCost, trade.entryUnits);
274
+ const excursion = (close - entry) * trade.size * pointValue;
275
+ if (excursion > trade.maxFavourable) trade.maxFavourable = excursion;
276
+ if (excursion < trade.maxAdverse) trade.maxAdverse = excursion;
277
+ }
278
+ }
279
+
280
+ function averageOf(cost: number, units: number): number {
281
+ return units === 0 ? 0 : cost / units;
282
+ }
283
+
284
+ function finish(trade: Building, contract: Contract): Trade {
285
+ const entryPrice = averageOf(trade.entryCost, trade.entryUnits);
286
+ const exitPrice = trade.exitUnits === 0 ? null : trade.exitCost / trade.exitUnits;
287
+ const way = trade.side === 'long' ? 1 : -1;
288
+ const gross =
289
+ exitPrice === null
290
+ ? 0
291
+ : (exitPrice - entryPrice) * trade.exitUnits * contract.pointValue * way;
292
+ return {
293
+ index: trade.index,
294
+ positionRef: trade.positionRef,
295
+ side: trade.side,
296
+ openedOnBar: trade.openedOnBar,
297
+ openedAt: trade.openedAt,
298
+ closedOnBar: trade.closedOnBar,
299
+ closedAt: trade.closedAt,
300
+ barsHeld: trade.closedOnBar === null ? null : trade.closedOnBar - trade.openedOnBar,
301
+ units: trade.entryUnits,
302
+ entryPrice,
303
+ exitPrice,
304
+ entries: trade.entries,
305
+ exits: trade.exits,
306
+ grossProfit: gross,
307
+ charges: trade.charges,
308
+ netProfit: gross - trade.charges,
309
+ maxFavourable: trade.maxFavourable,
310
+ maxAdverse: trade.maxAdverse,
311
+ isOpen: trade.closedOnBar === null,
312
+ };
313
+ }