dexterm-cli 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (379) hide show
  1. package/dist/packages/agents/src/index.js +2 -0
  2. package/dist/packages/agents/src/schema.js +22 -1
  3. package/dist/packages/agents/src/spectator.js +54 -0
  4. package/dist/packages/agents/src/story.js +532 -0
  5. package/dist/packages/agents/src/tiers.js +28 -2
  6. package/dist/packages/agents/src/tools.js +31 -2
  7. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  8. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  9. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  10. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  11. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  12. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  13. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  14. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  15. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  16. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  17. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  18. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  19. package/dist/packages/autotrade/src/account.js +74 -0
  20. package/dist/packages/autotrade/src/backtest.js +357 -0
  21. package/dist/packages/autotrade/src/basis-text.js +32 -0
  22. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  23. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  24. package/dist/packages/autotrade/src/chat/config.js +93 -0
  25. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  26. package/dist/packages/autotrade/src/chat/control.js +103 -0
  27. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  28. package/dist/packages/autotrade/src/chat/format.js +71 -0
  29. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  30. package/dist/packages/autotrade/src/chat/index.js +18 -0
  31. package/dist/packages/autotrade/src/chat/notify.js +292 -0
  32. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  33. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  34. package/dist/packages/autotrade/src/chat/secrets.js +99 -0
  35. package/dist/packages/autotrade/src/chat/service.js +248 -0
  36. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  37. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  38. package/dist/packages/autotrade/src/engine.js +387 -0
  39. package/dist/packages/autotrade/src/execution-port.js +97 -0
  40. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  41. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  42. package/dist/packages/autotrade/src/fleet.js +639 -0
  43. package/dist/packages/autotrade/src/index.js +53 -0
  44. package/dist/packages/autotrade/src/ledger.js +108 -0
  45. package/dist/packages/autotrade/src/live-source.js +182 -0
  46. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  47. package/dist/packages/autotrade/src/monitor.js +190 -0
  48. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  49. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  50. package/dist/packages/autotrade/src/replay.js +198 -0
  51. package/dist/packages/autotrade/src/runner-config.js +99 -0
  52. package/dist/packages/autotrade/src/runner.js +872 -0
  53. package/dist/packages/autotrade/src/safety.js +243 -0
  54. package/dist/packages/autotrade/src/session.js +269 -0
  55. package/dist/packages/autotrade/src/sessions.js +503 -0
  56. package/dist/packages/autotrade/src/store.js +250 -0
  57. package/dist/packages/autotrade/src/strategy.js +256 -0
  58. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  59. package/dist/packages/autotrade/src/yolo.js +140 -0
  60. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  61. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +1 -1
  62. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +15 -8
  63. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  64. package/dist/packages/cli/src/agent-cmd.js +6 -4
  65. package/dist/packages/cli/src/agent-spectator.js +17 -1
  66. package/dist/packages/cli/src/ai-cmd.js +412 -0
  67. package/dist/packages/cli/src/analyst/backend.js +105 -15
  68. package/dist/packages/cli/src/analyst/codex.js +14 -9
  69. package/dist/packages/cli/src/analyst/larp-gauge.js +60 -0
  70. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  71. package/dist/packages/cli/src/analyst/session.js +17 -3
  72. package/dist/packages/cli/src/app-help-gen.js +51 -15
  73. package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
  74. package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
  75. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  76. package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
  77. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  78. package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
  79. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  80. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  81. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  82. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  83. package/dist/packages/cli/src/autotrade-cmd.js +168 -0
  84. package/dist/packages/cli/src/backtest-cmd.js +13 -2
  85. package/dist/packages/cli/src/display.js +208 -23
  86. package/dist/packages/cli/src/doctor.js +62 -3
  87. package/dist/packages/cli/src/export.js +10 -2
  88. package/dist/packages/cli/src/index.js +1 -1
  89. package/dist/packages/cli/src/ledger-cmd.js +288 -0
  90. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  91. package/dist/packages/cli/src/live/dev-history.js +31 -15
  92. package/dist/packages/cli/src/live/index.js +28 -90
  93. package/dist/packages/cli/src/live/screens/newpairs-live.js +1 -1
  94. package/dist/packages/cli/src/live/screens/token-live.js +48 -15
  95. package/dist/packages/cli/src/live/screens/watchlist-live.js +1 -1
  96. package/dist/packages/cli/src/live/trade.js +30 -453
  97. package/dist/packages/cli/src/live/watchlist.js +1 -1
  98. package/dist/packages/cli/src/main.js +290 -199
  99. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  100. package/dist/packages/cli/src/mcp-follow.js +103 -6
  101. package/dist/packages/cli/src/plan-cmd.js +15 -5
  102. package/dist/packages/cli/src/pnl-app.js +31 -21
  103. package/dist/packages/cli/src/pnl-card.js +158 -21
  104. package/dist/packages/cli/src/popup.js +38 -6
  105. package/dist/packages/cli/src/record-cmd.js +141 -20
  106. package/dist/packages/cli/src/scan/ui.js +7 -1
  107. package/dist/packages/cli/src/screens/agent-hud.js +773 -0
  108. package/dist/packages/cli/src/screens/analyst-session.js +11 -4
  109. package/dist/packages/cli/src/screens/analyst.js +3 -2
  110. package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
  111. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  112. package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
  113. package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
  114. package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
  115. package/dist/packages/cli/src/screens/brain.js +634 -0
  116. package/dist/packages/cli/src/screens/newpairs.js +1 -1
  117. package/dist/packages/cli/src/screens/overlay.js +5 -2
  118. package/dist/packages/cli/src/screens/plans.js +5 -17
  119. package/dist/packages/cli/src/screens/pnl-chip.js +144 -0
  120. package/dist/packages/cli/src/screens/positions.js +6 -28
  121. package/dist/packages/cli/src/screens/screener.js +1 -0
  122. package/dist/packages/cli/src/screens/ticket.js +38 -205
  123. package/dist/packages/cli/src/screens/token.js +18 -9
  124. package/dist/packages/cli/src/screens/trending.js +1 -1
  125. package/dist/packages/cli/src/state.js +88 -18
  126. package/dist/packages/cli/src/telegram-cmd.js +451 -0
  127. package/dist/packages/cli/src/trade.js +3 -128
  128. package/dist/packages/cli/src/version.js +1 -1
  129. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  130. package/dist/packages/cli/src/wallet-command.js +8 -11
  131. package/dist/packages/cli/src/widgets.js +0 -12
  132. package/dist/packages/custom/src/config.js +20 -2
  133. package/dist/packages/custom/src/schema.js +41 -10
  134. package/dist/packages/custom/src/workspace.js +3 -3
  135. package/dist/packages/data/src/basis.js +191 -0
  136. package/dist/packages/data/src/detail.js +53 -2
  137. package/dist/packages/data/src/index.js +2 -14
  138. package/dist/packages/data/src/larp/score.js +220 -0
  139. package/dist/packages/data/src/larp/x-account.js +316 -0
  140. package/dist/packages/data/src/store.js +14 -5
  141. package/dist/packages/data/src/trade/agent/paper-desk.js +300 -23
  142. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  143. package/dist/packages/data/src/trade/bridge.js +4 -102
  144. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  145. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  146. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  147. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  148. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  149. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  150. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  151. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  152. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  153. package/dist/packages/data/src/trade/plans/engine.js +7 -0
  154. package/dist/packages/data/src/trade/plans/index.js +0 -1
  155. package/dist/packages/data/src/trade/plans/mandate.js +28 -15
  156. package/dist/packages/data/src/trade/plans/service.js +4 -28
  157. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  158. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  159. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  160. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  161. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  162. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  163. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  164. package/dist/packages/mcp/assets/kb/app-help/dev-history.md +1 -1
  165. package/dist/packages/mcp/assets/kb/app-help/larp-check.md +5 -1
  166. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  167. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  168. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  169. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  170. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  171. package/dist/packages/mcp/assets/kb/app-help/scan-mode.md +1 -1
  172. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  173. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
  174. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  175. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  176. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  177. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  178. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +6 -0
  179. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  180. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  181. package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +2 -1
  182. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  183. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  184. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +7 -3
  186. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  188. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  189. package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +2 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
  194. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  195. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  198. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  200. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  201. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  219. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  220. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +32 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  223. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  225. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +9 -7
  226. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  227. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  228. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  229. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  230. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  233. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  234. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  235. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +1 -1
  236. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  237. package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +1 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-larp-score.md +60 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +0 -2
  240. package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +4 -1
  241. package/dist/packages/mcp/assets/kb/docs/docs-larp.md +1 -1
  242. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  243. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  244. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
  246. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  248. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  250. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  264. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +4 -13
  266. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  267. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +40 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  269. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  271. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  273. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  274. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  275. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +1 -1
  277. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +3 -1
  278. package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +2 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +10 -7
  281. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  282. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +1 -1
  284. package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +2 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-agent-hud-spectator-v2.md +58 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +46 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +1 -1
  288. package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +1 -1
  289. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  290. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  291. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  292. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  293. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  295. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  297. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  298. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  299. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  302. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  309. package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
  310. package/dist/packages/mcp/src/app-help.js +1 -1
  311. package/dist/packages/mcp/src/autotrade-tools.js +944 -0
  312. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  313. package/dist/packages/mcp/src/engine/config.js +159 -0
  314. package/dist/packages/mcp/src/engine/index.js +10 -0
  315. package/dist/packages/mcp/src/engine/loop.js +101 -0
  316. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  317. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  318. package/dist/packages/mcp/src/engine/probe.js +110 -0
  319. package/dist/packages/mcp/src/engine/reason.js +35 -0
  320. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  321. package/dist/packages/mcp/src/index.js +7 -1
  322. package/dist/packages/mcp/src/ledger-tools.js +346 -0
  323. package/dist/packages/mcp/src/paper-agent.js +5 -2
  324. package/dist/packages/mcp/src/server.js +285 -36
  325. package/dist/packages/mcp/src/spectator-sink.js +85 -0
  326. package/dist/packages/packs/src/backtest.js +3 -1
  327. package/dist/packages/packs/src/index.js +1 -0
  328. package/dist/packages/packs/src/larp-score-backtest.js +119 -0
  329. package/dist/packages/popup/src/pages/chart-lib.js +169 -35
  330. package/dist/packages/popup/src/pages/chart.css +18 -0
  331. package/dist/packages/popup/src/pages/chart.html +6 -1
  332. package/dist/packages/popup/src/pages/chart.js +136 -10
  333. package/dist/packages/popup/src/pages/spectator.css +45 -0
  334. package/dist/packages/popup/src/pages/spectator.html +1 -0
  335. package/dist/packages/popup/src/pages/spectator.js +69 -0
  336. package/dist/packages/popup/src/protocol.js +4 -0
  337. package/dist/packages/popup/src/server.js +9 -1
  338. package/dist/packages/recorder/src/dev-history.js +7 -3
  339. package/dist/packages/recorder/src/index.js +2 -0
  340. package/dist/packages/recorder/src/recorder.js +63 -6
  341. package/dist/packages/recorder/src/retention.js +100 -0
  342. package/dist/packages/recorder/src/schema.js +34 -1
  343. package/dist/packages/recorder/src/ticks.js +237 -0
  344. package/dist/packages/tui/src/canvas.js +7 -0
  345. package/dist/packages/tui/src/fmt.js +53 -0
  346. package/dist/packages/tui/src/index.js +1 -0
  347. package/dist/packages/tui/src/keymap.js +25 -1
  348. package/dist/packages/tui/src/markers.js +219 -0
  349. package/dist/packages/tui/src/widgets.js +158 -6
  350. package/package.json +2 -1
  351. package/dist/packages/data/src/trade/caps.js +0 -323
  352. package/dist/packages/data/src/trade/executor.js +0 -933
  353. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  354. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  355. package/dist/packages/data/src/trade/signer/client.js +0 -197
  356. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  357. package/dist/packages/data/src/trade/signer/host.js +0 -97
  358. package/dist/packages/data/src/trade/signer/main.js +0 -121
  359. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  360. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  361. package/dist/packages/data/src/trade/signer/service.js +0 -400
  362. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  363. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  364. package/dist/packages/data/src/trade/swap-build.js +0 -108
  365. package/dist/packages/data/src/trade/token-delta.js +0 -201
  366. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  367. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  368. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  369. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  370. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  371. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  372. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  373. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  374. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  375. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  376. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  377. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  378. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  379. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -268,6 +268,10 @@ export const DEFAULT_KEYMAP = new Keymap([
268
268
  // the open coin. (`P` is already the global positions key, so chart uses `C`.) Collision-free with the
269
269
  // token screen's other capitals (B/S trade, W/X/T/L links, V vet).
270
270
  { keys: ['C'], action: 'popup.chart', label: 'chart popup', screen: 'token' },
271
+ // task-1472: `A` (axis) on the token screen flips the chart Y axis MARKET CAP ⇄ PRICE (both charts; saved as
272
+ // display.chartAxis). Free capital on the token screen (A is only auto-mirror on the follows screen); deliberately
273
+ // not Y, which confirms inside the PAPER ticket and the modals. The chart popup uses the same `A`.
274
+ { keys: ['A'], action: 'display.chartAxis', label: 'chart axis mcap ⇄ price', screen: 'token' },
271
275
  // task-1359: `I` on the token screen opens/toggles the TOKEN-DETAIL HUD (identity, market, risk, snipers/bundled
272
276
  // from the wallet-intel scan); `M` on the trending screen opens/toggles the FEEDS HUD (the keyless social feed +
273
277
  // official news, newest first; ⏎ in the window opens that coin here). Both are read-only windows. `I` is unused
@@ -335,7 +339,6 @@ export const DEFAULT_KEYMAP = new Keymap([
335
339
  { keys: ['!'], action: 'plans.panic', label: 'PANIC', screen: 'plans' },
336
340
  { keys: ['M'], action: 'plans.popup', label: 'popup', screen: 'plans' },
337
341
  { keys: ['G'], action: 'plans.gate', label: 'hands-free gate', screen: 'plans' },
338
- { keys: ['L'], action: 'plans.live', label: 'fire path paper/live', screen: 'plans' },
339
342
  // task-1360 copy trading. `U` opens the FOLLOWED WALLETS screen from anywhere (free capital: not B/S/P/W/V/X/T/L/C/F/Z/R/
340
343
  // K/M/N/D/O/G/I). On it: `i` types a wallet address (+ optional label) to follow, `d` unfollows the selected wallet,
341
344
  // `r` (global refresh) polls now. Nothing on this screen trades: a buy/sell alert is information only.
@@ -364,4 +367,25 @@ export const DEFAULT_KEYMAP = new Keymap([
364
367
  { keys: ['d'], action: 'rules.remove', label: 'delete rule', screen: 'rules' },
365
368
  { keys: ['tab'], action: 'rules.tab', label: 'rules ⇄ proposals', screen: 'rules' },
366
369
  { keys: ['x'], action: 'rules.dismiss', label: 'dismiss proposal', screen: 'rules' },
370
+ // task-1508 AUTOTRADE (paper). `Q` (free capital everywhere) opens the screen; tab cycles its five views (strategies,
371
+ // editor, compare, backtest, runner). The lowercase verbs below override the global w/b/x/p/i/s on this screen only.
372
+ // `!` soft-stops the paper runner, `p` pauses its new entries, `f` flattens after a y confirm. Edits only touch the
373
+ // user's own strategy files (validated, journaled); there is no key here for live mode, mandates or the signer.
374
+ { keys: ['Q'], action: 'screen.autotrade', label: 'autotrade (paper)' },
375
+ { keys: ['tab'], action: 'autotrade.view', label: 'next view', screen: 'autotrade' },
376
+ { keys: ['n'], action: 'autotrade.new', label: 'new from preset', screen: 'autotrade' },
377
+ { keys: ['d'], action: 'autotrade.dup', label: 'duplicate', screen: 'autotrade' },
378
+ { keys: ['e'], action: 'autotrade.edit', label: 'edit', screen: 'autotrade' },
379
+ { keys: ['c'], action: 'autotrade.compare', label: 'mark to compare', screen: 'autotrade' },
380
+ { keys: ['b'], action: 'autotrade.backtest', label: 'backtest', screen: 'autotrade' },
381
+ { keys: ['w'], action: 'autotrade.sweep', label: 'sweep (held-out)', screen: 'autotrade' },
382
+ { keys: ['x'], action: 'autotrade.export', label: 'export file', screen: 'autotrade' },
383
+ { keys: ['i'], action: 'autotrade.import', label: 'import file', screen: 'autotrade' },
384
+ { keys: ['r'], action: 'autotrade.run', label: 'runner', screen: 'autotrade' },
385
+ { keys: ['s'], action: 'autotrade.save', label: 'save (new version)', screen: 'autotrade' },
386
+ { keys: ['u'], action: 'autotrade.undo', label: 'undo', screen: 'autotrade' },
387
+ { keys: ['!'], action: 'autotrade.kill', label: 'kill (soft stop)', screen: 'autotrade' },
388
+ { keys: ['p'], action: 'autotrade.pause', label: 'pause new entries', screen: 'autotrade' },
389
+ { keys: ['f'], action: 'autotrade.flatten', label: 'flatten (confirm)', screen: 'autotrade' },
390
+ { keys: ['o'], action: 'autotrade.proposal', label: 'review chat proposal', screen: 'autotrade' },
367
391
  ]);
@@ -0,0 +1,219 @@
1
+ // @dexterm/tui — task-1483 BUY / SELL markers: the pure model every chart draws from (terminal candles, the agent
2
+ // views' traces, and — mirrored in packages/popup/src/pages/chart-lib.js, parity-tested — the chart popup).
3
+ //
4
+ // Input: the fills the app already holds (the paper ledger's trades and the live ledger's confirmed swaps), never a
5
+ // fetch. Output: one marker per (bar, side, source) for the bars on screen, the open position's average entry per
6
+ // source, and the closed round trips. Display only: nothing here can arm, size, sign or confirm anything.
7
+ //
8
+ // Placement rules (the card's "never clamped to a wrong place"):
9
+ // - a fill belongs to the bar whose period contains its time (bar open = floor(t / period) · period);
10
+ // - a fill older than the first bar on screen, newer than the last bar, or in a period with no bar (a quiet coin
11
+ // can skip a minute) is OMITTED and counted, never moved onto a neighbouring bar;
12
+ // - the price is the fill's own USD price (token-weighted when one bar holds several fills of a side); with no
13
+ // known price the marker is drawn against the bar (below it for a buy, above it for a sell) and says so.
14
+ // The axis never changes a marker's position: bars and markers are both laid out in USD price; an mcap axis only
15
+ // relabels (price × the current supply, task-1366/1472).
16
+ /** Bar periods by the app's timeframe names. */
17
+ export const MARKER_PERIOD_MS = { '1m': 60e3, '5m': 300e3, '15m': 900e3, '1h': 3600e3, '4h': 14400e3, '1d': 86400e3 };
18
+ /** The open time of the bar a time falls in. */
19
+ export function bucketOf(t, periodMs) { return Math.floor(t / periodMs) * periodMs; }
20
+ const fin = (n) => typeof n === 'number' && Number.isFinite(n);
21
+ const EPS = 1e-9;
22
+ /** Valid fills only (finite time, a side, a source, a positive token amount), oldest first; stable for equal times. */
23
+ export function cleanFills(fills) {
24
+ const out = [];
25
+ for (const f of fills ?? []) {
26
+ if (!f || !fin(f.t) || (f.side !== 'buy' && f.side !== 'sell') || (f.source !== 'paper' && f.source !== 'live'))
27
+ continue;
28
+ if (!fin(f.tokens) || f.tokens <= 0)
29
+ continue;
30
+ out.push({ t: f.t, side: f.side, source: f.source, priceUsd: fin(f.priceUsd) && f.priceUsd > 0 ? f.priceUsd : null, tokens: f.tokens, sol: fin(f.sol) && f.sol >= 0 ? f.sol : null });
31
+ }
32
+ return out.map((f, i) => [f, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([f]) => f);
33
+ }
34
+ /**
35
+ * Replay the fills per source (oldest first) the way the ledgers book them: a buy adds tokens and cost, a sell
36
+ * removes tokens at the average cost. A sell bigger than what the replay holds (the history was truncated) is
37
+ * treated as closing. Returns every fill annotated, the open position's average entry, and the closed trips.
38
+ */
39
+ export function replayFills(fills) {
40
+ const st = {
41
+ paper: { held: 0, cost: 0, costOk: true, trip: null },
42
+ live: { held: 0, cost: 0, costOk: true, trip: null },
43
+ };
44
+ const out = [];
45
+ const trips = [];
46
+ for (const f of cleanFills(fills)) {
47
+ const s = st[f.source];
48
+ const before = s.held;
49
+ if (f.side === 'buy') {
50
+ if (s.held <= EPS) {
51
+ s.held = 0;
52
+ s.cost = 0;
53
+ s.costOk = true;
54
+ s.trip = { openT: f.t, bTok: 0, bUsd: 0, bOk: true, sTok: 0, sUsd: 0, sOk: true };
55
+ }
56
+ s.held += f.tokens;
57
+ if (f.priceUsd === null)
58
+ s.costOk = false;
59
+ else
60
+ s.cost += f.tokens * f.priceUsd;
61
+ if (s.trip) {
62
+ s.trip.bTok += f.tokens;
63
+ if (f.priceUsd === null)
64
+ s.trip.bOk = false;
65
+ else
66
+ s.trip.bUsd += f.tokens * f.priceUsd;
67
+ }
68
+ out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: before > EPS, closes: false });
69
+ continue;
70
+ }
71
+ const sold = Math.min(f.tokens, s.held);
72
+ const frac = s.held > EPS ? sold / s.held : 1;
73
+ s.cost -= s.cost * frac;
74
+ s.held = Math.max(0, s.held - f.tokens);
75
+ const closes = s.held <= f.tokens * 1e-9 || s.held <= EPS;
76
+ if (closes)
77
+ s.held = 0;
78
+ if (s.trip) {
79
+ s.trip.sTok += f.tokens;
80
+ if (f.priceUsd === null)
81
+ s.trip.sOk = false;
82
+ else
83
+ s.trip.sUsd += f.tokens * f.priceUsd;
84
+ }
85
+ out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: !closes, closes });
86
+ if (closes) {
87
+ if (s.trip)
88
+ trips.push({ source: f.source, openT: s.trip.openT, closeT: f.t, entryUsd: s.trip.bOk && s.trip.bTok > 0 ? s.trip.bUsd / s.trip.bTok : null, exitUsd: s.trip.sOk && s.trip.sTok > 0 ? s.trip.sUsd / s.trip.sTok : null });
89
+ s.trip = null;
90
+ s.cost = 0;
91
+ s.costOk = true;
92
+ }
93
+ }
94
+ const avg = (src) => { const s = st[src]; return s.held > EPS && s.costOk && s.cost > 0 ? s.cost / s.held : null; };
95
+ return { fills: out, avgEntry: { paper: avg('paper'), live: avg('live') }, trips };
96
+ }
97
+ /** Index of `t` in an ascending list, or -1 (binary search). */
98
+ function indexOf(times, t) {
99
+ let lo = 0, hi = times.length - 1;
100
+ while (lo <= hi) {
101
+ const mid = (lo + hi) >> 1;
102
+ const v = times[mid];
103
+ if (v === t)
104
+ return mid;
105
+ if (v < t)
106
+ lo = mid + 1;
107
+ else
108
+ hi = mid - 1;
109
+ }
110
+ return -1;
111
+ }
112
+ /**
113
+ * The markers for a window of bars: one per (bar, side, source), oldest first; buys before sells within a bar.
114
+ * Fills outside the window (or in a period with no bar) are counted in before / after / gap, never placed.
115
+ */
116
+ export function chartMarkers(fills, win) {
117
+ const rp = replayFills(fills);
118
+ const times = win.barTimes;
119
+ const empty = () => ({ markers: [], before: 0, after: 0, gap: 0, avgEntry: rp.avgEntry, trips: [] });
120
+ if (!(win.periodMs > 0) || !times.length) {
121
+ const e = empty();
122
+ e.before = rp.fills.length;
123
+ return e;
124
+ }
125
+ const first = times[0], last = times[times.length - 1];
126
+ const res = empty();
127
+ const by = new Map();
128
+ for (const f of rp.fills) {
129
+ const b = bucketOf(f.t, win.periodMs);
130
+ if (b < first) {
131
+ res.before++;
132
+ continue;
133
+ }
134
+ if (b > last) {
135
+ res.after++;
136
+ continue;
137
+ }
138
+ const index = indexOf(times, b);
139
+ if (index < 0) {
140
+ res.gap++;
141
+ continue;
142
+ }
143
+ const key = `${b}|${f.side}|${f.source}`;
144
+ let m = by.get(key);
145
+ if (!m) {
146
+ m = { side: f.side, source: f.source, bucket: b, index, t: f.t, tLast: f.t, priceUsd: null, tokens: 0, sol: 0, count: 0, partial: false, closes: false, pTok: 0, pUsd: 0, solOk: true };
147
+ by.set(key, m);
148
+ }
149
+ m.count++;
150
+ m.tLast = f.t;
151
+ m.tokens += f.tokens;
152
+ if (f.sol === null)
153
+ m.solOk = false;
154
+ else
155
+ m.sol = (m.sol ?? 0) + f.sol;
156
+ if (f.priceUsd !== null) {
157
+ m.pTok += f.tokens;
158
+ m.pUsd += f.tokens * f.priceUsd;
159
+ }
160
+ if (f.partial)
161
+ m.partial = true;
162
+ if (f.closes)
163
+ m.closes = true;
164
+ }
165
+ const order = (m) => (m.side === 'buy' ? 0 : 1) * 2 + (m.source === 'live' ? 1 : 0);
166
+ res.markers = [...by.values()].map(({ pTok, pUsd, solOk, ...m }) => ({ ...m, priceUsd: pTok > 0 ? pUsd / pTok : null, sol: solOk ? m.sol : null }))
167
+ .sort((a, b) => a.index - b.index || order(a) - order(b));
168
+ res.trips = rp.trips.filter((r) => bucketOf(r.closeT, win.periodMs) >= first && bucketOf(r.openT, win.periodMs) <= last);
169
+ return res;
170
+ }
171
+ /** The glyph for a marker: solid ▲ / ▼ for live fills, hollow △ / ▽ for paper. Reads without colour (NO_COLOR). */
172
+ export function markerGlyph(side, source) {
173
+ if (source === 'paper')
174
+ return side === 'buy' ? '△' : '▽';
175
+ return side === 'buy' ? '▲' : '▼';
176
+ }
177
+ /** The letter for a marker (always shown with the glyph, so BUY / SELL never depends on colour). */
178
+ export function markerLetter(side) { return side === 'buy' ? 'B' : 'S'; }
179
+ /** "△ ▽ paper ▲ ▼ live" — the legend for the sources actually on screen (empty when none). */
180
+ export function markerLegend(markers, avgShown = false) {
181
+ const has = (s) => markers.some((m) => m.source === s);
182
+ const parts = [];
183
+ if (has('paper'))
184
+ parts.push('△B ▽S paper');
185
+ if (has('live'))
186
+ parts.push('▲B ▼S live');
187
+ if (avgShown)
188
+ parts.push('┄ avg entry');
189
+ return parts.join(' ');
190
+ }
191
+ /** "+2 fills off-chart (1 earlier, 1 newer than the last candle)" — what the window omitted, or ''. */
192
+ export function omittedNote(s) {
193
+ const n = s.before + s.after + s.gap;
194
+ if (!n)
195
+ return '';
196
+ const why = [];
197
+ if (s.before)
198
+ why.push(`${s.before} earlier`);
199
+ if (s.after)
200
+ why.push(`${s.after} newer than the last candle`);
201
+ if (s.gap)
202
+ why.push(`${s.gap} in a period with no candle`);
203
+ return `+${n} fill${n === 1 ? '' : 's'} off-chart (${why.join(', ')})`;
204
+ }
205
+ /**
206
+ * The plot row of a price on a terminal chart laid out between `lo` and `hi` over `plotH` rows (row 0 = `hi`), the same
207
+ * mapping candleChart uses for candle bodies. Null when the price is outside the plotted range (omitted, not clamped).
208
+ */
209
+ export function markerRow(priceUsd, lo, hi, plotH) {
210
+ if (!fin(priceUsd) || !(hi > lo) || plotH < 1)
211
+ return null;
212
+ const r = Math.floor(((hi - priceUsd) / (hi - lo)) * plotH);
213
+ return r >= 0 && r < plotH ? r : null;
214
+ }
215
+ /** "B 41.2k" / "S×2 0.0₄123": the compact terminal label (axis value = price × scale, formatted by the axis). */
216
+ export function markerLabel(m, fmt, scale = 1) {
217
+ const head = `${markerLetter(m.side)}${m.count > 1 ? `×${m.count}` : ''}`;
218
+ return m.priceUsd === null ? head : `${head} ${fmt(m.priceUsd * (scale > 0 ? scale : 1))}`;
219
+ }
@@ -5,8 +5,9 @@
5
5
  // background (hard cells); glow is only ever applied to neighbouring blank cells or to bg tints.
6
6
  import { Canvas } from "./canvas.js";
7
7
  import { mix, luminance } from "./color.js";
8
- import { fmtUsd, fmtPct, fmtNum, fmtPrice, fmtAgeSec, shortAddr, clamp } from "./fmt.js";
8
+ import { fmtUsd, fmtPct, fmtNum, fmtPrice, fmtPriceBare, fmtCompactUsd, compactDecimalsFor, fmtAgeSec, shortAddr, clamp } from "./fmt.js";
9
9
  import { launchpadColor } from "./theme.js";
10
+ import { chartMarkers, markerGlyph, markerLetter, markerLabel, markerLegend, markerRow, omittedNote, MARKER_PERIOD_MS } from "./markers.js";
10
11
  export const BLOCKS = '▁▂▃▄▅▆▇█';
11
12
  // braille dot bits: column 0 rows 0..3, column 1 rows 0..3 (row 0 = top)
12
13
  const BR_L = [0x01, 0x02, 0x04, 0x40];
@@ -107,6 +108,7 @@ export function brailleLine(c, x, y, w, h, values, opts = {}) {
107
108
  c.put(x + col, y + r, String.fromCharCode(0x2800 + bits), { fg: neon(color, col / (w - 1), bg), bg, soft: true });
108
109
  }
109
110
  }
111
+ /** task-1472: the price axis never prints scientific notation: subscript zeros below 0.01 (0.0₅493), like fmtPrice. */
110
112
  function fmtPriceAxis(p) {
111
113
  if (p >= 1000)
112
114
  return p.toFixed(0);
@@ -114,7 +116,7 @@ function fmtPriceAxis(p) {
114
116
  return p.toFixed(3);
115
117
  if (p >= 0.001)
116
118
  return p.toFixed(5);
117
- return p.toExponential(2);
119
+ return fmtPriceBare(p);
118
120
  }
119
121
  /**
120
122
  * Candlestick chart with right-hand price axis, last-price tag, volume strip and time axis.
@@ -130,20 +132,37 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
130
132
  const plotW = w - axisW;
131
133
  const plotH = h - volH - timeH;
132
134
  if (plotW < 10 || plotH < 4 || candles.length === 0)
133
- return;
135
+ return null;
134
136
  const CW = 4; // 3-col body with centered wick + 1-col gap
135
137
  const n = Math.min(candles.length, Math.floor(plotW / CW));
136
138
  const data = candles.slice(-n);
137
139
  // task-1366: the bars are laid out in price units either way (identical glyphs); only the labels are scaled
138
140
  const s = opts.scale && Number.isFinite(opts.scale) && opts.scale > 0 ? opts.scale : 1;
139
141
  const baseFmt = opts.axisFmt ?? fmtPriceAxis;
140
- const fmtAxis = s === 1 ? baseFmt : (p) => baseFmt(p * s);
141
142
  let lo = Infinity, hi = -Infinity, vmax = 0;
142
143
  for (const k of data) {
143
144
  lo = Math.min(lo, k.l);
144
145
  hi = Math.max(hi, k.h);
145
146
  vmax = Math.max(vmax, k.v);
146
147
  }
148
+ // task-1483: the trade marks for the bars on screen. Their prices (and the avg entry) widen the range when they sit
149
+ // within 3× of the last close, like the popup's levels; a farther price is left off the plot rather than squashing it.
150
+ const tm = opts.trades && (opts.trades.fills.length || opts.trades.avgEntry) ? opts.trades : null;
151
+ const period = MARKER_PERIOD_MS[String(opts.interval || '1h')] ?? 3600e3;
152
+ const set = tm ? chartMarkers(tm.fills, { periodMs: period, barTimes: data.map((k) => k.t) }) : null;
153
+ const lastC = data[data.length - 1].c;
154
+ const near = (p) => typeof p === 'number' && Number.isFinite(p) && p > 0 && lastC > 0 && p / lastC < 3 && lastC / p < 3;
155
+ if (set)
156
+ for (const m of set.markers)
157
+ if (near(m.priceUsd)) {
158
+ lo = Math.min(lo, m.priceUsd);
159
+ hi = Math.max(hi, m.priceUsd);
160
+ }
161
+ const avgUsd = tm?.avgEntry && near(tm.avgEntry.usd) ? tm.avgEntry.usd : null;
162
+ if (avgUsd !== null) {
163
+ lo = Math.min(lo, avgUsd);
164
+ hi = Math.max(hi, avgUsd);
165
+ }
147
166
  const pad = (hi - lo) * 0.06 || hi * 0.01;
148
167
  lo -= pad;
149
168
  hi += pad;
@@ -152,6 +171,9 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
152
171
  const gridStyle = { fg: mix(bg, t.border, 0.9), bg, soft: true };
153
172
  const labelStyle = { fg: t.muted, bg };
154
173
  const ticks = Math.max(2, Math.min(6, Math.floor(plotH / 3)));
174
+ // task-1472: one more decimal only when two neighbouring ticks would otherwise print the same compact label
175
+ const extra = opts.axisFmt ? compactDecimalsFor(((hi + lo) / 2) * s, (range / ticks) * s) : 0;
176
+ const fmtAxis = (p) => baseFmt(p * s, extra);
155
177
  for (let i = 0; i <= ticks; i++) {
156
178
  const row = Math.round((i / ticks) * (plotH - 1));
157
179
  const price = hi - (row / (plotH - 1)) * range;
@@ -159,6 +181,30 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
159
181
  c.put(x + k, y + row, '·', gridStyle);
160
182
  c.textR(x + plotW, y + row, axisW, ' ' + fmtAxis(price), labelStyle);
161
183
  }
184
+ // task-1483: closed round trips, entry bar → exit bar, as a faint wash behind the candles (green = sold above the entry)
185
+ if (set && tm?.band !== false) {
186
+ for (const r of set.trips) {
187
+ const a = Math.max(0, data.findIndex((k) => k.t + period > r.openT));
188
+ let b = n - 1;
189
+ for (let i = n - 1; i >= 0; i--)
190
+ if (data[i].t <= r.closeT) {
191
+ b = i;
192
+ break;
193
+ }
194
+ const win = r.entryUsd !== null && r.exitUsd !== null ? r.exitUsd >= r.entryUsd : null;
195
+ const tint = mix(bg, win === null ? t.muted : win ? t.up : t.down, 0.07);
196
+ for (let i = a; i <= b; i++)
197
+ for (let q = 0; q < CW; q++)
198
+ for (let row = 0; row < plotH; row++) {
199
+ const cx = x + i * CW + q;
200
+ if (cx >= x + plotW)
201
+ continue;
202
+ const gi = c.idx(cx, y + row);
203
+ if (!c.hard[gi])
204
+ c.bg[gi] = tint;
205
+ }
206
+ }
207
+ }
162
208
  // last price line + tag
163
209
  const last = data[data.length - 1];
164
210
  const lastRow = Math.round(rowsOf(last.c));
@@ -226,6 +272,7 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
226
272
  }
227
273
  }
228
274
  }
275
+ const info = set ? drawTradeMarks(c, { x, y, plotW, plotH, n, CW, bg, lo, hi, axisW, fmtAxis, lastRow: lastRow >= 0 && lastRow < plotH ? lastRow : null, data, set, avgUsd, avgSource: tm?.avgEntry?.source ?? 'paper', scale: s }) : null;
229
276
  if (volH) {
230
277
  const vy = y + plotH;
231
278
  for (let i = 0; i < n; i++) {
@@ -245,7 +292,7 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
245
292
  }
246
293
  }
247
294
  c.text(x + plotW + 1, vy, 'vol', labelStyle);
248
- c.text(x + plotW + 1, vy + 1, fmtUsd(last.v, { digits: 1 }), { fg: t.text, bg });
295
+ c.text(x + plotW + 1, vy + 1, fmtCompactUsd(last.v), { fg: t.text, bg }); // task-1472: volume stays USD, compact k/M
249
296
  }
250
297
  const ty = y + h - 1;
251
298
  const labels = Math.max(2, Math.floor(plotW / 18));
@@ -257,6 +304,108 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
257
304
  c.text(cx, ty, lab, labelStyle);
258
305
  }
259
306
  c.textR(x + plotW, ty, axisW, (opts.interval || '1h') + ' UTC', { fg: t.dim, bg });
307
+ return info;
308
+ }
309
+ const freeCell = (c, cx, cy) => { const ch = c.ch[c.idx(cx, cy)]; return ch === ' ' || ch === '·' || ch === '┄'; };
310
+ /**
311
+ * task-1483: the avg-entry line, the B / S markers and their legend, on top of the drawn candles. Glyph + letter on
312
+ * every marker (△B ▽S paper, ▲B ▼S live), so NO_COLOR and calm read the same; colour only adds to it.
313
+ */
314
+ function drawTradeMarks(c, g) {
315
+ const t = c.theme;
316
+ const { x, y, plotW, plotH, CW, bg } = g;
317
+ const rowsOf = (p) => ((g.hi - p) / (g.hi - g.lo)) * plotH;
318
+ // the open position's average entry: a dashed line through the empty cells (candles stay on top) + an axis tag
319
+ let avgRow = null;
320
+ let avgShown = false;
321
+ if (g.avgUsd !== null) {
322
+ avgRow = markerRow(g.avgUsd, g.lo, g.hi, plotH);
323
+ if (avgRow !== null) {
324
+ const col = g.avgSource === 'paper' ? t.warn : t.text;
325
+ for (let k = 0; k < plotW; k++)
326
+ if (freeCell(c, x + k, y + avgRow)) {
327
+ c.put(x + k, y + avgRow, '┄', { fg: mix(bg, col, 0.8), bg, soft: true });
328
+ avgShown = true;
329
+ }
330
+ // `avg` at the line's left end so it reads without colour (also over the last-price dashes when both share a row),
331
+ // and the value as an axis tag
332
+ const labelOk = (cx) => freeCell(c, cx, y + avgRow) || c.ch[c.idx(cx, y + avgRow)] === '╌';
333
+ if (labelOk(x) && labelOk(x + 1) && labelOk(x + 2)) {
334
+ c.text(x, y + avgRow, 'avg', { fg: col, bg, b: true, hard: true });
335
+ avgShown = true;
336
+ }
337
+ if (avgRow !== g.lastRow) {
338
+ c.fill(x + plotW, y + avgRow, g.axisW, 1, ' ', { bg }); // the grid label under it goes, not half of it
339
+ c.textR(x + plotW, y + avgRow, g.axisW, ' ' + g.fmtAxis(g.avgUsd), { fg: '#000000', bg: col, b: true });
340
+ avgShown = true;
341
+ }
342
+ }
343
+ }
344
+ const used = new Set();
345
+ const drawn = [];
346
+ let offPlot = 0;
347
+ for (const m of g.set.markers) {
348
+ const k = g.data[m.index];
349
+ const col0 = x + m.index * CW;
350
+ const buy = m.side === 'buy';
351
+ // the fill's own price; no price → against the bar (below the low for a buy, above the high for a sell)
352
+ let row = m.priceUsd !== null ? markerRow(m.priceUsd, g.lo, g.hi, plotH) : Math.floor(rowsOf(buy ? k.l : k.h)) + (buy ? 1 : -1);
353
+ if (row === null || row < 0 || row >= plotH) {
354
+ offPlot++;
355
+ continue;
356
+ }
357
+ // two marks in one bar on the same row (buy + sell, or paper + live): step away from the bar, never across it
358
+ while (row >= 0 && row < plotH && (used.has((row << 12) | m.index)))
359
+ row += buy ? 1 : -1;
360
+ if (row < 0 || row >= plotH) {
361
+ offPlot++;
362
+ continue;
363
+ }
364
+ used.add((row << 12) | m.index);
365
+ const sideCol = buy ? t.up : t.down;
366
+ const st = m.source === 'live' ? { fg: '#000000', bg: sideCol, b: true, hard: true } : { fg: sideCol, bg: mix(bg, sideCol, 0.2), b: true, hard: true };
367
+ c.put(col0 + 1, y + row, markerGlyph(m.side, m.source), st);
368
+ c.put(col0 + 2, y + row, markerLetter(m.side), st);
369
+ if (m.count > 1 && col0 + 3 < x + plotW)
370
+ c.put(col0 + 3, y + row, m.count > 9 ? '+' : String(m.count), st);
371
+ drawn.push({ marker: m, col: col0 + 1, row });
372
+ }
373
+ // the newest marker gets a compact label (B 41k) where the row has room, right of it first, else left
374
+ const newest = drawn[drawn.length - 1];
375
+ if (newest) {
376
+ const label = markerLabel(newest.marker, g.fmtAxis, 1);
377
+ const fits = (sx) => sx >= x && sx + label.length <= x + plotW && [...label].every((_, i) => freeCell(c, sx + i, y + newest.row));
378
+ const right = newest.col + 3 + (newest.marker.count > 1 ? 1 : 0);
379
+ const sx = fits(right) ? right : fits(newest.col - 2 - label.length) ? newest.col - 2 - label.length : null;
380
+ if (sx !== null)
381
+ c.text(sx, y + newest.row, label, { fg: newest.marker.side === 'buy' ? t.up : t.down, bg, hard: true });
382
+ }
383
+ // legend + what was left off, on the top plot row where it is empty (else the bottom row), never over a candle
384
+ const note = [markerLegend(drawn.map((d) => d.marker), avgShown), omittedNote(g.set)].filter(Boolean).join(' · ');
385
+ if (note) {
386
+ const text = note.length > plotW - 2 ? note.slice(0, Math.max(0, plotW - 3)) + '…' : note;
387
+ // the outermost rows first (top, bottom, then inwards); in a row, the leftmost run of empty cells long enough
388
+ const rows = [];
389
+ for (let i = 0; i < Math.ceil(plotH / 2); i++) {
390
+ rows.push(i);
391
+ if (plotH - 1 - i !== i)
392
+ rows.push(plotH - 1 - i);
393
+ }
394
+ const len = [...text].length;
395
+ place: for (const row of rows) {
396
+ if (row === avgRow || row === g.lastRow || drawn.some((d) => d.row === row))
397
+ continue;
398
+ let run = 0;
399
+ for (let cx = x + 1; cx < x + plotW; cx++) {
400
+ run = freeCell(c, cx, y + row) ? run + 1 : 0;
401
+ if (run === len) {
402
+ c.text(cx - len + 1, y + row, text, { fg: t.dim, bg, i: true });
403
+ break place;
404
+ }
405
+ }
406
+ }
407
+ }
408
+ return { set: g.set, drawn, offPlot, avgRow };
260
409
  }
261
410
  /** Horizontal progress bar: gradient fill, hot head cell, one-cell ▓ glow past the head, ░ empty. pct 0..100 */
262
411
  export function bar(c, x, y, w, pct, opts = {}) {
@@ -386,6 +535,8 @@ export function table(c, x, y, w, h, spec) {
386
535
  const selected = ri === spec.selected;
387
536
  const rowBg = selected ? t.selBg : (r % 2 === 1 && spec.zebra !== false ? mix(bg, t.panelAlt, 1) : bg);
388
537
  c.fill(x, ry, w, 1, ' ', { bg: rowBg });
538
+ if (spec.rowKey)
539
+ c.rowMarks.push({ key: spec.rowKey(row), x, y: ry, w });
389
540
  if (selected)
390
541
  c.put(x - 1 >= 0 ? x - 1 : x, ry, '▎', { fg: t.accent, bg: rowBg, soft: true });
391
542
  cx = x;
@@ -478,7 +629,8 @@ export function tradeTape(c, x, y, w, h, trades, now, opts = {}) {
478
629
  continue;
479
630
  }
480
631
  cx = c.textR(cx + 1, yy, 8, fmtUsd(tr.usd), { fg: t.text, bg });
481
- cx = c.text(cx + 2, yy, tr.price > 0 ? fmtPrice(tr.price) : '—', { fg: t.muted, bg }, Math.max(0, x + w - cx - 2));
632
+ // task-1537: the caller's price basis (a market cap at the coin's supply by default); plain price without one
633
+ cx = c.text(cx + 2, yy, tr.price > 0 ? (opts.priceFmt ?? fmtPrice)(tr.price) : '—', { fg: t.muted, bg }, Math.max(0, x + w - cx - 2));
482
634
  c.text(cx + 2, yy, shortAddr(tr.wallet, 3), { fg: t.dim, bg }, Math.max(0, x + w - cx - 2));
483
635
  n += 1;
484
636
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dexterm-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "mcpName": "io.github.dexterm-xyz/dexterm",
5
5
  "description": "dexterm: terminal DEX screener with a local Claude CLI analyst",
6
6
  "author": "The dexterm authors",
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@dexterm/agents": "0.0.1",
28
+ "@dexterm/autotrade": "0.0.1",
28
29
  "@dexterm/custom": "0.0.1",
29
30
  "@dexterm/data": "0.0.1",
30
31
  "@dexterm/mcp": "0.0.1",