dexterm-cli 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/story.js +11 -2
  3. package/dist/packages/agents/src/tiers.js +27 -2
  4. package/dist/packages/agents/src/tools.js +30 -2
  5. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  6. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  7. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  8. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  9. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  10. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  11. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  12. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  13. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  14. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  15. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  16. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  17. package/dist/packages/autotrade/src/account.js +74 -0
  18. package/dist/packages/autotrade/src/backtest.js +357 -0
  19. package/dist/packages/autotrade/src/basis-text.js +32 -0
  20. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  21. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  22. package/dist/packages/autotrade/src/chat/config.js +93 -0
  23. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  24. package/dist/packages/autotrade/src/chat/control.js +103 -0
  25. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  26. package/dist/packages/autotrade/src/chat/format.js +71 -0
  27. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  28. package/dist/packages/autotrade/src/chat/index.js +18 -0
  29. package/dist/packages/autotrade/src/chat/notify.js +292 -0
  30. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  31. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  32. package/dist/packages/autotrade/src/chat/secrets.js +99 -0
  33. package/dist/packages/autotrade/src/chat/service.js +248 -0
  34. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  35. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  36. package/dist/packages/autotrade/src/engine.js +387 -0
  37. package/dist/packages/autotrade/src/execution-port.js +97 -0
  38. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  39. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  40. package/dist/packages/autotrade/src/fleet.js +639 -0
  41. package/dist/packages/autotrade/src/index.js +53 -0
  42. package/dist/packages/autotrade/src/ledger.js +108 -0
  43. package/dist/packages/autotrade/src/live-source.js +182 -0
  44. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  45. package/dist/packages/autotrade/src/monitor.js +190 -0
  46. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  47. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  48. package/dist/packages/autotrade/src/replay.js +198 -0
  49. package/dist/packages/autotrade/src/runner-config.js +99 -0
  50. package/dist/packages/autotrade/src/runner.js +872 -0
  51. package/dist/packages/autotrade/src/safety.js +243 -0
  52. package/dist/packages/autotrade/src/session.js +269 -0
  53. package/dist/packages/autotrade/src/sessions.js +503 -0
  54. package/dist/packages/autotrade/src/store.js +250 -0
  55. package/dist/packages/autotrade/src/strategy.js +256 -0
  56. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  57. package/dist/packages/autotrade/src/yolo.js +140 -0
  58. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  59. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  60. package/dist/packages/cli/src/ai-cmd.js +412 -0
  61. package/dist/packages/cli/src/analyst/backend.js +82 -15
  62. package/dist/packages/cli/src/analyst/codex.js +14 -9
  63. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  64. package/dist/packages/cli/src/analyst/session.js +5 -2
  65. package/dist/packages/cli/src/app-help-gen.js +44 -8
  66. package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
  67. package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
  68. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  69. package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
  70. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  71. package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
  72. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  73. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  74. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  75. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  76. package/dist/packages/cli/src/autotrade-cmd.js +168 -0
  77. package/dist/packages/cli/src/display.js +156 -17
  78. package/dist/packages/cli/src/doctor.js +62 -3
  79. package/dist/packages/cli/src/export.js +10 -2
  80. package/dist/packages/cli/src/index.js +1 -1
  81. package/dist/packages/cli/src/ledger-cmd.js +288 -0
  82. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  83. package/dist/packages/cli/src/live/index.js +23 -89
  84. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  85. package/dist/packages/cli/src/live/trade.js +12 -457
  86. package/dist/packages/cli/src/live/watchlist.js +1 -1
  87. package/dist/packages/cli/src/main.js +147 -190
  88. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  89. package/dist/packages/cli/src/plan-cmd.js +1 -1
  90. package/dist/packages/cli/src/pnl-app.js +31 -21
  91. package/dist/packages/cli/src/pnl-card.js +158 -21
  92. package/dist/packages/cli/src/popup.js +22 -6
  93. package/dist/packages/cli/src/record-cmd.js +141 -20
  94. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  95. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  96. package/dist/packages/cli/src/screens/analyst.js +3 -2
  97. package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
  98. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  99. package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
  100. package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
  101. package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
  102. package/dist/packages/cli/src/screens/brain.js +37 -11
  103. package/dist/packages/cli/src/screens/overlay.js +5 -2
  104. package/dist/packages/cli/src/screens/plans.js +5 -17
  105. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  106. package/dist/packages/cli/src/screens/positions.js +6 -28
  107. package/dist/packages/cli/src/screens/ticket.js +38 -205
  108. package/dist/packages/cli/src/screens/token.js +3 -3
  109. package/dist/packages/cli/src/state.js +25 -19
  110. package/dist/packages/cli/src/telegram-cmd.js +451 -0
  111. package/dist/packages/cli/src/trade.js +3 -128
  112. package/dist/packages/cli/src/version.js +1 -1
  113. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  114. package/dist/packages/cli/src/wallet-command.js +8 -11
  115. package/dist/packages/cli/src/widgets.js +0 -12
  116. package/dist/packages/custom/src/config.js +20 -2
  117. package/dist/packages/custom/src/schema.js +35 -10
  118. package/dist/packages/custom/src/workspace.js +3 -3
  119. package/dist/packages/data/src/basis.js +191 -0
  120. package/dist/packages/data/src/detail.js +53 -2
  121. package/dist/packages/data/src/index.js +0 -14
  122. package/dist/packages/data/src/store.js +14 -5
  123. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  124. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  125. package/dist/packages/data/src/trade/bridge.js +4 -102
  126. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  127. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  128. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  129. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  130. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  131. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  132. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  133. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  134. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  135. package/dist/packages/data/src/trade/plans/index.js +0 -1
  136. package/dist/packages/data/src/trade/plans/service.js +3 -27
  137. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  138. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  139. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  140. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  141. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  142. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  143. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  144. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  145. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  146. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  147. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  148. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  149. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  150. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
  151. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  152. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  153. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  154. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  155. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  156. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  157. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  158. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  159. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  160. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  161. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  162. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  163. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  164. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  165. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  166. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  167. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  168. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
  169. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  170. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  171. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  172. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  173. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
  174. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  175. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  176. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  177. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  178. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  179. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  180. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
  181. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  182. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  183. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  184. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  185. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  186. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  187. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  188. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
  189. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  190. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  191. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  192. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  193. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  194. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  195. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  196. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  197. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  201. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  202. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  203. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  204. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  205. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  208. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  209. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  210. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  211. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  231. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  232. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  233. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  235. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  236. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  237. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  238. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  240. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  242. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  243. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  244. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  247. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  249. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  251. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  254. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  255. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  256. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  257. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  260. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  262. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  263. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  270. package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
  271. package/dist/packages/mcp/src/app-help.js +1 -1
  272. package/dist/packages/mcp/src/autotrade-tools.js +944 -0
  273. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  274. package/dist/packages/mcp/src/engine/config.js +159 -0
  275. package/dist/packages/mcp/src/engine/index.js +10 -0
  276. package/dist/packages/mcp/src/engine/loop.js +101 -0
  277. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  278. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  279. package/dist/packages/mcp/src/engine/probe.js +110 -0
  280. package/dist/packages/mcp/src/engine/reason.js +35 -0
  281. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  282. package/dist/packages/mcp/src/index.js +7 -1
  283. package/dist/packages/mcp/src/ledger-tools.js +346 -0
  284. package/dist/packages/mcp/src/paper-agent.js +2 -0
  285. package/dist/packages/mcp/src/server.js +127 -19
  286. package/dist/packages/packs/src/backtest.js +3 -1
  287. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  288. package/dist/packages/popup/src/pages/chart.css +9 -0
  289. package/dist/packages/popup/src/pages/chart.html +3 -1
  290. package/dist/packages/popup/src/pages/chart.js +104 -7
  291. package/dist/packages/popup/src/protocol.js +2 -0
  292. package/dist/packages/popup/src/server.js +4 -0
  293. package/dist/packages/recorder/src/index.js +2 -0
  294. package/dist/packages/recorder/src/recorder.js +63 -6
  295. package/dist/packages/recorder/src/retention.js +100 -0
  296. package/dist/packages/recorder/src/schema.js +34 -1
  297. package/dist/packages/recorder/src/ticks.js +237 -0
  298. package/dist/packages/tui/src/index.js +1 -0
  299. package/dist/packages/tui/src/keymap.js +21 -1
  300. package/dist/packages/tui/src/markers.js +219 -0
  301. package/dist/packages/tui/src/widgets.js +149 -2
  302. package/package.json +2 -1
  303. package/dist/packages/data/src/trade/caps.js +0 -323
  304. package/dist/packages/data/src/trade/executor.js +0 -933
  305. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  306. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  307. package/dist/packages/data/src/trade/signer/client.js +0 -197
  308. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  309. package/dist/packages/data/src/trade/signer/host.js +0 -97
  310. package/dist/packages/data/src/trade/signer/main.js +0 -121
  311. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  312. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  313. package/dist/packages/data/src/trade/signer/service.js +0 -400
  314. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  315. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  316. package/dist/packages/data/src/trade/swap-build.js +0 -108
  317. package/dist/packages/data/src/trade/token-delta.js +0 -201
  318. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  319. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  320. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  321. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  322. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  323. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  324. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  325. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  326. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  327. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  328. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  329. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  330. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  331. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -0,0 +1,135 @@
1
+ // task-1530 — the unified trade ledger FILE: $DEXTERM_HOME/ledger/trades.jsonl (0600, the directory 0700).
2
+ //
3
+ // Appends only. An append re-reads the file, verifies the whole chain, skips any draft whose `ref` is already
4
+ // there, and writes the new lines in one append, all under a lock file, so two syncs never interleave. A file whose
5
+ // chain is broken is never appended to (the user sees where it broke with `dexterm ledger verify`); a file written
6
+ // by a newer dexterm is refused rather than half-read.
7
+ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { dirname, join } from 'node:path';
10
+ import { GENESIS, LEDGER_SCHEMA, LEDGER_VERSION, ledgerHash, verifyLedger } from "./schema.js";
11
+ export function dextermHomeDir(env = process.env) {
12
+ return env.DEXTERM_HOME && env.DEXTERM_HOME.trim() ? env.DEXTERM_HOME : join(homedir(), '.dexterm');
13
+ }
14
+ export const tradeLedgerPath = (env = process.env) => join(dextermHomeDir(env), 'ledger', 'trades.jsonl');
15
+ /** Read and verify. A missing file is an empty, valid ledger. A line that is not JSON fails the check at that line. */
16
+ export function readTradeLedger(path) {
17
+ if (!existsSync(path))
18
+ return { events: [], check: { ok: true, events: 0, head: GENESIS }, exists: false };
19
+ const events = [];
20
+ const lines = readFileSync(path, 'utf8').split('\n');
21
+ for (let i = 0; i < lines.length; i++) {
22
+ const l = lines[i];
23
+ if (!l.trim())
24
+ continue;
25
+ try {
26
+ events.push(JSON.parse(l));
27
+ }
28
+ catch {
29
+ return { events, check: { ok: false, events: events.length, at: events.length + 1, error: 'a line is not JSON (the file was edited or cut off)' }, exists: true };
30
+ }
31
+ }
32
+ const head = events[0];
33
+ if (head && head.kind === 'header' && head.schema !== LEDGER_SCHEMA) {
34
+ return { events, check: { ok: false, events: events.length, at: 1, error: `unknown ledger schema ${JSON.stringify(head.schema)} (this build reads ${LEDGER_SCHEMA})` }, exists: true };
35
+ }
36
+ return { events, check: verifyLedger(events), exists: true };
37
+ }
38
+ export class LedgerBusyError extends Error {
39
+ constructor(m) { super(m); this.name = 'LedgerBusyError'; }
40
+ }
41
+ export class LedgerBrokenError extends Error {
42
+ constructor(m) { super(m); this.name = 'LedgerBrokenError'; }
43
+ }
44
+ const LOCK_STALE_MS = 60_000;
45
+ function withLock(path, fn) {
46
+ const lock = `${path}.lock`;
47
+ let fd = null;
48
+ for (let attempt = 0; attempt < 2 && fd === null; attempt++) {
49
+ try {
50
+ fd = openSync(lock, 'wx', 0o600);
51
+ }
52
+ catch (e) {
53
+ if (e.code !== 'EEXIST')
54
+ throw e;
55
+ let age = 0;
56
+ try {
57
+ age = Date.now() - statSync(lock).mtimeMs;
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ if (age > LOCK_STALE_MS) {
63
+ try {
64
+ unlinkSync(lock);
65
+ }
66
+ catch { /* raced */ }
67
+ continue;
68
+ }
69
+ throw new LedgerBusyError('another ledger sync is running (try again in a few seconds)');
70
+ }
71
+ }
72
+ if (fd === null)
73
+ throw new LedgerBusyError('could not take the ledger lock');
74
+ try {
75
+ writeSync(fd, String(process.pid));
76
+ return fn();
77
+ }
78
+ finally {
79
+ closeSync(fd);
80
+ try {
81
+ unlinkSync(lock);
82
+ }
83
+ catch { /* gone */ }
84
+ }
85
+ }
86
+ /**
87
+ * Append drafts to the ledger (creating it with its header when missing). Drafts whose `ref` is already in the
88
+ * file are skipped, so an import can be re-run any number of times. `now` stamps the header only.
89
+ */
90
+ export function appendTradeLedger(path, drafts, now = Date.now()) {
91
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
92
+ return withLock(path, () => {
93
+ const cur = readTradeLedger(path);
94
+ if (!cur.check.ok)
95
+ throw new LedgerBrokenError(`the ledger at ${path} fails its check at line ${cur.check.at}: ${cur.check.error}. Nothing was appended.`);
96
+ const seen = new Set();
97
+ for (const e of cur.events)
98
+ if ('ref' in e && typeof e.ref === 'string')
99
+ seen.add(e.ref);
100
+ let prev = cur.events.length ? cur.events[cur.events.length - 1].hash : GENESIS;
101
+ let seq = cur.events.length;
102
+ const out = [];
103
+ const push = (d) => {
104
+ const e = { ...d, seq: ++seq, v: LEDGER_VERSION };
105
+ delete e.prev;
106
+ delete e.hash;
107
+ const h = ledgerHash(prev, e);
108
+ e.prev = prev;
109
+ e.hash = h;
110
+ prev = h;
111
+ out.push(JSON.stringify(e));
112
+ };
113
+ if (!cur.events.length)
114
+ push({ kind: 'header', t: now, schema: LEDGER_SCHEMA, createdAt: now });
115
+ let skipped = 0;
116
+ for (const d of drafts) {
117
+ const ref = d.ref;
118
+ if (typeof ref !== 'string' || !ref)
119
+ throw new Error('a ledger event needs a ref');
120
+ if (seen.has(ref)) {
121
+ skipped++;
122
+ continue;
123
+ }
124
+ seen.add(ref);
125
+ push(d);
126
+ }
127
+ const added = out.length - (cur.events.length ? 0 : 1);
128
+ if (out.length) {
129
+ if (!cur.exists)
130
+ closeSync(openSync(path, 'a', 0o600));
131
+ appendFileSync(path, `${out.join('\n')}\n`);
132
+ }
133
+ return { added, skipped, head: prev, events: seq };
134
+ });
135
+ }
@@ -0,0 +1,191 @@
1
+ // task-1530 — fold ledger events into TRADES (one row per round trip). Pure: the same events give the same rows.
2
+ //
3
+ // A trade is every fill that shares a `tradeKey`, from the first buy until the source says it is flat (or a
4
+ // `close` event). What the row carries, and where each field comes from:
5
+ // strategy id + version + hash from the fills (autotrade runs, live runner orders)
6
+ // profile the entry fill's tag, else a late PROFILE tag, then every REPROFILE in order (the last wins)
7
+ // entry / exit the entry fill's reason; the closing sell's exit reason code and the agent's words
8
+ // size SOL (and USD) in, % of the book the source had just before the first buy
9
+ // prices average entry, blended exit, as SOL price, USD price and market cap (price × recorded supply)
10
+ // costs fees, the SOL-weighted price impact of entries and exits, the slippage limit used
11
+ // hold first buy to the closing fill (an open trade: to `now`)
12
+ // MFE / MAE best and worst move from the entry price that the source SAW: the close event's extremes
13
+ // plus every fill's own price ('observed' vs 'fills only' says which)
14
+ // rug an annotate event, else a bag that could not be sold (stuck), else a liquidity exit that
15
+ // lost 80 %+ ('why' names the rule)
16
+ // model every session id on the trade's fills / close, and the sum of their recorded costs
17
+ /** Exit codes that mean the pool went away under the position. */
18
+ const LIQUIDITY_EXITS = new Set(['thesis-liquidity', 'illiquid', 'rug', 'liquidity-pull', 'lp-pull', 'stuck']);
19
+ const RUG_LOSS_PCT = -80;
20
+ const sum = (xs) => { let s = 0, n = 0; for (const x of xs)
21
+ if (x !== null) {
22
+ s += x;
23
+ n++;
24
+ } return n ? s : null; };
25
+ const r = (x, p = 6) => (x === null || !Number.isFinite(x) ? null : Number(x.toPrecision(p)));
26
+ const rank = { structured: 6, rule: 5, 'tag-line': 4, late: 3, default: 2, missing: 1 };
27
+ /** Weighted mean of `v` by `w` over the fills where both are known. */
28
+ function wmean(fs, v, w) {
29
+ let s = 0, ws = 0;
30
+ for (const f of fs) {
31
+ const a = v(f), b = w(f);
32
+ if (a !== null && b !== null && b > 0) {
33
+ s += a * b;
34
+ ws += b;
35
+ }
36
+ }
37
+ return ws > 0 ? s / ws : null;
38
+ }
39
+ export function foldTrades(events, o = {}) {
40
+ const now = o.now ?? Date.now();
41
+ const fills = new Map();
42
+ const tags = [];
43
+ const closes = new Map();
44
+ const looseCloses = [];
45
+ const costs = new Map();
46
+ const notes = new Map();
47
+ for (const e of events) {
48
+ if (e.kind === 'fill') {
49
+ const a = fills.get(e.tradeKey) ?? [];
50
+ a.push(e);
51
+ fills.set(e.tradeKey, a);
52
+ }
53
+ else if (e.kind === 'tag')
54
+ tags.push(e);
55
+ else if (e.kind === 'close') {
56
+ if (e.tradeKey)
57
+ closes.set(e.tradeKey, e);
58
+ else
59
+ looseCloses.push(e);
60
+ }
61
+ else if (e.kind === 'cost') {
62
+ const c = costs.get(e.sessionId);
63
+ costs.set(e.sessionId, { usd: (c?.usd ?? 0) + e.costUsd, model: e.model ?? c?.model ?? null });
64
+ }
65
+ else if (e.kind === 'annotate')
66
+ notes.set(e.tradeKey, e);
67
+ }
68
+ const rows = [];
69
+ for (const [key, fsRaw] of fills) {
70
+ const fs = [...fsRaw].sort((a, b) => a.t - b.t || a.seq - b.seq);
71
+ const buys = fs.filter((f) => f.side === 'buy');
72
+ const sells = fs.filter((f) => f.side === 'sell');
73
+ if (!buys.length)
74
+ continue; // a sell with no buy on record has no basis; it is not a trade
75
+ const first = buys[0];
76
+ const closing = [...fs].reverse().find((f) => f.closes === true) ?? null;
77
+ // a loose close (the run that traded this coin on the desk): the first one after the entry, near the last fill
78
+ const lastT = fs[fs.length - 1].t;
79
+ const close = closes.get(key) ?? looseCloses.find((c) => c.mint === first.mint && c.source === first.source && c.t >= first.t - 5_000 && (closing ? c.t <= closing.t + 120_000 : c.t <= lastT + 120_000) && c.t >= lastT - 120_000) ?? null;
80
+ const status = close || closing ? 'closed' : 'open';
81
+ const closedAt = status === 'closed' ? Math.max(close?.t ?? -Infinity, closing?.t ?? -Infinity, fs[fs.length - 1].t) : null;
82
+ // ── tags ──
83
+ const myTags = tags.filter((g) => g.tradeKey === key || (g.tradeKey === null && g.mint === first.mint && g.source === first.source && g.t >= first.t && (closedAt === null || g.t <= closedAt + 120_000)));
84
+ let profile = { id: null, source: 'missing' };
85
+ const pick = (id, source) => { if (id && rank[source] > rank[profile.source])
86
+ profile = { id, source }; };
87
+ pick(first.tag?.profile, first.tag?.source ?? 'missing');
88
+ for (const g of myTags)
89
+ if (g.tagKind === 'profile')
90
+ pick(g.profile, g.tagSource);
91
+ const reprofiles = myTags.filter((g) => g.tagKind === 'reprofile').sort((a, b) => a.t - b.t).map((g) => ({ t: g.t, from: g.from ?? null, to: g.to ?? null, reason: g.reason ?? null }));
92
+ if (reprofiles.length && reprofiles[reprofiles.length - 1].to)
93
+ profile = { id: reprofiles[reprofiles.length - 1].to, source: profile.source === 'missing' ? 'late' : profile.source };
94
+ const entryTagged = first.tag && first.tag.reason ? first.tag : (myTags.find((g) => g.tagKind === 'profile' && g.reason) ?? null);
95
+ const entryReason = entryTagged
96
+ ? { code: null, text: entryTagged.reason ?? null, line: entryTagged.line ?? null, source: 'tagSource' in entryTagged ? entryTagged.tagSource : entryTagged.reasonSource ?? entryTagged.source }
97
+ : { code: null, text: null, line: null, source: first.tag?.source === 'default' ? 'default' : 'missing' };
98
+ const lastSell = closing ?? sells[sells.length - 1] ?? null;
99
+ const exitTag = lastSell?.tag ?? null;
100
+ const lateExit = [...myTags].reverse().find((g) => g.tagKind === 'exit') ?? null;
101
+ let exitReason = { code: null, text: null, line: null, source: 'missing' };
102
+ if (exitTag && (exitTag.exitReason || exitTag.reason))
103
+ exitReason = { code: exitTag.exitReason ?? null, text: exitTag.reason ?? null, line: exitTag.line ?? null, source: exitTag.source };
104
+ if (lateExit && rank[lateExit.tagSource] > rank[exitReason.source])
105
+ exitReason = { code: lateExit.exitReason ?? exitReason.code, text: lateExit.reason ?? exitReason.text, line: lateExit.line ?? null, source: lateExit.tagSource };
106
+ if (!exitReason.code && close?.reason)
107
+ exitReason = { ...exitReason, code: close.reason, source: exitReason.source === 'missing' ? 'rule' : exitReason.source };
108
+ if (status === 'open')
109
+ exitReason = { code: null, text: null, line: null, source: 'missing' };
110
+ // ── money ──
111
+ const sizeSol = sum(buys.map((f) => f.sol));
112
+ const sizeUsd = sum(buys.map((f) => f.usd));
113
+ const proceedsSol = sells.length ? sum(sells.map((f) => f.sol)) : null;
114
+ const proceedsUsd = sells.length ? sum(sells.map((f) => f.usd)) : null;
115
+ const boughtTok = sum(buys.map((f) => f.tokens));
116
+ const soldTok = sum(sells.map((f) => f.tokens));
117
+ const entryPriceSol = sizeSol !== null && boughtTok ? sizeSol / boughtTok : wmean(buys, (f) => f.priceSol, (f) => f.tokens);
118
+ const entryPriceUsd = sizeUsd !== null && boughtTok ? sizeUsd / boughtTok : wmean(buys, (f) => f.priceUsd, (f) => f.tokens);
119
+ const exitPriceSol = proceedsSol !== null && soldTok ? proceedsSol / soldTok : wmean(sells, (f) => f.priceSol, (f) => f.tokens);
120
+ const exitPriceUsd = proceedsUsd !== null && soldTok ? proceedsUsd / soldTok : wmean(sells, (f) => f.priceUsd, (f) => f.tokens);
121
+ const supply = [...fs].reverse().find((f) => f.supply && f.supply.tokens > 0)?.supply ?? null;
122
+ const mcap = (p) => (p !== null && supply ? p * supply.tokens : null);
123
+ const closedOut = status === 'closed';
124
+ const pnlSol = closedOut && sizeSol !== null ? (proceedsSol ?? 0) - sizeSol : null;
125
+ const pnlUsd = closedOut && sizeUsd !== null ? (proceedsUsd ?? 0) - sizeUsd : null;
126
+ const pnlPct = pnlSol !== null && sizeSol ? (pnlSol / sizeSol) * 100 : pnlUsd !== null && sizeUsd ? (pnlUsd / sizeUsd) * 100 : null;
127
+ const bookSol = first.bookSol;
128
+ const bookUsd = first.bookUsd;
129
+ const pctOfBook = sizeSol !== null && bookSol ? (first.sol ?? sizeSol) / bookSol * 100 : first.usd !== null && bookUsd ? first.usd / bookUsd * 100 : null;
130
+ // ── excursions: the source's extremes + every fill's own price, in the unit the entry is known in ──
131
+ const unit = close?.priceUnit ?? (entryPriceSol !== null ? 'sol' : entryPriceUsd !== null ? 'usd' : null);
132
+ const entryRef = close?.entryPrice ?? (unit === 'sol' ? entryPriceSol : unit === 'usd' ? entryPriceUsd : null);
133
+ const obs = [];
134
+ for (const f of fs) {
135
+ const p = unit === 'sol' ? f.priceSol : f.priceUsd;
136
+ if (p !== null && p > 0)
137
+ obs.push(p);
138
+ }
139
+ if (close?.peakPrice)
140
+ obs.push(close.peakPrice);
141
+ if (close?.troughPrice)
142
+ obs.push(close.troughPrice);
143
+ const mfePct = entryRef && obs.length ? (Math.max(...obs) / entryRef - 1) * 100 : null;
144
+ const maePct = entryRef && obs.length ? (Math.min(...obs) / entryRef - 1) * 100 : null;
145
+ const excursionBasis = mfePct === null ? null : close && (close.peakPrice || close.troughPrice) ? 'observed' : 'fills only';
146
+ // ── rug ──
147
+ const stuck = close?.stuck ?? false;
148
+ const note = notes.get(key) ?? null;
149
+ let rug = false, rugWhy = null;
150
+ if (note && note.rug !== null) {
151
+ rug = note.rug;
152
+ rugWhy = note.why;
153
+ }
154
+ else if (stuck) {
155
+ rug = true;
156
+ rugWhy = 'stuck: the bag could not be sold (no exit liquidity)';
157
+ }
158
+ else if (closedOut && exitReason.code && LIQUIDITY_EXITS.has(exitReason.code) && pnlPct !== null && pnlPct <= RUG_LOSS_PCT) {
159
+ rug = true;
160
+ rugWhy = `${exitReason.code} exit at ${pnlPct.toFixed(0)}%`;
161
+ }
162
+ // ── model ──
163
+ const sessionIds = [...new Set([...fs.map((f) => f.sessionId), close?.sessionId ?? null].filter((x) => !!x))];
164
+ const known = sessionIds.filter((id) => costs.has(id));
165
+ const costUsd = known.length ? known.reduce((s, id) => s + costs.get(id).usd, 0) : null;
166
+ const models = [...new Set([...fs.map((f) => f.model), ...known.map((id) => costs.get(id).model)].filter((x) => !!x))];
167
+ const weightSol = (f) => f.sol ?? f.usd;
168
+ rows.push({
169
+ tradeId: key, source: first.source, walletMode: first.walletMode, status, mint: first.mint, symbol: first.symbol, decimals: fs.find((f) => f.decimals !== null)?.decimals ?? null,
170
+ runId: first.runId, agent: first.agent, strategy: [...fs].reverse().find((f) => f.strategy)?.strategy ?? null,
171
+ account: fs.find((f) => f.account)?.account ?? null,
172
+ profile, reprofiles, entryReason, exitReason,
173
+ openedAt: first.t, closedAt, holdSec: Math.max(0, Math.round(((closedAt ?? now) - first.t) / 1000)),
174
+ buys: buys.length, sells: sells.length,
175
+ sizeSol: r(sizeSol), sizeUsd: r(sizeUsd), pctOfBook: r(pctOfBook, 4), bookSol: r(bookSol), bookUsd: r(bookUsd),
176
+ entryPriceSol: r(entryPriceSol), entryPriceUsd: r(entryPriceUsd), entryMcapUsd: r(mcap(entryPriceUsd)),
177
+ exitPriceSol: r(exitPriceSol), exitPriceUsd: r(exitPriceUsd), exitMcapUsd: r(mcap(exitPriceUsd)),
178
+ supply, proceedsSol: r(proceedsSol), proceedsUsd: r(proceedsUsd),
179
+ pnlSol: r(pnlSol), pnlUsd: r(pnlUsd), pnlPct: r(pnlPct, 4),
180
+ feesSol: r(sum(fs.map((f) => f.feeSol))),
181
+ impactPctEntry: r(wmean(buys, (f) => f.impactPct, weightSol), 4), impactPctExit: r(wmean(sells, (f) => f.impactPct, weightSol), 4),
182
+ slippageBps: fs.reduce((m, f) => (f.slippageBps === null ? m : Math.max(m ?? 0, f.slippageBps)), null),
183
+ mfePct: r(mfePct, 4), maePct: r(maePct, 4), excursionBasis,
184
+ rug, rugWhy, stuck,
185
+ txSigs: fs.map((f) => f.txSig).filter((x) => !!x),
186
+ model: { sessionIds, costUsd: r(costUsd), models },
187
+ fills: fs.map((f) => ({ ref: f.ref, t: f.t, side: f.side, sol: f.sol, usd: f.usd, tokens: f.tokens, priceSol: f.priceSol, priceUsd: f.priceUsd, mcapUsd: r(mcap(f.priceUsd)), feeSol: f.feeSol, impactPct: f.impactPct, exitReason: f.tag?.exitReason ?? null, txSig: f.txSig })),
188
+ });
189
+ }
190
+ return rows.sort((a, b) => a.openedAt - b.openedAt || a.tradeId.localeCompare(b.tradeId));
191
+ }
@@ -0,0 +1,9 @@
1
+ // task-1530 — @dexterm/data/ledger: the unified trade ledger (schema, file, importers, fold, stats, exports).
2
+ export * from "./schema.js";
3
+ export * from "./file.js";
4
+ export * from "./tags.js";
5
+ export * from "./sources.js";
6
+ export * from "./fold.js";
7
+ export * from "./stats.js";
8
+ export * from "./export.js";
9
+ export * from "./view.js";
@@ -0,0 +1,83 @@
1
+ // task-1530 — the UNIFIED TRADE LEDGER (`dexterm-ledger/1`): one schema for every trade dexterm books, whatever
2
+ // booked it (the paper wallet, the agent paper desk, an autotrade run, the live signer).
3
+ //
4
+ // The ledger is an append-only, hash-chained JSONL file of EVENTS. A trade is never stored as a mutable row: it is
5
+ // folded from its events (`fold.ts`), so a late PROFILE line, a rug found after the exit or a session's cost can
6
+ // be added later without rewriting anything.
7
+ //
8
+ // header line 1: the schema, when the file was made
9
+ // import provenance: which source journal was read, how many events, its own chain head and check
10
+ // fill one buy or sell (SOL, USD, tokens, price, supply, fees, impact, slippage, the book before it, tag, tx sig)
11
+ // tag a PROFILE / REPROFILE / EXIT declared apart from its fill (a late line, a reprofile)
12
+ // close the source closed the position (stuck or not), with the price extremes it saw (MFE / MAE)
13
+ // cost a model session's cost (joined to the trades that name the session)
14
+ // annotate a fact found later about a trade (a rug)
15
+ //
16
+ // Every event carries `ref`, unique per source event, so importing the same journal twice adds nothing. Its hash
17
+ // covers the previous hash plus the canonical event, so an edited, dropped or reordered line breaks the chain from
18
+ // that line on (`verifyLedger`). Nothing in a ledger event can hold a key: there is no field for one, and every
19
+ // string is passed through `redactKeyShaped` on the way in.
20
+ import { createHash } from 'node:crypto';
21
+ export const LEDGER_SCHEMA = 'dexterm-ledger/1';
22
+ /** The event format this build writes and reads. A file from a newer build is refused (never half-read). */
23
+ export const LEDGER_VERSION = 1;
24
+ export const GENESIS = 'genesis';
25
+ export const LEDGER_SOURCES = ['paper', 'desk', 'autotrade', 'live'];
26
+ export const WALLET_MODES = ['paper', 'devnet', 'live'];
27
+ // ───────────────────────────── hygiene ─────────────────────────────
28
+ /** A base58 secret key (64 bytes ≈ 87-88 chars) or a 64-number byte array: never stored, whatever field it is in. */
29
+ const KEY_SHAPED = /(?:[1-9A-HJ-NP-Za-km-z]{85,90})|(?:\[(?:\s*\d{1,3}\s*,){63}\s*\d{1,3}\s*\])/g;
30
+ export const redactKeyShaped = (s) => s.replace(KEY_SHAPED, '[REDACTED]');
31
+ /** Text from a journal or an agent: one line, control characters out, capped, key-shaped runs redacted. */
32
+ export function cleanLedgerText(s, max = 200) {
33
+ if (typeof s !== 'string')
34
+ return null;
35
+ // eslint-disable-next-line no-control-regex
36
+ const t = redactKeyShaped(s.replace(/[\u0000-\u001f\u007f\u2028\u2029]+/g, ' ').replace(/\s+/g, ' ').trim());
37
+ return t ? t.slice(0, max) : null;
38
+ }
39
+ /** Ids, codes and refs: a strict alphabet, so nothing odd ever lands in an id column. */
40
+ export function cleanCode(s, max = 64) {
41
+ if (typeof s !== 'string')
42
+ return null;
43
+ const t = s.trim().toLowerCase().replace(/[^a-z0-9:_.@-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, max);
44
+ return t || null;
45
+ }
46
+ export const finite = (x) => (typeof x === 'number' && Number.isFinite(x) ? x : null);
47
+ // ───────────────────────────── the chain ─────────────────────────────
48
+ /** Sorted keys, undefined dropped, so a stored line re-hashes to the same value. (No key is special.) */
49
+ export function canonicalJson(v) {
50
+ if (v === null || typeof v !== 'object')
51
+ return JSON.stringify(v) ?? 'null';
52
+ if (Array.isArray(v))
53
+ return `[${v.map(canonicalJson).join(',')}]`;
54
+ const o = v;
55
+ return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(o[k])}`).join(',')}}`;
56
+ }
57
+ /** sha256(prev + canonical(event without prev/hash)), full hex. */
58
+ export function ledgerHash(prev, e) {
59
+ const { prev: _p, hash: _h, ...body } = e;
60
+ return createHash('sha256').update(prev + canonicalJson(body)).digest('hex');
61
+ }
62
+ /** Recompute the chain: seq 1..n, every prev is the previous hash, every hash matches its content, line 1 is the header. */
63
+ export function verifyLedger(events) {
64
+ let prev = GENESIS;
65
+ for (let i = 0; i < events.length; i++) {
66
+ const e = events[i];
67
+ if (e.seq !== i + 1)
68
+ return { ok: false, events: events.length, at: i + 1, error: `seq ${String(e.seq)} where ${i + 1} was expected` };
69
+ if (i === 0 && e.kind !== 'header')
70
+ return { ok: false, events: events.length, at: 1, error: 'line 1 is not the ledger header' };
71
+ if (i > 0 && e.kind === 'header')
72
+ return { ok: false, events: events.length, at: i + 1, error: 'a second header' };
73
+ if (typeof e.v !== 'number' || e.v > LEDGER_VERSION)
74
+ return { ok: false, events: events.length, at: i + 1, error: `event version ${String(e.v)} is newer than this build reads (${LEDGER_VERSION}); update dexterm` };
75
+ if (e.prev !== prev)
76
+ return { ok: false, events: events.length, at: i + 1, error: 'prev does not match the previous hash (a line was removed, added or reordered)' };
77
+ const want = ledgerHash(prev, e);
78
+ if (e.hash !== want)
79
+ return { ok: false, events: events.length, at: i + 1, error: 'hash does not match the content (the line was edited)' };
80
+ prev = want;
81
+ }
82
+ return { ok: true, events: events.length, head: prev };
83
+ }