dexterm-cli 0.1.6 → 0.2.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 (395) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/spectator.js +3 -1
  3. package/dist/packages/agents/src/story.js +11 -2
  4. package/dist/packages/agents/src/tiers.js +36 -2
  5. package/dist/packages/agents/src/tools.js +35 -2
  6. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  7. package/dist/packages/autotrade/assets/limits.schema.json +569 -0
  8. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  9. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  10. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  11. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  12. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  13. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  14. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  15. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  16. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  17. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  18. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  19. package/dist/packages/autotrade/examples/executor/README.md +70 -0
  20. package/dist/packages/autotrade/examples/executor/jsonl-tail-executor.mjs +57 -0
  21. package/dist/packages/autotrade/examples/executor/kit.mjs +144 -0
  22. package/dist/packages/autotrade/examples/executor/signal-cmd-executor.mjs +25 -0
  23. package/dist/packages/autotrade/examples/executor/telegram-bot-recipe.md +47 -0
  24. package/dist/packages/autotrade/examples/executor/webhook-executor.mjs +47 -0
  25. package/dist/packages/autotrade/src/account.js +74 -0
  26. package/dist/packages/autotrade/src/backtest.js +358 -0
  27. package/dist/packages/autotrade/src/basis-text.js +32 -0
  28. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  29. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  30. package/dist/packages/autotrade/src/chat/config.js +95 -0
  31. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  32. package/dist/packages/autotrade/src/chat/control.js +104 -0
  33. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  34. package/dist/packages/autotrade/src/chat/format.js +71 -0
  35. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  36. package/dist/packages/autotrade/src/chat/index.js +18 -0
  37. package/dist/packages/autotrade/src/chat/notify.js +302 -0
  38. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  39. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  40. package/dist/packages/autotrade/src/chat/secrets.js +106 -0
  41. package/dist/packages/autotrade/src/chat/service.js +251 -0
  42. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  43. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  44. package/dist/packages/autotrade/src/engine.js +401 -0
  45. package/dist/packages/autotrade/src/execution-port.js +97 -0
  46. package/dist/packages/autotrade/src/executor-health.js +217 -0
  47. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  48. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  49. package/dist/packages/autotrade/src/fleet.js +642 -0
  50. package/dist/packages/autotrade/src/forward.js +355 -0
  51. package/dist/packages/autotrade/src/index.js +72 -0
  52. package/dist/packages/autotrade/src/journey.js +477 -0
  53. package/dist/packages/autotrade/src/ledger.js +108 -0
  54. package/dist/packages/autotrade/src/limits.js +682 -0
  55. package/dist/packages/autotrade/src/live-source.js +182 -0
  56. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  57. package/dist/packages/autotrade/src/monitor.js +190 -0
  58. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  59. package/dist/packages/autotrade/src/outbox.js +394 -0
  60. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  61. package/dist/packages/autotrade/src/paper-limits.js +87 -0
  62. package/dist/packages/autotrade/src/real-mode.js +688 -0
  63. package/dist/packages/autotrade/src/receipts.js +330 -0
  64. package/dist/packages/autotrade/src/replay.js +198 -0
  65. package/dist/packages/autotrade/src/report.js +168 -0
  66. package/dist/packages/autotrade/src/runner-config.js +99 -0
  67. package/dist/packages/autotrade/src/runner.js +923 -0
  68. package/dist/packages/autotrade/src/safety.js +243 -0
  69. package/dist/packages/autotrade/src/service-guard.js +125 -0
  70. package/dist/packages/autotrade/src/service-install.js +213 -0
  71. package/dist/packages/autotrade/src/service-recover.js +232 -0
  72. package/dist/packages/autotrade/src/service.js +289 -0
  73. package/dist/packages/autotrade/src/session.js +269 -0
  74. package/dist/packages/autotrade/src/sessions.js +503 -0
  75. package/dist/packages/autotrade/src/signal-channels.js +312 -0
  76. package/dist/packages/autotrade/src/signal.js +196 -0
  77. package/dist/packages/autotrade/src/store.js +250 -0
  78. package/dist/packages/autotrade/src/strategy.js +258 -0
  79. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  80. package/dist/packages/autotrade/src/yolo.js +140 -0
  81. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  82. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  83. package/dist/packages/cli/src/ai-cmd.js +412 -0
  84. package/dist/packages/cli/src/analyst/backend.js +83 -15
  85. package/dist/packages/cli/src/analyst/codex.js +15 -9
  86. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  87. package/dist/packages/cli/src/analyst/session.js +5 -2
  88. package/dist/packages/cli/src/app-help-gen.js +56 -8
  89. package/dist/packages/cli/src/autotrade-cmd/audit.js +74 -0
  90. package/dist/packages/cli/src/autotrade-cmd/backtest.js +216 -0
  91. package/dist/packages/cli/src/autotrade-cmd/executor.js +91 -0
  92. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  93. package/dist/packages/cli/src/autotrade-cmd/fleet.js +401 -0
  94. package/dist/packages/cli/src/autotrade-cmd/forward.js +283 -0
  95. package/dist/packages/cli/src/autotrade-cmd/journey.js +208 -0
  96. package/dist/packages/cli/src/autotrade-cmd/limits.js +393 -0
  97. package/dist/packages/cli/src/autotrade-cmd/receipt.js +63 -0
  98. package/dist/packages/cli/src/autotrade-cmd/report.js +79 -0
  99. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  100. package/dist/packages/cli/src/autotrade-cmd/run.js +657 -0
  101. package/dist/packages/cli/src/autotrade-cmd/service.js +873 -0
  102. package/dist/packages/cli/src/autotrade-cmd/signal.js +824 -0
  103. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  104. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  105. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  106. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  107. package/dist/packages/cli/src/autotrade-cmd.js +214 -0
  108. package/dist/packages/cli/src/display.js +156 -17
  109. package/dist/packages/cli/src/doctor.js +62 -3
  110. package/dist/packages/cli/src/export.js +10 -2
  111. package/dist/packages/cli/src/index.js +1 -1
  112. package/dist/packages/cli/src/ledger-cmd.js +290 -0
  113. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  114. package/dist/packages/cli/src/live/index.js +23 -89
  115. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  116. package/dist/packages/cli/src/live/trade.js +12 -457
  117. package/dist/packages/cli/src/live/watchlist.js +1 -1
  118. package/dist/packages/cli/src/main.js +147 -190
  119. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  120. package/dist/packages/cli/src/plan-cmd.js +1 -1
  121. package/dist/packages/cli/src/pnl-app.js +31 -21
  122. package/dist/packages/cli/src/pnl-card.js +162 -21
  123. package/dist/packages/cli/src/popup.js +22 -6
  124. package/dist/packages/cli/src/record-cmd.js +141 -20
  125. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  126. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  127. package/dist/packages/cli/src/screens/analyst.js +3 -2
  128. package/dist/packages/cli/src/screens/autotrade-journey.js +134 -0
  129. package/dist/packages/cli/src/screens/autotrade-keys.js +182 -0
  130. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  131. package/dist/packages/cli/src/screens/autotrade-screen.js +776 -0
  132. package/dist/packages/cli/src/screens/autotrade-ui.js +650 -0
  133. package/dist/packages/cli/src/screens/autotrade-worker.js +91 -0
  134. package/dist/packages/cli/src/screens/brain.js +37 -11
  135. package/dist/packages/cli/src/screens/overlay.js +5 -2
  136. package/dist/packages/cli/src/screens/plans.js +5 -17
  137. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  138. package/dist/packages/cli/src/screens/positions.js +6 -28
  139. package/dist/packages/cli/src/screens/ticket.js +38 -205
  140. package/dist/packages/cli/src/screens/token.js +3 -3
  141. package/dist/packages/cli/src/state.js +25 -19
  142. package/dist/packages/cli/src/telegram-cmd.js +454 -0
  143. package/dist/packages/cli/src/trade.js +3 -128
  144. package/dist/packages/cli/src/version.js +1 -1
  145. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  146. package/dist/packages/cli/src/wallet-command.js +8 -11
  147. package/dist/packages/cli/src/widgets.js +0 -12
  148. package/dist/packages/custom/src/config.js +20 -2
  149. package/dist/packages/custom/src/schema.js +35 -10
  150. package/dist/packages/custom/src/workspace.js +3 -3
  151. package/dist/packages/data/src/basis.js +191 -0
  152. package/dist/packages/data/src/detail.js +53 -2
  153. package/dist/packages/data/src/index.js +0 -14
  154. package/dist/packages/data/src/store.js +14 -5
  155. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  156. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  157. package/dist/packages/data/src/trade/bridge.js +4 -102
  158. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  159. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  160. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  161. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  162. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  163. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  164. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  165. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  166. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  167. package/dist/packages/data/src/trade/plans/index.js +0 -1
  168. package/dist/packages/data/src/trade/plans/service.js +3 -27
  169. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  170. package/dist/packages/mcp/assets/kb/app-help/autotrade-executor-kit.md +44 -0
  171. package/dist/packages/mcp/assets/kb/app-help/autotrade-journey.md +46 -0
  172. package/dist/packages/mcp/assets/kb/app-help/autotrade-limits.md +48 -0
  173. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  174. package/dist/packages/mcp/assets/kb/app-help/autotrade-real-signal.md +45 -0
  175. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  176. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  177. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  178. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  179. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  180. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  181. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  182. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  183. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  184. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  185. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  186. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +229 -0
  187. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  188. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  189. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  190. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  191. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  192. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  193. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  194. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  195. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  196. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  201. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +51 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +38 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-always-on-service.md +57 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +50 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  231. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  232. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-1-ask-for-a-strategy.md +27 -0
  233. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-2-backtest-it-held-out-split.md +31 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-3-forward-test-it-on-live-data.md +44 -0
  235. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-4-promote-it-to-always-on-paper.md +36 -0
  236. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-5-real-only-if-you-choose.md +36 -0
  237. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-ladder.md +39 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-report.md +31 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-what-the-analyst-knows.md +30 -0
  240. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat.md +24 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  242. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  243. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  244. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  247. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  249. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  251. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  254. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  255. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  256. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  257. package/dist/packages/mcp/assets/kb/docs/docs-going-real-executor-health.md +38 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-going-real-paper-vs-real.md +33 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-digest.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-executor-kit.md +48 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-whole-path.md +37 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-going-real-what-dexterm-does-and-never-does.md +44 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-going-real-your-limits.md +77 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-going-real.md +24 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-real-mode-your-strategy-s-trades-to-your.md +31 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +31 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  271. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  273. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  274. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  275. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  277. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  278. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  281. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  282. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  284. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  288. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  289. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  290. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  291. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  292. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  293. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  295. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  297. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  298. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  299. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-receipts-what-your-executor-did.md +42 -0
  302. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-safety.md +34 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-set-it-up.md +47 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-channels.md +42 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-signal.md +48 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-is.md +35 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-will-not-do.md +34 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-where-you-see-it.md +34 -0
  309. package/dist/packages/mcp/assets/kb/docs/docs-real-mode.md +27 -0
  310. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  311. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  312. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  313. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  314. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  315. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  316. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  317. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  318. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  319. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  320. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  321. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  322. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  323. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  324. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  325. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  326. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  327. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  328. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  329. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  330. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  331. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  332. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  333. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  334. package/dist/packages/mcp/assets/kb/entries/autotrade.md +40 -0
  335. package/dist/packages/mcp/src/app-help.js +1 -1
  336. package/dist/packages/mcp/src/autotrade-tools.js +1107 -0
  337. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  338. package/dist/packages/mcp/src/engine/config.js +159 -0
  339. package/dist/packages/mcp/src/engine/index.js +10 -0
  340. package/dist/packages/mcp/src/engine/loop.js +101 -0
  341. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  342. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  343. package/dist/packages/mcp/src/engine/probe.js +110 -0
  344. package/dist/packages/mcp/src/engine/reason.js +35 -0
  345. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  346. package/dist/packages/mcp/src/index.js +7 -1
  347. package/dist/packages/mcp/src/ledger-tools.js +494 -0
  348. package/dist/packages/mcp/src/paper-agent.js +2 -0
  349. package/dist/packages/mcp/src/server.js +138 -19
  350. package/dist/packages/packs/src/backtest.js +3 -1
  351. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  352. package/dist/packages/popup/src/pages/chart.css +9 -0
  353. package/dist/packages/popup/src/pages/chart.html +3 -1
  354. package/dist/packages/popup/src/pages/chart.js +104 -7
  355. package/dist/packages/popup/src/protocol.js +2 -0
  356. package/dist/packages/popup/src/server.js +4 -0
  357. package/dist/packages/recorder/src/index.js +2 -0
  358. package/dist/packages/recorder/src/recorder.js +63 -6
  359. package/dist/packages/recorder/src/retention.js +100 -0
  360. package/dist/packages/recorder/src/schema.js +34 -1
  361. package/dist/packages/recorder/src/ticks.js +237 -0
  362. package/dist/packages/tui/src/index.js +1 -0
  363. package/dist/packages/tui/src/keymap.js +28 -1
  364. package/dist/packages/tui/src/markers.js +219 -0
  365. package/dist/packages/tui/src/widgets.js +149 -2
  366. package/package.json +2 -1
  367. package/dist/packages/data/src/trade/caps.js +0 -323
  368. package/dist/packages/data/src/trade/executor.js +0 -933
  369. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  370. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  371. package/dist/packages/data/src/trade/signer/client.js +0 -197
  372. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  373. package/dist/packages/data/src/trade/signer/host.js +0 -97
  374. package/dist/packages/data/src/trade/signer/main.js +0 -121
  375. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  376. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  377. package/dist/packages/data/src/trade/signer/service.js +0 -400
  378. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  379. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  380. package/dist/packages/data/src/trade/swap-build.js +0 -108
  381. package/dist/packages/data/src/trade/token-delta.js +0 -201
  382. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  383. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  384. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  385. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  386. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  387. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  388. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  389. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  390. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  391. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  392. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  393. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  394. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  395. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -0,0 +1,355 @@
1
+ // dexterm autotrade — FORWARD TESTS (0.2.0 card C). PAPER ONLY.
2
+ //
3
+ // A forward test runs one strategy, frozen at the version it started with, on the LIVE tape, on paper, for a chosen
4
+ // window (hours, or N closed trades, whichever comes first), with its own bankroll (an isolated simulated book: no
5
+ // paper account, no other strategy's cash) and its own run file. It is started and stopped from the chat
6
+ // (strategy_forward_start / _stop), the Q screen's journey view and `dexterm autotrade forward start|status|stop|report`.
7
+ //
8
+ // It does not take the machine's run.lock: a forward test runs next to the always-on service or a foreground run, in
9
+ // its own process (`dexterm autotrade forward run <fid>`, started detached), with its own status file and stop marker:
10
+ //
11
+ // $DEXTERM_HOME/autotrade/forward/tests/<fid>.json dexterm-forward-test/1: the frozen strategy, the window, state,
12
+ // heartbeat, progress and, at the end, the metrics
13
+ // $DEXTERM_HOME/autotrade/forward/tests/<fid>.stop the stop marker (CLI, chat, Q screen); the host flattens and ends
14
+ // $DEXTERM_HOME/autotrade/forward/tests/<fid>.log the detached host's output
15
+ // $DEXTERM_HOME/autotrade/forward/<id>.json the latest result line for real mode's arm panel (card A)
16
+ // $DEXTERM_HOME/autotrade/runs/<runId>.jsonl the run ledger, hash-chained like every other run
17
+ //
18
+ // At the end of its window the host flattens (every position sold at its next tick, so the numbers are closed
19
+ // numbers), then writes the metrics in the backtest's own shape (backtest.ts report()) and records the result in
20
+ // the strategy's journey. Only a test that ran its whole window on live data is the "forward-tested" rung; one that
21
+ // was stopped early, ran on a replay or died is shown, never counted. The panic file stops it like any run.
22
+ //
23
+ // Model spend: a forward test of an agent strategy opens sessions on the user's own engine inside the strategy's
24
+ // budgets and runner.json's caps, like `run`. A strategy that needs a chat approval for every entry cannot be
25
+ // forward-tested (no tap reaches a forward test). Nothing here holds a key, signs or touches a wallet.
26
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
27
+ import { randomBytes } from 'node:crypto';
28
+ import { join } from 'node:path';
29
+ import { report } from "./backtest.js";
30
+ import { compareForward, renderComparison, latestBacktest, loadJourney, metricsLine, metricsOf, recordForwardResult, rulesHash, runFills, usd } from "./journey.js";
31
+ import { fileLedger, writeJsonAtomic } from "./ledger.js";
32
+ import { runAutotrade, simExecutor } from "./runner.js";
33
+ import { STRATEGY_ID } from "./store.js";
34
+ import { validateStrategy } from "./strategy.js";
35
+ export const FORWARD_SCHEMA = 'dexterm-forward-test/1';
36
+ export const FORWARD_LIMITS = { minHours: 0.25, maxHours: 72, maxTrades: 200, maxRunning: 3, minBankrollUsd: 10, maxBankrollUsd: 1_000_000 };
37
+ /** A running host rewrites its status at least this often; older means the host is gone. */
38
+ export const FORWARD_HEARTBEAT_MS = 10_000;
39
+ export const FORWARD_STALE_MS = 90_000;
40
+ /** After the window ends, how long (tape time) the flatten may keep trying before the stream is closed. */
41
+ export const FLATTEN_GRACE_MS = 30 * 60_000;
42
+ /** Live data: an ended test whose stream took no step for this long is closed (the report is written). */
43
+ export const STALL_MS = 2 * 60_000;
44
+ export const forwardRoot = (home) => join(home, 'autotrade', 'forward');
45
+ export const forwardTestsDir = (home) => join(forwardRoot(home), 'tests');
46
+ const FID = /^fw_[a-z0-9]{6,12}_[a-f0-9]{8}$/;
47
+ export const isForwardId = (x) => typeof x === 'string' && FID.test(x);
48
+ const fpath = (home, fid, ext) => {
49
+ if (!isForwardId(fid))
50
+ throw new Error(`bad forward test id ${JSON.stringify(fid)}`);
51
+ return join(forwardTestsDir(home), `${fid}.${ext}`);
52
+ };
53
+ export const forwardTestPath = (home, fid) => fpath(home, fid, 'json');
54
+ export const forwardStopPath = (home, fid) => fpath(home, fid, 'stop');
55
+ export const forwardLogPath = (home, fid) => fpath(home, fid, 'log');
56
+ /** Card A's arm panel reads `autotrade/forward/<id>.json` (summary line). */
57
+ export const forwardSummaryPath = (home, id) => {
58
+ if (!STRATEGY_ID.test(id))
59
+ throw new Error(`bad strategy id ${JSON.stringify(id)}`);
60
+ return join(forwardRoot(home), `${id}.json`);
61
+ };
62
+ export const newForwardId = (now = Date.now()) => `fw_${now.toString(36)}_${randomBytes(4).toString('hex')}`;
63
+ const pidAlive = (pid) => { try {
64
+ process.kill(pid, 0);
65
+ return true;
66
+ }
67
+ catch (e) {
68
+ return e.code === 'EPERM';
69
+ } };
70
+ export function liveState(t, now = Date.now(), alive = pidAlive) {
71
+ if (t.state !== 'starting' && t.state !== 'running')
72
+ return t.state;
73
+ if (t.state === 'starting')
74
+ return now - t.createdAt > FORWARD_STALE_MS && (!t.pid || !alive(t.pid)) ? 'lost' : 'starting';
75
+ if (t.pid && !alive(t.pid))
76
+ return 'lost';
77
+ return t.heartbeatAt && now - t.heartbeatAt > FORWARD_STALE_MS ? 'lost' : 'running';
78
+ }
79
+ export function loadForwardTest(home, fid) {
80
+ if (!isForwardId(fid))
81
+ return null;
82
+ const p = forwardTestPath(home, fid);
83
+ if (!existsSync(p))
84
+ return null;
85
+ try {
86
+ const d = JSON.parse(readFileSync(p, 'utf8'));
87
+ return d.schema === FORWARD_SCHEMA && d.forwardId === fid ? d : null;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ export function saveForwardTest(home, t) {
94
+ mkdirSync(forwardTestsDir(home), { recursive: true, mode: 0o700 });
95
+ writeJsonAtomic(forwardTestPath(home, t.forwardId), t);
96
+ }
97
+ /** Newest first. */
98
+ export function listForwardTests(home, filter = {}) {
99
+ const dir = forwardTestsDir(home);
100
+ if (!existsSync(dir))
101
+ return [];
102
+ return readdirSync(dir).filter((f) => f.endsWith('.json')).map((f) => loadForwardTest(home, f.slice(0, -5))).filter((t) => !!t && (!filter.strategyId || t.strategyId === filter.strategyId))
103
+ .sort((a, b) => b.createdAt - a.createdAt);
104
+ }
105
+ export const isActive = (t, now = Date.now()) => { const s = liveState(t, now); return s === 'starting' || s === 'running'; };
106
+ /** A forward test id, or a strategy id (its running test, else its newest). */
107
+ export function findForwardTest(home, ref, now = Date.now()) {
108
+ if (isForwardId(ref))
109
+ return loadForwardTest(home, ref);
110
+ if (!STRATEGY_ID.test(ref))
111
+ return null;
112
+ const all = listForwardTests(home, { strategyId: ref });
113
+ return all.find((t) => isActive(t, now)) ?? all[0] ?? null;
114
+ }
115
+ /**
116
+ * Freeze a strategy into a new forward test (state `starting`); the caller then starts its host. Refuses a window
117
+ * outside the limits, a second running test of the same strategy, more than FORWARD_LIMITS.maxRunning at once,
118
+ * mode live, and a strategy that needs a chat approval for every entry.
119
+ */
120
+ export function createForwardTest(home, s, raw, o) {
121
+ const now = o.now ?? Date.now();
122
+ if (s.mode === 'live')
123
+ return { ok: false, why: `${s.id} is mode live, which does not run (dexterm holds no key and places no live order)` };
124
+ if (s.entry.decide === 'agent+approve')
125
+ return { ok: false, why: `${s.id} needs a chat approval for every entry (entry.decide "agent+approve"); no approval reaches a forward test, so it could never enter. Make a copy with decide "agent" or "auto" to forward-test it` };
126
+ const hours = o.hours ?? null;
127
+ const trades = o.trades ?? null;
128
+ if (hours === null && trades === null)
129
+ return { ok: false, why: 'a forward test needs a window: hours, a number of closed trades, or both (whichever comes first ends it)' };
130
+ if (hours !== null && !(Number.isFinite(hours) && hours >= FORWARD_LIMITS.minHours && hours <= FORWARD_LIMITS.maxHours))
131
+ return { ok: false, why: `hours must be from ${FORWARD_LIMITS.minHours} to ${FORWARD_LIMITS.maxHours}` };
132
+ if (trades !== null && !(Number.isInteger(trades) && trades >= 1 && trades <= FORWARD_LIMITS.maxTrades))
133
+ return { ok: false, why: `trades must be a whole number from 1 to ${FORWARD_LIMITS.maxTrades}` };
134
+ const bankrollUsd = o.bankrollUsd ?? s.bankroll.startUsd;
135
+ if (!(Number.isFinite(bankrollUsd) && bankrollUsd >= FORWARD_LIMITS.minBankrollUsd && bankrollUsd <= FORWARD_LIMITS.maxBankrollUsd))
136
+ return { ok: false, why: `the paper bankroll must be from $${FORWARD_LIMITS.minBankrollUsd} to $${FORWARD_LIMITS.maxBankrollUsd}` };
137
+ const running = listForwardTests(home).filter((t) => isActive(t, now));
138
+ const same = running.find((t) => t.strategyId === s.id);
139
+ if (same)
140
+ return { ok: false, why: `${s.id} already has a forward test running (${same.forwardId}); stop it first or read its report` };
141
+ if (running.length >= FORWARD_LIMITS.maxRunning)
142
+ return { ok: false, why: `${running.length} forward tests are already running (at most ${FORWARD_LIMITS.maxRunning} at once)` };
143
+ // The frozen document: always paper (a real strategy forward-tests as its paper twin), the test's own bankroll.
144
+ const doc = { ...structuredClone(raw), mode: 'paper', bankroll: { ...raw.bankroll, startUsd: bankrollUsd } };
145
+ const v = validateStrategy(doc);
146
+ if (!v.ok)
147
+ return { ok: false, why: `${s.id} does not validate as a paper forward test: ${v.errors.map((e) => `${e.path} ${e.msg}`).join('; ')}` };
148
+ const test = {
149
+ schema: FORWARD_SCHEMA, forwardId: newForwardId(now), strategyId: s.id, version: s.version, hash: s.hash, rulesHash: rulesHash(s), doc, bankrollUsd,
150
+ window: { hours, trades }, createdAt: now, by: o.by, source: o.source ?? 'live', stub: !!o.stub, msPerTick: o.msPerTick ?? null,
151
+ state: 'starting', pid: null, runId: null, startedAt: null, endsAt: null, heartbeatAt: null, endedAt: null, endReason: null, progress: null, metrics: null, problems: [],
152
+ };
153
+ saveForwardTest(home, test);
154
+ return { ok: true, test };
155
+ }
156
+ /** Ask a forward test to end (the host flattens and writes its report). Idempotent. */
157
+ export function requestForwardStop(home, fid, by, now = Date.now()) {
158
+ const t = loadForwardTest(home, fid);
159
+ if (!t)
160
+ return { ok: false, why: `no forward test ${fid}` };
161
+ const st = liveState(t, now);
162
+ if (st !== 'starting' && st !== 'running')
163
+ return { ok: false, why: `${fid} is not running (${st})` };
164
+ const p = forwardStopPath(home, fid);
165
+ const already = existsSync(p);
166
+ if (!already)
167
+ writeFileSync(p, `${JSON.stringify({ at: now, by })}\n`, { mode: 0o600 });
168
+ return { ok: true, already, test: t };
169
+ }
170
+ export function forwardStopRequested(home, fid) {
171
+ const p = forwardStopPath(home, fid);
172
+ if (!existsSync(p))
173
+ return null;
174
+ try {
175
+ const d = JSON.parse(readFileSync(p, 'utf8'));
176
+ return { at: Number(d.at) || 0, by: typeof d.by === 'string' ? d.by.slice(0, 40) : 'unknown' };
177
+ }
178
+ catch {
179
+ return { at: 0, by: 'unknown' };
180
+ }
181
+ }
182
+ /** Mark a test whose host is gone as failed (a reader that finds it `lost` may call this). */
183
+ export function markLost(home, t, now = Date.now()) {
184
+ const next = { ...t, state: 'failed', endedAt: now, endReason: 'the forward test host stopped without a report (crash, logout or kill)' };
185
+ saveForwardTest(home, next);
186
+ recordForwardResult(home, resultOf(next));
187
+ return next;
188
+ }
189
+ function resultOf(t) {
190
+ const state = t.state === 'done' || t.state === 'stopped' ? t.state : 'failed';
191
+ return {
192
+ kind: 'forward', forwardId: t.forwardId, id: t.strategyId, version: t.version, rulesHash: t.rulesHash, startedAt: t.startedAt ?? t.createdAt, endedAt: t.endedAt ?? Date.now(),
193
+ // Only a test on live data can be the forward-tested rung: a replay is a backtest in disguise.
194
+ state: state === 'done' && !t.source.startsWith('live') ? 'stopped' : state,
195
+ endReason: state === 'done' && !t.source.startsWith('live') ? `${t.endReason ?? 'window reached'} (on a replay, which does not count as a forward test)` : (t.endReason ?? ''),
196
+ window: t.window, source: t.source, metrics: t.metrics,
197
+ };
198
+ }
199
+ const dur = (ms) => { const m = Math.max(0, Math.round(ms / 60_000)); return m < 60 ? `${m} min` : `${Math.floor(m / 60)} h ${m % 60} min`; };
200
+ export const windowText = (w) => [w.hours !== null ? `${w.hours} h` : '', w.trades !== null ? `${w.trades} closed trades` : ''].filter(Boolean).join(' or ') + (w.hours !== null && w.trades !== null ? ' (whichever first)' : '');
201
+ /** The forward test next to the backtest (held-out split) of the same rules, from the journey file. */
202
+ export function forwardReport(home, t, now = Date.now()) {
203
+ const state = liveState(t, now);
204
+ const bt = latestBacktest(loadJourney(home, t.strategyId), t.rulesHash);
205
+ const comparison = compareForward(bt, t.metrics);
206
+ const elapsed = t.startedAt ? dur((t.endedAt ?? now) - t.startedAt) : '0 min';
207
+ const lines = [
208
+ `FORWARD TEST ${t.forwardId} · ${t.strategyId} v${t.version} · PAPER · ${t.source} · own bankroll $${t.bankrollUsd}`,
209
+ ` state ${state.toUpperCase()}${t.endReason ? ` (${t.endReason})` : ''} · window ${windowText(t.window)} · ran ${elapsed}`,
210
+ t.progress && !t.metrics ? ` so far: ${t.progress.closed} closed · ${t.progress.open} open · equity $${t.progress.equityUsd.toFixed(2)} (${usd(t.progress.netUsd)}) · ${t.progress.candidates} candidates` : '',
211
+ t.metrics ? ` result: ${metricsLine(t.metrics)}` : '',
212
+ ...(t.metrics || bt ? [' forward vs backtest of the same rules (paper, modelled fills; not a prediction, not financial advice):', ...renderComparison(comparison)] : []),
213
+ t.source.startsWith('live') ? '' : ' NOTE: this ran on a replay, so it is not a forward test of live data and does not count toward the journey',
214
+ ].filter(Boolean);
215
+ return { forwardId: t.forwardId, strategyId: t.strategyId, version: t.version, state, source: t.source, window: windowText(t.window), elapsed, endReason: t.endReason, progress: t.progress, metrics: t.metrics, comparison, lines };
216
+ }
217
+ /**
218
+ * Run a `starting` forward test to its end in this process: own ledger, own book (the frozen paper strategy and its
219
+ * bankroll), the window, the stop marker and the panic file; then the metrics, the journey record and the summary.
220
+ */
221
+ export async function hostForward(o) {
222
+ const { home, forwardId } = o;
223
+ const clock = o.now ?? Date.now;
224
+ const say = o.onLine ?? (() => { });
225
+ let t = loadForwardTest(home, forwardId);
226
+ if (!t)
227
+ return { ok: false, why: `no forward test ${forwardId}` };
228
+ if (!o.source === !o.tape)
229
+ return { ok: false, why: 'a forward test runs on one live source or one replay tape' };
230
+ const srcName = o.source?.name ?? `replay ${o.tape.source}`;
231
+ if (t.state !== 'starting')
232
+ return { ok: false, why: `${forwardId} is ${t.state}; a forward test runs once` };
233
+ const v = validateStrategy(t.doc);
234
+ if (!v.ok || v.strategy.mode !== 'paper')
235
+ return { ok: false, why: `${forwardId}: the frozen strategy does not validate as paper` };
236
+ const strategy = v.strategy;
237
+ const runId = `${new Date(clock()).toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '')}-fwd-${strategy.id}-${forwardId.slice(-4)}`;
238
+ let closed = 0;
239
+ const { ledger, files } = fileLedger(home, runId, (e) => { if (e.kind === 'trade_closed')
240
+ closed++; });
241
+ const startedAt = clock();
242
+ t = { ...t, state: 'running', pid: o.pid ?? process.pid, runId, startedAt, heartbeatAt: startedAt };
243
+ saveForwardTest(home, t);
244
+ ledger.add(startedAt, 'forward_start', { forwardId, strategy: strategy.id, version: t.version, rulesHash: t.rulesHash, window: t.window, bankrollUsd: t.bankrollUsd, source: srcName, by: t.by });
245
+ say(`forward test ${forwardId} · ${strategy.id} v${t.version} · PAPER · ${srcName} · own bankroll $${t.bankrollUsd} · window ${windowText(t.window)} · run ${runId}`);
246
+ // The window clock: tape time on a tape or a lockstep feed; WALL time on live data, where a cached row keeps the
247
+ // last tick's time and the tape clock can stall (an hours window measured on it might never close).
248
+ const wall = !o.tape && (o.pacing?.mode ?? 'live') === 'live';
249
+ let tapeT = null;
250
+ let endsAt = wall && t.window.hours !== null ? startedAt + t.window.hours * 3_600_000 : null;
251
+ let ended = null;
252
+ let endedT = null;
253
+ let lastStepAt = startedAt;
254
+ let last = null;
255
+ let lastBeat = 0;
256
+ const windowNow = () => (wall ? clock() : tapeT);
257
+ /** Panic, the stop marker and the window; checked on every step and on every heartbeat (a stream can go quiet). */
258
+ const checkEnd = (panic) => {
259
+ if (panic && !ended)
260
+ ended = { state: 'stopped', reason: `the panic switch is engaged (${panic.reason})` };
261
+ const stop = forwardStopRequested(home, forwardId);
262
+ if (stop && !ended)
263
+ ended = { state: 'stopped', reason: `stopped early by ${stop.by}` };
264
+ const nowT = windowNow();
265
+ if (!ended && nowT !== null && endsAt !== null && nowT >= endsAt)
266
+ ended = { state: 'done', reason: `window reached (${t.window.hours} h)` };
267
+ if (!ended && t.window.trades !== null && closed >= t.window.trades)
268
+ ended = { state: 'done', reason: `window reached (${closed} closed trades)` };
269
+ if (ended && endedT === null)
270
+ endedT = nowT ?? clock();
271
+ };
272
+ const beat = (force = false) => {
273
+ const n = clock();
274
+ if (!force && n - lastBeat < FORWARD_HEARTBEAT_MS / 2)
275
+ return;
276
+ lastBeat = n;
277
+ const cur = loadForwardTest(home, forwardId) ?? t;
278
+ t = { ...cur, heartbeatAt: n, endsAt, progress: last ? { closed, open: last.positions.length, equityUsd: last.book.equityUsd, netUsd: last.book.equityUsd - last.book.startUsd, candidates: last.candidates.length } : cur.progress };
279
+ try {
280
+ saveForwardTest(home, t);
281
+ }
282
+ catch { /* the next beat retries */ }
283
+ };
284
+ const timer = setInterval(() => {
285
+ beat(true);
286
+ if (!wall)
287
+ return;
288
+ checkEnd(o.killSwitch?.engaged() ?? null);
289
+ // Ended, but no step for a while (a quiet stream): close the stream so the run ends and the report is written.
290
+ if (ended && clock() - lastStepAt > STALL_MS)
291
+ o.source?.stop?.();
292
+ }, FORWARD_HEARTBEAT_MS);
293
+ timer.unref?.();
294
+ const safety = () => {
295
+ const panic = o.killSwitch?.engaged() ?? null;
296
+ checkEnd(panic);
297
+ // The window end flattens at the next tick; a bag that cannot sell (no liquidity) ends the stream after a grace.
298
+ const nowT = windowNow();
299
+ if (ended && endedT !== null && nowT !== null && nowT - endedT > FLATTEN_GRACE_MS)
300
+ o.source?.stop?.();
301
+ if (ended) {
302
+ return { halt: ended.state === 'done' ? 'forward-window' : panic ? 'kill-switch' : 'forward-stopped', detail: ended.reason, flatten: true, pauseEntries: null, endWhenFlat: true };
303
+ }
304
+ return { halt: null, flatten: false, pauseEntries: null, endWhenFlat: false };
305
+ };
306
+ let res = null;
307
+ let error = null;
308
+ try {
309
+ res = await runAutotrade({
310
+ strategy, ...(o.source ? { source: o.source } : { tape: o.tape }), sessions: o.sessions, gate: o.gate, dossier: o.dossier, vet: o.vet, executor: simExecutor(strategy.execution),
311
+ pacing: o.pacing ?? { mode: 'live' }, ledger, runId, verbose: true, onLine: say, safety,
312
+ onState: (st) => {
313
+ if (!wall && tapeT === null && t.window.hours !== null)
314
+ endsAt = st.t + t.window.hours * 3_600_000;
315
+ tapeT = st.t;
316
+ lastStepAt = clock();
317
+ last = st;
318
+ beat();
319
+ },
320
+ });
321
+ }
322
+ catch (e) {
323
+ error = e.message.slice(0, 300);
324
+ }
325
+ clearInterval(timer);
326
+ const endedAt = clock();
327
+ const cur = loadForwardTest(home, forwardId) ?? t;
328
+ let metrics = null;
329
+ if (res) {
330
+ const r = report(strategy, { source: srcName, ticks: [], mints: [] }, res);
331
+ metrics = metricsOf(r, runFills(res));
332
+ }
333
+ // A live stream that ends by itself (the data layer stopped) before the window is an early end, never "done".
334
+ const fin = error ? { state: 'failed', reason: `the run failed: ${error}` }
335
+ : ended ? ended : { state: 'stopped', reason: 'the tick stream ended before the window' };
336
+ t = { ...cur, state: fin.state, endReason: fin.reason, endedAt, endsAt, heartbeatAt: endedAt, metrics, progress: last ? { closed: metrics?.trades ?? closed, open: res?.openAtEnd.length ?? 0, equityUsd: last.book.equityUsd, netUsd: metrics?.netUsd ?? 0, candidates: res?.candidates ?? 0 } : cur.progress };
337
+ ledger.add(endedAt, 'forward_end', { forwardId, state: fin.state, reason: fin.reason, ...(metrics ? { trades: metrics.trades, netUsd: metrics.netUsd } : {}) });
338
+ saveForwardTest(home, t);
339
+ rmSync(forwardStopPath(home, forwardId), { force: true });
340
+ recordForwardResult(home, resultOf(t));
341
+ writeForwardSummary(home, t);
342
+ say(`forward test ${forwardId} ${fin.state}: ${fin.reason}${metrics ? ` · ${metricsLine(metrics)}` : ''} · ledger ${files.ledger}`);
343
+ return { ok: true, test: t };
344
+ }
345
+ /** The one-line result real mode's arm panel shows (card A reads `summary`). */
346
+ export function writeForwardSummary(home, t) {
347
+ if (!t.metrics)
348
+ return;
349
+ const days = t.startedAt && t.endedAt ? Math.round(((t.endedAt - t.startedAt) / 86_400_000) * 100) / 100 : null;
350
+ const counted = t.state === 'done' && t.source.startsWith('live');
351
+ // ASCII only: real mode's arm panel prints this line through a printable-ASCII filter.
352
+ const summary = `${t.forwardId} ${t.state}${counted ? '' : ' (does not count)'}; v${t.version}; ${t.metrics.trades} trades; net ${usd(t.metrics.netUsd)} (${t.metrics.netPct}%); ${t.source.startsWith('live') ? 'paper, live data' : 'paper, replay'}${t.metrics.trades < 20 ? '; too few trades to claim anything' : ''}`;
353
+ mkdirSync(forwardRoot(home), { recursive: true, mode: 0o700 });
354
+ writeJsonAtomic(forwardSummaryPath(home, t.strategyId), { schema: 'dexterm-forward-summary/1', forwardId: t.forwardId, version: t.version, rulesHash: t.rulesHash, state: t.state, counted, summary, trades: t.metrics.trades, netPct: t.metrics.netPct, days });
355
+ }
@@ -0,0 +1,72 @@
1
+ // @dexterm/autotrade — the autotrade engine (AUTOTRADE-SPEC, task-1503 AT-1). PAPER ONLY.
2
+ //
3
+ // strategy the dexterm-strategy/1 document, exit profiles 1.0.0..1.2.0, the validator, presets
4
+ // engine entry rules on the screener grammar, sizing, the clamp, the constant-product fill model
5
+ // monitor mechanical exits + wake events (no LLM)
6
+ // replay the recorded tape (MarketTick) read from the recorder DB or a committed fixture
7
+ // session the decision protocol, dossier/delta text, the stub session (types for the runner card)
8
+ // runner the run loop (lockstep for backtests, paced for replays, live for `autotrade run`), lock + rendezvous
9
+ // ledger the hash-chained run ledger, runs/<runId>.jsonl, chain verification (task-1505)
10
+ // sessions the user's own `claude -p` sessions, day caps + allowance guard, the mcp dossier (task-1505)
11
+ // live-source Store-fed MarketTicks: the screener pool every 10 s, candidate/held coins every 5 s (task-1505)
12
+ // runner-config / account runner.json (dexterm-runner/1) and the four Claude account kinds (task-1505)
13
+ // backtest the report, every-candidate mode, runner recall, held-out split + sweeps
14
+ // store ~/.dexterm/strategies/<id>.json CRUD + the append-only .journal.jsonl
15
+ // desk-executor orders through the app's paper desk (armed paper-only mandate, exit plans, quote-sized cost recovery)
16
+ // monitor-daemon the position monitor process: §5 exits on the desk, one wake message per tick, restart recovery
17
+ // safety stop / flatten / pause (the control file), the kill switch + dead-man every step, the run audit, session isolation (task-1509)
18
+ // fleet parallel strategies: N strategies on one feed, each with its own book, ledger, caps and budget, under global caps (task-1529)
19
+ // fleet-config / fleet-control autotrade.json (dexterm-autotrade/1), per-strategy control files, the fleet status + comparison table
20
+ // paper-accounts shared paper accounts: many strategies trading from one simulated wallet, one strategy on many (task-1538)
21
+ // trade-ledger the run ledgers as unified trade ledger drafts (task-1530, read-only)
22
+ // execution-port where an approved entry goes: the paper port (local single-use proofs) and the live stub (task-1527)
23
+ // chat Telegram notify, control and approve-to-trade through the user's own bot; chat/ (task-1527)
24
+ // yolo task-1532: the full-port replay (rugs, loss spread, how often it zeroed the wallet); paper only
25
+ // signal / outbox / signal-channels / receipts / real-mode task-1645: mode real = the paper engine + a LIVE TRADE FIRE
26
+ // signal per fill to the user's own executor (webhook, command hook, JSONL, MCP feed, Telegram copy); no key
27
+ // limits / executor-health / report task-1654: every real-mode limit user-set per scope (stricter wins), executor
28
+ // heartbeats + the silence pause, the paper-vs-real divergence report and the digest
29
+ //
30
+ // The screener filter grammar is imported by path from the CLI's screener (one parser for the screen and
31
+ // for strategies); nothing in this package touches a key or a wallet, and nothing places a live order (task-1571). The desk executor
32
+ // trades only through the paper desk (a paper executor, by construction) on a keyless quote.
33
+ export * from "./strategy.js";
34
+ export * from "./engine.js";
35
+ export * from "./monitor.js";
36
+ export * from "./replay.js";
37
+ export * from "./session.js";
38
+ export * from "./runner.js";
39
+ export * from "./ledger.js";
40
+ export * from "./sessions.js";
41
+ export * from "./live-source.js";
42
+ export * from "./runner-config.js";
43
+ export * from "./basis-text.js";
44
+ export * from "./account.js";
45
+ export * from "./backtest.js";
46
+ export * from "./store.js";
47
+ export * from "./desk-executor.js";
48
+ export * from "./monitor-daemon.js";
49
+ export * from "./safety.js";
50
+ export * from "./fleet-config.js";
51
+ export * from "./fleet-control.js";
52
+ export * from "./fleet.js";
53
+ export * from "./paper-accounts.js";
54
+ export * from "./trade-ledger.js";
55
+ export * from "./execution-port.js";
56
+ export * from "./chat/index.js";
57
+ export * from "./yolo.js";
58
+ export * from "./signal.js";
59
+ export * from "./outbox.js";
60
+ export * from "./signal-channels.js";
61
+ export * from "./receipts.js";
62
+ export * from "./real-mode.js";
63
+ export * from "./journey.js";
64
+ export * from "./forward.js";
65
+ export * from "./service.js";
66
+ export * from "./service-guard.js";
67
+ export * from "./service-recover.js";
68
+ export * from "./service-install.js";
69
+ export * from "./limits.js";
70
+ export * from "./paper-limits.js";
71
+ export * from "./executor-health.js";
72
+ export * from "./report.js";