dexterm-cli 0.1.5 → 0.1.7

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 (379) hide show
  1. package/dist/packages/agents/src/index.js +2 -0
  2. package/dist/packages/agents/src/schema.js +22 -1
  3. package/dist/packages/agents/src/spectator.js +54 -0
  4. package/dist/packages/agents/src/story.js +532 -0
  5. package/dist/packages/agents/src/tiers.js +28 -2
  6. package/dist/packages/agents/src/tools.js +31 -2
  7. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -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/src/account.js +74 -0
  20. package/dist/packages/autotrade/src/backtest.js +357 -0
  21. package/dist/packages/autotrade/src/basis-text.js +32 -0
  22. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  23. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  24. package/dist/packages/autotrade/src/chat/config.js +93 -0
  25. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  26. package/dist/packages/autotrade/src/chat/control.js +103 -0
  27. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  28. package/dist/packages/autotrade/src/chat/format.js +71 -0
  29. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  30. package/dist/packages/autotrade/src/chat/index.js +18 -0
  31. package/dist/packages/autotrade/src/chat/notify.js +292 -0
  32. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  33. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  34. package/dist/packages/autotrade/src/chat/secrets.js +99 -0
  35. package/dist/packages/autotrade/src/chat/service.js +248 -0
  36. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  37. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  38. package/dist/packages/autotrade/src/engine.js +387 -0
  39. package/dist/packages/autotrade/src/execution-port.js +97 -0
  40. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  41. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  42. package/dist/packages/autotrade/src/fleet.js +639 -0
  43. package/dist/packages/autotrade/src/index.js +53 -0
  44. package/dist/packages/autotrade/src/ledger.js +108 -0
  45. package/dist/packages/autotrade/src/live-source.js +182 -0
  46. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  47. package/dist/packages/autotrade/src/monitor.js +190 -0
  48. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  49. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  50. package/dist/packages/autotrade/src/replay.js +198 -0
  51. package/dist/packages/autotrade/src/runner-config.js +99 -0
  52. package/dist/packages/autotrade/src/runner.js +872 -0
  53. package/dist/packages/autotrade/src/safety.js +243 -0
  54. package/dist/packages/autotrade/src/session.js +269 -0
  55. package/dist/packages/autotrade/src/sessions.js +503 -0
  56. package/dist/packages/autotrade/src/store.js +250 -0
  57. package/dist/packages/autotrade/src/strategy.js +256 -0
  58. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  59. package/dist/packages/autotrade/src/yolo.js +140 -0
  60. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  61. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +1 -1
  62. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +15 -8
  63. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  64. package/dist/packages/cli/src/agent-cmd.js +6 -4
  65. package/dist/packages/cli/src/agent-spectator.js +17 -1
  66. package/dist/packages/cli/src/ai-cmd.js +412 -0
  67. package/dist/packages/cli/src/analyst/backend.js +105 -15
  68. package/dist/packages/cli/src/analyst/codex.js +14 -9
  69. package/dist/packages/cli/src/analyst/larp-gauge.js +60 -0
  70. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  71. package/dist/packages/cli/src/analyst/session.js +17 -3
  72. package/dist/packages/cli/src/app-help-gen.js +51 -15
  73. package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
  74. package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
  75. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  76. package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
  77. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  78. package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
  79. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  80. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  81. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  82. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  83. package/dist/packages/cli/src/autotrade-cmd.js +168 -0
  84. package/dist/packages/cli/src/backtest-cmd.js +13 -2
  85. package/dist/packages/cli/src/display.js +208 -23
  86. package/dist/packages/cli/src/doctor.js +62 -3
  87. package/dist/packages/cli/src/export.js +10 -2
  88. package/dist/packages/cli/src/index.js +1 -1
  89. package/dist/packages/cli/src/ledger-cmd.js +288 -0
  90. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  91. package/dist/packages/cli/src/live/dev-history.js +31 -15
  92. package/dist/packages/cli/src/live/index.js +28 -90
  93. package/dist/packages/cli/src/live/screens/newpairs-live.js +1 -1
  94. package/dist/packages/cli/src/live/screens/token-live.js +48 -15
  95. package/dist/packages/cli/src/live/screens/watchlist-live.js +1 -1
  96. package/dist/packages/cli/src/live/trade.js +30 -453
  97. package/dist/packages/cli/src/live/watchlist.js +1 -1
  98. package/dist/packages/cli/src/main.js +290 -199
  99. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  100. package/dist/packages/cli/src/mcp-follow.js +103 -6
  101. package/dist/packages/cli/src/plan-cmd.js +15 -5
  102. package/dist/packages/cli/src/pnl-app.js +31 -21
  103. package/dist/packages/cli/src/pnl-card.js +158 -21
  104. package/dist/packages/cli/src/popup.js +38 -6
  105. package/dist/packages/cli/src/record-cmd.js +141 -20
  106. package/dist/packages/cli/src/scan/ui.js +7 -1
  107. package/dist/packages/cli/src/screens/agent-hud.js +773 -0
  108. package/dist/packages/cli/src/screens/analyst-session.js +11 -4
  109. package/dist/packages/cli/src/screens/analyst.js +3 -2
  110. package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
  111. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  112. package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
  113. package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
  114. package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
  115. package/dist/packages/cli/src/screens/brain.js +634 -0
  116. package/dist/packages/cli/src/screens/newpairs.js +1 -1
  117. package/dist/packages/cli/src/screens/overlay.js +5 -2
  118. package/dist/packages/cli/src/screens/plans.js +5 -17
  119. package/dist/packages/cli/src/screens/pnl-chip.js +144 -0
  120. package/dist/packages/cli/src/screens/positions.js +6 -28
  121. package/dist/packages/cli/src/screens/screener.js +1 -0
  122. package/dist/packages/cli/src/screens/ticket.js +38 -205
  123. package/dist/packages/cli/src/screens/token.js +18 -9
  124. package/dist/packages/cli/src/screens/trending.js +1 -1
  125. package/dist/packages/cli/src/state.js +88 -18
  126. package/dist/packages/cli/src/telegram-cmd.js +451 -0
  127. package/dist/packages/cli/src/trade.js +3 -128
  128. package/dist/packages/cli/src/version.js +1 -1
  129. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  130. package/dist/packages/cli/src/wallet-command.js +8 -11
  131. package/dist/packages/cli/src/widgets.js +0 -12
  132. package/dist/packages/custom/src/config.js +20 -2
  133. package/dist/packages/custom/src/schema.js +41 -10
  134. package/dist/packages/custom/src/workspace.js +3 -3
  135. package/dist/packages/data/src/basis.js +191 -0
  136. package/dist/packages/data/src/detail.js +53 -2
  137. package/dist/packages/data/src/index.js +2 -14
  138. package/dist/packages/data/src/larp/score.js +220 -0
  139. package/dist/packages/data/src/larp/x-account.js +316 -0
  140. package/dist/packages/data/src/store.js +14 -5
  141. package/dist/packages/data/src/trade/agent/paper-desk.js +300 -23
  142. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  143. package/dist/packages/data/src/trade/bridge.js +4 -102
  144. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  145. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  146. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  147. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  148. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  149. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  150. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  151. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  152. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  153. package/dist/packages/data/src/trade/plans/engine.js +7 -0
  154. package/dist/packages/data/src/trade/plans/index.js +0 -1
  155. package/dist/packages/data/src/trade/plans/mandate.js +28 -15
  156. package/dist/packages/data/src/trade/plans/service.js +4 -28
  157. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  158. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  159. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  160. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  161. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  162. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  163. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  164. package/dist/packages/mcp/assets/kb/app-help/dev-history.md +1 -1
  165. package/dist/packages/mcp/assets/kb/app-help/larp-check.md +5 -1
  166. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  167. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  168. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  169. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  170. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  171. package/dist/packages/mcp/assets/kb/app-help/scan-mode.md +1 -1
  172. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  173. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
  174. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  175. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  176. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  177. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  178. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +6 -0
  179. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  180. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  181. package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +2 -1
  182. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  183. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  184. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +7 -3
  186. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  188. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  189. package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +2 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
  194. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  195. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  198. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  200. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  201. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  219. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  220. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +32 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  223. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  225. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +9 -7
  226. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  227. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  228. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  229. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  230. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  233. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  234. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  235. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +1 -1
  236. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  237. package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +1 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-larp-score.md +60 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +0 -2
  240. package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +4 -1
  241. package/dist/packages/mcp/assets/kb/docs/docs-larp.md +1 -1
  242. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  243. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  244. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
  246. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  248. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  250. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  264. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +4 -13
  266. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  267. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +40 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  269. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  271. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  273. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  274. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  275. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +1 -1
  277. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +3 -1
  278. package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +2 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +10 -7
  281. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  282. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +1 -1
  284. package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +2 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-agent-hud-spectator-v2.md +58 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +46 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +1 -1
  288. package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +1 -1
  289. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  290. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  291. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  292. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  293. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  295. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  297. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  298. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  299. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  302. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  309. package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
  310. package/dist/packages/mcp/src/app-help.js +1 -1
  311. package/dist/packages/mcp/src/autotrade-tools.js +944 -0
  312. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  313. package/dist/packages/mcp/src/engine/config.js +159 -0
  314. package/dist/packages/mcp/src/engine/index.js +10 -0
  315. package/dist/packages/mcp/src/engine/loop.js +101 -0
  316. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  317. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  318. package/dist/packages/mcp/src/engine/probe.js +110 -0
  319. package/dist/packages/mcp/src/engine/reason.js +35 -0
  320. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  321. package/dist/packages/mcp/src/index.js +7 -1
  322. package/dist/packages/mcp/src/ledger-tools.js +346 -0
  323. package/dist/packages/mcp/src/paper-agent.js +5 -2
  324. package/dist/packages/mcp/src/server.js +285 -36
  325. package/dist/packages/mcp/src/spectator-sink.js +85 -0
  326. package/dist/packages/packs/src/backtest.js +3 -1
  327. package/dist/packages/packs/src/index.js +1 -0
  328. package/dist/packages/packs/src/larp-score-backtest.js +119 -0
  329. package/dist/packages/popup/src/pages/chart-lib.js +169 -35
  330. package/dist/packages/popup/src/pages/chart.css +18 -0
  331. package/dist/packages/popup/src/pages/chart.html +6 -1
  332. package/dist/packages/popup/src/pages/chart.js +136 -10
  333. package/dist/packages/popup/src/pages/spectator.css +45 -0
  334. package/dist/packages/popup/src/pages/spectator.html +1 -0
  335. package/dist/packages/popup/src/pages/spectator.js +69 -0
  336. package/dist/packages/popup/src/protocol.js +4 -0
  337. package/dist/packages/popup/src/server.js +9 -1
  338. package/dist/packages/recorder/src/dev-history.js +7 -3
  339. package/dist/packages/recorder/src/index.js +2 -0
  340. package/dist/packages/recorder/src/recorder.js +63 -6
  341. package/dist/packages/recorder/src/retention.js +100 -0
  342. package/dist/packages/recorder/src/schema.js +34 -1
  343. package/dist/packages/recorder/src/ticks.js +237 -0
  344. package/dist/packages/tui/src/canvas.js +7 -0
  345. package/dist/packages/tui/src/fmt.js +53 -0
  346. package/dist/packages/tui/src/index.js +1 -0
  347. package/dist/packages/tui/src/keymap.js +25 -1
  348. package/dist/packages/tui/src/markers.js +219 -0
  349. package/dist/packages/tui/src/widgets.js +158 -6
  350. package/package.json +2 -1
  351. package/dist/packages/data/src/trade/caps.js +0 -323
  352. package/dist/packages/data/src/trade/executor.js +0 -933
  353. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  354. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  355. package/dist/packages/data/src/trade/signer/client.js +0 -197
  356. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  357. package/dist/packages/data/src/trade/signer/host.js +0 -97
  358. package/dist/packages/data/src/trade/signer/main.js +0 -121
  359. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  360. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  361. package/dist/packages/data/src/trade/signer/service.js +0 -400
  362. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  363. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  364. package/dist/packages/data/src/trade/swap-build.js +0 -108
  365. package/dist/packages/data/src/trade/token-delta.js +0 -201
  366. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  367. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  368. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  369. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  370. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  371. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  372. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  373. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  374. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  375. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  376. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  377. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  378. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  379. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -0,0 +1,169 @@
1
+ // dexterm autotrade — the multi-strategy config, `autotrade.json` (task-1529, parallel strategies). PAPER today.
2
+ //
3
+ // $DEXTERM_HOME/autotrade/autotrade.json, schema dexterm-autotrade/1, lists the strategies one runner process runs at
4
+ // the same time on the same live data feed. Each one is a MEMBER with its own id, its own bankroll, its own run
5
+ // ledger, caps, breaker, candidate sessions and spend budget; `global` holds the caps across all of them:
6
+ //
7
+ // { "schema": "dexterm-autotrade/1",
8
+ // "strategies": [
9
+ // { "id": "moon-a", "preset": "moonbag", "bankroll": { "usd": 500 } },
10
+ // { "id": "yolo", "preset": "my-yolo", "bankroll": { "sol": 1 }, "caps": { "modelUsdPerDay": 2, "maxSessions": 1 } },
11
+ // { "id": "dip", "file": "/abs/path/dip.json", "bankroll": { "usd": 200 }, "enabled": false } ],
12
+ // "global": { "maxConcurrentSessions": 3, "modelUsdPerDay": 15, "candidateSessionsPerDay": 60 } }
13
+ //
14
+ // `preset` names a shipped preset or one of the user's own strategies (~/.dexterm/strategies); `file` is an absolute
15
+ // path to a strategy file. Member caps can only TIGHTEN: the stricter of the strategy, the member caps, runner.json
16
+ // and the global caps applies, so a shared config can never loosen a strategy. `mode` is "paper" (the default);
17
+ // "live" is refused (task-1571: dexterm holds no key and places no live order).
18
+ //
19
+ // Paper ACCOUNTS (task-1538): `accounts` lists shared simulated wallets, and a strategy with `account` trades from
20
+ // that wallet's cash instead of its own bankroll, next to every other strategy on it (paper-accounts.ts). One strategy
21
+ // is not tied to one account: `"account": ["main", "yolo"]` runs it once per account, as members `<id>-main` and
22
+ // `<id>-yolo` (each its own run, ledger, positions and caps):
23
+ //
24
+ // "accounts": [ { "id": "main", "bankroll": { "usd": 1000 } }, { "id": "yolo", "bankroll": { "sol": 1 } } ],
25
+ // "strategies": [ { "id": "moon", "preset": "moonbag", "account": "main" },
26
+ // { "id": "scalp", "preset": "scalp", "account": ["main", "yolo"] } ]
27
+ //
28
+ // A member on an account takes no `bankroll` of its own (the account holds the money). Accounts are paper only.
29
+ //
30
+ // The file is user data: no login, no key, no token, no wallet address (a paper account is a name and a starting
31
+ // balance). Like runner.json, it is machine-local.
32
+ import { existsSync, readFileSync } from 'node:fs';
33
+ import { join } from 'node:path';
34
+ import { validate } from '../../custom/src/index.js';
35
+ import { STRATEGY_ID } from "./store.js";
36
+ import { MAX_PAPER_ACCOUNTS } from "./paper-accounts.js";
37
+ import { strategyHash } from "./strategy.js";
38
+ export const FLEET_CONFIG_SCHEMA = 'dexterm-autotrade/1';
39
+ export const fleetConfigPath = (home) => join(home, 'autotrade', 'autotrade.json');
40
+ /** How many strategies one process runs at most (each one is a book, a ledger and up to its own session cap). */
41
+ export const MAX_MEMBERS = 8;
42
+ export const DEFAULT_GLOBAL_CAPS = { maxConcurrentSessions: 3, modelUsdPerDay: 20, candidateSessionsPerDay: 96, positionSessionsPerDay: 96, turnsPerDay: 4000 };
43
+ const num = (min, max, int = false) => ({ type: 'number', min, max, int });
44
+ const CAPS = { type: 'object', additional: false, props: {
45
+ maxSessions: num(1, 8, true), modelUsdPerDay: num(0, 10_000), candidateSessionsPerDay: num(0, 10_000, true), positionSessionsPerDay: num(0, 10_000, true), turnsPerDay: num(0, 1_000_000, true),
46
+ } };
47
+ const BANKROLL = { oneOf: [
48
+ { type: 'object', additional: false, required: ['usd'], props: { usd: num(1, 1e7) } },
49
+ { type: 'object', additional: false, required: ['sol'], props: { sol: num(0.001, 1e5) } },
50
+ ] };
51
+ const ACCOUNT_REF = { oneOf: [
52
+ { type: 'string', pattern: STRATEGY_ID.source },
53
+ { type: 'array', max: MAX_PAPER_ACCOUNTS, items: { type: 'string', pattern: STRATEGY_ID.source } },
54
+ ] };
55
+ const SCHEMA = {
56
+ type: 'object', additional: false, required: ['schema', 'strategies'],
57
+ props: {
58
+ schema: { type: 'string', enum: [FLEET_CONFIG_SCHEMA] },
59
+ strategies: { type: 'array', max: MAX_MEMBERS, items: {
60
+ type: 'object', additional: false, required: ['id'],
61
+ props: {
62
+ id: { type: 'string', pattern: STRATEGY_ID.source },
63
+ preset: { type: 'string', pattern: STRATEGY_ID.source },
64
+ file: { type: 'string', max: 500 },
65
+ bankroll: BANKROLL,
66
+ account: ACCOUNT_REF,
67
+ mode: { type: 'string', enum: ['paper', 'live'] },
68
+ enabled: { type: 'boolean' },
69
+ caps: CAPS,
70
+ },
71
+ } },
72
+ global: { type: 'object', additional: false, props: {
73
+ maxConcurrentSessions: num(1, 32, true), modelUsdPerDay: num(0, 10_000), candidateSessionsPerDay: num(0, 10_000, true), positionSessionsPerDay: num(0, 10_000, true), turnsPerDay: num(0, 1_000_000, true),
74
+ } },
75
+ solUsd: num(1, 100_000),
76
+ accounts: { type: 'array', max: MAX_PAPER_ACCOUNTS, items: {
77
+ type: 'object', additional: false, required: ['id', 'bankroll'],
78
+ props: { id: { type: 'string', pattern: STRATEGY_ID.source }, bankroll: BANKROLL },
79
+ } },
80
+ },
81
+ };
82
+ /** Validate a parsed autotrade.json and fill the defaults. */
83
+ export function parseFleetConfig(doc, path = 'autotrade.json', o = {}) {
84
+ const errs = validate(SCHEMA, doc, '');
85
+ if (errs.length)
86
+ return { ok: false, errors: errs.map((e) => `${e.path || '/'} ${e.msg}`), path };
87
+ const d = doc;
88
+ const errors = [];
89
+ const accounts = d.accounts ?? [];
90
+ const accountIds = new Set();
91
+ accounts.forEach((a, i) => { if (accountIds.has(a.id))
92
+ errors.push(`/accounts/${i}/id ${a.id} is used twice (every paper account needs its own id)`); accountIds.add(a.id); });
93
+ const seen = new Set();
94
+ const strategies = [];
95
+ d.strategies.forEach((m, i) => {
96
+ const at = `/strategies/${i}`;
97
+ if (!!m.preset === !!m.file)
98
+ errors.push(`${at} needs exactly one of preset or file`);
99
+ if (m.file && !m.file.startsWith('/'))
100
+ errors.push(`${at}/file must be an absolute path`);
101
+ if (m.mode === 'live' && !o.liveAvailable)
102
+ errors.push(`${at}/mode live execution is not available for parallel strategies in this build; every strategy runs on paper`);
103
+ const refs = m.account === undefined ? [] : Array.isArray(m.account) ? m.account : [m.account];
104
+ if (m.account !== undefined) {
105
+ if (!refs.length)
106
+ errors.push(`${at}/account lists no paper account`);
107
+ if (new Set(refs).size !== refs.length)
108
+ errors.push(`${at}/account names an account twice`);
109
+ for (const a of refs)
110
+ if (!accountIds.has(a))
111
+ errors.push(`${at}/account ${a} is not a paper account in /accounts`);
112
+ if (m.bankroll)
113
+ errors.push(`${at}/bankroll a strategy on a paper account trades from the account's balance; drop its own bankroll`);
114
+ if (m.mode === 'live')
115
+ errors.push(`${at}/account paper accounts hold simulated cash only; a live strategy cannot use one`);
116
+ }
117
+ const base = { ...(m.preset ? { preset: m.preset } : {}), ...(m.file ? { file: m.file } : {}), ...(m.bankroll ? { bankroll: m.bankroll } : {}), mode: m.mode ?? 'paper', enabled: m.enabled ?? true, caps: m.caps ?? {} };
118
+ // A list of accounts runs the same strategy once per account: one member each, `<id>-<account>`.
119
+ const ids = Array.isArray(m.account) ? m.account.map((a) => ({ id: `${m.id}-${a}`, account: a })) : [{ id: m.id, account: refs[0] }];
120
+ for (const x of ids) {
121
+ if (!STRATEGY_ID.test(x.id))
122
+ errors.push(`${at}/account ${x.id} (strategy id + "-" + account id) is not a valid member id (lowercase, at most 32 characters): shorten one of them`);
123
+ if (seen.has(x.id))
124
+ errors.push(`${at}/id ${x.id} is used twice (every strategy in the list needs its own id)`);
125
+ seen.add(x.id);
126
+ strategies.push({ id: x.id, ...base, ...(x.account ? { account: x.account } : {}), ...(Array.isArray(m.account) ? { entry: m.id } : {}) });
127
+ }
128
+ });
129
+ if (!strategies.length)
130
+ errors.push('/strategies lists no strategy');
131
+ if (strategies.length > MAX_MEMBERS)
132
+ errors.push(`/strategies ${strategies.length} strategy runs (after one per listed account); at most ${MAX_MEMBERS} run at once`);
133
+ if (errors.length)
134
+ return { ok: false, errors, path };
135
+ return { ok: true, config: { schema: FLEET_CONFIG_SCHEMA, strategies, global: { ...DEFAULT_GLOBAL_CAPS, ...d.global }, ...(accounts.length ? { accounts: accounts.map((a) => ({ id: a.id, bankroll: a.bankroll })) } : {}), ...(d.solUsd ? { solUsd: d.solUsd } : {}) }, path };
136
+ }
137
+ export function loadFleetConfig(home, file, o = {}) {
138
+ const path = file ?? fleetConfigPath(home);
139
+ if (!existsSync(path))
140
+ return { ok: false, missing: true, path, errors: [`no ${path}: list the strategies to run at once there (dexterm autotrade run --all --help)`] };
141
+ let doc;
142
+ try {
143
+ doc = JSON.parse(readFileSync(path, 'utf8'));
144
+ }
145
+ catch (e) {
146
+ return { ok: false, errors: [`not JSON: ${e.message}`], path };
147
+ }
148
+ return parseFleetConfig(doc, path, o);
149
+ }
150
+ /**
151
+ * A member's strategy: the resolved strategy under the member's id, with the member's bankroll, re-hashed so every
152
+ * ledger line names the exact rules it ran (two members on one preset with different bankrolls hash differently).
153
+ * The base strategy's id / version / hash stay in `basedOn` (the member's first ledger line).
154
+ */
155
+ export function memberStrategy(base, m, startUsd) {
156
+ const { hash: _h, ...rest } = base;
157
+ const body = { ...rest, id: m.id, bankroll: { ...base.bankroll, ...(startUsd !== null ? { startUsd } : {}), source: 'fixed' } };
158
+ return { strategy: { ...body, hash: strategyHash(body) }, basedOn: { id: base.id, version: base.version, hash: base.hash } };
159
+ }
160
+ /** The bankroll in USD (a SOL bankroll at the given SOL price), or null when the strategy's own startUsd applies. */
161
+ export function bankrollUsd(b, solUsd) {
162
+ if (!b)
163
+ return { usd: null };
164
+ if ('usd' in b)
165
+ return { usd: b.usd };
166
+ if (!solUsd || !(solUsd > 0))
167
+ return { usd: null, error: 'a SOL bankroll needs the SOL price (live: read at start; a replay: "solUsd" in autotrade.json or --sol-usd)' };
168
+ return { usd: Math.round(b.sol * solUsd * 100) / 100 };
169
+ }
@@ -0,0 +1,276 @@
1
+ // dexterm autotrade — per-strategy control, the fleet status file and the comparison table (task-1529). PAPER today.
2
+ //
3
+ // One process runs every strategy of autotrade.json (fleet.ts). It holds the machine's one runner lock under the
4
+ // FLEET's run id, so the existing `dexterm autotrade stop | flatten`, the Q screen's `!` / `f` / `p`, the panic file
5
+ // and the dead-man act on EVERY strategy at once (the global switch, safety.ts). This file adds the per-strategy
6
+ // switch:
7
+ //
8
+ // $DEXTERM_HOME/autotrade/fleet/<strategy>.control.json (dexterm-autotrade-fleet-control/1)
9
+ // stop | flatten | pause | resume-entries for that strategy's CURRENT run (its run id), same meaning as the
10
+ // global ones, scoped to one book: another strategy never sees it
11
+ // start a strategy of autotrade.json that is not running starts at the next
12
+ // tick (paper only, its own config; nothing else can be changed here)
13
+ //
14
+ // One file per strategy, so two requests never overwrite each other. A stop or flatten is final for that run (only
15
+ // stop → flatten escalates); an unreadable file halts THAT strategy (fail closed) and nothing else.
16
+ //
17
+ // $DEXTERM_HOME/autotrade/fleet-status.json (dexterm-autotrade-fleet/1), rewritten every heartbeat: each
18
+ // strategy's state, book, spend and stats, the global caps and their use today, the machine load.
19
+ //
20
+ // compareRows() turns that file into the comparison table (`dexterm autotrade status --all`, the Q screen's FLEET
21
+ // view, the MCP compare tool): P/L, win rate, rug exposure, missed runners, model spend per entry.
22
+ //
23
+ // Nothing here holds a key, touches a wallet or the signer, or can switch a strategy to live.
24
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from 'node:fs';
25
+ import { join } from 'node:path';
26
+ import { writeJsonAtomic } from "./ledger.js";
27
+ import { STRATEGY_ID } from "./store.js";
28
+ export const FLEET_CONTROL_SCHEMA = 'dexterm-autotrade-fleet-control/1';
29
+ export const FLEET_STATUS_SCHEMA = 'dexterm-autotrade-fleet/1';
30
+ export const MEMBER_ACTIONS = ['start', 'stop', 'flatten', 'pause', 'resume-entries'];
31
+ const BY = ['cli', 'tui', 'mcp', 'chat'];
32
+ export const fleetDir = (home) => join(home, 'autotrade', 'fleet');
33
+ export const fleetStatusPath = (home) => join(home, 'autotrade', 'fleet-status.json');
34
+ export function memberControlPath(home, member) {
35
+ if (!STRATEGY_ID.test(member))
36
+ throw new Error(`not a strategy id: ${JSON.stringify(member)}`);
37
+ return join(fleetDir(home), `${member}.control.json`);
38
+ }
39
+ export function readMemberControl(home, member) {
40
+ const p = memberControlPath(home, member);
41
+ if (!existsSync(p))
42
+ return null;
43
+ try {
44
+ const d = JSON.parse(readFileSync(p, 'utf8'));
45
+ if (d.schema !== FLEET_CONTROL_SCHEMA || typeof d.fleetId !== 'string' || d.member !== member || !(d.runId === null || typeof d.runId === 'string')
46
+ || !MEMBER_ACTIONS.includes(d.action) || typeof d.at !== 'number')
47
+ return 'unreadable';
48
+ return { schema: FLEET_CONTROL_SCHEMA, fleetId: d.fleetId, member, runId: d.runId, action: d.action, at: d.at, by: BY.includes(d.by) ? d.by : 'cli' };
49
+ }
50
+ catch {
51
+ return 'unreadable';
52
+ }
53
+ }
54
+ const FINAL = ['stop', 'flatten'];
55
+ /**
56
+ * Ask one strategy of the running fleet to start, stop, flatten, pause or resume entries. The fleet's status file
57
+ * says which strategies exist and which run is current; a request for a strategy that is not in the fleet, a start
58
+ * for one that runs, or a stop for one that does not, is refused here with the reason.
59
+ */
60
+ export function requestMemberControl(home, member, action, by, now = Date.now()) {
61
+ if (!STRATEGY_ID.test(member))
62
+ return { ok: false, why: `not a strategy id: ${JSON.stringify(member)}` };
63
+ const st = readFleetStatus(home);
64
+ if (!st.ok)
65
+ return { ok: false, why: st.why };
66
+ if (st.status.state === 'stopped')
67
+ return { ok: false, why: 'the parallel runner has stopped; start it with `dexterm autotrade run --all --paper`' };
68
+ const m = st.status.members.find((x) => x.id === member);
69
+ if (!m)
70
+ return { ok: false, why: `${member} is not one of the running strategies (${st.status.members.map((x) => x.id).join(', ')}); add it to autotrade.json and start the runner again` };
71
+ const running = m.runId !== null && m.state !== 'stopped' && m.state !== 'idle' && m.state !== 'error';
72
+ if (action === 'start') {
73
+ if (running)
74
+ return { ok: false, why: `${member} is already running (run ${m.runId})` };
75
+ }
76
+ else if (!running)
77
+ return { ok: false, why: `${member} is not running${m.state === 'error' ? ` (${m.error ?? 'it failed to start'})` : ''}${action === 'stop' || action === 'flatten' ? '; nothing to stop' : ''}` };
78
+ const runId = action === 'start' ? null : m.runId;
79
+ const cur = readMemberControl(home, member);
80
+ if (cur && cur !== 'unreadable' && cur.fleetId === st.status.fleetId && cur.runId === runId && runId !== null && FINAL.includes(cur.action)) {
81
+ if (cur.action === action || (cur.action === 'flatten' && action === 'stop'))
82
+ return { ok: true, request: cur, already: true };
83
+ if (action !== 'flatten')
84
+ return { ok: false, why: `${member} is already ${cur.action === 'stop' ? 'stopping' : 'flattening'}; only a flatten can follow a stop` };
85
+ }
86
+ const request = { schema: FLEET_CONTROL_SCHEMA, fleetId: st.status.fleetId, member, runId, action, at: now, by };
87
+ mkdirSync(fleetDir(home), { recursive: true, mode: 0o700 });
88
+ writeJsonAtomic(memberControlPath(home, member), request);
89
+ return { ok: true, request, already: false };
90
+ }
91
+ /** At a fleet's start and end: drop the per-strategy requests of other fleets (they can never apply). */
92
+ export function clearMemberControls(home, keepFleetId) {
93
+ const dir = fleetDir(home);
94
+ if (!existsSync(dir))
95
+ return;
96
+ for (const f of readdirSafe(dir)) {
97
+ if (!f.endsWith('.control.json'))
98
+ continue;
99
+ const id = f.slice(0, -'.control.json'.length);
100
+ if (!STRATEGY_ID.test(id))
101
+ continue;
102
+ const cur = readMemberControl(home, id);
103
+ if (cur !== 'unreadable' && (!cur || cur.fleetId !== keepFleetId))
104
+ rmSync(join(dir, f), { force: true });
105
+ }
106
+ }
107
+ function readdirSafe(dir) {
108
+ try {
109
+ return readdirSync(dir);
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ }
115
+ /**
116
+ * One strategy's switch, read on every step of its run: a stat per step, a parse when the file changed. A stop or
117
+ * flatten is sticky for the run; pause lifts with resume-entries; an unreadable file halts this strategy only.
118
+ */
119
+ export class MemberSafety {
120
+ haltWhy = null;
121
+ haltDetail = '';
122
+ flattening = false;
123
+ stopAsked = false;
124
+ sig = '';
125
+ ctl = null;
126
+ home;
127
+ fleetId;
128
+ member;
129
+ runId;
130
+ constructor(o) { this.home = o.home; this.fleetId = o.fleetId; this.member = o.member; this.runId = o.runId; }
131
+ control() {
132
+ const p = memberControlPath(this.home, this.member);
133
+ let sig = '';
134
+ try {
135
+ const st = statSync(p);
136
+ sig = `${st.mtimeMs}:${st.size}:${st.ino}`;
137
+ }
138
+ catch {
139
+ sig = '';
140
+ }
141
+ if (sig !== this.sig) {
142
+ this.sig = sig;
143
+ this.ctl = sig ? readMemberControl(this.home, this.member) : null;
144
+ }
145
+ return this.ctl;
146
+ }
147
+ step() {
148
+ const c = this.control();
149
+ let pause = null;
150
+ const halt = (why, detail) => { if (!this.haltWhy) {
151
+ this.haltWhy = why;
152
+ this.haltDetail = detail;
153
+ } };
154
+ if (c === 'unreadable') {
155
+ halt('control-unreadable', `${this.member}'s control file cannot be read (treated as a stop of this strategy)`);
156
+ this.stopAsked = true;
157
+ }
158
+ else if (c && c.fleetId === this.fleetId && c.runId === this.runId) {
159
+ const from = c.by === 'tui' ? 'the Q screen' : c.by === 'mcp' ? 'an MCP client' : c.by === 'chat' ? 'a paired chat' : 'the command line';
160
+ if (c.action === 'stop' || c.action === 'flatten') {
161
+ halt(c.action, `this strategy only, requested from ${from}`);
162
+ this.stopAsked = true;
163
+ if (c.action === 'flatten')
164
+ this.flattening = true;
165
+ }
166
+ else if (c.action === 'pause')
167
+ pause = 'paused';
168
+ }
169
+ return { halt: this.haltWhy, ...(this.haltWhy ? { detail: this.haltDetail } : {}), flatten: this.flattening, pauseEntries: pause, endWhenFlat: this.stopAsked };
170
+ }
171
+ }
172
+ /** The fleet's own verdict and a strategy's, folded: a global halt, flatten or pause reaches every strategy; a member one only its own. */
173
+ export function mergeVerdicts(global, member) {
174
+ const halt = global.halt ?? member.halt;
175
+ const detail = global.halt ? global.detail : member.detail;
176
+ return { halt, ...(halt && detail ? { detail } : {}), flatten: global.flatten || member.flatten, pauseEntries: global.pauseEntries ?? member.pauseEntries, endWhenFlat: global.endWhenFlat || member.endWhenFlat };
177
+ }
178
+ export const FLEET_STALE_MS = 30_000;
179
+ export function readFleetStatus(home, now = Date.now()) {
180
+ const path = fleetStatusPath(home);
181
+ if (!existsSync(path))
182
+ return { ok: false, why: 'no parallel runner has run here yet (`dexterm autotrade run --all --paper`)', path };
183
+ let d;
184
+ try {
185
+ d = JSON.parse(readFileSync(path, 'utf8'));
186
+ }
187
+ catch (e) {
188
+ return { ok: false, why: `the fleet status file is not JSON: ${e.message}`, path };
189
+ }
190
+ if (d.schema !== FLEET_STATUS_SCHEMA)
191
+ return { ok: false, why: `unknown fleet status shape ${JSON.stringify(d.schema)}`, path };
192
+ // Paper only: a status that claims anything else is an error, never shown as a running live fleet.
193
+ if (d.mode !== 'paper')
194
+ return { ok: false, why: `mode ${JSON.stringify(d.mode)}: only paper fleets are shown`, path };
195
+ if (typeof d.fleetId !== 'string' || typeof d.heartbeatAt !== 'number' || !Array.isArray(d.members) || !d.global)
196
+ return { ok: false, why: 'the fleet status file is missing fields', path };
197
+ const status = d;
198
+ return { ok: true, status, stale: status.state !== 'stopped' && now - status.heartbeatAt > FLEET_STALE_MS, path };
199
+ }
200
+ // ───────────────────────────── the comparison table ─────────────────────────────
201
+ export const MISSED_RUNNER_X = 2;
202
+ const RUG_REASONS = new Set(['rug-liquidity', 'thesis-liquidity']);
203
+ export const isRugExit = (reason, stuck) => stuck || RUG_REASONS.has(reason);
204
+ const r2 = (v) => Math.round(v * 100) / 100;
205
+ export function compareRows(st) {
206
+ return st.members.map((m) => {
207
+ // On a shared account the book is the account's: the strategy's P/L is its own trades (pnlUsd), against the account.
208
+ const onAccount = typeof m.account === 'string';
209
+ const eq = onAccount ? null : m.book?.equityUsd ?? null;
210
+ const start = m.book?.startUsd ?? m.bankroll.usd;
211
+ const pnl = onAccount ? (typeof m.pnlUsd === 'number' ? r2(m.pnlUsd) : null) : eq === null ? null : r2(eq - start);
212
+ const s = m.stats;
213
+ return {
214
+ id: m.id, account: onAccount ? m.account : null, state: m.state, basedOn: `${m.basedOn.id} v${m.basedOn.version}`, bankrollUsd: start,
215
+ equityUsd: eq, pnlUsd: pnl, pnlPct: pnl === null || !start ? null : Math.round((pnl / start) * 1000) / 10,
216
+ closed: s.closed, winRatePct: s.closed ? Math.round((s.wins / s.closed) * 1000) / 10 : null, open: m.open.length,
217
+ rugExits: s.rugExits, rugExposurePct: s.enteredUsd > 0 ? Math.round((s.rugCostUsd / s.enteredUsd) * 1000) / 10 : null,
218
+ missedRunners: s.missedRunners, spendUsd: Math.round(s.spendUsd * 1e4) / 1e4, spendPerEntryUsd: s.entries ? Math.round((s.spendUsd / s.entries) * 1e4) / 1e4 : null, entries: s.entries,
219
+ };
220
+ });
221
+ }
222
+ /** The fleet totals (every strategy's own book, and every shared paper account once; paper). */
223
+ export function fleetTotals(st) {
224
+ let startUsd = 0, equityUsd = 0, cashUsd = 0, closed = 0, wins = 0, spendUsd = 0, open = 0;
225
+ for (const m of st.members) {
226
+ if (!m.book)
227
+ continue;
228
+ closed += m.stats.closed;
229
+ wins += m.stats.wins;
230
+ spendUsd += m.stats.spendUsd;
231
+ open += m.open.length;
232
+ if (typeof m.account === 'string')
233
+ continue; // counted once below, with its account
234
+ startUsd += m.book.startUsd;
235
+ equityUsd += m.book.equityUsd;
236
+ cashUsd += m.book.cashUsd;
237
+ }
238
+ for (const a of st.accounts ?? []) {
239
+ startUsd += a.startUsd;
240
+ equityUsd += a.equityUsd;
241
+ cashUsd += a.cashUsd;
242
+ }
243
+ const pnlUsd = r2(equityUsd - startUsd);
244
+ return { startUsd: r2(startUsd), equityUsd: r2(equityUsd), cashUsd: r2(cashUsd), pnlUsd, pnlPct: startUsd ? Math.round((pnlUsd / startUsd) * 1000) / 10 : null, closed, wins, spendUsd: Math.round(spendUsd * 1e4) / 1e4, open };
245
+ }
246
+ /** The shared paper accounts as plain text lines (empty when the run has none). */
247
+ export function accountTable(st) {
248
+ const accts = st.accounts ?? [];
249
+ if (!accts.length)
250
+ return [];
251
+ const head = ['ACCOUNT', 'START', 'EQUITY', 'CASH', 'P/L', 'P/L %', 'OPEN', 'STRATEGIES'];
252
+ const body = accts.map((a) => [
253
+ a.id, `$${a.startUsd.toFixed(0)}${a.sol !== undefined ? ` (${a.sol} SOL)` : ''}`, `$${a.equityUsd.toFixed(2)}`, `$${a.cashUsd.toFixed(2)}`, fmtUsd(a.pnlUsd), fmtPct(a.pnlPct), String(a.positions),
254
+ st.members.filter((m) => m.account === a.id).map((m) => m.id).join(' ') || '—',
255
+ ]);
256
+ const w = head.map((h, i) => Math.max(h.length, ...body.map((b) => b[i].length)));
257
+ const line = (cells) => cells.map((c, i) => (i === 0 || i === cells.length - 1 ? c.padEnd(w[i]) : c.padStart(w[i]))).join(' ').trimEnd();
258
+ return [line(head), ...body.map(line)];
259
+ }
260
+ const fmtPct = (v) => (v === null ? '—' : `${v >= 0 ? '+' : ''}${v.toFixed(1)}%`);
261
+ const fmtUsd = (v) => (v === null ? '—' : `${v >= 0 ? '+' : '-'}$${Math.abs(v).toFixed(2)}`);
262
+ /** The table as plain text lines (the CLI, the MCP text and the Q screen share one layout). */
263
+ export function compareTable(rows) {
264
+ // An ACCOUNT column only when a strategy trades from a shared paper account (task-1538); BANKROLL is then the account's.
265
+ const acct = rows.some((r) => r.account);
266
+ const head = ['STRATEGY', ...(acct ? ['ACCOUNT'] : []), 'STATE', 'BANKROLL', 'P/L', 'P/L %', 'WIN', 'CLOSED', 'OPEN', 'RUGS', 'RUG EXP', 'MISSED', 'SPEND', '$/ENTRY'];
267
+ const body = rows.map((r) => [
268
+ r.id, ...(acct ? [r.account ?? '—'] : []), r.state, `$${r.bankrollUsd.toFixed(0)}`, fmtUsd(r.pnlUsd), fmtPct(r.pnlPct), r.winRatePct === null ? '—' : `${r.winRatePct.toFixed(0)}%`,
269
+ String(r.closed), String(r.open), String(r.rugExits), r.rugExposurePct === null ? '—' : `${r.rugExposurePct.toFixed(0)}%`, String(r.missedRunners),
270
+ `$${r.spendUsd.toFixed(2)}`, r.spendPerEntryUsd === null ? '—' : `$${r.spendPerEntryUsd.toFixed(3)}`,
271
+ ]);
272
+ const left = acct ? 2 : 1;
273
+ const w = head.map((h, i) => Math.max(h.length, ...body.map((b) => b[i].length)));
274
+ const line = (cells) => cells.map((c, i) => (i <= left ? c.padEnd(w[i]) : c.padStart(w[i]))).join(' ');
275
+ return [line(head), ...body.map(line)];
276
+ }