dexterm-cli 0.1.6 → 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 (331) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/story.js +11 -2
  3. package/dist/packages/agents/src/tiers.js +27 -2
  4. package/dist/packages/agents/src/tools.js +30 -2
  5. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  6. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  7. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  8. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  9. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  10. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  11. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  12. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  13. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  14. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  15. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  16. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  17. package/dist/packages/autotrade/src/account.js +74 -0
  18. package/dist/packages/autotrade/src/backtest.js +357 -0
  19. package/dist/packages/autotrade/src/basis-text.js +32 -0
  20. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  21. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  22. package/dist/packages/autotrade/src/chat/config.js +93 -0
  23. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  24. package/dist/packages/autotrade/src/chat/control.js +103 -0
  25. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  26. package/dist/packages/autotrade/src/chat/format.js +71 -0
  27. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  28. package/dist/packages/autotrade/src/chat/index.js +18 -0
  29. package/dist/packages/autotrade/src/chat/notify.js +292 -0
  30. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  31. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  32. package/dist/packages/autotrade/src/chat/secrets.js +99 -0
  33. package/dist/packages/autotrade/src/chat/service.js +248 -0
  34. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  35. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  36. package/dist/packages/autotrade/src/engine.js +387 -0
  37. package/dist/packages/autotrade/src/execution-port.js +97 -0
  38. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  39. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  40. package/dist/packages/autotrade/src/fleet.js +639 -0
  41. package/dist/packages/autotrade/src/index.js +53 -0
  42. package/dist/packages/autotrade/src/ledger.js +108 -0
  43. package/dist/packages/autotrade/src/live-source.js +182 -0
  44. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  45. package/dist/packages/autotrade/src/monitor.js +190 -0
  46. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  47. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  48. package/dist/packages/autotrade/src/replay.js +198 -0
  49. package/dist/packages/autotrade/src/runner-config.js +99 -0
  50. package/dist/packages/autotrade/src/runner.js +872 -0
  51. package/dist/packages/autotrade/src/safety.js +243 -0
  52. package/dist/packages/autotrade/src/session.js +269 -0
  53. package/dist/packages/autotrade/src/sessions.js +503 -0
  54. package/dist/packages/autotrade/src/store.js +250 -0
  55. package/dist/packages/autotrade/src/strategy.js +256 -0
  56. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  57. package/dist/packages/autotrade/src/yolo.js +140 -0
  58. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  59. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  60. package/dist/packages/cli/src/ai-cmd.js +412 -0
  61. package/dist/packages/cli/src/analyst/backend.js +82 -15
  62. package/dist/packages/cli/src/analyst/codex.js +14 -9
  63. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  64. package/dist/packages/cli/src/analyst/session.js +5 -2
  65. package/dist/packages/cli/src/app-help-gen.js +44 -8
  66. package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
  67. package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
  68. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  69. package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
  70. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  71. package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
  72. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  73. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  74. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  75. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  76. package/dist/packages/cli/src/autotrade-cmd.js +168 -0
  77. package/dist/packages/cli/src/display.js +156 -17
  78. package/dist/packages/cli/src/doctor.js +62 -3
  79. package/dist/packages/cli/src/export.js +10 -2
  80. package/dist/packages/cli/src/index.js +1 -1
  81. package/dist/packages/cli/src/ledger-cmd.js +288 -0
  82. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  83. package/dist/packages/cli/src/live/index.js +23 -89
  84. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  85. package/dist/packages/cli/src/live/trade.js +12 -457
  86. package/dist/packages/cli/src/live/watchlist.js +1 -1
  87. package/dist/packages/cli/src/main.js +147 -190
  88. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  89. package/dist/packages/cli/src/plan-cmd.js +1 -1
  90. package/dist/packages/cli/src/pnl-app.js +31 -21
  91. package/dist/packages/cli/src/pnl-card.js +158 -21
  92. package/dist/packages/cli/src/popup.js +22 -6
  93. package/dist/packages/cli/src/record-cmd.js +141 -20
  94. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  95. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  96. package/dist/packages/cli/src/screens/analyst.js +3 -2
  97. package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
  98. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  99. package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
  100. package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
  101. package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
  102. package/dist/packages/cli/src/screens/brain.js +37 -11
  103. package/dist/packages/cli/src/screens/overlay.js +5 -2
  104. package/dist/packages/cli/src/screens/plans.js +5 -17
  105. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  106. package/dist/packages/cli/src/screens/positions.js +6 -28
  107. package/dist/packages/cli/src/screens/ticket.js +38 -205
  108. package/dist/packages/cli/src/screens/token.js +3 -3
  109. package/dist/packages/cli/src/state.js +25 -19
  110. package/dist/packages/cli/src/telegram-cmd.js +451 -0
  111. package/dist/packages/cli/src/trade.js +3 -128
  112. package/dist/packages/cli/src/version.js +1 -1
  113. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  114. package/dist/packages/cli/src/wallet-command.js +8 -11
  115. package/dist/packages/cli/src/widgets.js +0 -12
  116. package/dist/packages/custom/src/config.js +20 -2
  117. package/dist/packages/custom/src/schema.js +35 -10
  118. package/dist/packages/custom/src/workspace.js +3 -3
  119. package/dist/packages/data/src/basis.js +191 -0
  120. package/dist/packages/data/src/detail.js +53 -2
  121. package/dist/packages/data/src/index.js +0 -14
  122. package/dist/packages/data/src/store.js +14 -5
  123. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  124. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  125. package/dist/packages/data/src/trade/bridge.js +4 -102
  126. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  127. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  128. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  129. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  130. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  131. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  132. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  133. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  134. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  135. package/dist/packages/data/src/trade/plans/index.js +0 -1
  136. package/dist/packages/data/src/trade/plans/service.js +3 -27
  137. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  138. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  139. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  140. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  141. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  142. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  143. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  144. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  145. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  146. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  147. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  148. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  149. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  150. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
  151. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  152. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  153. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  154. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  155. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  156. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  157. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  158. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  159. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  160. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  161. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  162. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  163. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  164. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  165. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  166. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  167. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  168. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
  169. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  170. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  171. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  172. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  173. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
  174. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  175. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  176. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  177. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  178. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  179. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  180. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
  181. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  182. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  183. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  184. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  186. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  188. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
  189. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  194. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  195. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  197. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  201. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  202. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  203. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  204. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  205. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  208. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  209. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  210. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  211. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  233. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  235. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  236. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  237. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  238. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  240. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  242. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  243. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  244. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  260. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  262. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  263. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  270. package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
  271. package/dist/packages/mcp/src/app-help.js +1 -1
  272. package/dist/packages/mcp/src/autotrade-tools.js +944 -0
  273. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  274. package/dist/packages/mcp/src/engine/config.js +159 -0
  275. package/dist/packages/mcp/src/engine/index.js +10 -0
  276. package/dist/packages/mcp/src/engine/loop.js +101 -0
  277. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  278. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  279. package/dist/packages/mcp/src/engine/probe.js +110 -0
  280. package/dist/packages/mcp/src/engine/reason.js +35 -0
  281. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  282. package/dist/packages/mcp/src/index.js +7 -1
  283. package/dist/packages/mcp/src/ledger-tools.js +346 -0
  284. package/dist/packages/mcp/src/paper-agent.js +2 -0
  285. package/dist/packages/mcp/src/server.js +127 -19
  286. package/dist/packages/packs/src/backtest.js +3 -1
  287. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  288. package/dist/packages/popup/src/pages/chart.css +9 -0
  289. package/dist/packages/popup/src/pages/chart.html +3 -1
  290. package/dist/packages/popup/src/pages/chart.js +104 -7
  291. package/dist/packages/popup/src/protocol.js +2 -0
  292. package/dist/packages/popup/src/server.js +4 -0
  293. package/dist/packages/recorder/src/index.js +2 -0
  294. package/dist/packages/recorder/src/recorder.js +63 -6
  295. package/dist/packages/recorder/src/retention.js +100 -0
  296. package/dist/packages/recorder/src/schema.js +34 -1
  297. package/dist/packages/recorder/src/ticks.js +237 -0
  298. package/dist/packages/tui/src/index.js +1 -0
  299. package/dist/packages/tui/src/keymap.js +21 -1
  300. package/dist/packages/tui/src/markers.js +219 -0
  301. package/dist/packages/tui/src/widgets.js +149 -2
  302. package/package.json +2 -1
  303. package/dist/packages/data/src/trade/caps.js +0 -323
  304. package/dist/packages/data/src/trade/executor.js +0 -933
  305. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  306. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  307. package/dist/packages/data/src/trade/signer/client.js +0 -197
  308. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  309. package/dist/packages/data/src/trade/signer/host.js +0 -97
  310. package/dist/packages/data/src/trade/signer/main.js +0 -121
  311. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  312. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  313. package/dist/packages/data/src/trade/signer/service.js +0 -400
  314. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  315. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  316. package/dist/packages/data/src/trade/swap-build.js +0 -108
  317. package/dist/packages/data/src/trade/token-delta.js +0 -201
  318. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  319. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  320. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  321. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  322. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  323. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  324. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  325. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  326. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  327. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  328. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  329. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  330. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  331. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -0,0 +1,288 @@
1
+ // `dexterm ledger <sub>` (task-1530): the unified TRADE LEDGER. One row per round trip from every place dexterm books a
2
+ // trade: the paper ticket (paper wallet), the agent paper desk (its journal), autotrade runs (their hash-chained run
3
+ // ledgers) and the live signer (its trade ledger). Each row carries the strategy id + version + hash, the declared exit
4
+ // profile and entry reason (and where each came from), the exit reason, size and % of book, entry / exit price AND
5
+ // market cap, fees, impact, hold, rug flag, wallet mode and the model sessions that made it.
6
+ //
7
+ // Reads never write: every command folds $DEXTERM_HOME/ledger/trades.jsonl plus what the sources hold that the file
8
+ // does not yet (shown as "pending"). `sync` appends those to the file (append-only, sha256-chained; a broken file is
9
+ // refused, never rewritten) and `verify` recomputes the chain. No network, no model, no key, no signer.
10
+ import { writeFileSync } from 'node:fs';
11
+ export const LEDGER_HELP = `dexterm ledger: the unified trade ledger (paper, agent paper desk, autotrade runs, live). Read-only unless sync.
12
+
13
+ dexterm ledger trades [filters] [--limit N] [--json] one row per round trip, newest first
14
+ dexterm ledger positions [filters] [--json] the open trades (cost, % of book, held, profile)
15
+ dexterm ledger summary [filters] [--json] win rate (95% interval), P/L, drawdown, rug rate, by profile / exit / strategy / source / mode
16
+ dexterm ledger equity [filters] [--json] the realized equity curve and its max drawdown
17
+ dexterm ledger compare [filters] [--json] one row per strategy version: the forward-test table
18
+ dexterm ledger export csv|json|parquet-lite [filters] [--out FILE] (or --format csv|json|parquet-lite)
19
+ dexterm ledger card <tradeId> [--square] [--price] [--exact] [--sol-usd N] a PnL card (market cap at entry / exit)
20
+ dexterm ledger sync [--json] append what the sources hold to the ledger file
21
+ dexterm ledger verify [--json] recompute the ledger file's sha256 chain
22
+
23
+ filters: --source paper|desk|autotrade|live --mode paper|devnet|live --strategy ID[@VERSION|@HASH] --profile P
24
+ --exit REASON --mint MINT --run RUN_ID --account ID --days N --status open|closed --wallet-db PATH
25
+
26
+ The file is $DEXTERM_HOME/ledger/trades.jsonl (default ~/.dexterm). PAPER rows are simulated; filter by --mode
27
+ before comparing results. Not financial advice.`;
28
+ const VALUE_FLAGS = new Set(['source', 'mode', 'strategy', 'profile', 'exit', 'mint', 'run', 'account', 'days', 'status', 'limit', 'out', 'sol-usd', 'wallet-db', 'format']);
29
+ function parse(argv) {
30
+ const o = { _: [] };
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const a = argv[i];
33
+ if (!a.startsWith('--')) {
34
+ o._.push(a);
35
+ continue;
36
+ }
37
+ const eq = a.indexOf('=');
38
+ const k = eq > 0 ? a.slice(2, eq) : a.slice(2);
39
+ if (VALUE_FLAGS.has(k)) {
40
+ const v = eq > 0 ? a.slice(eq + 1) : argv[++i];
41
+ if (v === undefined || v === '')
42
+ return `--${k} needs a value`;
43
+ o[k] = v;
44
+ }
45
+ else
46
+ o[k] = true;
47
+ }
48
+ return o;
49
+ }
50
+ /** CLI flags → the MCP tool args shape, validated by the same filterFromArgs the ledger tools use. */
51
+ async function filterOf(o, now) {
52
+ const { filterFromArgs } = await import('../../mcp/src/index.js');
53
+ const a = {};
54
+ for (const [flag, key] of [['source', 'source'], ['mode', 'mode'], ['strategy', 'strategy'], ['profile', 'profile'], ['exit', 'exit_reason'], ['mint', 'mint'], ['run', 'run_id'], ['account', 'account'], ['status', 'status']]) {
55
+ if (typeof o[flag] === 'string')
56
+ a[key] = o[flag];
57
+ }
58
+ if (typeof o.days === 'string')
59
+ a.days = Number(o.days);
60
+ return filterFromArgs(a, now);
61
+ }
62
+ async function loadView(io, o, now) {
63
+ const at = await import('../../autotrade/src/index.js');
64
+ return at.loadLedgerView({ env: io.env, now, ...(typeof o['wallet-db'] === 'string' ? { walletDb: o['wallet-db'] } : {}) });
65
+ }
66
+ // ───────────────────────────── formatting ─────────────────────────────
67
+ const pad = (s, n) => (s.length >= n ? s.slice(0, n) : s + ' '.repeat(n - s.length));
68
+ const lpad = (s, n) => (s.length >= n ? s : ' '.repeat(n - s.length) + s);
69
+ const num = (x, d = 2) => (x === null || x === undefined || !Number.isFinite(x) ? '—' : x.toFixed(d));
70
+ const pct = (x) => (x === null || x === undefined || !Number.isFinite(x) ? '—' : `${x >= 0 ? '+' : ''}${x.toFixed(1)}%`);
71
+ const frac = (x) => (x === null || x === undefined ? '—' : `${(x * 100).toFixed(0)}%`);
72
+ export function fmtCap(v) {
73
+ if (v === null || v === undefined || !Number.isFinite(v))
74
+ return '—';
75
+ if (v >= 1e9)
76
+ return `$${(v / 1e9).toFixed(2)}B`;
77
+ if (v >= 1e6)
78
+ return `$${(v / 1e6).toFixed(2)}M`;
79
+ if (v >= 1e3)
80
+ return `$${(v / 1e3).toFixed(1)}K`;
81
+ return `$${v.toFixed(0)}`;
82
+ }
83
+ const hold = (sec) => (sec < 90 ? `${Math.round(sec)}s` : sec < 5400 ? `${Math.round(sec / 60)}m` : sec < 172800 ? `${(sec / 3600).toFixed(1)}h` : `${(sec / 86400).toFixed(1)}d`);
84
+ const when = (t) => new Date(t).toISOString().slice(0, 16).replace('T', ' ');
85
+ const stratOf = (t) => (t.strategy ? `${t.strategy.id}@${t.strategy.version ?? (t.strategy.hash ?? '').slice(0, 8)}` : 'manual');
86
+ const tagOf = (id, source) => (id ? (source === 'structured' || source === 'rule' ? id : `${id}*`) : '—');
87
+ function banner(v, rows) {
88
+ const out = [];
89
+ const f = v.file;
90
+ out.push(f.exists ? (f.check.ok ? `ledger ${v.path}: ${f.events.length} events, chain verified` : `ledger ${v.path}: FAILS its check at line ${f.check.at}: ${f.check.error}`) : `ledger ${v.path}: not written yet (\`dexterm ledger sync\` writes it)`);
91
+ if (v.pending.length)
92
+ out.push(` + ${v.pending.length} events from the sources not in the file yet (shown here; \`dexterm ledger sync\` appends them)`);
93
+ for (const s of v.sources)
94
+ if (s.problem)
95
+ out.push(` ! ${s.problem}`);
96
+ const modes = [...new Set(rows.map((t) => t.walletMode))];
97
+ if (modes.length > 1)
98
+ out.push(' PAPER, DEVNET and LIVE trades are mixed here: use --mode before comparing results.');
99
+ else if (modes[0] === 'paper')
100
+ out.push(' PAPER (simulated) trades.');
101
+ return out;
102
+ }
103
+ function tradesTable(rows) {
104
+ const head = `${pad('opened', 16)} ${pad('symbol', 8)} ${pad('src', 9)} ${pad('mode', 6)} ${pad('strategy', 18)} ${pad('profile', 10)} ${pad('exit', 14)} ${lpad('size', 8)} ${lpad('%book', 6)} ${lpad('entry mc', 9)} ${lpad('exit mc', 9)} ${lpad('P/L', 8)} ${lpad('hold', 6)} ${lpad('MFE', 7)} ${lpad('MAE', 7)} id`;
105
+ const out = [head];
106
+ for (const t of rows) {
107
+ const size = t.sizeSol !== null ? `${num(t.sizeSol, 3)}◎` : t.sizeUsd !== null ? `$${num(t.sizeUsd, 0)}` : '—';
108
+ out.push([
109
+ pad(when(t.openedAt), 16), ' ', pad(t.symbol, 8), ' ', pad(t.source, 9), ' ', pad(t.walletMode, 6), ' ', pad(stratOf(t), 18), ' ',
110
+ pad(tagOf(t.profile.id, t.profile.source), 10), ' ', pad(t.status === 'open' ? 'open' : `${t.exitReason.code ?? 'untagged'}${t.rug ? ' RUG' : ''}`, 14), ' ',
111
+ lpad(size, 8), ' ', lpad(t.pctOfBook === null ? '—' : `${t.pctOfBook.toFixed(0)}%`, 6), ' ', lpad(fmtCap(t.entryMcapUsd), 9), ' ', lpad(fmtCap(t.exitMcapUsd), 9), ' ',
112
+ lpad(t.status === 'open' ? '—' : pct(t.pnlPct), 8), ' ', lpad(hold(t.holdSec), 6), ' ', lpad(pct(t.mfePct), 7), ' ', lpad(pct(t.maePct), 7), ' ', t.tradeId,
113
+ ].join(''));
114
+ }
115
+ return out;
116
+ }
117
+ // ───────────────────────────── subcommands ─────────────────────────────
118
+ export async function ledgerCommand(argv, io) {
119
+ const sub = argv[0];
120
+ if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
121
+ io.stdout.write(`${LEDGER_HELP}\n`);
122
+ return 0;
123
+ }
124
+ const o = parse(argv.slice(1));
125
+ if (typeof o === 'string') {
126
+ io.stderr.write(`dexterm ledger: ${o}\n`);
127
+ return 2;
128
+ }
129
+ const now = Date.now();
130
+ const json = o.json === true;
131
+ const out = (s) => io.stdout.write(s.endsWith('\n') ? s : `${s}\n`);
132
+ try {
133
+ if (sub === 'verify') {
134
+ const { readTradeLedger, tradeLedgerPath } = await import('../../data/src/trade/ledger/index.js');
135
+ const path = tradeLedgerPath(io.env);
136
+ const r = readTradeLedger(path);
137
+ if (json)
138
+ out(JSON.stringify({ path, exists: r.exists, ...r.check }));
139
+ else if (!r.exists)
140
+ out(`${path}: not written yet (nothing to verify)`);
141
+ else
142
+ out(r.check.ok ? `${path}: ${r.check.events} events, chain verified, head ${r.check.head.slice(0, 16)}` : `${path}: FAILS at line ${r.check.at}: ${r.check.error}`);
143
+ return r.exists && !r.check.ok ? 1 : 0;
144
+ }
145
+ if (sub === 'sync') {
146
+ const at = await import('../../autotrade/src/index.js');
147
+ const r = at.syncLedger({ env: io.env, now, ...(typeof o['wallet-db'] === 'string' ? { walletDb: o['wallet-db'] } : {}) });
148
+ const problems = r.view.sources.filter((s) => s.problem).map((s) => s.problem);
149
+ if (json)
150
+ out(JSON.stringify({ path: r.view.path, added: r.added, skipped: r.skipped, events: r.events, head: r.head, sources: r.view.sources }));
151
+ else {
152
+ out(`${r.view.path}: +${r.added} events (${r.skipped} already there), ${r.events} total, head ${r.head.slice(0, 16)}`);
153
+ for (const s of r.view.sources)
154
+ out(` ${pad(s.source, 9)} ${s.label}: ${s.events} source events, ${s.pending} new${s.note ? ` · ${s.note}` : ''}`);
155
+ for (const p of problems)
156
+ out(` ! ${p}`);
157
+ }
158
+ return 0;
159
+ }
160
+ const fr = await filterOf(o, now);
161
+ if (!fr.ok) {
162
+ io.stderr.write(`dexterm ledger: ${fr.error}\n`);
163
+ return 2;
164
+ }
165
+ const { filterTrades, summarize, equityCurve, compareStrategies, exportTrades, EXPORT_FORMATS } = await import('../../data/src/trade/ledger/index.js');
166
+ const v = await loadView(io, o, now);
167
+ const filter = sub === 'positions' ? { ...fr.filter, status: 'open' } : fr.filter;
168
+ const rows = filterTrades(v.trades, filter).sort((a, b) => b.openedAt - a.openedAt);
169
+ switch (sub) {
170
+ case 'trades':
171
+ case 'positions': {
172
+ const limit = typeof o.limit === 'string' ? Math.max(1, Math.min(10_000, Number.parseInt(o.limit, 10) || 50)) : sub === 'positions' ? 10_000 : 50;
173
+ const shown = rows.slice(0, limit);
174
+ if (json) {
175
+ out(JSON.stringify({ count: rows.length, trades: shown }));
176
+ return 0;
177
+ }
178
+ for (const l of banner(v, rows))
179
+ out(l);
180
+ if (!rows.length) {
181
+ out(sub === 'positions' ? 'no open trades' : 'no trades match');
182
+ return 0;
183
+ }
184
+ for (const l of tradesTable(shown))
185
+ out(l);
186
+ out(`${shown.length} of ${rows.length} shown · profile/exit with * = not declared structurally (tag line, late, default)`);
187
+ return 0;
188
+ }
189
+ case 'summary': {
190
+ const s = summarize(rows);
191
+ if (json) {
192
+ out(JSON.stringify(s));
193
+ return 0;
194
+ }
195
+ for (const l of banner(v, rows))
196
+ out(l);
197
+ const u = s.unit === 'SOL' ? '◎' : '$';
198
+ out(`trades ${s.trades} closed · ${s.open} open · wins ${s.wins} · losses ${s.losses}`);
199
+ out(`win rate ${s.winRate ? `${frac(s.winRate.value)} (95% ${frac(s.winRate.lo)}–${frac(s.winRate.hi)})` : '—'} · P/L ${num(s.pnl, s.unit === 'SOL' ? 3 : 2)}${u} · avg ${pct(s.avgPnlPct)} · median ${pct(s.medianPnlPct)} · best ${pct(s.bestPct)} · worst ${pct(s.worstPct)}`);
200
+ out(`profit factor ${num(s.profitFactor)} · max drawdown ${num(s.maxDrawdown, 3)}${u}${s.maxDrawdownPct !== null ? ` (${num(s.maxDrawdownPct, 1)}%)` : ''} · rugs ${s.rugs} (${frac(s.rugRate)}) · avg hold ${s.avgHoldSec === null ? '—' : hold(s.avgHoldSec)} · MFE ${pct(s.avgMfePct)} · MAE ${pct(s.avgMaePct)}`);
201
+ out(`fees ${num(s.feesSol, 4)}◎ · model cost $${num(s.modelCostUsd)} · declared profile ${frac(s.tagCoverage.profileDeclared)} · declared exit ${frac(s.tagCoverage.exitDeclared)}`);
202
+ for (const [title, gs] of [['by profile', s.byProfile], ['by exit reason', s.byExitReason], ['by strategy', s.byStrategy], ['by source', s.bySource], ['by mode', s.byMode]]) {
203
+ if (!gs.length)
204
+ continue;
205
+ out(`\n${title}`);
206
+ for (const g of gs)
207
+ out(` ${pad(g.key, 22)} ${lpad(String(g.trades), 4)} closed ${lpad(String(g.open), 3)} open win ${lpad(frac(g.winRate), 4)} P/L ${lpad(num(g.pnl, s.unit === 'SOL' ? 3 : 2), 9)}${u} avg ${lpad(pct(g.avgPnlPct), 7)} rugs ${g.rugs}`);
208
+ }
209
+ if (s.trades < 30)
210
+ out(`\n${s.trades} closed trades: a small sample says little (see the interval).`);
211
+ return 0;
212
+ }
213
+ case 'equity': {
214
+ const c = equityCurve(rows);
215
+ if (json) {
216
+ out(JSON.stringify(c));
217
+ return 0;
218
+ }
219
+ for (const l of banner(v, rows))
220
+ out(l);
221
+ const u = c.unit === 'SOL' ? '◎' : '$';
222
+ out(`realized equity (${c.unit}), ${c.points.length} closes · final ${num(c.final, 3)}${u} · max drawdown ${num(c.maxDrawdown, 3)}${u}${c.maxDrawdownPct !== null ? ` (${num(c.maxDrawdownPct, 1)}%)` : ''}`);
223
+ const step = Math.max(1, Math.ceil(c.points.length / 40));
224
+ for (const [i, p] of c.points.entries())
225
+ if (i % step === 0 || i === c.points.length - 1)
226
+ out(` ${when(p.t)} ${pad(p.symbol, 8)} ${lpad(num(p.pnl, 3), 9)} cum ${lpad(num(p.cum, 3), 9)} dd ${lpad(num(p.drawdown, 3), 8)}`);
227
+ return 0;
228
+ }
229
+ case 'compare': {
230
+ const c = compareStrategies(rows);
231
+ if (json) {
232
+ out(JSON.stringify(c));
233
+ return 0;
234
+ }
235
+ for (const l of banner(v, rows))
236
+ out(l);
237
+ const u = c.unit === 'SOL' ? '◎' : '$';
238
+ out(`${pad('strategy', 24)} ${pad('modes', 12)} ${lpad('trades', 6)} ${lpad('open', 4)} ${lpad('win', 4)} ${pad(' (95%)', 10)} ${lpad(`P/L ${u}`, 9)} ${lpad('avg', 7)} ${lpad('maxDD', 8)} ${lpad('rugs', 5)} ${lpad('hold', 6)} ${lpad('MFE', 7)} ${lpad('MAE', 7)} ${lpad('model $', 8)}`);
239
+ for (const r of c.rows)
240
+ out(`${pad(r.key, 24)} ${pad(r.modes.join(','), 12)} ${lpad(String(r.trades), 6)} ${lpad(String(r.open), 4)} ${lpad(frac(r.winRate?.value), 4)} ${pad(r.winRate ? ` ${frac(r.winRate.lo)}–${frac(r.winRate.hi)}` : '', 10)} ${lpad(num(r.pnl, 3), 9)} ${lpad(pct(r.avgPnlPct), 7)} ${lpad(num(r.maxDrawdown, 3), 8)} ${lpad(frac(r.rugRate), 5)} ${lpad(r.avgHoldSec === null ? '—' : hold(r.avgHoldSec), 6)} ${lpad(pct(r.avgMfePct), 7)} ${lpad(pct(r.avgMaePct), 7)} ${lpad(num(r.modelCostUsd), 8)}`);
241
+ return 0;
242
+ }
243
+ case 'export': {
244
+ const fmt = o._[0] ?? (typeof o.format === 'string' ? o.format : undefined);
245
+ if (!fmt || !EXPORT_FORMATS.includes(fmt)) {
246
+ io.stderr.write(`dexterm ledger export: the format is ${EXPORT_FORMATS.join(' | ')}\n`);
247
+ return 2;
248
+ }
249
+ const text = exportTrades(rows, fmt, { exportedAt: now, ledgerHead: v.file.check.ok ? v.file.check.head : null, unit: summarize(rows).unit, filters: { ...filter } });
250
+ if (typeof o.out === 'string') {
251
+ writeFileSync(o.out, text, { mode: 0o600 });
252
+ io.stderr.write(`wrote ${rows.length} trades to ${o.out}\n`);
253
+ }
254
+ else
255
+ io.stdout.write(text);
256
+ return 0;
257
+ }
258
+ case 'card': {
259
+ const id = o._[0];
260
+ const t = id ? v.trades.find((x) => x.tradeId === id) : undefined;
261
+ if (!t) {
262
+ io.stderr.write(`dexterm ledger card: ${id ? `no trade ${id}` : 'pass a tradeId (from `dexterm ledger trades`)'}\n`);
263
+ return 2;
264
+ }
265
+ const card = await import("./pnl-card.js");
266
+ const solUsd = typeof o['sol-usd'] === 'string' ? Number(o['sol-usd']) : null;
267
+ if (solUsd !== null && !(solUsd > 0)) {
268
+ io.stderr.write('dexterm ledger card: --sol-usd is a positive number\n');
269
+ return 2;
270
+ }
271
+ const input = card.ledgerCardInput(t, { solUsd: solUsd ? { usd: solUsd, source: '--sol-usd' } : null });
272
+ const s = card.computeCard(input, now);
273
+ const opts = { aspect: o.square === true ? 'square' : 'wide', exactSize: o.exact === true, ...(o.price === true ? { basis: 'price' } : {}) };
274
+ const files = card.saveCard(s, opts, { env: io.env, now });
275
+ out(card.cardText(s, opts));
276
+ out(`saved ${files.wide}\n ${files.square}\n ${files.txt}`);
277
+ return 0;
278
+ }
279
+ default:
280
+ io.stderr.write(`dexterm ledger: unknown subcommand ${JSON.stringify(sub)}\n\n${LEDGER_HELP}\n`);
281
+ return 2;
282
+ }
283
+ }
284
+ catch (e) {
285
+ io.stderr.write(`dexterm ledger: ${e instanceof Error ? e.message : String(e)}\n`);
286
+ return 1;
287
+ }
288
+ }
@@ -0,0 +1,46 @@
1
+ const abs = (n) => (n < 0n ? -n : n);
2
+ const fin = (n) => typeof n === 'number' && Number.isFinite(n) && n > 0;
3
+ /** A paper ledger row → a chart fill (its own USD price when the paper engine recorded one). */
4
+ export function paperChartFill(r) {
5
+ return {
6
+ t: r.t, side: r.side, source: 'paper',
7
+ priceUsd: fin(r.priceUsd) ? r.priceUsd : null,
8
+ tokens: Number(abs(r.tokenDelta)) / 10 ** r.decimals,
9
+ sol: Number(abs(r.grossLamports)) / 1e9,
10
+ };
11
+ }
12
+ /** How many paper rows are scanned for one mint (the wallet has no per-mint read; older history drops off first). */
13
+ export const CHART_FILLS_PAPER_SCAN = 2000;
14
+ /** How long one mint's fills are reused (the render loop asks every frame; the popup up to ~20×/s). */
15
+ export const CHART_FILLS_MEMO_MS = 1000;
16
+ /** Every paper fill for one mint, oldest first. A ledger that cannot be read contributes nothing. */
17
+ export function chartFillsFor(mint, rd) {
18
+ const out = [];
19
+ try {
20
+ for (const r of rd.paper?.(CHART_FILLS_PAPER_SCAN) ?? [])
21
+ if (r.mint === mint)
22
+ out.push(paperChartFill(r));
23
+ }
24
+ catch { /* unreadable paper ledger: no paper marks */ }
25
+ return out.sort((a, b) => a.t - b.t);
26
+ }
27
+ /** chartFillsFor memoised per mint for CHART_FILLS_MEMO_MS (a fill shows on the chart within a second). */
28
+ export class ChartFills {
29
+ memo = new Map();
30
+ rd;
31
+ now;
32
+ constructor(rd, now = Date.now) { this.rd = rd; this.now = now; }
33
+ for(mint) {
34
+ const now = this.now();
35
+ const m = this.memo.get(mint);
36
+ if (m && now - m.at >= 0 && now - m.at < CHART_FILLS_MEMO_MS)
37
+ return m.v;
38
+ const v = chartFillsFor(mint, this.rd);
39
+ if (this.memo.size > 32)
40
+ this.memo.clear();
41
+ this.memo.set(mint, { at: now, v });
42
+ return v;
43
+ }
44
+ /** Forget every memo (after a fill this run booked). */
45
+ invalidate() { this.memo.clear(); }
46
+ }
@@ -2,20 +2,21 @@
2
2
  // run, created over the screener feed's data layer so every screen shares the cache, limiter, health
3
3
  // registry and (when the Helius key is configured) the credit meter. Screens stay pure renderers: the
4
4
  // context owns the loaders and tells the UI loop when to redraw.
5
- import { PumpPortalClient, PUMPPORTAL_WS, WatchlistStore, PaperWallet, PaperExecutor, FastLane, WalletLinkStore, WalletBridge, KillSwitch, CapLedger, SignerClient, defaultLiveLedgerPath, defaultLinkPath, defaultPanicPath, defaultCapLedgerPath, shortPubkey, } from '../../../data/src/index.js';
5
+ import { PumpPortalClient, PUMPPORTAL_WS, WatchlistStore, PaperWallet, PaperExecutor, FastLane, WalletLinkStore, WalletBridge, KillSwitch, defaultLinkPath, defaultPanicPath, shortPubkey, } from '../../../data/src/index.js';
6
6
  import { SocialFeed, KolStore, socialDeps, chainSignalsFromToken, chainSignalsFromIntel, chainSignalToRaw, RSS_FEEDS, JUPITER_HOSTS } from '../../../data/src/index.js';
7
7
  import { XSource, XApi, XOAuth, XTokenStore, XSpendMeter } from '../../../data/src/index.js';
8
8
  import { TokenLive, INTERVALS } from "./token.js";
9
9
  import { NewPairsLive } from "./newpairs.js";
10
10
  import { WatchlistLive } from "./watchlist.js";
11
- import { TradeTicketLive, PositionsLive, LivePositions, CombinedPositions, POSITIONS_REFRESH_MS } from "./trade.js";
11
+ import { TradeTicketLive, PositionsLive, POSITIONS_REFRESH_MS } from "./trade.js";
12
12
  import { IntelLive } from "./intel.js";
13
13
  import { LiveSignalsLive } from "./signals.js";
14
14
  import { CopyLive } from "./copy.js";
15
+ import { ChartFills } from "./chart-fills.js";
15
16
  export { TokenLive, INTERVALS, panelCaption, emptyView } from "./token.js";
16
17
  export { NewPairsLive, latencyStats, percentile, RECENT_POLL_MS, NEW_TOKENS_POLL_MS, ENRICH_POLL_MS } from "./newpairs.js";
17
18
  export { WatchlistLive, WATCH_REFRESH_MS } from "./watchlist.js";
18
- export { TradeTicketLive, PositionsLive, LivePositions, CombinedPositions, POSITIONS_REFRESH_MS } from "./trade.js";
19
+ export { TradeTicketLive, PositionsLive, POSITIONS_REFRESH_MS } from "./trade.js";
19
20
  export { IntelLive, popupBubbleVM, unavailableText, INTEL_AUTOSCAN_DELAY_MS } from "./intel.js";
20
21
  export { LiveSignalsLive, buildObservation, SIGNALS_LOG_MAX } from "./signals.js";
21
22
  export { renderTokenLive } from "./screens/token-live.js";
@@ -37,17 +38,14 @@ export class LiveContext {
37
38
  watch;
38
39
  /** PAPER trading (task-1310): the simulated wallet, its executor and the two screens' bindings. */
39
40
  wallet;
40
- /** The linked (non-custodial) wallet pubkey, or null. Shown in the status bar (T1). */
41
+ /** The linked wallet's PUBLIC key, or null. Shown in the status bar (display only; nothing signs with it). */
41
42
  linkedWallet = null;
42
43
  executor;
43
44
  positions;
44
- /** LIVE positions from the signer's ledger file (read-only); null when live trading is off for this run. */
45
- livePositions;
46
- /** Paper + live behind the task-1323 PositionSource (token screen row, popup chart). */
45
+ /** The paper ledger behind the task-1323 PositionSource (token screen row, popup chart). */
47
46
  positionSource;
48
- /** The signer process handle once started (lazily, on the first LIVE action). Never respawned. */
49
- signer = null;
50
- signerFailed = null;
47
+ /** task-1483: every paper fill per mint (memoised 1 s) for the chart BUY/SELL markers. */
48
+ chartFills;
51
49
  trade;
52
50
  linkStore;
53
51
  ticket = null;
@@ -68,8 +66,8 @@ export class LiveContext {
68
66
  /** task-1360 copy trading Tier 1: the follow store + wallet tracker (alerts on a followed wallet's buys/sells). */
69
67
  copy;
70
68
  /**
71
- * task-1340 trade moments: set by the UI loop; the ticket calls it once per SETTLED outcome (paper fill, live
72
- * fill, refusal, failure) and never for a quote, plan or in-flight send. Effects + sound hang off this only.
69
+ * task-1340 trade moments: set by the UI loop; the ticket calls it once per SETTLED outcome (paper fill or
70
+ * failure) and never for a quote or an in-flight fill. Effects + sound hang off this only.
73
71
  */
74
72
  onSettled = () => { };
75
73
  /**
@@ -173,12 +171,8 @@ export class LiveContext {
173
171
  this.linkedWallet = this.linkStore.get();
174
172
  this.executor = new PaperExecutor(this.wallet, { clock: dl.clock });
175
173
  this.positions = new PositionsLive(dl, this.wallet, { now: this.now, onChange: () => emit('positions') });
176
- // task-1328: live positions read the signer's ledger file (read-only). Present whenever live trading
177
- // is configured for the run, so a position booked by a previous run shows even before the signer starts.
178
- this.livePositions = this.trade
179
- ? new LivePositions(this.trade.paths?.liveLedger ?? defaultLiveLedgerPath(), { now: this.now, mark: (m) => this.positions.markOf(m), solUsd: () => this.positions.solUsd() ?? this.solUsd })
180
- : null;
181
- this.positionSource = new CombinedPositions(this.positions, this.livePositions);
174
+ this.positionSource = this.positions;
175
+ this.chartFills = new ChartFills({ paper: (n) => this.wallet.trades(n) }, this.now);
182
176
  this.intel = new IntelLive(dl, { now: this.now, onChange: () => { this.syncIntelRows(); this.observeSignals(); emit('intel'); }, isWatched: (mint) => this.watch.has(mint) });
183
177
  // task-1404: the live holder/rug signals engine. A fired signal sets a transient notice and redraws; the
184
178
  // token screen shows the recent signals, and the rules engine reads `signals.fields()` so each is an alert trigger.
@@ -186,64 +180,33 @@ export class LiveContext {
186
180
  // task-1360: followed wallets are polled only in a real live run (replay never spends credits); the store is always readable.
187
181
  this.copy = new CopyLive(opts.mode === 'live' ? dl : null, { onChange: (r) => emit(r), solUsd: () => this.solUsd, env: opts.copy?.env, followsPath: opts.copy?.followsPath, history: opts.copy?.history, pollMs: opts.copy?.pollMs });
188
182
  }
189
- /** Open a trade ticket (PAPER by default; `L` flips it LIVE when a wallet is linked). Replaces any open ticket. */
190
- openTicket(seed, side, mode = 'paper') {
191
- const live = this.trade ? {
192
- signer: () => this.ensureSigner(),
193
- linked: () => this.linkedWallet,
194
- onFill: () => { this.livePositions?.invalidate(); this.onChange('positions'); },
195
- } : undefined;
183
+ /** Open a PAPER trade ticket. Replaces any open ticket. */
184
+ openTicket(seed, side) {
196
185
  const t = new TradeTicketLive(this.dl, this.wallet, this.executor, { mint: seed.mint, symbol: seed.symbol, decimals: seed.decimals, side, mode: 'paper' }, {
197
- now: this.now, onChange: () => this.onChange('ticket'), live, onSettled: (tx) => this.onSettled(tx),
198
- livePositionRaw: (mint) => { const p = this.livePositions?.positions().find((x) => x.mint === mint); return p?.tokensRaw ?? 0n; },
186
+ now: this.now, onChange: () => this.onChange('ticket'), onSettled: (tx) => this.onSettled(tx),
199
187
  });
200
188
  this.ticket = t;
201
- if (mode === 'live')
202
- t.setMode('live');
203
189
  return t;
204
190
  }
205
- /**
206
- * The signer process (task-1328), started on first use with the run's caps argv. A signer that
207
- * failed to start or died is never respawned: LIVE stays off until dexterm restarts (fail closed).
208
- */
209
- ensureSigner() {
210
- if (!this.trade || this.signerFailed)
211
- return this.signer;
212
- if (!this.signer) {
213
- try {
214
- this.signer = this.trade.createSigner ? this.trade.createSigner() : new SignerClient({ argv: this.trade.signerArgv ?? [], log: this.onLog ? (l) => this.onLog?.(` signer ${l}\n`) : undefined });
215
- }
216
- catch (e) {
217
- this.signerFailed = e instanceof Error ? e.message : String(e);
218
- return null;
219
- }
220
- }
221
- return this.signer;
222
- }
223
191
  /** `/wallet …` typed in the app (task-1328). Returns the line to show. */
224
192
  async walletCommand(cmd) {
225
193
  const paths = this.trade?.paths ?? {};
226
194
  switch (cmd) {
227
195
  case 'status': {
228
- const linked = this.linkedWallet ? `linked: ${shortPubkey(this.linkedWallet.publicKey)}` : 'no wallet linked (/wallet link)';
196
+ const linked = this.linkedWallet ? `linked: ${shortPubkey(this.linkedWallet.publicKey)} (public key only)` : 'no wallet linked (/wallet link)';
229
197
  const ks = new KillSwitch(paths.panic ?? defaultPanicPath()).engaged();
230
- const s = this.signer?.alive ? await this.signer.status().catch(() => null) : null;
231
- if (s) {
232
- const caps = s.caps;
233
- return `${linked} · signer ${s.network.toUpperCase()} (${s.rpcHost}) · $${caps.perTradeUsd}/trade $${caps.dailyUsd}/day · spent $${s.spentTodayUsd.toFixed(2)}${s.panic || s.ledgerLocked ? ' · ⛔ PANICKED' : ''}`;
234
- }
235
- return `${linked} · signer ${this.signer ? (this.signerFailed ?? 'stopped') : 'not started (starts on the first LIVE plan)'}${ks ? ' · ⛔ PANICKED' : ''}${this.trade ? '' : ' · live trading off in this run'}`;
198
+ return `${linked}${ks ? ' · ⛔ PANICKED' : ''} · dexterm holds no key and signs nothing`;
236
199
  }
237
200
  case 'link': {
238
201
  if (!this.trade)
239
- return 'live trading is off in this run';
240
- const bridge = new WalletBridge(this.trade.open ? { open: this.trade.open } : {});
202
+ return 'wallet link is off in this run (replay / offline)';
203
+ const bridge = new WalletBridge(this.trade?.open ? { open: this.trade.open } : {});
241
204
  try {
242
205
  const res = await bridge.link();
243
- const rec = this.linkStore.set(res.publicKey, { network: 'devnet' });
206
+ const rec = this.linkStore.set(res.publicKey);
244
207
  this.linkedWallet = rec;
245
208
  this.onChange('wallet');
246
- return `linked: ${shortPubkey(rec.publicKey)} (public key only) · L on a ticket for LIVE`;
209
+ return `linked: ${shortPubkey(rec.publicKey)} (public key only; dexterm never signs with it)`;
247
210
  }
248
211
  catch (e) {
249
212
  return `wallet link failed: ${e instanceof Error ? e.message : String(e)}`;
@@ -252,40 +215,15 @@ export class LiveContext {
252
215
  case 'unlink': {
253
216
  const had = this.linkStore.clear();
254
217
  this.linkedWallet = null;
255
- if (this.ticket?.ticket.mode === 'live')
256
- this.ticket.setMode('paper');
257
218
  this.onChange('wallet');
258
219
  return had ? 'unlinked' : 'no wallet was linked';
259
220
  }
260
221
  case 'panic': {
261
- // Local first (the kill-switch FILE every signer re-reads + the shared ledger lock), then the signer.
222
+ // The kill-switch FILE every autotrade runner and the paper agent desk re-read.
262
223
  const ks = new KillSwitch(paths.panic ?? defaultPanicPath());
263
224
  ks.engage('panic from dexterm UI', this.now());
264
- try {
265
- const l = new CapLedger({ path: paths.capLedger ?? defaultCapLedgerPath(), clock: this.dl.clock });
266
- try {
267
- l.lock('panic from dexterm UI', this.now());
268
- }
269
- finally {
270
- l.close();
271
- }
272
- }
273
- catch { /* the file alone disables trading */ }
274
- if (this.signer?.alive)
275
- await this.signer.panic('panic from dexterm UI').catch(() => { });
276
- if (this.ticket?.ticket.mode === 'live') {
277
- this.ticket.ticket.phase = 'refused';
278
- this.ticket.ticket.message = '⛔ PANIC — live signing disabled';
279
- }
280
225
  this.onChange('wallet');
281
- return '⛔ PANIC: live signing disabled everywhere · re-enable only from a shell: dexterm wallet resume';
282
- }
283
- case 'probe': {
284
- if (!this.trade)
285
- return 'live trading is off in this run';
286
- const t = this.openTicket({ mint: this.WSOL, symbol: 'SOL', decimals: 9 }, 'buy');
287
- const why = t.startProbe();
288
- return why ?? 'devnet probe: review the plan, then Y to sign the wrap-and-unwrap in Phantom (fees only)';
226
+ return '⛔ PANIC: autotrade runs and the paper agent desk halted · clear it only from a shell: dexterm wallet resume';
289
227
  }
290
228
  }
291
229
  return '';
@@ -560,10 +498,6 @@ export class LiveContext {
560
498
  this.social.close();
561
499
  }
562
500
  catch { /* ignore */ }
563
- try {
564
- this.signer?.close();
565
- }
566
- catch { /* ignore */ }
567
501
  try {
568
502
  this.intel.close();
569
503
  }
@@ -19,7 +19,7 @@ import { launchIntelCells, holderTags } from "../intel.js";
19
19
  import { devBookOf, devRows } from "../dev-history.js";
20
20
  import { EX_POOL_TAGS } from '../../../../data/src/index.js';
21
21
  import { layoutFor } from "../../custom.js";
22
- import { chartMode, chartTitle, priceModeOf, chartAxisOf, mcapAt, positionPriceCells } from "../../display.js";
22
+ import { chartMode, chartTitle, priceModeOf, chartAxisOf, chartMarkersOn, mcapAt, positionPriceCells, levelCell, supplyRatio } from "../../display.js";
23
23
  const dash = '—';
24
24
  const EX_POOL_TAG_SET = EX_POOL_TAGS;
25
25
  /** task-1367 holder-class badge colours: venues/escrows accent, the creator/linked warn, the rest muted. */
@@ -139,8 +139,11 @@ export function renderTokenLive(c, state, v) {
139
139
  let tx = cb.x + cb.w - 2 - (chartCap.length + 4) - INTERVALS.reduce((s, f) => s + f.length + 1, 0);
140
140
  for (const f of INTERVALS)
141
141
  tx = c.text(tx, cb.y - 1, f, f === v.interval ? { fg: t.accent, bg: cb.bg, b: true } : { fg: t.dim, bg: cb.bg }) + 1;
142
+ // task-1483: BUY / SELL markers for every paper + live fill of this coin (the ledgers, no fetch) and the open position's
143
+ // average entry as a dashed line (the position row's own figure); `display.chartMarkers off` hides them.
144
+ const trades = chartMarkersOn(state) ? { fills: state.live?.chartFills?.for(v.mint) ?? [], avgEntry: prow && prow.avgEntryUsd !== null ? { usd: prow.avgEntryUsd, source: held?.kind ?? src?.kind ?? 'paper' } : null } : null;
142
145
  if (v.candles.length)
143
- candleChart(c, cb.x + 1, cb.y, cb.w - 2, cb.h, v.candles, { bg: cb.bg, interval: v.interval, scale: cm.scale ?? undefined, axisFmt: cm.axisFmt });
146
+ candleChart(c, cb.x + 1, cb.y, cb.w - 2, cb.h, v.candles, { bg: cb.bg, interval: v.interval, scale: cm.scale ?? undefined, axisFmt: cm.axisFmt, trades });
144
147
  else
145
148
  c.textC(cb.x, cb.y + Math.floor(cb.h / 2), cb.w, cp.state === 'loading' ? 'loading candles…' : `no candles · ${cp.reason ?? 'source did not answer'}`, { fg: t.muted, bg: cb.bg, i: true });
146
149
  y += chartH;
@@ -322,7 +325,7 @@ export function renderTokenLive(c, state, v) {
322
325
  c.text(hb.x + 1, hy, 'TAPE', { fg: t.dim, bg: hb.bg });
323
326
  c.textR(hb.x + 1, hy, hb.w - 2, tp.state === 'ok' || tp.fetchedAt !== null ? `geckoterminal · 20s poll${tp.stale || tp.state === 'error' ? ' · stale' : ''}` : panelCaption(tp, now, 'tape unavailable'), { fg: tp.state === 'error' && tp.fetchedAt === null ? t.warn : t.dim, bg: hb.bg });
324
327
  hy += 1;
325
- hy += tradeTape(c, hb.x + 1, hy, hb.w - 2, hb.y + hb.h - hy, v.trades, now, { bg: hb.bg });
328
+ hy += tradeTape(c, hb.x + 1, hy, hb.w - 2, hb.y + hb.h - hy, v.trades, now, { bg: hb.bg, priceFmt: (p) => levelCell(priceModeOf(state), supplyRatio(v.price, v.mcap), p) }); // task-1537
326
329
  }
327
330
  else if (flow) {
328
331
  c.text(hb.x + 1, hy, 'BUY/SELL FLOW', { fg: t.dim, bg: hb.bg });
@@ -337,7 +340,7 @@ export function renderTokenLive(c, state, v) {
337
340
  }
338
341
  if (showAi) {
339
342
  // task-1473: in an auto mode (following an agent / AI scan) the panel is the BRAIN VIEW; otherwise the analyst
340
- const facts = factsFromView(v, { top10: brainTop10, dev: brainDev, larpVerdict: state.larpByMint?.get(v.mint)?.verdict ?? null, price: shownPx });
343
+ const facts = factsFromView(v, { top10: brainTop10, dev: brainDev, larpVerdict: state.larpByMint?.get(v.mint)?.verdict ?? null, price: shownPx, fills: trades?.fills ?? null });
341
344
  const brain = brainFor(state, facts, isCalm(state.vfx), state.live?.scanMeterLabel?.() ?? '');
342
345
  if (brain)
343
346
  renderBrainPanel(c, mainW, top, aiW, bottom - top, brain.model, brain.opts);
@@ -394,9 +397,9 @@ function positionPanel(c, x, y, w, r, kind, mode = 'price', supply = null) {
394
397
  if (r.realizedSol !== 0)
395
398
  cols.push({ label: 'REALIZED', value: usd && r.realizedUsd !== null ? signedUsd(r.realizedUsd) : `${signedSol(r.realizedSol)} SOL`, st: { fg: pnlColor(r.realizedSol), bg }, prio: 2 });
396
399
  if (r.stopUsd !== null)
397
- cols.push({ label: 'STOP', value: `${fmtPrice(r.stopUsd)}${r.stopDistPct !== null ? ` ${signedPctOf(r.stopDistPct)}` : ''}`, st: { fg: t.down, bg }, prio: 3 });
400
+ cols.push({ label: 'STOP', value: `${levelCell(mode, supply, r.stopUsd)}${r.stopDistPct !== null ? ` ${signedPctOf(r.stopDistPct)}` : ''}`, st: { fg: t.down, bg }, prio: 3 });
398
401
  if (r.targetUsd !== null)
399
- cols.push({ label: 'TARGET', value: `${fmtPrice(r.targetUsd)}${r.targetDistPct !== null ? ` ${signedPctOf(r.targetDistPct)}` : ''}`, st: { fg: t.up, bg }, prio: 3 });
402
+ cols.push({ label: 'TARGET', value: `${levelCell(mode, supply, r.targetUsd)}${r.targetDistPct !== null ? ` ${signedPctOf(r.targetDistPct)}` : ''}`, st: { fg: t.up, bg }, prio: 3 });
400
403
  const gap = 3;
401
404
  const widthOf = (list) => list.reduce((s, k) => s + Math.max(k.label.length, k.value.length) + gap, -gap);
402
405
  const inner = box.w - 2;