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,188 @@
1
+ // dexterm autotrade — the paper-desk executor (task-1506 AT-4, AUTOTRADE-SPEC §5, §6.1). PAPER ONLY.
2
+ //
3
+ // Autotrade's orders through the app's existing paper desk (PaperExitDesk: the PaperAgentDesk plus exit plans)
4
+ // instead of the backtest fill model, so the agent
5
+ // path inherits every fence the desk already has: the armed mandate's caps at the agent tier (hands-free × 0.25),
6
+ // the panic file, the control file, the keyless Jupiter quote with its bounded retry, and the hash-chained
7
+ // journal. The strategy clamp has already sized the order; the desk's caps sit around it and both apply.
8
+ //
9
+ // buy() an entry or a ladder add. Refused unless the mandate is armed AND paper-only, and refused while the
10
+ // position monitor's heartbeat is stale (the monitor must be up to run the exits). Every entry fill
11
+ // registers its desk-side exit plan (P1), and carries a note (P4) naming the run, position and profile.
12
+ // sell() a mechanical or agent exit, journaled by the desk as agent.paper.exit.<rule>.
13
+ // costRecoverySellUsd() the proceeds curve a cost-recovery sell is sized on: a linear-impact estimate from one
14
+ // quote of the largest allowed slice, one confirming quote at the estimate, and at most one correction:
15
+ // never more than 3 quotes, inside the keyless budget.
16
+ // book() the desk's book in USD, for `bankroll.source: desk`.
17
+ //
18
+ // Nothing here can sign, send or touch a key: the desk only accepts a paper executor, and this file imports
19
+ // types only from the data layer (a refusal is recognised by its error name).
20
+ export const DESK_EXIT_OWNER = 'autotrade-monitor';
21
+ /** Most quotes a cost-recovery slice is sized on (the fill then takes its own fresh quote, as every desk fill does). */
22
+ export const MAX_SIZING_QUOTES = 3;
23
+ const errReason = (e) => (e instanceof Error && e.name === 'PaperDeskError' ? `desk-${e.reason}` : `desk-error:${e instanceof Error ? e.message.slice(0, 120) : String(e)}`);
24
+ const r6 = (x) => Number(x.toPrecision(6));
25
+ export class DeskExecutor {
26
+ name = 'paper-desk';
27
+ /** The monitor process owns the exits of every position this executor opens (a runner must not run them inline). */
28
+ exitsByMonitor = true;
29
+ d;
30
+ now;
31
+ seq = 0;
32
+ constructor(deps) {
33
+ this.d = deps;
34
+ this.now = deps.now ?? Date.now;
35
+ }
36
+ /** Why an entry may not go to the desk right now, or null. */
37
+ entryBlock() {
38
+ const m = this.d.mandate();
39
+ if (!m)
40
+ return 'no-mandate';
41
+ if (m.paperOnly !== true)
42
+ return 'mandate-not-paper-only';
43
+ if (m.status !== 'armed' || !m.armedBy)
44
+ return 'mandate-not-armed';
45
+ if (!this.d.monitorAlive())
46
+ return 'monitor-heartbeat-stale';
47
+ return null;
48
+ }
49
+ held(mint) {
50
+ return this.d.desk.positions().find((p) => p.mint === mint && p.tokens > 0) ?? null;
51
+ }
52
+ toFill(k, rec, reason) {
53
+ const solUsd = this.d.solUsd() ?? (rec.priceUsd && rec.priceSol ? rec.priceUsd / rec.priceSol : 0);
54
+ const usd = (Math.abs(Number(rec.solDelta)) / 1e9) * solUsd;
55
+ const tokens = Math.abs(Number(rec.tokenDelta)) / 10 ** rec.decimals;
56
+ return {
57
+ t: rec.t, side: rec.side, usd: r6(usd), tokens, price: k.price ?? (rec.priceSol * solUsd), avgPrice: tokens > 0 ? r6(usd / tokens) : 0,
58
+ impactPct: rec.priceImpactPct ?? 0, partial: false, reason,
59
+ };
60
+ }
61
+ /** An entry (or a ladder add) of `usd` through the desk, with its exit plan registered on the first fill. */
62
+ async buy(k, usd, ctx = {}) {
63
+ const block = this.entryBlock();
64
+ if (block)
65
+ return { refused: block };
66
+ const decimals = this.d.decimals(k.mint);
67
+ if (decimals === null)
68
+ return { refused: 'decimals-unknown' };
69
+ const kind = ctx.kind ?? (this.held(k.mint) ? 'add' : 'open');
70
+ const profile = ctx.profile ?? this.d.defaultProfile;
71
+ const positionId = ctx.positionId ?? `${this.d.runId ?? 'run'}:${k.mint.slice(0, 8)}:${this.now().toString(36)}${++this.seq}`;
72
+ const note = {
73
+ by: 'autotrade', kind, positionId, profile, runId: this.d.runId ?? null, sessionId: ctx.sessionId ?? null, mid: k.price,
74
+ ...(this.d.strategy ? { strategy: this.d.strategy.id, strategyHash: this.d.strategy.hash } : {}),
75
+ ...(ctx.reason ? { reason: ctx.reason.slice(0, 200) } : {}),
76
+ };
77
+ try {
78
+ const t = this.d.desk.open({ mint: k.mint, symbol: k.symbol, decimals, side: 'buy' });
79
+ this.d.desk.size(t.id, { usd });
80
+ const { fill } = await this.d.desk.confirm(t.id, { note });
81
+ if (kind === 'open')
82
+ this.d.desk.registerExitPlan({ mint: k.mint, owner: DESK_EXIT_OWNER, positionId, profileId: profile, runId: this.d.runId ?? null, note: { sessionId: ctx.sessionId ?? null } });
83
+ return this.toFill(k, fill, kind === 'open' ? 'entry' : 'ladder-add');
84
+ }
85
+ catch (e) {
86
+ return { refused: errReason(e) };
87
+ }
88
+ }
89
+ /** Sell `tokens` of the held position as exit rule `reason` (journaled agent.paper.exit.<reason>). */
90
+ async sell(k, tokens, reason, ctx = {}) {
91
+ const pos = this.held(k.mint);
92
+ if (!pos)
93
+ return { refused: 'no-position' };
94
+ const fraction = Math.min(1, tokens / pos.tokens);
95
+ if (!(fraction > 0))
96
+ return { refused: 'nothing-to-sell' };
97
+ try {
98
+ const r = await this.d.desk.exit({
99
+ mint: k.mint, fraction: fraction >= 0.999999 ? 1 : fraction, rule: reason, detail: ctx.detail,
100
+ note: { by: 'autotrade', runId: this.d.runId ?? null, ...(ctx.positionId ? { positionId: ctx.positionId } : {}), ...(ctx.agentReason ? { agentReason: ctx.agentReason.slice(0, 200) } : {}) },
101
+ });
102
+ return this.toFill(k, r.fill, reason);
103
+ }
104
+ catch (e) {
105
+ return { refused: errReason(e) };
106
+ }
107
+ }
108
+ /**
109
+ * The proceeds curve for a cost-recovery sell, from at most MAX_SIZING_QUOTES desk quotes (spec §9 AT-4):
110
+ * 1. quote the largest allowed slice (maxSellFrac of the bag): proceeds P at size C;
111
+ * 2. a linear-impact model through it (proceeds(t) = t·m·(1 − a·t), m = the tick's mid price) gives the
112
+ * slice t* that nets the owed cost; quote t* to confirm;
113
+ * 3. if the confirmation is short, scale once (t3 = t*·owed/proceeds(t*), at most C) and quote that.
114
+ * Returns a monotone piecewise-linear `sellUsd(tokens)` through the quoted points for monitorTick's search, or
115
+ * null when no quote could be had (the monitor then waits for the next tick: a cost-recovery sell is never
116
+ * sized on a guess).
117
+ */
118
+ async costRecoverySellUsd(p, prof, k) {
119
+ const cr = prof.costRecovery;
120
+ const solUsd = this.d.solUsd();
121
+ if (!cr || !solUsd || !k.price || p.tokens <= 0)
122
+ return null;
123
+ const owed = p.costUsd - p.realizedUsd;
124
+ const cap = p.tokens * cr.maxSellFrac;
125
+ let quotes = 0;
126
+ const quote = async (tokens) => {
127
+ if (quotes >= MAX_SIZING_QUOTES)
128
+ return null;
129
+ quotes++;
130
+ try {
131
+ const q = await this.d.desk.previewSell({ mint: p.mint, fraction: Math.min(1, tokens / p.tokens) });
132
+ return q.solOut * solUsd;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ };
138
+ const pts = [[0, 0]];
139
+ const pc = await quote(cap);
140
+ if (pc === null)
141
+ return null;
142
+ pts.push([cap, pc]);
143
+ if (owed > 0 && pc > owed) {
144
+ const m = k.price;
145
+ const a = Math.max(0, (1 - pc / (cap * m)) / cap);
146
+ // smaller root of a·m·t² − m·t + owed = 0 (or the no-impact line when a = 0)
147
+ const disc = m * m - 4 * a * m * owed;
148
+ const est = a > 0 && disc > 0 ? (m - Math.sqrt(disc)) / (2 * a * m) : owed / m;
149
+ const t1 = Math.min(cap, Math.max(cap * 1e-6, est));
150
+ const p1 = await quote(t1);
151
+ if (p1 !== null) {
152
+ pts.push([t1, p1]);
153
+ if (p1 < owed && p1 > 0) {
154
+ const t2 = Math.min(cap, (t1 * owed) / p1);
155
+ const p2 = t2 > t1 ? await quote(t2) : null;
156
+ if (p2 !== null)
157
+ pts.push([t2, p2]);
158
+ }
159
+ }
160
+ }
161
+ pts.sort((x, y) => x[0] - y[0]);
162
+ for (let i = 1; i < pts.length; i++)
163
+ pts[i][1] = Math.max(pts[i][1], pts[i - 1][1]); // monotone
164
+ const sellUsd = (t) => {
165
+ if (t <= 0)
166
+ return 0;
167
+ for (let i = 1; i < pts.length; i++) {
168
+ const [x0, y0] = pts[i - 1], [x1, y1] = pts[i];
169
+ if (t <= x1)
170
+ return x1 === x0 ? y1 : y0 + ((y1 - y0) * (t - x0)) / (x1 - x0);
171
+ }
172
+ return pts[pts.length - 1][1];
173
+ };
174
+ return { sellUsd, quotes };
175
+ }
176
+ /** The desk's paper book in USD (for `bankroll.source: desk`): cash, open cost, and their sum. */
177
+ book() {
178
+ const solUsd = this.d.solUsd();
179
+ if (!solUsd)
180
+ return null;
181
+ const v = this.d.desk.pnl();
182
+ const cashUsd = v.balanceSol * solUsd;
183
+ const openCostUsd = v.openCostSol * solUsd;
184
+ return { paper: true, cashUsd: r6(cashUsd), openCostUsd: r6(openCostUsd), equityUsd: r6(cashUsd + openCostUsd), solUsd };
185
+ }
186
+ /** The desk's exit plans (read-only). */
187
+ exitPlans() { return this.d.desk.exitPlans(); }
188
+ }
@@ -0,0 +1,401 @@
1
+ // dexterm autotrade — the strategy engine (task-1498 spike, AUTOTRADE-SPEC §1, §4, §6).
2
+ //
3
+ // Pure, deterministic, no I/O. Three jobs:
4
+ // screen() does this tick make the coin a candidate under the strategy's entry rules?
5
+ // clampEntry() turn an agent's (or the auto rule's) entry request into the size the strategy allows,
6
+ // with every reduction named. The agent can ask for anything; it gets at most this.
7
+ // SimBook the paper book + the fill model shared by replays and backtests (constant-product impact
8
+ // on the recorded liquidity, fee, slippage, partial fills at the impact cap, unsellable exits).
9
+ //
10
+ // In a live paper run the fills come from the app's paper desk instead (real Jupiter quotes, the mandate
11
+ // caps, the journal). The clamp is the first of
12
+ // three independent fences, never the only one.
13
+ import { parseFilter } from "../../cli/src/screener/filter.js";
14
+ import { tickRow } from "./replay.js";
15
+ export const DAY_MS = 86_400_000;
16
+ const r2 = (x) => Math.round(x * 100) / 100;
17
+ const r6 = (x) => Number(x.toPrecision(6));
18
+ /** Compiled entry rules (the filter is parsed once per strategy). */
19
+ export class EntryRules {
20
+ strategy;
21
+ predicate;
22
+ constructor(strategy) {
23
+ this.strategy = strategy;
24
+ const f = parseFilter(strategy.entry.screen);
25
+ if (!f.ok)
26
+ throw new Error(`entry.screen: ${f.error}`);
27
+ this.predicate = (k) => f.filter.predicate(tickRow(k));
28
+ }
29
+ screen(k) {
30
+ const e = this.strategy.entry;
31
+ const failed = [];
32
+ const matched = [];
33
+ if (k.kind !== 'live')
34
+ failed.push('not-listed');
35
+ if (k.price === null || k.price <= 0)
36
+ failed.push('no-price');
37
+ if (!this.predicate(k))
38
+ failed.push('screen');
39
+ const ageH = k.createdAt ? (k.t - k.createdAt) / 3600e3 : null;
40
+ if (ageH === null)
41
+ failed.push('age-unknown');
42
+ else if (ageH < e.ageHours.min || (e.ageHours.max !== null && ageH > e.ageHours.max))
43
+ failed.push('age');
44
+ const s = e.safety;
45
+ if (s.requireNoMintAuthority && k.mintAuth !== false)
46
+ failed.push(k.mintAuth === null ? 'mint-authority-unknown' : 'mint-authority');
47
+ if (s.requireNoFreezeAuthority && k.freezeAuth !== false)
48
+ failed.push(k.freezeAuth === null ? 'freeze-authority-unknown' : 'freeze-authority');
49
+ if (s.maxTop10Pct !== null) {
50
+ if (k.top10 === null) {
51
+ if (s.unknownHolders === 'reject')
52
+ failed.push('holders-unknown');
53
+ }
54
+ else if (k.top10 > s.maxTop10Pct)
55
+ failed.push('top10');
56
+ }
57
+ if (s.maxDevPct !== null && k.dev !== null && k.dev > s.maxDevPct)
58
+ failed.push('dev-share');
59
+ if (s.maxMcapSplitX !== null && (k.mcapFlagged || splitX(k) > s.maxMcapSplitX))
60
+ failed.push('mcap-split');
61
+ if (s.youngVertical && ageH !== null && ageH < s.youngVertical.maxAgeHours && (k.chg1h ?? 0) >= s.youngVertical.maxH1Pct)
62
+ failed.push('young-vertical');
63
+ const fired = e.triggers.map((t) => {
64
+ switch (t.kind) {
65
+ case 'screen': return true;
66
+ case 'momentum': return (k.chg5m ?? -Infinity) >= t.m5MinPct && ratio(k.buys5m, k.sells5m) >= t.buySellMin;
67
+ // Live feeds only: a replay has no narrative heat or followed-wallet stream, so these never fire here.
68
+ case 'narrative': return false;
69
+ case 'wallet': return false;
70
+ }
71
+ });
72
+ e.triggers.forEach((t, i) => { if (fired[i])
73
+ matched.push(`trigger:${t.kind}`); });
74
+ const trig = e.triggerMode === 'all' ? fired.every(Boolean) : fired.some(Boolean);
75
+ if (!trig)
76
+ failed.push('trigger');
77
+ return { pass: failed.length === 0, matched, failed };
78
+ }
79
+ }
80
+ export function splitX(k) {
81
+ if (!k.mcap || !k.mcapResolved)
82
+ return 1;
83
+ return Math.max(k.mcap, k.mcapResolved) / Math.max(1e-9, Math.min(k.mcap, k.mcapResolved));
84
+ }
85
+ const ratio = (a, b) => (a === null || b === null ? 0 : b === 0 ? (a > 0 ? Infinity : 0) : a / b);
86
+ /** Buy `usd` against the recorded pool. Constant product on half the liquidity; a partial fill at the impact cap. */
87
+ export function simBuy(k, usd, x) {
88
+ if (!k.price || k.price <= 0)
89
+ return { refused: 'no-price' };
90
+ if (!k.liq || k.liq <= 0)
91
+ return { refused: 'no-liquidity' };
92
+ const R = k.liq / 2;
93
+ const net = usd - x.networkFeeUsd;
94
+ if (net <= 0)
95
+ return { refused: 'below-network-fee' };
96
+ let input = net * (1 - x.feeBps / 1e4);
97
+ // Constant product: the average price paid is mid × (1 + input / R), so the impact is input / R.
98
+ const capIn = R * (x.maxPriceImpactPct / 100);
99
+ const partial = input > capIn;
100
+ if (partial)
101
+ input = capIn;
102
+ const tokens = ((R / k.price) * input) / (R + input) / (1 + x.slippageBps / 1e4);
103
+ const spent = partial ? input / (1 - x.feeBps / 1e4) + x.networkFeeUsd : usd;
104
+ return { t: k.t, side: 'buy', usd: r6(spent), tokens, price: k.price, avgPrice: r6(spent / tokens), impactPct: r2((input / R) * 100), partial, reason: 'entry' };
105
+ }
106
+ /** Sell `tokens` into the recorded pool. Below minExitLiquidityUsd (or no pool) nothing sells: the bag is stuck. */
107
+ export function simSell(k, tokens, x, reason) {
108
+ if (!k.price || k.price <= 0)
109
+ return { refused: 'no-price' };
110
+ if (!k.liq || k.liq < x.minExitLiquidityUsd)
111
+ return { refused: 'illiquid' };
112
+ const R = k.liq / 2;
113
+ const V = tokens * k.price;
114
+ const gross = (V * R) / (R + V);
115
+ const usd = gross * (1 - x.feeBps / 1e4) * (1 - x.slippageBps / 1e4) - x.networkFeeUsd;
116
+ if (usd <= 0)
117
+ return { refused: 'dust' };
118
+ return { t: k.t, side: 'sell', usd: r6(usd), tokens, price: k.price, avgPrice: r6(usd / tokens), impactPct: r2((V / (R + V)) * 100), partial: false, reason };
119
+ }
120
+ /** Value of a position if sold now through the same model (0 when it cannot be sold). */
121
+ export function liquidationValue(p, k, x) {
122
+ if (p.tokens <= 0)
123
+ return 0;
124
+ const f = simSell(k, p.tokens, x, 'mark');
125
+ return 'refused' in f ? 0 : f.usd;
126
+ }
127
+ export class SimBook {
128
+ strategy;
129
+ state;
130
+ open = new Map();
131
+ closed = [];
132
+ seq = 0;
133
+ triggers = new Map();
134
+ /** The shared paper account this book trades from (task-1538), or null: the book owns its bankroll. */
135
+ account;
136
+ constructor(strategy, now, account, member) {
137
+ this.strategy = strategy;
138
+ this.account = account ?? null;
139
+ const start = account ? account.bookEquity() : strategy.bankroll.startUsd;
140
+ this.state = { cashUsd: start, peakEquityUsd: start, dayStart: dayOf(now), dayStartEquityUsd: start, dayRealizedUsd: 0, entriesToday: 0, lossStreak: 0, cooldownUntil: 0, halted: null, sizeDown: false, perCoin: {} };
141
+ if (account) {
142
+ // On a shared account the cash IS the account's: every fill and sale below moves the wallet's cash.
143
+ Object.defineProperty(this.state, 'cashUsd', { get: () => account.cashUsd, set: (v) => { account.cashUsd = v; }, enumerable: true });
144
+ account.join(this, member);
145
+ }
146
+ }
147
+ /** Book equity: cash + the cost basis still open (unrealized gains do not count for sizing). On an account: the account's. */
148
+ bookEquity() {
149
+ if (this.account)
150
+ return this.account.bookEquity();
151
+ let e = this.state.cashUsd;
152
+ for (const p of this.open.values())
153
+ e += Math.max(0, p.costUsd - p.realizedUsd);
154
+ return e;
155
+ }
156
+ /** Marked equity: cash + what every open bag would sell for now (stuck bags count 0). On an account: the account's. */
157
+ markedEquity() {
158
+ if (this.account)
159
+ return this.account.markedEquity();
160
+ let e = this.state.cashUsd;
161
+ for (const p of this.open.values())
162
+ e += liquidationValue(p, p.lastTick, this.strategy.execution);
163
+ return e;
164
+ }
165
+ /** This book's own open cost basis (its exposure, also on a shared account). */
166
+ exposureUsd() { return this.openCostUsd(); }
167
+ openCostUsd() {
168
+ let e = 0;
169
+ for (const p of this.open.values())
170
+ e += Math.max(0, p.costUsd - p.realizedUsd);
171
+ return e;
172
+ }
173
+ /** What this book's open bags would sell for now. */
174
+ openMarkUsd() {
175
+ let e = 0;
176
+ for (const p of this.open.values())
177
+ e += liquidationValue(p, p.lastTick, this.strategy.execution);
178
+ return e;
179
+ }
180
+ /** This book's own P/L: closed trades + the open bags marked now (on a shared account, the strategy's share). */
181
+ ownPnlUsd() {
182
+ let e = 0;
183
+ for (const t of this.closed)
184
+ e += t.pnlUsd;
185
+ for (const p of this.open.values())
186
+ e += liquidationValue(p, p.lastTick, this.strategy.execution) + p.realizedUsd - p.costUsd;
187
+ return e;
188
+ }
189
+ /** Day roll, drawdown size-down / halt. Called by the runner on every step. */
190
+ roll(now) {
191
+ const s = this.state;
192
+ const d = dayOf(now);
193
+ if (d !== s.dayStart) {
194
+ s.dayStart = d;
195
+ s.dayRealizedUsd = 0;
196
+ s.entriesToday = 0;
197
+ s.dayStartEquityUsd = this.bookEquity();
198
+ if (s.halted === 'daily-loss')
199
+ s.halted = null;
200
+ }
201
+ const eq = this.markedEquity();
202
+ s.peakEquityUsd = Math.max(s.peakEquityUsd, eq);
203
+ const dd = ((s.peakEquityUsd - eq) / s.peakEquityUsd) * 100;
204
+ const r = this.strategy.risk.drawdown;
205
+ if (dd >= r.haltAtPct && !s.halted)
206
+ s.halted = 'drawdown';
207
+ if (dd >= r.sizeDownAtPct)
208
+ s.sizeDown = true;
209
+ else if (dd < r.recoverBelowPct)
210
+ s.sizeDown = false;
211
+ }
212
+ openPosition(k, fill, profile, sessionId, trigger, ladder) {
213
+ const id = `p${++this.seq}`;
214
+ const p = {
215
+ id, mint: k.mint, symbol: k.symbol, profileId: profile.id, openedAt: k.t, sessionId,
216
+ tokens: fill.tokens, origTokens: fill.tokens, costUsd: fill.usd, realizedUsd: 0, entryPrice: fill.price, peakPrice: fill.price, troughPrice: fill.price,
217
+ tpFilled: 0, trailArmed: false, ladder, fills: [fill], status: 'open', stuck: false, closedAt: null, exitReason: null,
218
+ liqWindow: [{ t: k.t, liq: k.liq ?? 0 }], lowTapeSince: null, lastTick: k, wakeSent: {},
219
+ };
220
+ this.state.cashUsd -= fill.usd;
221
+ this.state.entriesToday++;
222
+ const c = (this.state.perCoin[k.mint] ??= { entries: 0, lastExit: 0 });
223
+ c.entries++;
224
+ this.open.set(k.mint, p);
225
+ this.triggers.set(id, trigger);
226
+ return p;
227
+ }
228
+ /**
229
+ * 0.2.0 card B (service-recover.ts): take over a position a crashed run left open. Its open cost leaves this
230
+ * book's cash (it is exposure here now); the coin counts as entered, so the per-coin rules see it.
231
+ */
232
+ adopt(p) {
233
+ if (this.open.has(p.mint))
234
+ return false;
235
+ this.state.cashUsd -= Math.max(0, p.costUsd - p.realizedUsd);
236
+ (this.state.perCoin[p.mint] ??= { entries: 0, lastExit: 0 }).entries++;
237
+ this.open.set(p.mint, p);
238
+ this.triggers.set(p.id, ['resumed']);
239
+ return true;
240
+ }
241
+ addTranche(p, fill) {
242
+ this.state.cashUsd -= fill.usd;
243
+ const tokens = p.tokens + fill.tokens;
244
+ p.entryPrice = (p.entryPrice * p.tokens + fill.price * fill.tokens) / tokens;
245
+ p.tokens = tokens;
246
+ p.origTokens += fill.tokens;
247
+ p.costUsd += fill.usd;
248
+ p.fills.push(fill);
249
+ }
250
+ /** Book a sell; closes the position when nothing is left. */
251
+ sell(p, fill) {
252
+ p.tokens = Math.max(0, p.tokens - fill.tokens);
253
+ if (p.tokens < p.origTokens * 1e-6)
254
+ p.tokens = 0;
255
+ p.realizedUsd += fill.usd;
256
+ this.state.cashUsd += fill.usd;
257
+ p.fills.push(fill);
258
+ if (p.tokens > 0)
259
+ return null;
260
+ return this.close(p, fill.t, fill.reason, false);
261
+ }
262
+ /** Close the books on a position (also used at the end of a run: stuck bags close at 0). */
263
+ close(p, t, reason, stuck) {
264
+ p.status = 'closed';
265
+ p.closedAt = t;
266
+ p.exitReason = reason;
267
+ p.stuck = stuck;
268
+ this.open.delete(p.mint);
269
+ const pnl = p.realizedUsd - p.costUsd;
270
+ const s = this.state;
271
+ s.dayRealizedUsd += pnl;
272
+ if (pnl < 0) {
273
+ s.lossStreak++;
274
+ if (s.lossStreak >= this.strategy.risk.lossStreak.losses) {
275
+ s.cooldownUntil = t + this.strategy.risk.lossStreak.cooldownMin * 60e3;
276
+ s.lossStreak = 0;
277
+ }
278
+ }
279
+ else
280
+ s.lossStreak = 0;
281
+ const lossPct = (-s.dayRealizedUsd / s.dayStartEquityUsd) * 100;
282
+ if (lossPct >= this.strategy.risk.dailyLossHaltPct && !s.halted)
283
+ s.halted = 'daily-loss';
284
+ (s.perCoin[p.mint] ??= { entries: 0, lastExit: 0 }).lastExit = t;
285
+ const ct = {
286
+ id: p.id, mint: p.mint, symbol: p.symbol, profileId: p.profileId, openedAt: p.openedAt, closedAt: t,
287
+ costUsd: r6(p.costUsd), proceedsUsd: r6(p.realizedUsd), pnlUsd: r6(pnl), pnlPct: r2((pnl / p.costUsd) * 100),
288
+ exitReason: reason, stuck, entryTrigger: this.triggers.get(p.id) ?? [], sessionId: p.sessionId, fills: p.fills,
289
+ };
290
+ this.closed.push(ct);
291
+ return ct;
292
+ }
293
+ }
294
+ export const dayOf = (t) => Math.floor(t / DAY_MS);
295
+ /**
296
+ * The strategy's answer to "may I enter, and with how much?". Order: refusals (kill switch, halts,
297
+ * cooldowns, counts), then the size the strategy itself picks, then every ceiling, each reduction recorded.
298
+ * An agent request can only LOWER the size below the strategy's own pick, never raise it above a ceiling.
299
+ */
300
+ export function clampEntry(book, k, req, killSwitch = false) {
301
+ const s = book.strategy;
302
+ const st = book.state;
303
+ const clamps = [];
304
+ const no = (refused) => ({ ok: false, refused, clamps });
305
+ // task-1571: paper only (dexterm holds no key and places no live order). task-1645: real is the same paper book; its
306
+ // fills become trade-fire signals for the user's own executor (the runner admits it only through the real-mode hook).
307
+ if (s.mode !== 'paper' && s.mode !== 'real')
308
+ return no('mode-not-paper');
309
+ if (killSwitch)
310
+ return no('kill-switch');
311
+ if (st.halted)
312
+ return no(`halted:${st.halted}`);
313
+ if (k.t < st.cooldownUntil)
314
+ return no('loss-streak-cooldown');
315
+ if (book.open.has(k.mint))
316
+ return no('already-open');
317
+ // Free bags (cost recovered) can stop counting as open positions, up to their own cap (exit profile 1.2.0).
318
+ const rb = s.risk.recoveredBags;
319
+ const bags = rb && !rb.countAsOpen ? [...book.open.values()].filter((p) => p.costRecovered).length : 0;
320
+ if (book.open.size - bags >= s.risk.maxConcurrent)
321
+ return no('max-concurrent');
322
+ if (rb && !rb.countAsOpen && book.open.size >= s.risk.maxConcurrent + rb.maxBags)
323
+ return no('max-open-with-bags');
324
+ if (st.entriesToday >= s.risk.maxEntriesPerDay)
325
+ return no('max-entries-per-day');
326
+ const coin = st.perCoin[k.mint];
327
+ const profileId = req.profile ?? s.exits.default;
328
+ const profile = s.exits.profiles[profileId];
329
+ if (!profile)
330
+ return no(`unknown-profile:${profileId}`);
331
+ if (coin) {
332
+ if (coin.entries >= s.risk.perCoin.maxEntries)
333
+ return no('per-coin-max-entries');
334
+ if (!profile.reentry.allowed)
335
+ return no('profile-no-reentry');
336
+ const cool = Math.max(s.risk.perCoin.cooldownMin, profile.reentry.cooldownMinutes) * 60e3;
337
+ if (k.t - coin.lastExit < cool)
338
+ return no('per-coin-cooldown');
339
+ }
340
+ if ((k.liq ?? 0) < profile.fit.minLiquidityUsd)
341
+ return no('profile-min-liquidity');
342
+ const bank = book.bookEquity();
343
+ const z = s.sizing;
344
+ let usd;
345
+ let basis;
346
+ switch (z.mode) {
347
+ case 'fixed':
348
+ usd = z.fixedUsd ?? 0;
349
+ basis = `fixed $${z.fixedUsd}`;
350
+ break;
351
+ case 'pct':
352
+ usd = (bank * (z.pct ?? 0)) / 100;
353
+ basis = `${z.pct}% of $${r2(bank)}`;
354
+ break;
355
+ case 'risk': {
356
+ const stop = profile.hardStop.enabled && profile.hardStop.atPct !== null ? Math.abs(profile.hardStop.atPct) : s.risk.perTrade.assumedLossPctNoStop;
357
+ usd = (bank * (z.riskPct ?? 0)) / stop;
358
+ basis = `${z.riskPct}% risk / ${stop}% stop of $${r2(bank)}`;
359
+ break;
360
+ }
361
+ case 'confidence': {
362
+ const c = z.confidence ?? { minPct: 1, maxPct: 1, default: 0.5 };
363
+ const conf = clamp01(req.confidence ?? c.default);
364
+ const pct = c.minPct + (c.maxPct - c.minPct) * conf;
365
+ usd = (bank * pct) / 100;
366
+ basis = `confidence ${r2(conf)} → ${r2(pct)}% of $${r2(bank)}`;
367
+ break;
368
+ }
369
+ }
370
+ // The agent may ask for LESS than the strategy's pick (never more).
371
+ const asked = req.sizeUsd ?? (req.sizePct !== undefined ? (bank * req.sizePct) / 100 : undefined);
372
+ if (asked !== undefined && Number.isFinite(asked)) {
373
+ if (asked < usd) {
374
+ basis += `, agent asked $${r2(asked)}`;
375
+ usd = Math.max(0, asked);
376
+ }
377
+ else if (asked > usd)
378
+ clamps.push({ rule: 'strategy-size', fromUsd: r2(asked), toUsd: r2(usd) });
379
+ }
380
+ const cap = (rule, limit) => { if (usd > limit + 1e-9) {
381
+ clamps.push({ rule, fromUsd: r2(usd), toUsd: r2(Math.max(0, limit)) });
382
+ usd = Math.max(0, limit);
383
+ } };
384
+ if (st.sizeDown)
385
+ cap('drawdown-size-down', usd * s.risk.drawdown.sizeDownMult);
386
+ cap('strategy-max-pct', (bank * s.sizing.maxPctOfBankroll) / 100);
387
+ cap('profile-max-pct', (bank * profile.sizing.maxSizePctOfBankroll) / 100);
388
+ cap('strategy-max-usd', s.sizing.maxUsd);
389
+ const lossPct = profile.hardStop.enabled && profile.hardStop.atPct !== null ? Math.abs(profile.hardStop.atPct) : s.risk.perTrade.assumedLossPctNoStop;
390
+ cap('per-trade-max-loss', (bank * s.risk.perTrade.maxLossPctOfBankroll) / lossPct);
391
+ cap('max-exposure', (bank * s.risk.maxExposurePct) / 100 - book.exposureUsd());
392
+ cap('cash', st.cashUsd);
393
+ const R = (k.liq ?? 0) / 2;
394
+ const imp = s.execution.maxPriceImpactPct / 100;
395
+ cap('price-impact', (R * imp) / (1 - s.execution.feeBps / 1e4) + s.execution.networkFeeUsd);
396
+ if (usd < s.sizing.minUsd)
397
+ return no(clamps.length ? `below-min-after-${clamps[clamps.length - 1].rule}` : 'below-min-size');
398
+ const first = z.ladder ? usd * z.ladder[0].frac : usd;
399
+ return { ok: true, usd: r2(usd), firstTrancheUsd: r2(first), profile, clamps, basis };
400
+ }
401
+ const clamp01 = (x) => (Number.isFinite(x) ? Math.min(1, Math.max(0, x)) : 0.5);