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,393 @@
1
+ // `dexterm autotrade limits …` (task-1654, 0.2.0 card E): every real-mode limit, user-set, per scope.
2
+ //
3
+ // show the effective limits (for a strategy / account / mode) and where each number comes from
4
+ // set name=value … in a scope (global | real | paper | strategy:<id> | account:<id>); lowering is immediate,
5
+ // raising shows the old -> new diff and needs the typed phrase on a real terminal
6
+ // diff a proposed file (--file) against the limits now, or limits.json against dexterm's last journaled save
7
+ // reset a scope (or one --key) back to the placeholder defaults (a raise if that loosens anything: typed)
8
+ // accept take a hand-edited limits.json (typed when it raises anything; a lower-only edit is accepted as is)
9
+ // breaker `breaker reset`: lift a tripped drawdown breaker (typed)
10
+ // explain what each limit does schema the JSON Schema of the file
11
+ //
12
+ // No MCP tool, model decision or chat command reaches the writing paths: the analyst can read and explain the limits
13
+ // and propose a diff card (MCP limits_propose), which only YOU can apply here.
14
+ import { readFileSync } from 'node:fs';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { dextermHome } from '../../../custom/src/index.js';
17
+ import { DEFAULT_LIMITS, LIMIT_FIELDS, LIMIT_KEYS, changeLine, diffLimits, effectiveLimits, emptyLimits, limitField, limitsLines, limitsPath, limitsSummary, loadArmings, loadLimits, loadRiskState, parseLimitAssignment, planLimitsSave, raises, resetBreaker, saveLimits, scopeRef, validateLimits, } from '../../../autotrade/src/index.js';
18
+ import { parseArgs } from "./strategies.js";
19
+ import { confirmTyped, isTty, typedLine } from "./signal.js";
20
+ export const LIMITS_USAGE = [
21
+ 'dexterm autotrade limits show [--strategy ID] [--account ID] [--mode real|paper] [--all] [--json]',
22
+ 'dexterm autotrade limits set [--scope global|real|paper|strategy:ID|account:ID] name=value … (value "off" turns a global limit off)',
23
+ 'dexterm autotrade limits diff [--file FILE] [--json]',
24
+ 'dexterm autotrade limits reset [--scope S] [--key NAME]',
25
+ 'dexterm autotrade limits accept | breaker reset | explain [NAME] | schema',
26
+ ].join('\n');
27
+ const out = (io, s) => { io.stdout.write(`${s}\n`); };
28
+ const err = (io, s) => { io.stderr.write(`dexterm autotrade limits: ${s}\n`); };
29
+ export const LIMITS_SCHEMA_FILE = fileURLToPath(new URL('../../../autotrade/assets/limits.schema.json', import.meta.url));
30
+ export async function limitsCmd(argv, io, deps = {}) {
31
+ const home = dextermHome(io.env);
32
+ const now = deps.now ?? Date.now;
33
+ const sub = argv[0];
34
+ const rest = argv.slice(1);
35
+ switch (sub) {
36
+ case undefined:
37
+ case 'help':
38
+ out(io, LIMITS_USAGE);
39
+ return 0;
40
+ case 'show': return showCmd(rest, io, home, now);
41
+ case 'set': return setCmd(rest, io, home, deps, now);
42
+ case 'diff': return diffCmd(rest, io, home);
43
+ case 'reset': return resetCmd(rest, io, home, deps, now);
44
+ case 'accept': return acceptCmd(io, home, deps, now);
45
+ case 'breaker': {
46
+ if (rest[0] !== 'reset') {
47
+ err(io, 'usage: dexterm autotrade limits breaker reset');
48
+ return 2;
49
+ }
50
+ const r = loadRiskState(home, now());
51
+ if (!r.breaker) {
52
+ out(io, 'the drawdown breaker is not tripped');
53
+ return 0;
54
+ }
55
+ out(io, `the drawdown breaker tripped ${new Date(r.breaker.at).toISOString()} at ${r.breaker.drawdownSol} SOL below the peak; resetting it lets new real buys go out again (it also lifts a loss-streak pause and restarts the streak count)`);
56
+ const code = await confirmTyped(io, deps, 'RESET', 'breaker');
57
+ if (!code)
58
+ return 1;
59
+ const x = resetBreaker(home, { confirmed: true, now: now() });
60
+ if (!x.ok) {
61
+ err(io, x.why);
62
+ return 1;
63
+ }
64
+ out(io, 'drawdown breaker reset (journaled); the peak starts again from here; any loss-streak pause is lifted too');
65
+ return 0;
66
+ }
67
+ case 'explain': return explainCmd(rest, io);
68
+ case 'schema':
69
+ out(io, readFileSync(LIMITS_SCHEMA_FILE, 'utf8').trimEnd());
70
+ return 0;
71
+ default:
72
+ err(io, `unknown subcommand ${JSON.stringify(sub)}\n${LIMITS_USAGE}`);
73
+ return 2;
74
+ }
75
+ }
76
+ function current(home) {
77
+ const l = loadLimits(home);
78
+ if (l.ok)
79
+ return { doc: l.doc, ok: true, note: l.source === 'migrated' ? 'read from card A\'s caps.json (saved as limits.json on your next change)' : l.unjournaled ? `limits.json differs from dexterm's last save (${l.unjournaled.length} change(s)): dexterm autotrade limits diff` : null };
80
+ if (l.missing)
81
+ return { doc: emptyLimits(), ok: true, note: 'no limits saved yet: these are the placeholder defaults, NOT your numbers (real mode will not arm on them silently)' };
82
+ return { doc: emptyLimits(), ok: false, note: l.why };
83
+ }
84
+ function showCmd(argv, io, home, now) {
85
+ const { opts } = parseArgs(argv, ['json', 'all']);
86
+ const mode = opts.mode === 'paper' ? 'paper' : 'real';
87
+ const strategyId = typeof opts.strategy === 'string' ? opts.strategy : '-';
88
+ const accountId = typeof opts.account === 'string' ? opts.account : '-';
89
+ const c = current(home);
90
+ if (!c.ok) {
91
+ err(io, c.note ?? 'limits unreadable');
92
+ return 1;
93
+ }
94
+ const e = effectiveLimits(c.doc, { strategyId, accountId, mode });
95
+ const armed = Object.values(loadArmings(home)).map((a) => ({ id: a.strategyId, accountId: a.accountId, limits: effectiveLimits(c.doc, { strategyId: a.strategyId, accountId: a.accountId, mode: 'real' }).values }));
96
+ const risk = loadRiskState(home, now());
97
+ if (opts.json) {
98
+ out(io, JSON.stringify({ file: limitsPath(home), note: c.note, context: { mode, strategyId, accountId }, applies: e.applies, effective: e.values, from: e.from, scopes: c.doc, armed, risk: { day: risk.day, buys: risk.buys, buySol: risk.buySol, dayPnlSol: risk.dayPnlSol, cumPnlSol: risk.cumPnlSol, lossStreak: risk.lossStreak, pausedUntil: risk.pausedUntil, breaker: risk.breaker } }, null, 2));
99
+ return 0;
100
+ }
101
+ out(io, `real-mode limits (${limitsPath(home)}) · outside the model: only you change them, here · the defaults are placeholders, your numbers win`);
102
+ if (c.note)
103
+ out(io, ` note: ${c.note}`);
104
+ out(io, `effective for ${mode}${strategyId !== '-' ? ` · strategy ${strategyId}` : ''}${accountId !== '-' ? ` · account ${accountId}` : ''}${e.applies ? '' : ' · paper ignores the limits (add a "paper" scope to make forward tests obey them)'}:`);
105
+ for (const l of limitsLines(e, { all: opts.all === true }))
106
+ out(io, ` ${l}`);
107
+ const scoped = [...(c.doc.modes.real ? ['real'] : []), ...(c.doc.modes.paper ? ['paper'] : []), ...Object.keys(c.doc.strategies).map((id) => `strategy:${id}`), ...Object.keys(c.doc.accounts).map((id) => `account:${id}`)];
108
+ if (scoped.length)
109
+ out(io, ` tighter scopes set: ${scoped.join(', ')} (the stricter number always wins)`);
110
+ for (const a of armed)
111
+ out(io, ` armed ${a.id} (account ${a.accountId}): ${limitsSummary(a.limits)}`);
112
+ out(io, ` today: ${risk.buys} buys · ${risk.buySol} SOL · realized ${risk.dayPnlSol} SOL${risk.breaker ? ' · DRAWDOWN BREAKER TRIPPED (dexterm autotrade limits breaker reset)' : ''}${risk.pausedUntil && risk.pausedUntil > now() ? ` · loss-streak pause until ${new Date(risk.pausedUntil).toISOString().slice(11, 16)} UTC` : ''}`);
113
+ return 0;
114
+ }
115
+ /**
116
+ * Print the planned diff (planLimitsSave: measured from dexterm's last journaled save, so a hand edit's raises are
117
+ * shown too) and, when anything is raised, take the typed phrase. Returns the raises confirmed (the exact list
118
+ * saveLimits checks; empty when nothing raises), or null when not confirmed.
119
+ */
120
+ export async function confirmPlan(io, deps, plan, unjournaled = null, what = 'limits') {
121
+ for (const c of plan.changes)
122
+ out(io, ` ${changeLine(c)}`);
123
+ if (!plan.raised.length)
124
+ return [];
125
+ if (unjournaled && raises(unjournaled).length)
126
+ out(io, `limits.json was changed outside dexterm and that edit RAISES ${raises(unjournaled).map((c) => `${c.scope}.${c.key}`).join(', ')}: it is part of this save (dexterm autotrade limits diff shows it alone)`);
127
+ out(io, `${plan.raised.length} limit(s) RAISED (laxer). Raising applies from the next real run; a running one keeps the stricter number.`);
128
+ return (await confirmTyped(io, deps, 'RAISE', what)) ? plan.raised : null;
129
+ }
130
+ async function setCmd(argv, io, home, deps, now) {
131
+ const { pos, opts } = parseArgs(argv, ['json']);
132
+ if (!pos.length) {
133
+ err(io, 'set name=value … (dexterm autotrade limits explain lists the names)');
134
+ return 2;
135
+ }
136
+ const c = current(home);
137
+ if (!c.ok) {
138
+ err(io, `${c.note}; fix or remove the file first`);
139
+ return 1;
140
+ }
141
+ const next = structuredClone(c.doc);
142
+ delete next.migratedFrom;
143
+ const scope = typeof opts.scope === 'string' ? opts.scope : 'global';
144
+ const ref = scopeRef(next, scope, true);
145
+ if ('error' in ref) {
146
+ err(io, ref.error);
147
+ return 2;
148
+ }
149
+ for (const a of pos) {
150
+ const p = parseLimitAssignment(a);
151
+ if ('error' in p) {
152
+ err(io, p.error);
153
+ return 2;
154
+ }
155
+ if (p.value === null && !ref.isGlobal) {
156
+ delete ref.set[p.key];
157
+ continue;
158
+ }
159
+ ref.set[p.key] = p.value;
160
+ }
161
+ const issues = validateLimits(next);
162
+ if (issues.length) {
163
+ for (const i of issues)
164
+ err(io, `${i.path}: ${i.msg}`);
165
+ return 2;
166
+ }
167
+ const l = loadLimits(home);
168
+ const plan = planLimitsSave(home, next);
169
+ if (!plan.changes.length && l.ok) {
170
+ out(io, 'unchanged');
171
+ return 0;
172
+ }
173
+ out(io, `limits ${scope}:`);
174
+ const confirmed = await confirmPlan(io, deps, plan, l.ok ? l.unjournaled : null);
175
+ if (!confirmed)
176
+ return 1;
177
+ const r = saveLimits(home, next, { confirmed, baseline: plan.baseline, now: now() });
178
+ if (!r.ok) {
179
+ err(io, r.why);
180
+ return 1;
181
+ }
182
+ if (opts.json) {
183
+ out(io, JSON.stringify({ saved: true, hash: r.hash, changes: r.changes }, null, 2));
184
+ return 0;
185
+ }
186
+ out(io, `saved (journaled, ${r.hash}) · lowered limits apply at once, even to a running real run`);
187
+ return 0;
188
+ }
189
+ function diffCmd(argv, io, home) {
190
+ const { opts } = parseArgs(argv, ['json']);
191
+ const l = loadLimits(home);
192
+ let before;
193
+ let after;
194
+ if (typeof opts.file === 'string') {
195
+ let raw;
196
+ try {
197
+ raw = JSON.parse(readFileSync(opts.file, 'utf8'));
198
+ }
199
+ catch (e) {
200
+ err(io, `cannot read ${opts.file}: ${e.message.slice(0, 80)}`);
201
+ return 2;
202
+ }
203
+ const issues = validateLimits(raw);
204
+ if (issues.length) {
205
+ for (const i of issues)
206
+ err(io, `${i.path}: ${i.msg}`);
207
+ return 2;
208
+ }
209
+ before = l.ok ? l.doc : null;
210
+ after = raw;
211
+ }
212
+ else {
213
+ if (!l.ok) {
214
+ err(io, l.why);
215
+ return 1;
216
+ }
217
+ if (!l.unjournaled) {
218
+ out(io, 'limits.json matches dexterm\'s last save');
219
+ return 0;
220
+ }
221
+ if (opts.json) {
222
+ out(io, JSON.stringify({ unjournaled: l.unjournaled }, null, 2));
223
+ return 0;
224
+ }
225
+ out(io, 'limits.json vs dexterm\'s last journaled save:');
226
+ for (const c of l.unjournaled)
227
+ out(io, ` ${changeLine(c)}`);
228
+ if (raises(l.unjournaled).length)
229
+ out(io, 'it RAISES limits: a real run refuses it until you accept it (dexterm autotrade limits accept)');
230
+ return 0;
231
+ }
232
+ const changes = diffLimits(before, after);
233
+ if (opts.json) {
234
+ out(io, JSON.stringify({ changes }, null, 2));
235
+ return 0;
236
+ }
237
+ if (!changes.length) {
238
+ out(io, 'no change');
239
+ return 0;
240
+ }
241
+ for (const c of changes)
242
+ out(io, ` ${changeLine(c)}`);
243
+ return 0;
244
+ }
245
+ async function resetCmd(argv, io, home, deps, now) {
246
+ const { opts } = parseArgs(argv);
247
+ const c = current(home);
248
+ if (!c.ok) {
249
+ err(io, c.note ?? 'limits unreadable');
250
+ return 1;
251
+ }
252
+ const next = structuredClone(c.doc);
253
+ delete next.migratedFrom;
254
+ const scope = typeof opts.scope === 'string' ? opts.scope : 'global';
255
+ const ref = scopeRef(next, scope, false);
256
+ if ('error' in ref) {
257
+ err(io, ref.error);
258
+ return 2;
259
+ }
260
+ const key = typeof opts.key === 'string' ? parseLimitAssignment(`${opts.key}=0`) : null;
261
+ if (key && 'error' in key) {
262
+ err(io, key.error);
263
+ return 2;
264
+ }
265
+ if (!ref.set) {
266
+ out(io, `${scope} has nothing set`);
267
+ return 0;
268
+ }
269
+ if (key)
270
+ delete ref.set[key.key];
271
+ else if (ref.isGlobal)
272
+ next.global = {};
273
+ else if (scope === 'real' || scope === 'paper')
274
+ delete next.modes[scope];
275
+ else {
276
+ const [kind, id] = scope.split(':');
277
+ delete next[kind === 'strategy' ? 'strategies' : 'accounts'][id];
278
+ }
279
+ const l = loadLimits(home);
280
+ const plan = planLimitsSave(home, next);
281
+ if (!plan.changes.length) {
282
+ out(io, 'unchanged');
283
+ return 0;
284
+ }
285
+ out(io, `reset ${scope}${key ? `.${key.key}` : ''} (global numbers fall back to the placeholder defaults):`);
286
+ const confirmed = await confirmPlan(io, deps, plan, l.ok ? l.unjournaled : null);
287
+ if (!confirmed)
288
+ return 1;
289
+ const r = saveLimits(home, next, { confirmed, baseline: plan.baseline, action: 'limits-reset', now: now() });
290
+ if (!r.ok) {
291
+ err(io, r.why);
292
+ return 1;
293
+ }
294
+ out(io, `saved (journaled, ${r.hash})`);
295
+ return 0;
296
+ }
297
+ async function acceptCmd(io, home, deps, now) {
298
+ const l = loadLimits(home);
299
+ if (!l.ok) {
300
+ err(io, l.why);
301
+ return 1;
302
+ }
303
+ if (!l.unjournaled) {
304
+ out(io, 'limits.json already matches dexterm\'s last save');
305
+ return 0;
306
+ }
307
+ out(io, 'accept limits.json as it is now:');
308
+ const plan = planLimitsSave(home, l.doc);
309
+ const confirmed = await confirmPlan(io, deps, plan);
310
+ if (!confirmed)
311
+ return 1;
312
+ // task-1654 r4 (verify F4): belt and braces. The plan and the integrity check share one baseline, so they agree;
313
+ // should they ever not, an outside edit that raises anything still needs the typed phrase here.
314
+ const outside = raises(l.unjournaled).map((c) => `${c.scope}.${c.key}`);
315
+ if (outside.some((k) => !confirmed.includes(k))) {
316
+ err(io, `the edit raises ${outside.join(', ')} but the planned save does not show it: nothing accepted (dexterm autotrade limits diff)`);
317
+ return 1;
318
+ }
319
+ const r = saveLimits(home, l.doc, { confirmed, baseline: plan.baseline, action: 'limits-accept', now: now() });
320
+ if (!r.ok) {
321
+ err(io, r.why);
322
+ return 1;
323
+ }
324
+ out(io, `accepted (journaled, ${r.hash})`);
325
+ return 0;
326
+ }
327
+ function explainCmd(argv, io) {
328
+ const want = argv[0] ? parseLimitAssignment(`${argv[0]}=0`) : null;
329
+ if (want && 'error' in want) {
330
+ err(io, want.error);
331
+ return 2;
332
+ }
333
+ for (const f of LIMIT_FIELDS) {
334
+ if (want && f.key !== want.key)
335
+ continue;
336
+ const d = DEFAULT_LIMITS[f.key];
337
+ out(io, `${f.key} (${f.group}, ${f.unit}, ${f.dir === 'max' ? 'a cap: smaller is stricter' : 'a floor: larger is stricter'}; placeholder ${d === undefined ? 'off' : d})`);
338
+ out(io, ` ${f.help}`);
339
+ }
340
+ if (!want)
341
+ out(io, 'scopes: global (replaces the placeholder) · real / paper (a mode) · strategy:<id> · account:<id>; every scope but global can only tighten');
342
+ return 0;
343
+ }
344
+ // ───────────────────────────── the first-run wizard (arming) ─────────────────────────────
345
+ /** The numbers the wizard asks for, in order. Everything else keeps its placeholder (shown) or stays off. */
346
+ export const WIZARD_KEYS = ['perTradeSol', 'dailySol', 'buysPerDay', 'maxOpenPositions', 'dailyLossSol', 'executorSilenceMin'];
347
+ /**
348
+ * No limits saved yet: ask for YOUR numbers on the real terminal (Enter keeps the placeholder shown in brackets).
349
+ * Returns the `global` scope to save once the arming phrase is typed, or null (cancelled / not a terminal).
350
+ */
351
+ export async function limitsWizard(io, deps) {
352
+ if (!isTty(io, deps))
353
+ return null;
354
+ out(io, 'FIRST REAL RUN: set your own limits (they live outside the model; only you change them, on this terminal).');
355
+ out(io, 'The numbers in brackets are placeholders, not advice. Enter keeps one; "off" turns a limit off.');
356
+ const g = {};
357
+ for (const k of WIZARD_KEYS) {
358
+ const f = limitField(k);
359
+ let done = false;
360
+ for (let tries = 0; tries < 3 && !done; tries++) {
361
+ const ans = await typedLine(io, deps, ` ${k}: ${f.help}\n ${f.unit} [${DEFAULT_LIMITS[k] ?? 'off'}]: `);
362
+ if (ans === null)
363
+ return null;
364
+ const t = ans.trim();
365
+ if (!t) {
366
+ g[k] = DEFAULT_LIMITS[k] ?? null;
367
+ done = true;
368
+ continue;
369
+ }
370
+ const p = parseLimitAssignment(`${k}=${t}`);
371
+ if ('error' in p) {
372
+ err(io, p.error);
373
+ continue;
374
+ }
375
+ const issues = validateLimits({ ...emptyLimits(), global: { ...g, [k]: p.value } });
376
+ const mine = issues.filter((i) => i.path === `/global/${k}`);
377
+ if (mine.length) {
378
+ err(io, mine.map((i) => i.msg).join('; '));
379
+ continue;
380
+ }
381
+ g[k] = p.value;
382
+ done = true;
383
+ }
384
+ if (!done) {
385
+ err(io, `no valid ${k}; nothing saved`);
386
+ return null;
387
+ }
388
+ }
389
+ for (const k of LIMIT_KEYS)
390
+ if (!(k in g) && DEFAULT_LIMITS[k] !== undefined)
391
+ g[k] = DEFAULT_LIMITS[k];
392
+ return g;
393
+ }
@@ -0,0 +1,63 @@
1
+ // `dexterm autotrade receipt …` (task-1645, 0.2.0 card A): what your executor did with a LIVE TRADE FIRE signal, as
2
+ // DATA only. A receipt never makes dexterm do anything: it moves a real-mode position from `unconfirmed` to
3
+ // executed / rejected / failed and measures the drift between the signal and the fill (ledger, Q screen, audit).
4
+ // Your executor can also append `dexterm-trade-receipt/1` lines to autotrade/real/receipts.jsonl itself.
5
+ import { dextermHome } from '../../../custom/src/index.js';
6
+ import { Outbox, RECEIPT_SCHEMA, RECEIPT_STATUSES, appendReceipt, driftReport, readReceipts, receiptMismatch } from '../../../autotrade/src/index.js';
7
+ import { parseArgs } from "./strategies.js";
8
+ export const RECEIPT_USAGE = 'dexterm autotrade receipt <nonce|signal-id> executed|rejected|failed [--price USD] [--size-sol N] [--tokens N] [--tx SIGNATURE] [--reason TEXT] [--executor NAME] | receipt list [--json]';
9
+ export function receiptCmd(argv, io, o = {}) {
10
+ const { pos, opts } = parseArgs(argv, ['json']);
11
+ const home = dextermHome(io.env);
12
+ const now = o.now ?? Date.now;
13
+ const ob = Outbox.open(home, { channels: [], noLock: true });
14
+ if (pos[0] === 'list' || !pos.length) {
15
+ const d = driftReport(ob.signals(), readReceipts(home).receipts, now());
16
+ if (opts.json) {
17
+ io.stdout.write(`${JSON.stringify(d, null, 2)}\n`);
18
+ return 0;
19
+ }
20
+ if (!d.rows.length) {
21
+ io.stdout.write('no trade-fire signals yet\n');
22
+ return 0;
23
+ }
24
+ io.stdout.write(`signals vs receipts · executed ${d.counts.executed} · rejected ${d.counts.rejected} · failed ${d.counts.failed} · unconfirmed ${d.unconfirmed}${d.orphanReceipts ? ` · ${d.orphanReceipts} orphan receipt(s) ignored` : ''}\n`);
25
+ for (const r of d.rows.slice(-30))
26
+ io.stdout.write(` #${r.nonce} ${r.side.toUpperCase()} ${r.symbol} ${r.signalSol} SOL · ${r.state}${r.filledSol !== null ? ` · filled ${r.filledSol} SOL (${r.sizeDriftPct}%)` : ''}${r.priceDriftPct !== null ? ` · price ${r.priceDriftPct}%` : ''}${r.conflict ? ' · CONFLICTING receipts' : ''}\n`);
27
+ return 0;
28
+ }
29
+ const [ref, status] = pos;
30
+ if (!RECEIPT_STATUSES.includes(status)) {
31
+ io.stderr.write(`usage: ${RECEIPT_USAGE}\n`);
32
+ return 2;
33
+ }
34
+ const sig = /^\d+$/.test(ref) ? ob.get(Number(ref)) : ob.findById(ref);
35
+ const num = (k) => { if (opts[k] === undefined)
36
+ return null; const v = Number(opts[k]); return Number.isFinite(v) && v > 0 ? v : 'bad'; };
37
+ const price = num('price'), sizeSol = num('size-sol'), tokens = num('tokens');
38
+ if (price === 'bad' || sizeSol === 'bad' || tokens === 'bad') {
39
+ io.stderr.write('dexterm autotrade receipt: --price, --size-sol and --tokens are positive numbers\n');
40
+ return 2;
41
+ }
42
+ if (!sig) {
43
+ io.stderr.write(`dexterm autotrade receipt: no signal ${JSON.stringify(ref)} in the outbox\n`);
44
+ return 1;
45
+ }
46
+ const tx = typeof opts.tx === 'string' ? opts.tx : null;
47
+ const fill = price !== null || sizeSol !== null || tokens !== null || tx ? { priceUsd: price, sizeSol, tokens, txSig: tx } : null;
48
+ const r = { schema: RECEIPT_SCHEMA, signalId: sig.id, nonce: sig.nonce, accountId: sig.accountId, status: status, at: now(), fill, reason: typeof opts.reason === 'string' ? opts.reason : null, executor: typeof opts.executor === 'string' ? opts.executor : null };
49
+ const why = receiptMismatch(r, sig);
50
+ if (why) {
51
+ io.stderr.write(`dexterm autotrade receipt: ${why}\n`);
52
+ return 1;
53
+ }
54
+ try {
55
+ appendReceipt(home, r);
56
+ }
57
+ catch (e) {
58
+ io.stderr.write(`dexterm autotrade receipt: ${e.message}\n`);
59
+ return 2;
60
+ }
61
+ io.stdout.write(`receipt recorded · #${sig.nonce} ${status} (data only: dexterm places nothing)\n`);
62
+ return 0;
63
+ }
@@ -0,0 +1,79 @@
1
+ // `dexterm autotrade report …` (task-1654, 0.2.0 card E): the daily / weekly digest per strategy and mode.
2
+ //
3
+ // report [--period day|week] [--strategy ID] [--json] print it
4
+ // report --telegram send it to your paired chat through your own bot
5
+ // report --schedule daily|weekly|off a run with the bot on sends it by itself (default off)
6
+ //
7
+ // Read-only over the run ledgers, the outbox and the receipts. The numbers are what they are: the digest always
8
+ // carries the win rate's interval and a sample-size note, never a gain claim.
9
+ import { dextermHome } from '../../../custom/src/index.js';
10
+ import { buildDigest, digestLines, effectiveLimits, executorHealth, loadDigestState, loadLimits, readReceipts, saveDigestState, simExecutor, loadPreset } from '../../../autotrade/src/index.js';
11
+ import { parseArgs } from "./strategies.js";
12
+ import { startTelegramHost } from "./telegram-host.js";
13
+ const out = (io, s) => { io.stdout.write(`${s}\n`); };
14
+ const err = (io, s) => { io.stderr.write(`dexterm autotrade report: ${s}\n`); };
15
+ export function digestFor(home, o) {
16
+ const l = loadLimits(home);
17
+ const e = effectiveLimits(l.ok ? l.doc : null, { strategyId: '-', accountId: '-', mode: 'real' });
18
+ const ex = l.ok ? executorHealth(home, { now: o.now, windowMin: e.values.executorSilenceMin ?? null, receipts: readReceipts(home).receipts }) : null;
19
+ return buildDigest(home, { ...o, executor: ex && (ex.lastSeenAt || l.ok) ? ex : null });
20
+ }
21
+ /** Send lines through the user's own bot (one-shot: start, broadcast, stop). */
22
+ export async function sendDigest(home, lines, o) {
23
+ const t = await startTelegramHost({ home, exec: simExecutor(loadPreset('scalp').execution), say: o.say, ...o.telegram });
24
+ if (!t.ok)
25
+ return { ok: false, why: t.why };
26
+ try {
27
+ const before = t.host.service.audit.events.length;
28
+ await t.host.service.hub.broadcast({ kind: 'text', badge: o.real ? 'LIVE' : 'PAPER', lines }, 'report');
29
+ if (t.host.service.audit.events.slice(before).some((e) => e.kind === 'out_failed'))
30
+ return { ok: false, why: 'telegram send failed' };
31
+ return { ok: true };
32
+ }
33
+ finally {
34
+ await t.host.stop();
35
+ }
36
+ }
37
+ export async function reportCmd(argv, io, deps = {}) {
38
+ const home = dextermHome(io.env);
39
+ const now = deps.now ?? Date.now;
40
+ const { pos, opts } = parseArgs(argv, ['json', 'telegram']);
41
+ if (pos.length) {
42
+ err(io, `unexpected ${pos.join(' ')}`);
43
+ return 2;
44
+ }
45
+ if (opts.schedule !== undefined) {
46
+ const v = String(opts.schedule);
47
+ if (!['daily', 'weekly', 'off'].includes(v)) {
48
+ err(io, '--schedule daily|weekly|off');
49
+ return 2;
50
+ }
51
+ const s = loadDigestState(home);
52
+ saveDigestState(home, { ...s, schedule: v });
53
+ out(io, v === 'off' ? 'scheduled digest off' : `scheduled digest: ${v} (sent by a run with your Telegram bot on${v === 'weekly' ? ', on Mondays' : ', after local midnight'})`);
54
+ return 0;
55
+ }
56
+ const period = opts.period === 'week' ? 'week' : opts.period === undefined || opts.period === 'day' ? 'day' : null;
57
+ if (!period) {
58
+ err(io, '--period day|week');
59
+ return 2;
60
+ }
61
+ const strategyId = typeof opts.strategy === 'string' ? opts.strategy : undefined;
62
+ const d = digestFor(home, { period, now: now(), strategyId });
63
+ if (opts.json) {
64
+ out(io, JSON.stringify(d, null, 2));
65
+ return 0;
66
+ }
67
+ const lines = digestLines(d);
68
+ for (const l of lines)
69
+ out(io, l);
70
+ if (opts.telegram) {
71
+ const r = await sendDigest(home, lines, { say: (s) => io.stderr.write(`${s}\n`), real: d.strategies.some((x) => x.mode === 'real'), telegram: deps.telegram });
72
+ if (!r.ok) {
73
+ err(io, `not sent: ${r.why}`);
74
+ return 1;
75
+ }
76
+ out(io, 'sent to your paired Telegram chat');
77
+ }
78
+ return 0;
79
+ }
@@ -0,0 +1,79 @@
1
+ // `dexterm autotrade requests list|run|dismiss` (task-1530): the user's side of the MCP `backtest_request` tool. An
2
+ // agent can only FILE a request ($DEXTERM_HOME/autotrade/requests/<id>.json); nothing runs until the user runs it here.
3
+ // `run` is the ordinary `dexterm autotrade backtest <id> --days N` on the recorded history (paper, no network, no
4
+ // model), and its one-line result is written back so the agent reads it with `backtest_requests`.
5
+ import { BacktestRequests, isRequestId } from '../../../mcp/src/index.js';
6
+ import { dextermHomeDir } from '../../../data/src/trade/ledger/index.js';
7
+ import { backtestCmd } from "./backtest.js";
8
+ const USAGE = 'dexterm autotrade requests [list [--all] [--json] | run <br_id> | dismiss <br_id>]';
9
+ const when = (t) => new Date(t).toISOString().slice(0, 16).replace('T', ' ');
10
+ function line(r) {
11
+ const head = `${r.requestId} ${r.status.padEnd(9)} ${when(r.createdAt)} ${r.id}${r.version ? `@${r.version}` : ''} · ${r.days}d${r.everyCandidate ? ' · every candidate' : ''} · by ${r.actor}`;
12
+ return `${head}\n why: ${r.why}${r.result ? `\n result: ${r.result}` : ''}`;
13
+ }
14
+ export async function requestsCmd(argv, io) {
15
+ const files = new BacktestRequests(dextermHomeDir(io.env));
16
+ const sub = argv[0] ?? 'list';
17
+ if (sub === 'list') {
18
+ const all = argv.includes('--all');
19
+ const rows = files.list(all ? {} : { status: 'pending' });
20
+ if (argv.includes('--json')) {
21
+ io.stdout.write(`${JSON.stringify({ requests: rows })}\n`);
22
+ return 0;
23
+ }
24
+ if (!rows.length) {
25
+ io.stdout.write(all ? 'no backtest requests\n' : 'no pending backtest requests (--all shows the resolved ones)\n');
26
+ return 0;
27
+ }
28
+ for (const r of rows)
29
+ io.stdout.write(`${line(r)}\n`);
30
+ io.stdout.write('\nAn agent filed these; nothing ran. `dexterm autotrade requests run <id>` runs one (paper, recorded data); `dismiss <id>` drops it.\n');
31
+ return 0;
32
+ }
33
+ if (sub !== 'run' && sub !== 'dismiss') {
34
+ io.stderr.write(`dexterm autotrade requests: unknown action ${JSON.stringify(sub)}\n ${USAGE}\n`);
35
+ return 2;
36
+ }
37
+ const id = argv[1];
38
+ if (!isRequestId(id)) {
39
+ io.stderr.write(`dexterm autotrade requests ${sub}: pass a request id (br_…, from \`dexterm autotrade requests\`)\n`);
40
+ return 2;
41
+ }
42
+ const r = files.get(id);
43
+ if (!r) {
44
+ io.stderr.write(`dexterm autotrade requests: no request ${id}\n`);
45
+ return 2;
46
+ }
47
+ if (r.status !== 'pending') {
48
+ io.stderr.write(`dexterm autotrade requests: ${id} is already ${r.status}\n`);
49
+ return 2;
50
+ }
51
+ if (sub === 'dismiss') {
52
+ files.resolve(id, 'dismissed', 'dismissed by the user');
53
+ io.stdout.write(`${id} dismissed\n`);
54
+ return 0;
55
+ }
56
+ // run: the ordinary backtest, its JSON captured for the one-line result, the human report shown after
57
+ let out = '';
58
+ let err = '';
59
+ const args = [r.id, '--days', String(r.days), ...(r.everyCandidate ? ['--every-candidate'] : []), '--json'];
60
+ const rc = await backtestCmd(args, { ...io, stdout: { write: (s) => { out += s; return true; } }, stderr: { write: (s) => { err += s; return true; } } });
61
+ if (rc !== 0) {
62
+ const why = err.trim().split('\n')[0] || `the backtest exited ${rc}`;
63
+ files.resolve(id, 'failed', why);
64
+ io.stderr.write(`${id} failed: ${why}\n`);
65
+ return rc;
66
+ }
67
+ let result;
68
+ try {
69
+ const rep = JSON.parse(out);
70
+ const wr = rep.winRate ? `${rep.winRate.value}% (95% ${rep.winRate.lo}–${rep.winRate.hi}%)` : '—';
71
+ result = `paper backtest ${r.days}d: ${rep.trades ?? 0} trades · win ${wr} · net $${(rep.netUsd ?? 0).toFixed(2)} (${rep.netPct ?? 0}%) · max drawdown ${rep.maxDrawdownPct ?? 0}%`;
72
+ }
73
+ catch {
74
+ result = 'the backtest ran but its report was not readable JSON';
75
+ }
76
+ files.resolve(id, 'done', result);
77
+ io.stdout.write(`${id} done: ${result}\n (the agent reads this with backtest_requests; the full report: \`dexterm autotrade backtest ${r.id} --days ${r.days}\`)\n`);
78
+ return 0;
79
+ }