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
@@ -302,7 +302,13 @@ const USAGE = `dexterm mcp [<command>]
302
302
  --print only print the snippet, never write
303
303
  --node emit a "node <path> mcp" command (when dexterm is not on PATH)
304
304
  --yes write without the confirmation prompt
305
- doctor spawn the server, do the MCP handshake, and list the tools/prompts/resources`;
305
+ doctor spawn the server, do the MCP handshake, and list the tools/prompts/resources
306
+
307
+ for agent authors: \`dexterm mcp doctor\` lists every tool with its description. Reads (market data, the trade
308
+ ledger: ledger_trades / positions / summary / equity / compare, candidate_dossier, outcomes_lookup, trading_status,
309
+ telegram_status) never change anything; strategy_propose_* and backtest_request only file a proposal the user applies
310
+ or runs; the paper_* tools trade the SIMULATED wallet and only inside a mandate the user armed. No tool can sign, send,
311
+ enable live trading or raise a cap. Docs: https://dexterm.xyz/docs/agents/`;
306
312
  /** `dexterm mcp …`. With no subcommand (or an arg the server consumes, e.g. --replay) it runs the server. */
307
313
  export async function runMcp(rest, io) {
308
314
  const sub = rest[0];
@@ -253,7 +253,7 @@ export async function runMandate(rest, io) {
253
253
  }
254
254
  out(row.paperOnly
255
255
  ? `Arm PAPER-ONLY mandate ${id}? This lets the agent paper desk book simulated fills under these caps. No plan, copy trade or signer can use it.`
256
- : `Arm standing mandate ${id}? This lets armed plans fire at machine speed from the capped fast wallet.`);
256
+ : `Arm standing mandate ${id}? This lets armed plans fire at machine speed on PAPER (simulated fills; dexterm places no live order).`);
257
257
  out(` total ${fmtUsd(row.totalUsd)} · per-trade ${fmtUsd(row.perTradeUsd)} · per-coin ${fmtUsd(row.perCoinUsd)} · maxTier ${row.maxTier}`);
258
258
  out('Hold any key to confirm (physical keypress). A pipe or agent cannot arm. Waiting 15s…');
259
259
  const key = await ttyKeypress(io.stdin, 15_000);
@@ -4,17 +4,26 @@
4
4
  // positions → every coin with fills (newest activity first; ← → cycle), LIVE or PAPER as above
5
5
  // follows → the selected followed wallet's coins (TRACKED), newest first
6
6
  // In the preview: x share to X · t copy as text · s save only · a show/hide wallet · z exact size/band ·
7
- // f 16:9 ⇄ 1:1 · ← → other coin · esc close. Share = save + clipboard image + X compose intent in the user's own
7
+ // m market cap ⇄ SOL price (task-1518; mcap is the default when the coin's supply is known) · f 16:9 ⇄ 1:1 ·
8
+ // ← → other coin · esc close. Share = save + clipboard image + X compose intent in the user's own
8
9
  // browser (pnl-card.ts shareCard); dexterm posts nothing. Nothing here trades, signs or edits a number.
9
10
  import { mix } from '../../tui/src/index.js';
10
- import { computeCard, renderCard, previewCanvas, cardText, paperCardInput, liveCardInput, trackedCardInput, CardError, } from "./pnl-card.js";
11
+ import { computeCard, renderCard, previewCanvas, cardText, paperCardInput, trackedCardInput, cardSupply, mcapView, CardError, } from "./pnl-card.js";
11
12
  const MARK_VIA = 'dexscreener';
12
13
  function coinMeta(state, mint) {
13
14
  const tv = state.live?.token?.view;
14
15
  if (tv && tv.mint === mint)
15
- return { symbol: tv.symbol, name: tv.name, image: tv.image };
16
+ return { symbol: tv.symbol, name: tv.name, image: tv.image, launchpad: tv.launchpad ?? null, fdv: tv.fdv ?? null, price: tv.price ?? null };
16
17
  const row = state.screener.rows.find((r) => r.mint === mint);
17
- return { symbol: row?.symbol ?? null, name: row?.name ?? null, image: row?.image ?? null };
18
+ return { symbol: row?.symbol ?? null, name: row?.name ?? null, image: row?.image ?? null, launchpad: row?.launchpad ?? null, fdv: row?.fdv ?? null, price: row?.price ?? null };
19
+ }
20
+ /** The market-cap inputs for a coin: its recorded supply (FDV ÷ price, else pump.fun's 1B) and today's SOL/USD. */
21
+ function mcapInputs(state, mint, meta) {
22
+ const solUsd = state.live?.positions.solUsd() ?? null;
23
+ return {
24
+ supply: cardSupply({ mint, launchpad: meta.launchpad, fdv: meta.fdv, price: meta.price, via: 'market data' }),
25
+ usdPerSol: solUsd && solUsd > 0 ? { usd: solUsd, source: `$${solUsd.toFixed(2)}/SOL now (display conversion)` } : null,
26
+ };
18
27
  }
19
28
  function imageOf(state, url) {
20
29
  if (!url || !state.images)
@@ -35,7 +44,7 @@ export function markFor(state, mint) {
35
44
  return { priceSol: tv.price / solUsd, at: m.fetchedAt, via: `${m.via ?? 'market'} ÷ SOL/USD` };
36
45
  return null;
37
46
  }
38
- /** Coins with paper or live fills, newest activity first. */
47
+ /** Coins with paper fills, newest activity first. */
39
48
  function tradedCoins(state) {
40
49
  const live = state.live;
41
50
  if (!live)
@@ -45,8 +54,6 @@ function tradedCoins(state) {
45
54
  last.set(mint, { t, symbol }); };
46
55
  for (const r of live.wallet.trades(1000))
47
56
  see(r.mint, r.t, r.symbol);
48
- for (const r of live.livePositions?.records(1000) ?? [])
49
- see(r.mint, r.t, r.symbol);
50
57
  return [...last.entries()].sort((a, b) => b[1].t - a[1].t).map(([mint, v]) => ({ mint, symbol: v.symbol }));
51
58
  }
52
59
  /** Open the preview for the current screen. Returns a reason when there is nothing to card. */
@@ -75,15 +82,15 @@ export function openPnl(state) {
75
82
  const mint = state.live.token?.view?.mint ?? state.token?.mint ?? null;
76
83
  if (!mint)
77
84
  return 'open a coin first';
78
- const has = (state.live.livePositions?.records(1, mint).length ?? 0) > 0 || state.live.wallet.trades(1000).some((r) => r.mint === mint);
85
+ const has = state.live.wallet.trades(1000).some((r) => r.mint === mint);
79
86
  if (!has)
80
- return 'no paper or live fills for this coin yet: PnL cards come from your own recorded trades';
87
+ return 'no paper fills for this coin yet: PnL cards come from your own recorded trades';
81
88
  state.pnl = { origin: 'token', choices: [{ mint, symbol: coinMeta(state, mint).symbol ?? mint.slice(0, 4) }], wallet: null, ...base };
82
89
  }
83
90
  else if (state.screen === 'positions') {
84
91
  const choices = tradedCoins(state);
85
92
  if (!choices.length)
86
- return 'no paper or live fills yet: B / S on a token to trade, then H here';
93
+ return 'no paper fills yet: B / S on a token to paper-trade, then H here';
87
94
  state.pnl = { origin: 'positions', choices, wallet: null, ...base };
88
95
  }
89
96
  else
@@ -111,16 +118,12 @@ export async function pnlInput(state, ui) {
111
118
  const solUsd = live.positions.solUsd();
112
119
  if (!mark && m.priceUsd && m.priceAt && solUsd)
113
120
  mark = { priceSol: m.priceUsd / solUsd, at: m.priceAt, via: `${MARK_VIA} ÷ SOL/USD` };
114
- return trackedCardInput(copy.trades(ui.wallet), {
115
- wallet: ui.wallet, mint: ch.mint, symbol: meta.symbol ?? m.symbol ?? ch.symbol, name: meta.name, decimals, mark,
116
- historyAt: st.lastPollAt, complete: stats.sample.complete, image,
117
- });
118
- }
119
- const liveRows = live.livePositions?.fills(ch.mint) ?? [];
120
- if (liveRows.length) {
121
- return liveCardInput(liveRows, { mint: ch.mint, symbol: meta.symbol ?? undefined, name: meta.name, wallet: live.linkedWallet?.publicKey ?? null, mark: markFor(state, ch.mint), image });
121
+ return { ...trackedCardInput(copy.trades(ui.wallet), {
122
+ wallet: ui.wallet, mint: ch.mint, symbol: meta.symbol ?? m.symbol ?? ch.symbol, name: meta.name, decimals, mark,
123
+ historyAt: st.lastPollAt, complete: stats.sample.complete, image,
124
+ }), ...mcapInputs(state, ch.mint, meta) };
122
125
  }
123
- return paperCardInput(live.wallet.trades(1000), { mint: ch.mint, symbol: meta.symbol ?? undefined, name: meta.name, mark: markFor(state, ch.mint), image });
126
+ return { ...paperCardInput(live.wallet.trades(1000), { mint: ch.mint, symbol: meta.symbol ?? undefined, name: meta.name, mark: markFor(state, ch.mint), image }), ...mcapInputs(state, ch.mint, meta) };
124
127
  }
125
128
  /** (Re)build the card for the current choice; on refusal the preview shows the reason instead of a card. */
126
129
  export async function loadPnl(state, now = Date.now()) {
@@ -167,6 +170,13 @@ export function pnlKey(ui, key) {
167
170
  ui.notice = ui.opts.exactSize ? 'exact SOL size SHOWN on the card' : 'size shown as a band';
168
171
  return { kind: 'none' };
169
172
  }
173
+ if (ch === 'm') {
174
+ const now = ui.stats ? mcapView(ui.stats, ui.opts).basis : (ui.opts.basis ?? 'mcap');
175
+ ui.opts.basis = now === 'mcap' ? 'price' : 'mcap';
176
+ const v = ui.stats ? mcapView(ui.stats, ui.opts) : null;
177
+ ui.notice = ui.opts.basis === 'price' ? 'entry / exit as SOL price' : v?.fallback ?? 'entry / exit as market cap';
178
+ return { kind: 'none' };
179
+ }
170
180
  if (ch === 'f') {
171
181
  ui.opts.aspect = ui.opts.aspect === 'wide' ? 'square' : 'wide';
172
182
  return { kind: 'none' };
@@ -181,7 +191,7 @@ export function pnlKey(ui, key) {
181
191
  return { kind: 'share', action: 'save' };
182
192
  return { kind: 'none' };
183
193
  }
184
- const HINTS = 'x share to X · t copy text · s save · a wallet · z size · f 16:9/1:1 · ←→ coin · esc';
194
+ const HINTS = 'x share to X · t copy text · s save · a wallet · z size · m mcap/price · f 16:9/1:1 · ←→ coin · esc';
185
195
  /** The preview overlay: the card itself at cell size when the terminal fits it, else its text; then the keys. */
186
196
  export function renderPnlOverlay(c, state) {
187
197
  const ui = state.pnl;
@@ -191,7 +201,7 @@ export function renderPnlOverlay(c, state) {
191
201
  c.noRain(0, 0, c.w, c.h);
192
202
  c.fill(0, 0, c.w, c.h, ' ', { bg: mix(t.bg, '#000000', 0.5) });
193
203
  const ch = ui.choices[ui.idx];
194
- const head = `PnL CARD · $${ch?.symbol ?? '?'}${ui.choices.length > 1 ? ` (${ui.idx + 1}/${ui.choices.length})` : ''} · ${ui.opts.aspect === 'wide' ? '16:9' : '1:1'} · wallet ${ui.opts.showWallet ? 'SHOWN' : 'hidden'} · size ${ui.opts.exactSize ? 'EXACT' : 'band'}`;
204
+ const head = `PnL CARD · $${ch?.symbol ?? '?'}${ui.choices.length > 1 ? ` (${ui.idx + 1}/${ui.choices.length})` : ''} · ${ui.opts.aspect === 'wide' ? '16:9' : '1:1'} · wallet ${ui.opts.showWallet ? 'SHOWN' : 'hidden'} · size ${ui.opts.exactSize ? 'EXACT' : 'band'}${ui.stats ? ` · ${mcapView(ui.stats, ui.opts).basis === 'mcap' ? 'mcap' : 'SOL price'}` : ''}`;
195
205
  c.text(1, 0, head, { fg: t.text, bg: t.headBg, b: true }, c.w - 2);
196
206
  c.fill(0, c.h - 1, c.w, 1, ' ', { bg: t.headBg });
197
207
  c.text(1, c.h - 1, ui.busy ? 'working… (esc closes)' : (ui.notice ?? HINTS), { fg: ui.notice ? t.warn : t.muted, bg: t.headBg }, c.w - 2);
@@ -7,12 +7,16 @@
7
7
  // Honesty rules, enforced by construction:
8
8
  // - The stamp (PAPER / LIVE / TRACKED WALLET) comes from `CardInput.source` and is drawn by the renderer in three
9
9
  // places (the stamp block, the frame colour, the stamp strip). No option turns it off; an unknown source throws.
10
- // - Numbers come only from fills (the paper ledger, the signer's live ledger, a tracked wallet's decoded history).
10
+ // - Numbers come only from fills (the paper ledger, the trade ledger, a tracked wallet's decoded history).
11
11
  // There is no API that takes a PnL, a price or a size; every figure is computed here from those fills, with
12
12
  // the time window, the fee basis, what was excluded and the data ages printed on the card.
13
13
  // - Wallet addresses (and LIVE tx signatures, which point at the wallet) are hidden unless `showWallet`; size is a
14
14
  // band unless `exactSize`; no balance, no key, no file path is ever drawn.
15
15
  // - Footer: "not financial advice" and the dexterm URL. No performance claims beyond this one position's fills.
16
+ // - Market cap (task-1518): ENTRY / EXIT are shown as market cap when the token supply is known (`basis: 'mcap'`,
17
+ // the default then; `basis: 'price'` keeps the SOL price view). Each mcap is a fill-derived price × the supply,
18
+ // never a quoted mcap, and the card names the supply source (recorded FDV ÷ price, or pump.fun's fixed 1B) and
19
+ // the USD conversion. Exit mcap is the blended average of every sell; the last sell's mcap is its own labelled row.
16
20
  //
17
21
  // Rendering reuses the task-1401 exporter stack: a cell Canvas rasterized by @dexterm/tui (the 5×9 bitmap font) and
18
22
  // encoded by @dexterm/data. Big type (the stamp, the ticker, the PnL %) is the same font rasterized at a larger
@@ -20,7 +24,7 @@
20
24
  import { mkdirSync, writeFileSync } from 'node:fs';
21
25
  import { join } from 'node:path';
22
26
  import { spawn } from 'node:child_process';
23
- import { Canvas, THEMES, rasterizeCanvas, fmtPriceBare, mix } from '../../tui/src/index.js';
27
+ import { Canvas, THEMES, rasterizeCanvas, fmtPriceBare, fmtUsd, fmtNum, mix } from '../../tui/src/index.js';
24
28
  import { encodePng, lamportsToSol } from '../../data/src/index.js';
25
29
  import { exportsDir, fmtAge } from "./export.js";
26
30
  import { cleanText } from "./screener/rows.js";
@@ -41,18 +45,6 @@ export function paperCardInput(records, o) {
41
45
  excluded: [], fees: 'simulated network fee included', image: o.image ?? null,
42
46
  };
43
47
  }
44
- /** LIVE: the signer's ledger rows (confirmed on-chain fills, swaps only) for one mint. */
45
- export function liveCardInput(rows, o) {
46
- const rs = rows.filter((r) => r.mint === o.mint && r.kind === 'swap');
47
- const nets = new Set(rs.map((r) => r.network));
48
- const network = nets.size > 1 ? 'mixed' : rs[0]?.network === 'devnet' ? 'devnet' : 'mainnet';
49
- return {
50
- source: 'live', network, mint: o.mint, symbol: o.symbol ?? rs[0]?.symbol ?? '?', name: o.name ?? null, decimals: rs[0]?.decimals ?? null,
51
- fills: rs.map((r) => ({ t: r.t, side: r.side === 'sell' ? 'sell' : 'buy', solDelta: lamportsToSol(BigInt(r.solDelta)), tokenDelta: BigInt(r.tokenDelta), feeSol: lamportsToSol(BigInt(r.feeLamports)), sig: r.signature })),
52
- ledger: 'signer live ledger', wallet: o.wallet ?? null, mark: o.mark ?? null,
53
- excluded: network === 'mixed' ? ['devnet and mainnet fills are both in the ledger for this coin'] : [], fees: 'network + priority fees included', image: o.image ?? null,
54
- };
55
- }
56
48
  /** TRACKED: a followed wallet's decoded swaps for one mint (read-only public history). */
57
49
  export function trackedCardInput(trades, o) {
58
50
  const all = trades.filter((t) => t.mint === o.mint && t.wallet === o.wallet);
@@ -99,6 +91,74 @@ export function trackedCardInput(trades, o) {
99
91
  excluded, fees: 'network fees + tips included (balance deltas)', image: o.image ?? null,
100
92
  };
101
93
  }
94
+ /** pump.fun mints a fixed 1,000,000,000 supply per coin (none is minted later), so its market cap is price × 1B. */
95
+ export const PUMP_FUN_SUPPLY = 1_000_000_000;
96
+ /**
97
+ * The supply for the market-cap view: the token's recorded FDV ÷ price when both are known (total supply, so on a
98
+ * non-pump coin this is a fully diluted cap and says so), else pump.fun's fixed 1B for a pump.fun coin, else null
99
+ * (the card then stays on the price view; no supply is ever guessed).
100
+ */
101
+ export function cardSupply(o) {
102
+ if (o.fdv && o.price && Number.isFinite(o.fdv / o.price) && o.fdv > 0 && o.price > 0) {
103
+ return { tokens: o.fdv / o.price, source: `recorded FDV ÷ price${o.via ? ` (${o.via})` : ''}` };
104
+ }
105
+ if (o.launchpad === 'pump.fun' || ((!o.launchpad || o.launchpad === 'unknown') && o.mint.endsWith('pump'))) {
106
+ return { tokens: PUMP_FUN_SUPPLY, source: 'pump.fun fixed supply' };
107
+ }
108
+ return null;
109
+ }
110
+ /**
111
+ * AUTOTRADE PAPER: one position's fills from an autotrade run ledger. The desk books in USD, so each fill's SOL leg
112
+ * is its USD at `solUsd` (said on the card); the market caps come straight from the USD fills × supply, so the SOL
113
+ * rate changes only the size band, never a cap or the return.
114
+ */
115
+ export function autotradeCardInput(events, o) {
116
+ if (!(o.solUsd.usd > 0))
117
+ throw new CardError('an autotrade card needs a SOL/USD rate to show the USD desk in SOL');
118
+ const fs = events.filter((e) => e.kind === 'fill' && e.positionId === o.positionId && (e.side === 'buy' || e.side === 'sell') && (e.usd ?? 0) > 0 && (e.tokens ?? 0) > 0);
119
+ const mint = fs[0]?.mint ?? '';
120
+ const closed = events.find((e) => e.kind === 'trade_closed' && e.trade?.id === o.positionId);
121
+ const decimals = o.decimals ?? 6;
122
+ return {
123
+ source: 'paper', mint, symbol: o.symbol ?? closed?.trade?.symbol ?? mint.slice(0, 4), name: o.name ?? null, decimals,
124
+ fills: fs.map((e) => ({
125
+ t: e.t, side: e.side, solDelta: (e.side === 'buy' ? -1 : 1) * e.usd / o.solUsd.usd,
126
+ tokenDelta: BigInt(Math.round(e.tokens * 10 ** decimals)) * (e.side === 'buy' ? 1n : -1n), feeSol: null, sig: null,
127
+ })),
128
+ ledger: o.runId ? `autotrade paper run ${o.runId}` : 'autotrade paper ledger', wallet: null, mark: o.mark ?? null, excluded: [],
129
+ fees: `USD paper desk, impact in fills · SOL $${o.solUsd.usd.toFixed(2)} (${o.solUsd.source})`, image: o.image ?? null,
130
+ supply: o.supply === undefined ? cardSupply({ mint }) : o.supply,
131
+ usdPerSol: { usd: o.solUsd.usd, source: 'the desk\'s USD fills', native: true },
132
+ };
133
+ }
134
+ /**
135
+ * TRADE LEDGER (task-1530): one round trip of the unified ledger as a card. The stamp follows the wallet mode
136
+ * (paper → PAPER; devnet / live → LIVE with its network), and ENTRY / EXIT use the market-cap basis whenever the
137
+ * ledger recorded a supply (the same mcap code as every other card). A fill with no SOL leg (the autotrade USD desk)
138
+ * needs `solUsd`; a fill with no token amount refuses the card rather than guess.
139
+ */
140
+ export function ledgerCardInput(row, o = {}) {
141
+ const decimals = row.decimals ?? 6;
142
+ const usdDesk = row.fills.some((f) => f.sol === null);
143
+ if (usdDesk && !(o.solUsd && o.solUsd.usd > 0))
144
+ throw new CardError('this trade was booked in USD: a card needs a SOL/USD rate (--sol-usd)');
145
+ const fills = row.fills.map((f) => {
146
+ if (!(f.tokens !== null && f.tokens > 0))
147
+ throw new CardError(`fill ${f.t} has no token amount in the ledger`);
148
+ const sol = f.sol !== null && !usdDesk ? f.sol : (f.usd ?? 0) / o.solUsd.usd;
149
+ return { t: f.t, side: f.side, solDelta: (f.side === 'buy' ? -1 : 1) * Math.abs(sol), tokenDelta: BigInt(Math.round(f.tokens * 10 ** decimals)) * (f.side === 'buy' ? 1n : -1n), feeSol: f.feeSol, sig: row.walletMode === 'paper' ? null : f.txSig };
150
+ });
151
+ const where = row.source === 'autotrade' ? `autotrade paper run ${row.runId ?? ''}`.trim() : row.source === 'desk' ? 'agent paper desk' : row.source === 'live' ? 'signer live ledger' : 'dexterm paper ledger';
152
+ return {
153
+ source: row.walletMode === 'paper' ? 'paper' : 'live', network: row.walletMode === 'paper' ? null : row.walletMode === 'devnet' ? 'devnet' : 'mainnet',
154
+ mint: row.mint, symbol: row.symbol, name: o.name ?? null, decimals, fills,
155
+ ledger: `trade ledger · ${where} · ${row.tradeId}`, wallet: null, mark: o.mark ?? null, excluded: [],
156
+ fees: usdDesk ? `USD paper desk, impact in fills · SOL $${o.solUsd.usd.toFixed(2)} (${o.solUsd.source})` : row.walletMode === 'paper' ? 'simulated network fee included' : 'network + priority fees included',
157
+ image: o.image ?? null,
158
+ supply: row.supply ?? cardSupply({ mint: row.mint }),
159
+ ...(usdDesk ? { usdPerSol: { usd: o.solUsd.usd, source: 'the desk\'s USD fills', native: true } } : o.solUsd ? { usdPerSol: o.solUsd } : {}),
160
+ };
161
+ }
102
162
  /** Cost-basis maths over the fills (average cost, the paper and live ledgers' own rule). Throws CardError when a card would mislead. */
103
163
  export function computeCard(input, now) {
104
164
  if (input.source !== 'paper' && input.source !== 'live' && input.source !== 'tracked')
@@ -109,7 +169,7 @@ export function computeCard(input, now) {
109
169
  if (!fills.some((f) => f.side === 'buy'))
110
170
  throw new CardError('no recorded buy for this coin, so there is no cost basis to measure against');
111
171
  let held = 0n, peak = 0n, cost = 0, costIn = 0, proceeds = 0, realized = 0, boughtRaw = 0n, soldRaw = 0n, buys = 0, sells = 0, unmatched = 0;
112
- let fees = 0, feesKnown = false, firstBuy = Infinity, lastSell = -Infinity;
172
+ let fees = 0, feesKnown = false, firstBuy = Infinity, lastSell = -Infinity, lastSellAvg = null;
113
173
  for (const f of fills) {
114
174
  if (f.feeSol !== null) {
115
175
  fees += f.feeSol;
@@ -147,6 +207,8 @@ export function computeCard(input, now) {
147
207
  cost -= removed;
148
208
  held -= matched;
149
209
  soldRaw += matched;
210
+ if (input.decimals !== null)
211
+ lastSellAvg = got / (Number(matched) / 10 ** input.decimals);
150
212
  }
151
213
  }
152
214
  if (!buys)
@@ -174,7 +236,7 @@ export function computeCard(input, now) {
174
236
  input, buys, sells, firstT: fills[0].t, lastT: fills[fills.length - 1].t, open,
175
237
  costInSol: costIn, proceedsSol: proceeds, realizedSol: realized, unrealizedSol: unrealized, pnlSol: pnl, pnlPct: costIn > 0 ? pnl / costIn : 0,
176
238
  kind: !open ? 'realized' : sells ? 'realized + unrealized' : 'unrealized',
177
- entryAvg: boughtW ? costIn / boughtW : null, exitAvg: soldW ? proceeds / soldW : null,
239
+ entryAvg: boughtW ? costIn / boughtW : null, exitAvg: soldW ? proceeds / soldW : null, lastSellAvg,
178
240
  holdMs: Math.max(0, (open ? now : lastSell === -Infinity ? now : lastSell) - (firstBuy === Infinity ? fills[0].t : firstBuy)),
179
241
  feesSol: feesKnown ? fees : null, now,
180
242
  };
@@ -217,6 +279,66 @@ export function fmtWhen(t) {
217
279
  }
218
280
  const signedSol = (n) => `${n >= 0 ? '+' : '-'}${Math.abs(n).toFixed(4)} SOL`;
219
281
  const short = (a) => (a.length > 10 ? `${a.slice(0, 4)}…${a.slice(-4)}` : a);
282
+ /** `1B` / `998.8M` / `21,000,000` → a short supply figure for the provenance line. */
283
+ const fmtSupply = (n) => (n === PUMP_FUN_SUPPLY ? '1B' : fmtNum(n));
284
+ /** Resolve the basis and the market caps for a card: every figure is a fill-derived SOL price × supply (× USD/SOL). */
285
+ export function mcapView(s, o = {}) {
286
+ const sup = s.input.supply;
287
+ const want = o.basis ?? (sup ? 'mcap' : 'price');
288
+ const none = { basis: 'price', fallback: null, unit: 'SOL', entry: null, exitBlended: null, lastSell: null, mark: null };
289
+ if (want === 'price')
290
+ return none;
291
+ if (!sup || !(sup.tokens > 0))
292
+ return { ...none, fallback: 'market cap not shown: no token supply recorded, so prices are in SOL' };
293
+ if (s.input.decimals === null)
294
+ return { ...none, fallback: 'market cap not shown: token decimals unknown, so prices are in SOL' };
295
+ const k = sup.tokens * (s.input.usdPerSol && s.input.usdPerSol.usd > 0 ? s.input.usdPerSol.usd : 1);
296
+ const m = (p) => (p === null || p === undefined || !Number.isFinite(p) ? null : p * k);
297
+ return {
298
+ basis: 'mcap', fallback: null, unit: s.input.usdPerSol && s.input.usdPerSol.usd > 0 ? 'USD' : 'SOL',
299
+ entry: m(s.entryAvg), exitBlended: m(s.exitAvg), lastSell: m(s.lastSellAvg), mark: s.open ? m(s.input.mark?.priceSol) : null,
300
+ };
301
+ }
302
+ /** `$69.7K` / `$1.23M` in USD, `412.3 SOL` / `1.20K SOL` in SOL. */
303
+ export function fmtMcap(v, unit) {
304
+ if (v === null)
305
+ return '—';
306
+ return unit === 'USD' ? fmtUsd(v) : `${fmtNum(v)} SOL`;
307
+ }
308
+ /** The ENTRY / EXIT rows for the market-cap view (closed: entry, blended exit, last sell when there are 2+ sells). */
309
+ function mcapRows(s, v) {
310
+ const f = (x) => fmtMcap(x, v.unit);
311
+ const rows = [['ENTRY mcap', `${f(v.entry)} (avg fill)`]];
312
+ if (s.open)
313
+ rows.push(['MARK mcap', f(v.mark)]);
314
+ else
315
+ rows.push(['EXIT mcap', s.sells > 1 ? `${f(v.exitBlended)} (blended)` : f(v.exitBlended)]);
316
+ if (s.sells > 1 || (s.open && s.sells))
317
+ rows.push(['LAST SELL', `${f(v.lastSell)} mcap`]);
318
+ return rows;
319
+ }
320
+ /**
321
+ * The card's own statement of where the caps come from, as two stat rows (the provenance block is full on a LIVE
322
+ * card with the wallet shown, and OPENED / CLOSED repeat its window line, so these two take their place).
323
+ */
324
+ function mcapSourceRows(s, v) {
325
+ const sup = s.input.supply, usd = s.input.usdPerSol;
326
+ const src = sup.source.startsWith('pump.fun') ? 'pump.fun fixed' : sup.source.startsWith('recorded FDV') ? 'FDV/price' : cleanText(sup.source, 12);
327
+ return [
328
+ ['SUPPLY', `${fmtSupply(sup.tokens)} · ${src}`],
329
+ ['MCAP IN', v.unit === 'SOL' ? 'SOL (no USD rate)' : usd?.native ? 'USD (desk fills)' : `USD at $${usd.usd.toFixed(2)}/SOL`],
330
+ ];
331
+ }
332
+ /** The provenance line of the market-cap view (or of a fallback to the price view). */
333
+ function mcapLine(s, v) {
334
+ if (v.fallback)
335
+ return v.fallback;
336
+ if (v.basis !== 'mcap' || !s.input.supply)
337
+ return null;
338
+ const sup = s.input.supply, usd = s.input.usdPerSol;
339
+ const conv = v.unit === 'USD' ? ` · USD: ${usd.source}` : ' · in SOL';
340
+ return `mcap = fill price × ${fmtSupply(sup.tokens)} supply (${sup.source})${conv}`;
341
+ }
220
342
  /** The stamp of a source. Exhaustive: anything else throws, so a card can never be drawn without one. */
221
343
  export function stampOf(s, t) {
222
344
  const src = s.input.source;
@@ -251,6 +373,10 @@ export function provenance(s, o, mode = 'text') {
251
373
  if (mode === 'text')
252
374
  ages.push(`card made ${fmtWhen(s.now)}`);
253
375
  L.push(`data ages: ${ages.join(' · ')}`);
376
+ // the card states the market-cap source in its stat rows (mcapSourceRows); the text spells it out here
377
+ const ml = mode === 'text' ? mcapLine(s, mcapView(s, o)) : null;
378
+ if (ml)
379
+ L.push(ml);
254
380
  if (o.showWallet && i.wallet)
255
381
  L.push(`wallet: ${i.wallet}`);
256
382
  if (i.source === 'live' && o.showWallet) {
@@ -351,15 +477,22 @@ export function renderCard(s, o = {}) {
351
477
  : 'return on SOL spent · net of fees · size shown as a band';
352
478
  const dec = s.input.decimals !== null;
353
479
  const px = (p) => (p === null ? (dec ? '—' : 'n/a (decimals unknown)') : `${fmtPriceBare(p)} SOL`);
480
+ const mv = mcapView(s, o);
354
481
  const stats = [
355
- ['ENTRY avg', px(s.entryAvg)],
356
- s.open ? ['MARK', px(s.input.mark?.priceSol ?? null)] : ['EXIT avg', px(s.exitAvg)],
482
+ ...(mv.basis === 'mcap' ? mcapRows(s, mv) : [
483
+ ['ENTRY avg', px(s.entryAvg)],
484
+ s.open ? ['MARK', px(s.input.mark?.priceSol ?? null)] : ['EXIT avg', px(s.exitAvg)],
485
+ ]),
357
486
  ['HOLD', `${fmtHold(s.holdMs)}${s.open ? ' (open)' : ''}`],
358
487
  ['SIZE', o.exactSize ? `${s.costInSol.toFixed(4)} SOL in` : `${sizeBand(s.costInSol)} (band)`],
359
488
  ['FILLS', `${s.buys} buy${s.buys === 1 ? '' : 's'} · ${s.sells} sell${s.sells === 1 ? '' : 's'}`],
360
- ['OPENED', fmtWhen(s.firstT)],
361
- ['CLOSED', s.open ? 'still open' : fmtWhen(s.lastT)],
489
+ ...(mv.basis === 'mcap' ? mcapSourceRows(s, mv) : [
490
+ ['OPENED', fmtWhen(s.firstT)],
491
+ ['CLOSED', s.open ? 'still open' : fmtWhen(s.lastT)],
492
+ ]),
362
493
  ];
494
+ if (mv.fallback)
495
+ stats.push(['MCAP', mv.fallback.startsWith('market cap not shown: no token supply') ? 'n/a: no supply known' : 'n/a: decimals unknown']);
363
496
  const prov = [...provenance(s, o, 'card'), stamp.note];
364
497
  const coinTile = (x, y, w, h) => {
365
498
  if (s.input.image && s.input.image.w > 0 && s.input.image.h > 0) {
@@ -510,7 +643,11 @@ export function cardText(s, o = {}) {
510
643
  L.push(`${s.kind} PnL ${fmtPnlPct(s.pnlPct)} (net of fees)${o.exactSize ? ` · ${signedSol(s.pnlSol)} on ${s.costInSol.toFixed(4)} SOL in` : ` · size ${sizeBand(s.costInSol)} (band)`}`);
511
644
  const dec = s.input.decimals !== null;
512
645
  const p = (v) => (v === null ? (dec ? '—' : 'n/a') : `${fmtPriceBare(v)} SOL`);
513
- L.push(`entry avg ${p(s.entryAvg)} · ${s.open ? `mark ${p(s.input.mark?.priceSol ?? null)}` : `exit avg ${p(s.exitAvg)}`} · hold ${fmtHold(s.holdMs)}${s.open ? ' (open)' : ''} · ${s.buys} buys · ${s.sells} sells`);
646
+ const mv = mcapView(s, o);
647
+ const head = mv.basis === 'mcap'
648
+ ? mcapRows(s, mv).map(([k, v]) => `${k.toLowerCase()} ${v}`).join(' · ')
649
+ : `entry avg ${p(s.entryAvg)} · ${s.open ? `mark ${p(s.input.mark?.priceSol ?? null)}` : `exit avg ${p(s.exitAvg)}`}`;
650
+ L.push(`${head} · hold ${fmtHold(s.holdMs)}${s.open ? ' (open)' : ''} · ${s.buys} buys · ${s.sells} sells`);
514
651
  L.push(...provenance(s, o));
515
652
  L.push(st.note + ' Trading in dexterm is experimental.');
516
653
  L.push(`not financial advice · ${DEXTERM_URL}`);
@@ -9,11 +9,11 @@
9
9
  // changed, otherwise just the last two bars ("tail"), so a live tick costs the page one `series.update`.
10
10
  // When the coin is held (task-1323 PositionSource) the VM carries the position row + entry/stop/target levels
11
11
  // and the fills of the current position from the paper ledger. Read-only: nothing here can trade.
12
- import { createPopupManager, loadConfig, nativeShellAvailable, TerminalDock, CHART_INTERVALS } from '../../popup/src/index.js';
12
+ import { createPopupManager, loadConfig, nativeShellAvailable, TerminalDock, CHART_INTERVALS, CHART_MARKS_MAX } from '../../popup/src/index.js';
13
13
  import { fmtPrice, fmtNum } from '../../tui/src/index.js';
14
14
  import { positionRow } from "./live/trade.js";
15
15
  import { signedUsd, signedPctOf, fmtUsdFine, sol as fmtSol, signedSol } from "./screens/positions.js";
16
- import { supplyRatio, positionPriceCells } from "./display.js";
16
+ import { supplyRatio, positionPriceCells, levelCell } from "./display.js";
17
17
  import { chipSegments } from "./screens/pnl-chip.js";
18
18
  /** Map the app's active Theme to the popup protocol's theme-token subset. */
19
19
  export function popupThemeFrom(t) {
@@ -26,6 +26,11 @@ export function popupThemeFrom(t) {
26
26
  /** Most bars one VM carries (the 1m base is 1000 bars; this only guards a future bigger base). */
27
27
  export const CHART_MAX_BARS = 1500;
28
28
  export const DEFAULT_CHART_PREFS = { style: 'candles', scale: 'auto', indicators: ['volume', 'tape', 'ema9', 'ema21'] };
29
+ /** task-1483: the VM's markers layer: the newest CHART_MARKS_MAX fills (oldest first), and how many older ones were left out. */
30
+ export function chartMarksFrom(on, fills) {
31
+ const keep = fills.length > CHART_MARKS_MAX ? fills.slice(-CHART_MARKS_MAX) : fills;
32
+ return { on, fills: keep.map((f) => ({ t: f.t, side: f.side, source: f.source, priceUsd: f.priceUsd, tokens: f.tokens, sol: f.sol })), truncated: fills.length - keep.length };
33
+ }
29
34
  const finitePos = (n) => typeof n === 'number' && Number.isFinite(n) && n > 0;
30
35
  /** Token amounts up to trillions (same rule as the terminal's position row). */
31
36
  function fmtAmt(n) { return n >= 1e12 ? `${(n / 1e12).toFixed(2)}T` : fmtNum(n); }
@@ -57,9 +62,9 @@ export function positionCells(r, mode = 'price', supply = null) {
57
62
  if (r.realizedSol !== 0)
58
63
  cells.push({ label: 'REALIZED', value: usd && r.realizedUsd !== null ? signedUsd(r.realizedUsd) : `${signedSol(r.realizedSol)} SOL`, tone: tone(r.realizedSol) });
59
64
  if (r.stopUsd !== null)
60
- cells.push({ label: 'STOP', value: `${fmtPrice(r.stopUsd)}${r.stopDistPct !== null ? ` ${signedPctOf(r.stopDistPct)}` : ''}`, tone: 'down' });
65
+ cells.push({ label: 'STOP', value: `${levelCell(mode, supply, r.stopUsd)}${r.stopDistPct !== null ? ` ${signedPctOf(r.stopDistPct)}` : ''}`, tone: 'down' });
61
66
  if (r.targetUsd !== null)
62
- cells.push({ label: 'TARGET', value: `${fmtPrice(r.targetUsd)}${r.targetDistPct !== null ? ` ${signedPctOf(r.targetDistPct)}` : ''}`, tone: 'up' });
67
+ cells.push({ label: 'TARGET', value: `${levelCell(mode, supply, r.targetUsd)}${r.targetDistPct !== null ? ` ${signedPctOf(r.targetDistPct)}` : ''}`, tone: 'up' });
63
68
  return cells;
64
69
  }
65
70
  /** The chart's position model from the shared task-1323 selector (PositionSource → positionRow). */
@@ -138,7 +143,7 @@ export function chartVMFromView(v, x = {}) {
138
143
  vm.feed = x.feed ?? 'POLL';
139
144
  vm.livePrice = x.feed === 'LIVE' && finitePos(x.livePrice) ? x.livePrice : null;
140
145
  vm.trades = v.trades.map((t) => ({ t: t.t, side: t.side, usd: t.usd, price: t.price })); // no wallet addresses
141
- const mode = x.priceMode === 'mcap' ? 'mcap' : 'price';
146
+ const mode = x.priceMode === 'price' ? 'price' : 'mcap'; // task-1537: market cap unless the host says price
142
147
  const supply = mode === 'mcap' ? supplyRatio(v.price, v.mcap) : null;
143
148
  vm.position = x.held ? chartPositionFrom(x.held, tick, x.solUsd ?? null, x.positionKind ?? 'paper', x.fills ?? [], mode, supply) : null;
144
149
  vm.prefs = x.prefs ?? DEFAULT_CHART_PREFS;
@@ -147,6 +152,8 @@ export function chartVMFromView(v, x = {}) {
147
152
  // task-1366: display only — the bars stay USD prices; the page formats the axis as price × supply in mcap mode
148
153
  vm.priceMode = mode;
149
154
  vm.supply = supply;
155
+ if (x.marks)
156
+ vm.marks = chartMarksFrom(x.marks.on, x.marks.fills); // task-1483
150
157
  return vm;
151
158
  }
152
159
  /** Build the token-detail popup view model from the live TokenView (reused by the openPopup interface / Jarvis). */
@@ -447,6 +454,14 @@ export class TokenPopups {
447
454
  this.push(this.lastView);
448
455
  return;
449
456
  }
457
+ case 'chart.markers': { // task-1483: a display preference the terminal saves; nothing else
458
+ if (typeof p.on !== 'boolean')
459
+ return;
460
+ this.source?.setChartMarkers?.(p.on);
461
+ if (this.lastView)
462
+ this.push(this.lastView);
463
+ return;
464
+ }
450
465
  case 'chart.painted': {
451
466
  const ms = Number(p.ms);
452
467
  if (p.kind === 'first' && this.metrics.openedAt !== null && this.metrics.openToFirstPaintMs === null)
@@ -535,7 +550,8 @@ export class TokenPopups {
535
550
  prefs: this.prefs,
536
551
  now: this.now(),
537
552
  image: src.imagePath?.(view) ?? null,
538
- priceMode: src.chartAxis?.() ?? src.priceMode?.() ?? 'price', // task-1472: the chart axis drives the popup
553
+ priceMode: src.chartAxis?.() ?? src.priceMode?.() ?? 'mcap', // task-1472: the chart axis drives the popup
554
+ marks: src.markFills ? { on: src.chartMarkers?.() ?? true, fills: src.markFills(view.mint) } : null, // task-1483
539
555
  });
540
556
  vm.chip = chartChipFrom(src.pnlChip?.() ?? null); // task-1473 r2
541
557
  const key = vm.seriesKey ?? null;