dexterm-cli 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/spectator.js +3 -1
  3. package/dist/packages/agents/src/story.js +11 -2
  4. package/dist/packages/agents/src/tiers.js +36 -2
  5. package/dist/packages/agents/src/tools.js +35 -2
  6. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  7. package/dist/packages/autotrade/assets/limits.schema.json +569 -0
  8. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  9. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  10. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  11. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  12. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  13. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  14. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  15. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  16. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  17. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  18. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  19. package/dist/packages/autotrade/examples/executor/README.md +70 -0
  20. package/dist/packages/autotrade/examples/executor/jsonl-tail-executor.mjs +57 -0
  21. package/dist/packages/autotrade/examples/executor/kit.mjs +144 -0
  22. package/dist/packages/autotrade/examples/executor/signal-cmd-executor.mjs +25 -0
  23. package/dist/packages/autotrade/examples/executor/telegram-bot-recipe.md +47 -0
  24. package/dist/packages/autotrade/examples/executor/webhook-executor.mjs +47 -0
  25. package/dist/packages/autotrade/src/account.js +74 -0
  26. package/dist/packages/autotrade/src/backtest.js +358 -0
  27. package/dist/packages/autotrade/src/basis-text.js +32 -0
  28. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  29. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  30. package/dist/packages/autotrade/src/chat/config.js +95 -0
  31. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  32. package/dist/packages/autotrade/src/chat/control.js +104 -0
  33. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  34. package/dist/packages/autotrade/src/chat/format.js +71 -0
  35. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  36. package/dist/packages/autotrade/src/chat/index.js +18 -0
  37. package/dist/packages/autotrade/src/chat/notify.js +302 -0
  38. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  39. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  40. package/dist/packages/autotrade/src/chat/secrets.js +106 -0
  41. package/dist/packages/autotrade/src/chat/service.js +251 -0
  42. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  43. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  44. package/dist/packages/autotrade/src/engine.js +401 -0
  45. package/dist/packages/autotrade/src/execution-port.js +97 -0
  46. package/dist/packages/autotrade/src/executor-health.js +217 -0
  47. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  48. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  49. package/dist/packages/autotrade/src/fleet.js +642 -0
  50. package/dist/packages/autotrade/src/forward.js +355 -0
  51. package/dist/packages/autotrade/src/index.js +72 -0
  52. package/dist/packages/autotrade/src/journey.js +477 -0
  53. package/dist/packages/autotrade/src/ledger.js +108 -0
  54. package/dist/packages/autotrade/src/limits.js +682 -0
  55. package/dist/packages/autotrade/src/live-source.js +182 -0
  56. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  57. package/dist/packages/autotrade/src/monitor.js +190 -0
  58. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  59. package/dist/packages/autotrade/src/outbox.js +394 -0
  60. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  61. package/dist/packages/autotrade/src/paper-limits.js +87 -0
  62. package/dist/packages/autotrade/src/real-mode.js +688 -0
  63. package/dist/packages/autotrade/src/receipts.js +330 -0
  64. package/dist/packages/autotrade/src/replay.js +198 -0
  65. package/dist/packages/autotrade/src/report.js +168 -0
  66. package/dist/packages/autotrade/src/runner-config.js +99 -0
  67. package/dist/packages/autotrade/src/runner.js +923 -0
  68. package/dist/packages/autotrade/src/safety.js +243 -0
  69. package/dist/packages/autotrade/src/service-guard.js +125 -0
  70. package/dist/packages/autotrade/src/service-install.js +213 -0
  71. package/dist/packages/autotrade/src/service-recover.js +232 -0
  72. package/dist/packages/autotrade/src/service.js +289 -0
  73. package/dist/packages/autotrade/src/session.js +269 -0
  74. package/dist/packages/autotrade/src/sessions.js +503 -0
  75. package/dist/packages/autotrade/src/signal-channels.js +312 -0
  76. package/dist/packages/autotrade/src/signal.js +196 -0
  77. package/dist/packages/autotrade/src/store.js +250 -0
  78. package/dist/packages/autotrade/src/strategy.js +258 -0
  79. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  80. package/dist/packages/autotrade/src/yolo.js +140 -0
  81. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  82. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  83. package/dist/packages/cli/src/ai-cmd.js +412 -0
  84. package/dist/packages/cli/src/analyst/backend.js +83 -15
  85. package/dist/packages/cli/src/analyst/codex.js +15 -9
  86. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  87. package/dist/packages/cli/src/analyst/session.js +5 -2
  88. package/dist/packages/cli/src/app-help-gen.js +56 -8
  89. package/dist/packages/cli/src/autotrade-cmd/audit.js +74 -0
  90. package/dist/packages/cli/src/autotrade-cmd/backtest.js +216 -0
  91. package/dist/packages/cli/src/autotrade-cmd/executor.js +91 -0
  92. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  93. package/dist/packages/cli/src/autotrade-cmd/fleet.js +401 -0
  94. package/dist/packages/cli/src/autotrade-cmd/forward.js +283 -0
  95. package/dist/packages/cli/src/autotrade-cmd/journey.js +208 -0
  96. package/dist/packages/cli/src/autotrade-cmd/limits.js +393 -0
  97. package/dist/packages/cli/src/autotrade-cmd/receipt.js +63 -0
  98. package/dist/packages/cli/src/autotrade-cmd/report.js +79 -0
  99. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  100. package/dist/packages/cli/src/autotrade-cmd/run.js +657 -0
  101. package/dist/packages/cli/src/autotrade-cmd/service.js +873 -0
  102. package/dist/packages/cli/src/autotrade-cmd/signal.js +824 -0
  103. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  104. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  105. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  106. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  107. package/dist/packages/cli/src/autotrade-cmd.js +214 -0
  108. package/dist/packages/cli/src/display.js +156 -17
  109. package/dist/packages/cli/src/doctor.js +62 -3
  110. package/dist/packages/cli/src/export.js +10 -2
  111. package/dist/packages/cli/src/index.js +1 -1
  112. package/dist/packages/cli/src/ledger-cmd.js +290 -0
  113. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  114. package/dist/packages/cli/src/live/index.js +23 -89
  115. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  116. package/dist/packages/cli/src/live/trade.js +12 -457
  117. package/dist/packages/cli/src/live/watchlist.js +1 -1
  118. package/dist/packages/cli/src/main.js +147 -190
  119. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  120. package/dist/packages/cli/src/plan-cmd.js +1 -1
  121. package/dist/packages/cli/src/pnl-app.js +31 -21
  122. package/dist/packages/cli/src/pnl-card.js +162 -21
  123. package/dist/packages/cli/src/popup.js +22 -6
  124. package/dist/packages/cli/src/record-cmd.js +141 -20
  125. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  126. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  127. package/dist/packages/cli/src/screens/analyst.js +3 -2
  128. package/dist/packages/cli/src/screens/autotrade-journey.js +134 -0
  129. package/dist/packages/cli/src/screens/autotrade-keys.js +182 -0
  130. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  131. package/dist/packages/cli/src/screens/autotrade-screen.js +776 -0
  132. package/dist/packages/cli/src/screens/autotrade-ui.js +650 -0
  133. package/dist/packages/cli/src/screens/autotrade-worker.js +91 -0
  134. package/dist/packages/cli/src/screens/brain.js +37 -11
  135. package/dist/packages/cli/src/screens/overlay.js +5 -2
  136. package/dist/packages/cli/src/screens/plans.js +5 -17
  137. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  138. package/dist/packages/cli/src/screens/positions.js +6 -28
  139. package/dist/packages/cli/src/screens/ticket.js +38 -205
  140. package/dist/packages/cli/src/screens/token.js +3 -3
  141. package/dist/packages/cli/src/state.js +25 -19
  142. package/dist/packages/cli/src/telegram-cmd.js +454 -0
  143. package/dist/packages/cli/src/trade.js +3 -128
  144. package/dist/packages/cli/src/version.js +1 -1
  145. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  146. package/dist/packages/cli/src/wallet-command.js +8 -11
  147. package/dist/packages/cli/src/widgets.js +0 -12
  148. package/dist/packages/custom/src/config.js +20 -2
  149. package/dist/packages/custom/src/schema.js +35 -10
  150. package/dist/packages/custom/src/workspace.js +3 -3
  151. package/dist/packages/data/src/basis.js +191 -0
  152. package/dist/packages/data/src/detail.js +53 -2
  153. package/dist/packages/data/src/index.js +0 -14
  154. package/dist/packages/data/src/store.js +14 -5
  155. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  156. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  157. package/dist/packages/data/src/trade/bridge.js +4 -102
  158. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  159. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  160. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  161. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  162. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  163. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  164. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  165. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  166. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  167. package/dist/packages/data/src/trade/plans/index.js +0 -1
  168. package/dist/packages/data/src/trade/plans/service.js +3 -27
  169. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  170. package/dist/packages/mcp/assets/kb/app-help/autotrade-executor-kit.md +44 -0
  171. package/dist/packages/mcp/assets/kb/app-help/autotrade-journey.md +46 -0
  172. package/dist/packages/mcp/assets/kb/app-help/autotrade-limits.md +48 -0
  173. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  174. package/dist/packages/mcp/assets/kb/app-help/autotrade-real-signal.md +45 -0
  175. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  176. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  177. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  178. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  179. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  180. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  181. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  182. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  183. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  184. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  185. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  186. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +229 -0
  187. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  188. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  189. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  190. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  191. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  192. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  193. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  194. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  195. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  196. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  201. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +51 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +38 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-always-on-service.md +57 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +50 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  231. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  232. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-1-ask-for-a-strategy.md +27 -0
  233. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-2-backtest-it-held-out-split.md +31 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-3-forward-test-it-on-live-data.md +44 -0
  235. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-4-promote-it-to-always-on-paper.md +36 -0
  236. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-5-real-only-if-you-choose.md +36 -0
  237. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-ladder.md +39 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-report.md +31 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-what-the-analyst-knows.md +30 -0
  240. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat.md +24 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  242. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  243. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  244. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  247. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  249. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  251. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  254. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  255. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  256. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  257. package/dist/packages/mcp/assets/kb/docs/docs-going-real-executor-health.md +38 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-going-real-paper-vs-real.md +33 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-digest.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-executor-kit.md +48 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-whole-path.md +37 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-going-real-what-dexterm-does-and-never-does.md +44 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-going-real-your-limits.md +77 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-going-real.md +24 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-real-mode-your-strategy-s-trades-to-your.md +31 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +31 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  271. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  273. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  274. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  275. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  277. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  278. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  281. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  282. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  284. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  288. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  289. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  290. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  291. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  292. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  293. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  295. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  297. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  298. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  299. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-receipts-what-your-executor-did.md +42 -0
  302. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-safety.md +34 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-set-it-up.md +47 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-channels.md +42 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-signal.md +48 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-is.md +35 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-will-not-do.md +34 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-where-you-see-it.md +34 -0
  309. package/dist/packages/mcp/assets/kb/docs/docs-real-mode.md +27 -0
  310. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  311. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  312. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  313. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  314. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  315. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  316. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  317. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  318. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  319. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  320. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  321. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  322. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  323. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  324. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  325. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  326. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  327. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  328. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  329. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  330. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  331. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  332. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  333. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  334. package/dist/packages/mcp/assets/kb/entries/autotrade.md +40 -0
  335. package/dist/packages/mcp/src/app-help.js +1 -1
  336. package/dist/packages/mcp/src/autotrade-tools.js +1107 -0
  337. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  338. package/dist/packages/mcp/src/engine/config.js +159 -0
  339. package/dist/packages/mcp/src/engine/index.js +10 -0
  340. package/dist/packages/mcp/src/engine/loop.js +101 -0
  341. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  342. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  343. package/dist/packages/mcp/src/engine/probe.js +110 -0
  344. package/dist/packages/mcp/src/engine/reason.js +35 -0
  345. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  346. package/dist/packages/mcp/src/index.js +7 -1
  347. package/dist/packages/mcp/src/ledger-tools.js +494 -0
  348. package/dist/packages/mcp/src/paper-agent.js +2 -0
  349. package/dist/packages/mcp/src/server.js +138 -19
  350. package/dist/packages/packs/src/backtest.js +3 -1
  351. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  352. package/dist/packages/popup/src/pages/chart.css +9 -0
  353. package/dist/packages/popup/src/pages/chart.html +3 -1
  354. package/dist/packages/popup/src/pages/chart.js +104 -7
  355. package/dist/packages/popup/src/protocol.js +2 -0
  356. package/dist/packages/popup/src/server.js +4 -0
  357. package/dist/packages/recorder/src/index.js +2 -0
  358. package/dist/packages/recorder/src/recorder.js +63 -6
  359. package/dist/packages/recorder/src/retention.js +100 -0
  360. package/dist/packages/recorder/src/schema.js +34 -1
  361. package/dist/packages/recorder/src/ticks.js +237 -0
  362. package/dist/packages/tui/src/index.js +1 -0
  363. package/dist/packages/tui/src/keymap.js +28 -1
  364. package/dist/packages/tui/src/markers.js +219 -0
  365. package/dist/packages/tui/src/widgets.js +149 -2
  366. package/package.json +2 -1
  367. package/dist/packages/data/src/trade/caps.js +0 -323
  368. package/dist/packages/data/src/trade/executor.js +0 -933
  369. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  370. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  371. package/dist/packages/data/src/trade/signer/client.js +0 -197
  372. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  373. package/dist/packages/data/src/trade/signer/host.js +0 -97
  374. package/dist/packages/data/src/trade/signer/main.js +0 -121
  375. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  376. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  377. package/dist/packages/data/src/trade/signer/service.js +0 -400
  378. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  379. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  380. package/dist/packages/data/src/trade/swap-build.js +0 -108
  381. package/dist/packages/data/src/trade/token-delta.js +0 -201
  382. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  383. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  384. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  385. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  386. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  387. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  388. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  389. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  390. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  391. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  392. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  393. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  394. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  395. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -0,0 +1,394 @@
1
+ // dexterm autotrade — the trade-fire outbox (task-1645, 0.2.0 card A).
2
+ //
3
+ // Every real-mode signal is written here (fsync) BEFORE the runner books its fill, then delivered to each channel
4
+ // the user enabled: at-least-once, in nonce order per channel, retried with backoff (1 s doubling to 30 s) until
5
+ // the channel acks, rejects it for good, or the signal's TTL passes (then it is marked expired and never sent late).
6
+ //
7
+ // $DEXTERM_HOME/autotrade/real/outbox.jsonl append-only records, replayed on open (a restart resumes delivery)
8
+ // {k:"enq", signal, channels} a signal and the channels it was addressed to
9
+ // {k:"ack"|"rej"|"exp"|"drop", nonce, ch} a channel's terminal outcome (drop: the channel is no longer set up)
10
+ // {k:"try", nonce, ch, why, attempt} a failed attempt (retried)
11
+ // {k:"run", runId, state:"open"|"closed"} run markers: a run left "open" died without its halted signal
12
+ // {k:"base", lastNonce} the head of an outbox started by compaction (r2): the nonce floor
13
+ // $DEXTERM_HOME/autotrade/real/nonce.json the last nonce handed out (written before the signal exists)
14
+ // $DEXTERM_HOME/autotrade/real/outbox.lock one writer at a time (a stale lock of a dead pid is reclaimed)
15
+ //
16
+ // Nonces only grow: the next one is max(nonce.json, the outbox's base record, the newest in the outbox) + 1, so
17
+ // neither a restart, a compaction nor a lost nonce file can hand out a number twice. A record that cannot be parsed is skipped and counted (`corrupt`).
18
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ import { writeJsonAtomic } from "./ledger.js";
21
+ import { validateSignal } from "./signal.js";
22
+ export const CHANNEL_NAMES = ['webhook', 'cmd', 'jsonl', 'mcp', 'telegram'];
23
+ /** Channels an executor can read. The Telegram copy is for the human: a bot cannot read another bot's messages. */
24
+ export const EXECUTOR_CHANNELS = ['webhook', 'cmd', 'jsonl', 'mcp'];
25
+ export const realDir = (home) => join(home, 'autotrade', 'real');
26
+ export const outboxPath = (home) => join(realDir(home), 'outbox.jsonl');
27
+ export const noncePath = (home) => join(realDir(home), 'nonce.json');
28
+ const lockPath = (home) => join(realDir(home), 'outbox.lock');
29
+ export const BACKOFF_MIN_MS = 1000;
30
+ export const BACKOFF_MAX_MS = 30_000;
31
+ const COMPACT_ABOVE_BYTES = 1 << 20;
32
+ const MAX_PER_PUMP = 50;
33
+ const alive = (pid) => { try {
34
+ process.kill(pid, 0);
35
+ return true;
36
+ }
37
+ catch (e) {
38
+ return e.code === 'EPERM';
39
+ } };
40
+ function appendSync(path, line) {
41
+ const fd = openSync(path, 'a', 0o600);
42
+ try {
43
+ writeSync(fd, line.endsWith('\n') ? line : `${line}\n`);
44
+ fsyncSync(fd);
45
+ }
46
+ finally {
47
+ closeSync(fd);
48
+ }
49
+ }
50
+ export class Outbox {
51
+ home;
52
+ path;
53
+ channels = new Map();
54
+ defer;
55
+ entries = [];
56
+ byNonce = new Map();
57
+ runs = new Map();
58
+ health = new Map();
59
+ now;
60
+ lastNonce = 0;
61
+ running = null;
62
+ queued = null;
63
+ locked = false;
64
+ corrupt = 0;
65
+ constructor(home, o) {
66
+ this.home = home;
67
+ this.path = outboxPath(home);
68
+ this.now = o.now ?? Date.now;
69
+ for (const c of o.channels)
70
+ this.channels.set(c.name, c);
71
+ this.defer = new Set(o.defer ?? []);
72
+ }
73
+ /** Open (and replay) the outbox of a dexterm home. Throws when another live process holds it. */
74
+ static open(home, o) {
75
+ mkdirSync(realDir(home), { recursive: true, mode: 0o700 });
76
+ const b = new Outbox(home, o);
77
+ if (!o.noLock)
78
+ b.lock();
79
+ b.replay();
80
+ // Only the lock holder compacts (it writes): a read-only view never moves the file under a running writer.
81
+ if (!o.noLock)
82
+ b.compact();
83
+ return b;
84
+ }
85
+ lock() {
86
+ const p = lockPath(this.home);
87
+ for (let i = 0; i < 2; i++) {
88
+ try {
89
+ const fd = openSync(p, 'wx', 0o600);
90
+ writeSync(fd, `${JSON.stringify({ pid: process.pid, at: this.now() })}\n`);
91
+ closeSync(fd);
92
+ this.locked = true;
93
+ return;
94
+ }
95
+ catch (e) {
96
+ if (e.code !== 'EEXIST')
97
+ throw e;
98
+ }
99
+ let pid = 0;
100
+ try {
101
+ pid = Number(JSON.parse(readFileSync(p, 'utf8')).pid) || 0;
102
+ }
103
+ catch {
104
+ pid = 0;
105
+ }
106
+ if (pid && pid !== process.pid && alive(pid))
107
+ throw new Error(`the trade-fire outbox is in use by pid ${pid} (a running real-mode runner); stop it first`);
108
+ rmSync(p, { force: true });
109
+ }
110
+ throw new Error('could not lock the trade-fire outbox');
111
+ }
112
+ close() {
113
+ if (!this.locked)
114
+ return;
115
+ try {
116
+ const d = JSON.parse(readFileSync(lockPath(this.home), 'utf8'));
117
+ if (d.pid === process.pid)
118
+ rmSync(lockPath(this.home), { force: true });
119
+ }
120
+ catch { /* gone */ }
121
+ this.locked = false;
122
+ }
123
+ /** Swap the channel set (config changed). Pending deliveries to a channel that is gone are dropped at the next pump. */
124
+ setChannels(cs) { this.channels = new Map(cs.map((c) => [c.name, c])); }
125
+ channelNames() { return [...this.channels.keys()]; }
126
+ replay() {
127
+ if (existsSync(noncePath(this.home))) {
128
+ try {
129
+ const n = Number(JSON.parse(readFileSync(noncePath(this.home), 'utf8')).last);
130
+ if (Number.isSafeInteger(n) && n > 0)
131
+ this.lastNonce = n;
132
+ }
133
+ catch {
134
+ this.corrupt++;
135
+ }
136
+ }
137
+ if (!existsSync(this.path))
138
+ return;
139
+ for (const raw of readFileSync(this.path, 'utf8').split('\n')) {
140
+ if (!raw.trim())
141
+ continue;
142
+ let r;
143
+ try {
144
+ r = JSON.parse(raw);
145
+ }
146
+ catch {
147
+ this.corrupt++;
148
+ continue;
149
+ }
150
+ this.apply(r, false);
151
+ }
152
+ }
153
+ /** Apply one record to the in-memory state (replay and live writes share this). */
154
+ apply(r, live) {
155
+ const at = typeof r.at === 'number' ? r.at : 0;
156
+ if (r.k === 'enq') {
157
+ const v = validateSignal(r.signal);
158
+ if (!v.ok || this.byNonce.has(v.signal.nonce)) {
159
+ this.corrupt++;
160
+ return;
161
+ }
162
+ const chs = Array.isArray(r.channels) ? r.channels.filter((c) => CHANNEL_NAMES.includes(c)) : [];
163
+ const e = { signal: v.signal, line: JSON.stringify(v.signal), ch: new Map(chs.map((c) => [c, { done: null, attempts: 0, nextAt: 0, lastWhy: null }])) };
164
+ this.entries.push(e);
165
+ this.byNonce.set(e.signal.nonce, e);
166
+ this.lastNonce = Math.max(this.lastNonce, e.signal.nonce);
167
+ return;
168
+ }
169
+ if (r.k === 'base') {
170
+ const n = Number(r.lastNonce);
171
+ if (Number.isSafeInteger(n) && n > 0)
172
+ this.lastNonce = Math.max(this.lastNonce, n);
173
+ else
174
+ this.corrupt++;
175
+ return;
176
+ }
177
+ if (r.k === 'run' && typeof r.runId === 'string') {
178
+ this.runs.set(r.runId, { state: r.state === 'closed' ? 'closed' : 'open', at, strategy: r.strategy, accountId: typeof r.accountId === 'string' ? r.accountId : undefined, reason: typeof r.reason === 'string' ? r.reason : undefined });
179
+ return;
180
+ }
181
+ const e = this.byNonce.get(Number(r.nonce));
182
+ const name = r.ch;
183
+ const st = e?.ch.get(name);
184
+ if (!e || !st) {
185
+ if (!live && !['try', 'ack', 'rej', 'exp', 'drop'].includes(String(r.k)))
186
+ this.corrupt++;
187
+ return;
188
+ }
189
+ if (r.k === 'try') {
190
+ st.attempts = Number(r.attempt) || st.attempts + 1;
191
+ st.lastWhy = String(r.why ?? '').slice(0, 200);
192
+ st.nextAt = at + backoff(st.attempts);
193
+ return;
194
+ }
195
+ if (r.k === 'ack' || r.k === 'rej' || r.k === 'exp' || r.k === 'drop') {
196
+ st.done = r.k;
197
+ st.dup = r.dup === true;
198
+ st.lastWhy = typeof r.why === 'string' ? r.why.slice(0, 200) : st.lastWhy;
199
+ }
200
+ }
201
+ write(r) {
202
+ appendSync(this.path, JSON.stringify(r));
203
+ this.apply(r, true);
204
+ }
205
+ /** Move a fully settled outbox aside once it is large (the run ledgers keep every signal for the audit). */
206
+ compact() {
207
+ if (!existsSync(this.path) || statSync(this.path).size < COMPACT_ABOVE_BYTES)
208
+ return;
209
+ if (this.entries.some((e) => [...e.ch.values()].some((c) => !c.done)) || [...this.runs.values()].some((r) => r.state === 'open'))
210
+ return;
211
+ renameSync(this.path, join(realDir(this.home), `outbox.${this.now()}.done.jsonl`));
212
+ this.entries.length = 0;
213
+ this.byNonce.clear();
214
+ this.runs.clear();
215
+ // The fresh outbox starts with the nonce floor, so a lost nonce.json can never restart the count at 1.
216
+ if (this.lastNonce > 0)
217
+ this.write({ k: 'base', at: this.now(), lastNonce: this.lastNonce });
218
+ }
219
+ /** The next nonce, persisted before it is used (so a crash after this line can only skip a number, never reuse one). */
220
+ allocateNonce() {
221
+ const n = this.lastNonce + 1;
222
+ writeJsonAtomic(noncePath(this.home), { last: n, at: this.now() });
223
+ this.lastNonce = n;
224
+ return n;
225
+ }
226
+ get lastNonceIssued() { return this.lastNonce; }
227
+ /** Durably queue a signal for every channel set up now. Throws when the disk refuses (the caller then fires nothing). */
228
+ enqueue(signal) {
229
+ if (this.byNonce.has(signal.nonce))
230
+ throw new Error(`nonce ${signal.nonce} is already in the outbox`);
231
+ if (signal.nonce > this.lastNonce) {
232
+ writeJsonAtomic(noncePath(this.home), { last: signal.nonce, at: this.now() });
233
+ this.lastNonce = signal.nonce;
234
+ }
235
+ this.write({ k: 'enq', at: this.now(), channels: [...this.channels.keys()], signal });
236
+ }
237
+ markRun(runId, state, extra = {}) {
238
+ this.write({ k: 'run', at: this.now(), runId, state, ...extra });
239
+ }
240
+ /** Runs that were opened and never closed (their runner died before sending `halted`). */
241
+ unclosedRuns() {
242
+ return [...this.runs.entries()].filter(([, r]) => r.state === 'open').map(([runId, r]) => ({ runId, at: r.at, strategy: r.strategy, accountId: r.accountId }));
243
+ }
244
+ /**
245
+ * Deliver what is due. Serialized: a call while a pump runs queues ONE follow-up pump (signals queued meanwhile are
246
+ * covered) and resolves with its report.
247
+ */
248
+ pump() {
249
+ if (this.running) {
250
+ this.queued ??= this.running.then(() => { this.queued = null; return this.pump(); });
251
+ return this.queued;
252
+ }
253
+ this.running = this.pumpOnce().finally(() => { this.running = null; });
254
+ return this.running;
255
+ }
256
+ async pumpOnce() {
257
+ const rep = { acked: 0, retried: 0, rejected: 0, expired: 0, dropped: 0 };
258
+ const names = new Set();
259
+ for (const e of this.entries)
260
+ for (const [n, st] of e.ch)
261
+ if (!st.done)
262
+ names.add(n);
263
+ // Channels deliver side by side (a slow hook never holds up the stream); each one strictly in nonce order.
264
+ await Promise.all([...names].map((name) => this.pumpChannel(name, rep)));
265
+ return rep;
266
+ }
267
+ async pumpChannel(name, rep) {
268
+ const chan = this.channels.get(name);
269
+ if (!chan && this.defer.has(name))
270
+ return;
271
+ let sent = 0;
272
+ for (const e of this.entries) {
273
+ const st = e.ch.get(name);
274
+ if (!st || st.done)
275
+ continue;
276
+ const now = this.now();
277
+ if (!chan) {
278
+ this.write({ k: 'drop', at: now, nonce: e.signal.nonce, ch: name, why: 'channel-not-configured' });
279
+ rep.dropped++;
280
+ continue;
281
+ }
282
+ if (now > e.signal.expiresAt) {
283
+ this.write({ k: 'exp', at: now, nonce: e.signal.nonce, ch: name });
284
+ rep.expired++;
285
+ continue;
286
+ }
287
+ if (st.nextAt > now)
288
+ return; // in order: a channel waits on its oldest undelivered signal
289
+ if (sent++ >= MAX_PER_PUMP)
290
+ return;
291
+ let r;
292
+ try {
293
+ r = await chan.deliver(e.signal, e.line);
294
+ }
295
+ catch (err) {
296
+ r = { ok: false, retry: true, why: err.message.slice(0, 160) };
297
+ }
298
+ const at = this.now();
299
+ const h = this.healthOf(name);
300
+ if (r.ok) {
301
+ this.write({ k: 'ack', at, nonce: e.signal.nonce, ch: name, ...(r.duplicate ? { dup: true } : {}) });
302
+ h.lastOkAt = at;
303
+ h.failing = 0;
304
+ rep.acked++;
305
+ continue;
306
+ }
307
+ h.lastErrAt = at;
308
+ h.lastErr = r.why.slice(0, 160);
309
+ h.failing++;
310
+ if (!r.retry) {
311
+ this.write({ k: 'rej', at, nonce: e.signal.nonce, ch: name, why: r.why.slice(0, 200) });
312
+ rep.rejected++;
313
+ continue;
314
+ }
315
+ this.write({ k: 'try', at, nonce: e.signal.nonce, ch: name, why: r.why.slice(0, 200), attempt: st.attempts + 1 });
316
+ rep.retried++;
317
+ return;
318
+ }
319
+ }
320
+ healthOf(name) {
321
+ let h = this.health.get(name);
322
+ if (!h) {
323
+ h = { name, pending: 0, lastOkAt: null, lastErrAt: null, lastErr: null, failing: 0 };
324
+ this.health.set(name, h);
325
+ }
326
+ return h;
327
+ }
328
+ /** Per channel: pending deliveries, the last success and failure (this process), consecutive failures. */
329
+ channelHealth() {
330
+ const out = [];
331
+ for (const name of new Set([...this.channels.keys(), ...this.health.keys()])) {
332
+ const h = { ...this.healthOf(name) };
333
+ h.pending = this.entries.filter((e) => { const s = e.ch.get(name); return s && !s.done; }).length;
334
+ if (h.lastErr === null) {
335
+ const last = [...this.entries].reverse().map((e) => e.ch.get(name)).find((s) => s?.lastWhy);
336
+ h.lastErr = last?.lastWhy ?? null;
337
+ }
338
+ out.push(h);
339
+ }
340
+ return out;
341
+ }
342
+ /** Signals with at least one channel not settled yet. */
343
+ depth() { return this.entries.filter((e) => [...e.ch.values()].some((s) => !s.done)).length; }
344
+ stateOf(nonce) {
345
+ const e = this.byNonce.get(nonce);
346
+ if (!e)
347
+ return null;
348
+ const ss = [...e.ch.values()];
349
+ if (!ss.length)
350
+ return this.now() > e.signal.expiresAt ? 'expired' : 'pending';
351
+ if (ss.some((s) => !s.done))
352
+ return ss.some((s) => s.done === 'ack') ? 'partial' : 'pending';
353
+ if (ss.length && ss.every((s) => s.done === 'ack'))
354
+ return 'delivered';
355
+ if (ss.some((s) => s.done === 'ack'))
356
+ return 'partial';
357
+ return ss.some((s) => s.done === 'rej') ? 'rejected' : 'expired';
358
+ }
359
+ /** Trade signals of a run that were queued or delivered and have not expired yet (the halted signal lists them). */
360
+ outstanding(runId) {
361
+ const now = this.now();
362
+ return this.entries.filter((e) => e.signal.type === 'trade' && (!runId || e.signal.runId === runId) && now <= e.signal.expiresAt).map((e) => e.signal.nonce);
363
+ }
364
+ signals() { return this.entries.map((e) => e.signal); }
365
+ get(nonce) { return this.byNonce.get(nonce)?.signal; }
366
+ findById(id) { return this.entries.find((e) => e.signal.id === id)?.signal; }
367
+ }
368
+ /**
369
+ * Every valid signal ever queued on this home, oldest nonce first: the live outbox plus the archives compaction moved
370
+ * aside. Read-only; used to rebuild the real holdings when held.json is missing.
371
+ */
372
+ export function allOutboxSignals(home) {
373
+ const dir = realDir(home);
374
+ if (!existsSync(dir))
375
+ return [];
376
+ const byNonce = new Map();
377
+ for (const f of readdirSync(dir).filter((n) => /^outbox(\.\d+\.done)?\.jsonl$/.test(n))) {
378
+ for (const raw of readFileSync(join(dir, f), 'utf8').split('\n')) {
379
+ if (!raw.trim())
380
+ continue;
381
+ try {
382
+ const r = JSON.parse(raw);
383
+ if (r.k !== 'enq')
384
+ continue;
385
+ const v = validateSignal(r.signal);
386
+ if (v.ok && !byNonce.has(v.signal.nonce))
387
+ byNonce.set(v.signal.nonce, v.signal);
388
+ }
389
+ catch { /* torn line */ }
390
+ }
391
+ }
392
+ return [...byNonce.values()].sort((a, b) => a.nonce - b.nonce);
393
+ }
394
+ export const backoff = (attempt) => Math.min(BACKOFF_MAX_MS, BACKOFF_MIN_MS * 2 ** Math.max(0, attempt - 1));
@@ -0,0 +1,72 @@
1
+ // dexterm autotrade — paper ACCOUNTS: several strategies trading from one simulated wallet (task-1538). PAPER only.
2
+ //
3
+ // Without an account every strategy of a parallel run owns its bankroll (its own SimBook, its own cash). A paper
4
+ // account is one shared pool of simulated cash that any number of strategies trade from, the way several bots would
5
+ // share one real wallet:
6
+ //
7
+ // account "main" ($1000) ── strategy moon (own positions, loss streak, per-coin rules, entries/day)
8
+ // └─ strategy scalp (own positions, …)
9
+ // account "yolo" (1 SOL) ── strategy scalp again (the same rules, another account: its own member, ledger and book)
10
+ //
11
+ // What is shared: the cash (a fill spends from, and a sale pays into, the account), the equity every strategy sizes
12
+ // from (cash + every open cost basis on the account, so "5% of the bankroll" means 5% of the wallet), and the
13
+ // drawdown / day-start figures each strategy's breaker reads (the wallet's drawdown halts each strategy at its own
14
+ // threshold). What stays per strategy: positions, exposure (its own footprint against the wallet), loss streak,
15
+ // cooldowns, per-coin limits, entry counts, its session caps and its P/L.
16
+ //
17
+ // Nothing here holds a key or touches a wallet: an account is a number in memory for the length of one run.
18
+ /** How many paper accounts one parallel run holds at most. */
19
+ export const MAX_PAPER_ACCOUNTS = 8;
20
+ const r2 = (v) => Math.round(v * 100) / 100;
21
+ export class PaperAccount {
22
+ id;
23
+ startUsd;
24
+ sol;
25
+ solUsd;
26
+ cashUsd;
27
+ books = [];
28
+ constructor(id, startUsd, o = {}) {
29
+ if (!(startUsd > 0) || !Number.isFinite(startUsd))
30
+ throw new Error(`paper account ${id}: the starting balance must be a positive number`);
31
+ this.id = id;
32
+ this.startUsd = startUsd;
33
+ this.cashUsd = startUsd;
34
+ if (o.sol !== undefined) {
35
+ this.sol = o.sol;
36
+ this.solUsd = o.solUsd;
37
+ }
38
+ }
39
+ /** Cash held for fills in flight (an executor is async: the debit lands after it answers). */
40
+ held = 0;
41
+ /** Hold `usd` of the cash for one fill; false when the cash (less what other fills hold) is not there. */
42
+ hold(usd) {
43
+ if (!(usd >= 0) || usd > this.cashUsd - this.held + 1e-9)
44
+ return false;
45
+ this.held += usd;
46
+ return true;
47
+ }
48
+ /** Release a hold (the fill was booked, synchronously after the executor answered, or refused). */
49
+ release(usd) { this.held = Math.max(0, this.held - usd); }
50
+ /** A strategy's book joins the account (SimBook's constructor calls this). A book never leaves: a stopped strategy's bags stay on the account. */
51
+ join(book, member = book.strategy.id) { this.books.push({ member, book }); }
52
+ /** Cash + every open cost basis (unrealized gains do not count for sizing). */
53
+ bookEquity() { let e = this.cashUsd; for (const b of this.books)
54
+ e += b.book.openCostUsd(); return e; }
55
+ /** Cash + every open bag marked at what it would sell for now. */
56
+ markedEquity() { let e = this.cashUsd; for (const b of this.books)
57
+ e += b.book.openMarkUsd(); return e; }
58
+ view() {
59
+ const eq = this.markedEquity();
60
+ const pnl = r2(eq - this.startUsd);
61
+ let open = 0, positions = 0;
62
+ for (const b of this.books) {
63
+ open += b.book.openCostUsd();
64
+ positions += b.book.open.size;
65
+ }
66
+ return {
67
+ id: this.id, startUsd: r2(this.startUsd), cashUsd: r2(this.cashUsd), equityUsd: r2(eq), pnlUsd: pnl, pnlPct: Math.round((pnl / this.startUsd) * 1000) / 10,
68
+ openCostUsd: r2(open), positions, strategies: [...new Set(this.books.map((b) => b.member))],
69
+ ...(this.sol !== undefined ? { sol: this.sol, solUsd: this.solUsd } : {}),
70
+ };
71
+ }
72
+ }
@@ -0,0 +1,87 @@
1
+ // dexterm autotrade — the limits on a PAPER run (task-1654, 0.2.0 card E): the `paper` scope of limits.json.
2
+ //
3
+ // Paper ignores the limits unless the user adds a `modes.paper` scope; then a paper run (a forward test) obeys the
4
+ // same guard a real run does (limits.ts checkBuy / checkSell / recordSell), so it shows what real mode would have
5
+ // refused or clamped. The state is the run's own (in memory): paper never touches real mode's risk.json, held.json
6
+ // or counters. No signal, no executor, no network: it only wraps the paper fill model.
7
+ import { checkBuy, checkSell, freshRiskState, recordBuy, recordSell } from "./limits.js";
8
+ import { localDay } from "./chat/audit.js";
9
+ export class PaperLimitsExecutor {
10
+ name;
11
+ inner;
12
+ L;
13
+ solUsd;
14
+ bookUsd;
15
+ onTrip;
16
+ risk = null;
17
+ held = new Map();
18
+ constructor(o) {
19
+ this.inner = o.inner;
20
+ this.L = o.limits;
21
+ this.solUsd = o.solUsd;
22
+ this.bookUsd = o.bookUsd;
23
+ this.onTrip = o.onTrip ?? (() => { });
24
+ this.name = `paper-limits(${o.inner.name})`;
25
+ }
26
+ /** The run's own state, rolled at local midnight of the tick clock (a replay keeps its days). */
27
+ state(t) {
28
+ const day = localDay(t);
29
+ if (!this.risk)
30
+ this.risk = freshRiskState(day);
31
+ else if (this.risk.day !== day)
32
+ this.risk = { ...this.risk, day, buys: 0, buySol: 0, perCoin: {}, dayPnlSol: 0 };
33
+ this.risk.recentBuys = this.risk.recentBuys.filter((x) => t - x < 60_000);
34
+ this.risk.recentSells = this.risk.recentSells.filter((x) => t - x < 60_000);
35
+ return this.risk;
36
+ }
37
+ /** The maxHoldMinutes limit for the runner (RunOpts.maxHoldMinutes), or null. */
38
+ maxHoldMinutes() { return this.L.maxHoldMinutes ?? null; }
39
+ async buy(k, usd, ctx) {
40
+ const solUsd = this.solUsd();
41
+ if (!solUsd || !(solUsd > 0))
42
+ return { refused: 'paper-limits-sol-price-unknown' };
43
+ const s = this.state(k.t);
44
+ const prior = this.held.get(k.mint);
45
+ if (!prior && this.L.maxOpenPositions !== undefined && this.held.size >= this.L.maxOpenPositions)
46
+ return { refused: 'paper-limit-max-open' };
47
+ const openCostUsd = [...this.held.values()].reduce((a, h) => a + Math.max(0, h.costUsd - h.costOutUsd), 0);
48
+ const v = checkBuy(this.L, s, { now: k.t, solUsd, usd, bookUsd: this.bookUsd(), mint: k.mint, liqUsd: k.liq, mcapUsd: k.mcapResolved ?? k.mcap, positionCostUsd: prior ? Math.max(0, prior.costUsd - prior.costOutUsd) : 0, openCostUsd });
49
+ if (!v.ok)
50
+ return { refused: v.refused.replace(/^real-/, 'paper-limit-') };
51
+ const f = await this.inner.buy(k, v.usd, ctx);
52
+ if ('refused' in f)
53
+ return f;
54
+ recordBuy(s, k.mint, f.usd / solUsd, k.t);
55
+ const h = this.held.get(k.mint) ?? { tokens: 0, costUsd: 0, costOutUsd: 0, realizedUsd: 0 };
56
+ h.tokens += f.tokens;
57
+ h.costUsd += f.usd;
58
+ this.held.set(k.mint, h);
59
+ return v.clamps.length ? { ...f, limitClamps: v.clamps } : f;
60
+ }
61
+ async sell(k, tokens, reason) {
62
+ const s = this.state(k.t);
63
+ const cs = checkSell(this.L, s, k.t);
64
+ if (!cs.ok)
65
+ return { refused: cs.refused.replace(/^real-/, 'paper-limit-') };
66
+ const f = await this.inner.sell(k, tokens, reason);
67
+ if ('refused' in f)
68
+ return f;
69
+ const solUsd = this.solUsd() ?? 0;
70
+ const h = this.held.get(k.mint);
71
+ if (h && solUsd > 0) {
72
+ const frac = h.tokens > 0 ? Math.min(1, f.tokens / h.tokens) : 1;
73
+ const portion = Math.max(0, h.costUsd - h.costOutUsd) * frac;
74
+ h.tokens -= f.tokens;
75
+ h.costOutUsd += portion;
76
+ h.realizedUsd += Math.max(0, f.usd);
77
+ const closed = h.tokens <= 1e-12 || frac >= 0.999;
78
+ for (const line of recordSell(this.L, s, { pnlSol: (Math.max(0, f.usd) - portion) / solUsd, closedPnlSol: closed ? (h.realizedUsd - h.costUsd) / solUsd : null, now: k.t }))
79
+ this.onTrip(`PAPER LIMIT: ${line}`);
80
+ if (closed)
81
+ this.held.delete(k.mint);
82
+ }
83
+ else if (h && (h.tokens - f.tokens <= 1e-12))
84
+ this.held.delete(k.mint);
85
+ return f;
86
+ }
87
+ }