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,190 @@
1
+ // dexterm autotrade — the position monitor (task-1498 spike, AUTOTRADE-SPEC §5).
2
+ //
3
+ // No LLM in this loop. On every tick of a held coin it applies the exit profile's mechanical rules at once
4
+ // (rug / liquidity pull, thesis breaks, hard stop, take-profit ladder, trailing stop, time stop, ladder adds)
5
+ // and only then raises WAKE events for the judgement calls the profile hands to the position's agent session
6
+ // (a TP filled, a time review, a drawdown alert, an early thesis warning, a mechanical exit that already
7
+ // happened). The session can answer within bounds (hold / exit / tighten / re-profile); it can never block
8
+ // or undo a mechanical exit, and if it does not answer the mechanical rules simply keep running.
9
+ import { splitX } from "./engine.js";
10
+ export const pnlPct = (p, price) => ((price - p.entryPrice) / p.entryPrice) * 100;
11
+ /** The data layer's agreement ratio (@dexterm/data MCAP_AGREE_RATIO; equal by test, kept local so the engine stays pure). */
12
+ export const SOURCES_AGREE_X = 5;
13
+ /**
14
+ * Why a thesis-split fired, naming the trigger that did fire (task-1518). On a row the data layer flagged, the resolved
15
+ * cap IS DexScreener's (resolveMcap keeps it when the sources disagree), so the two numbers on the tick are equal and
16
+ * the computed ratio reads 1.0x: say that the sources' supplies were flagged instead of printing "1.0x apart".
17
+ */
18
+ export function splitDetail(k, maxX) {
19
+ const x = splitX(k);
20
+ if (x > maxX)
21
+ return `market-cap sources ${x.toFixed(1)}x apart (limit ${maxX}x)`;
22
+ return `the data layer flagged the two market-cap sources: their supplies are more than ${SOURCES_AGREE_X}x apart`;
23
+ }
24
+ const pct = (x) => `${x >= 0 ? '+' : ''}${x.toFixed(1)}%`;
25
+ /**
26
+ * One tick for one open position. Mutates the position's tracking fields (peak, windows, TP count, armed
27
+ * trail, wake flags) and returns the actions in the order they must be executed. A full exit ends the list.
28
+ */
29
+ export function monitorTick(p, prof, k, o) {
30
+ const out = [];
31
+ p.lastTick = k;
32
+ const wake = (event, detail, allowed = ['hold', 'exit', 'tighten']) => {
33
+ out.push({ kind: 'wake', wake: { event, positionId: p.id, mint: p.mint, t: k.t, detail, allowed } });
34
+ };
35
+ const exitAll = (reason, detail) => {
36
+ out.push({ kind: 'sell', tokens: p.tokens, all: true, reason, detail });
37
+ out.push({ kind: 'wake', wake: { event: 'mechanical_exit', positionId: p.id, mint: p.mint, t: k.t, detail: `${reason}: ${detail}`, allowed: ['hold'] } });
38
+ return out;
39
+ };
40
+ // A stuck bag (no pool to sell into) is retried on every tick; nothing else applies until it sells.
41
+ if (p.stuck) {
42
+ if ((k.liq ?? 0) >= o.minExitLiquidityUsd && k.price)
43
+ return exitAll('rug-liquidity', 'liquidity came back, selling the stuck bag');
44
+ return out;
45
+ }
46
+ if (k.price === null || k.price <= 0)
47
+ return out;
48
+ const liq = k.liq ?? 0;
49
+ // 1. Rug / liquidity pull: the pool is gone or below the exit floor.
50
+ if (liq < o.minExitLiquidityUsd)
51
+ return exitAll('rug-liquidity', `liquidity $${Math.round(liq)} is below the $${o.minExitLiquidityUsd} exit floor`);
52
+ // 2. Thesis breaks on the coin's own reads.
53
+ const tb = prof.thesisBreak;
54
+ const winMs = tb.liquidityWindowMin * 60e3;
55
+ p.liqWindow.push({ t: k.t, liq });
56
+ // Keep the window plus the newest read before it: on a ~2-minute tape a 5-minute window still has a reference.
57
+ while (p.liqWindow.length > 2 && p.liqWindow[1].t < k.t - winMs)
58
+ p.liqWindow.shift();
59
+ const liqHigh = Math.max(...p.liqWindow.map((w) => w.liq));
60
+ const liqDrop = liqHigh > 0 ? ((liqHigh - liq) / liqHigh) * 100 : 0;
61
+ if (liqDrop >= tb.liquidityDropPct)
62
+ return exitAll('thesis-liquidity', `liquidity down ${liqDrop.toFixed(0)}% from its ${tb.liquidityWindowMin}-min high`);
63
+ if (tb.mcapDivergenceX !== null && (k.mcapFlagged || splitX(k) > tb.mcapDivergenceX))
64
+ return exitAll('thesis-split', splitDetail(k, tb.mcapDivergenceX));
65
+ if (tb.volumeDeath) {
66
+ const trades = (k.buys5m ?? 0) + (k.sells5m ?? 0);
67
+ if (k.buys5m !== null && trades < tb.volumeDeath.m5TradesBelow)
68
+ p.lowTapeSince ??= k.t;
69
+ else
70
+ p.lowTapeSince = null;
71
+ if (p.lowTapeSince !== null && k.t - p.lowTapeSince >= tb.volumeDeath.forMinutes * 60e3)
72
+ return exitAll('thesis-volume', `under ${tb.volumeDeath.m5TradesBelow} trades per 5 min for ${tb.volumeDeath.forMinutes} min`);
73
+ }
74
+ // (devSell needs chain events; the recorded history has none, so a replay cannot fire it.)
75
+ const gain = pnlPct(p, k.price);
76
+ p.peakPrice = Math.max(p.peakPrice, k.price);
77
+ p.troughPrice = Math.min(p.troughPrice ?? p.entryPrice, k.price);
78
+ const peakGain = pnlPct(p, p.peakPrice);
79
+ // 3. Hard stop (opt-in per profile), then the holding cap (the position's own dead-man).
80
+ if (prof.maxHoldMinutes != null && k.t - p.openedAt >= prof.maxHoldMinutes * 60e3)
81
+ return exitAll('max-hold', `held ${prof.maxHoldMinutes} min (the profile's holding cap) at ${pct(gain)}`);
82
+ if (prof.hardStop.enabled && prof.hardStop.atPct !== null && gain <= prof.hardStop.atPct)
83
+ return exitAll('stop', `${pct(gain)} hit the ${prof.hardStop.atPct}% hard stop`);
84
+ // 3b. Cost recovery (exit profile 1.2.0): sell what pays back the net cost, once; the rest rides as a free bag.
85
+ // Proceeds come from the runner's own fill model (every cost a sell pays), and the sell is capped at
86
+ // maxSellFrac of the bag, so a thin pool recovers less rather than selling everything.
87
+ const cr = prof.costRecovery;
88
+ if (cr && !p.costRecovered && gain >= cr.atPct) {
89
+ const owed = p.costUsd - p.realizedUsd;
90
+ const price = k.price;
91
+ const sellUsd = o.sellUsd ?? ((tok) => tok * price);
92
+ const cap = p.tokens * cr.maxSellFrac;
93
+ let tokens = cap;
94
+ if (sellUsd(cap) > owed) { // smallest bag slice that nets the cost (proceeds rise with size)
95
+ let lo = 0, hi = cap;
96
+ for (let i = 0; i < 40; i++) {
97
+ const mid = (lo + hi) / 2;
98
+ if (sellUsd(mid) >= owed)
99
+ hi = mid;
100
+ else
101
+ lo = mid;
102
+ }
103
+ tokens = hi;
104
+ }
105
+ p.costRecovered = true;
106
+ if (owed > 0 && tokens > 0) {
107
+ out.push({ kind: 'sell', tokens, all: false, reason: 'cost-recovery', detail: `${pct(gain)} ≥ +${cr.atPct}%: sell ${Math.round((tokens / p.tokens) * 100)}% of the bag to take the $${owed.toFixed(2)} cost back` });
108
+ if (prof.wake?.onCostRecovered)
109
+ wake('cost_recovered', `cost taken back at ${pct(gain)}; ${Math.round(((p.tokens - tokens) / p.origTokens) * 100)}% of the original size rides as a free bag`, ['hold', 'exit', 'tighten']);
110
+ }
111
+ }
112
+ const held = p.tokens - out.reduce((a, x) => a + (x.kind === 'sell' ? x.tokens : 0), 0);
113
+ // 4. Take-profit ladder (fractions of the ORIGINAL size; several steps may fill on one gap).
114
+ let left = held;
115
+ while (p.tpFilled < prof.tpLadder.length && gain >= prof.tpLadder[p.tpFilled].atPct) {
116
+ const step = prof.tpLadder[p.tpFilled];
117
+ const tokens = Math.min(left, p.origTokens * step.sellFrac);
118
+ p.tpFilled++;
119
+ if (tokens <= 0)
120
+ continue;
121
+ left -= tokens;
122
+ out.push({ kind: 'sell', tokens, all: left <= p.origTokens * 1e-6, reason: `tp${p.tpFilled}`, detail: `${pct(gain)} ≥ +${step.atPct}%: sell ${Math.round(step.sellFrac * 100)}% of the original size` });
123
+ if (prof.wake?.onTpFilled)
124
+ wake('tp_filled', `TP${p.tpFilled} filled at ${pct(gain)}; ${Math.round((left / p.origTokens) * 100)}% of the original size left`, ['hold', 'exit', 'tighten', 'reprofile']);
125
+ }
126
+ if (left <= p.origTokens * 1e-6)
127
+ return out;
128
+ // 5a. Bag trail (exit profile 1.2.0): armed by the cost-recovery sell (and, with armAtPct, a higher peak),
129
+ // measured off the peak since entry.
130
+ if (prof.bagTrail && p.costRecovered && peakGain >= (prof.bagTrail.armAtPct ?? 0)) {
131
+ const off = ((p.peakPrice - k.price) / p.peakPrice) * 100;
132
+ const trail = p.bagTrailPct ?? prof.bagTrail.trailPct;
133
+ if (off >= trail) {
134
+ out.push({ kind: 'sell', tokens: left, all: true, reason: 'bag-trail', detail: `the bag is ${off.toFixed(0)}% off its peak (${pct(peakGain)}), bag trail ${trail}%` });
135
+ out.push({ kind: 'wake', wake: { event: 'mechanical_exit', positionId: p.id, mint: p.mint, t: k.t, detail: `bag-trail: ${off.toFixed(0)}% off the peak`, allowed: ['hold'] } });
136
+ return out;
137
+ }
138
+ }
139
+ // 5. Trailing stop.
140
+ const ts = prof.trailingStop;
141
+ if (ts) {
142
+ if (!p.trailArmed && peakGain >= ts.armAtPct)
143
+ p.trailArmed = true;
144
+ const off = ((p.peakPrice - k.price) / p.peakPrice) * 100;
145
+ if (p.trailArmed && off >= ts.trailPct) {
146
+ out.push({ kind: 'sell', tokens: left, all: true, reason: 'trail', detail: `${off.toFixed(0)}% off the peak (${pct(peakGain)}), trail ${ts.trailPct}%` });
147
+ out.push({ kind: 'wake', wake: { event: 'mechanical_exit', positionId: p.id, mint: p.mint, t: k.t, detail: `trail: ${off.toFixed(0)}% off the peak`, allowed: ['hold'] } });
148
+ return out;
149
+ }
150
+ }
151
+ // 6. Time stop.
152
+ const tm = prof.timeStop;
153
+ if (tm && k.t - p.openedAt >= tm.minutes * 60e3 && gain < tm.ifBelowPct) {
154
+ if (tm.action === 'exit') {
155
+ out.push({ kind: 'sell', tokens: left, all: true, reason: 'time', detail: `${tm.minutes} min in at ${pct(gain)} (needed +${tm.ifBelowPct}%)` });
156
+ out.push({ kind: 'wake', wake: { event: 'mechanical_exit', positionId: p.id, mint: p.mint, t: k.t, detail: `time: ${tm.minutes} min at ${pct(gain)}`, allowed: ['hold'] } });
157
+ return out;
158
+ }
159
+ if (!p.wakeSent.time && (prof.wake?.onTimeReview ?? true)) {
160
+ p.wakeSent.time = true;
161
+ wake(tm.action === 'review' ? 'time_review' : 'time_tighten', `${tm.minutes} min in at ${pct(gain)}, below the +${tm.ifBelowPct}% the profile wanted by now`);
162
+ }
163
+ }
164
+ // 7. Judgement wakes (once each).
165
+ const w = prof.wake;
166
+ if (w?.drawdownPct != null && gain <= w.drawdownPct && !p.wakeSent.drawdown) {
167
+ p.wakeSent.drawdown = true;
168
+ wake('drawdown', `position at ${pct(gain)}; no hard stop in this profile`);
169
+ }
170
+ if (w?.thesisWarnFrac != null && liqDrop >= tb.liquidityDropPct * w.thesisWarnFrac && !p.wakeSent.thesis) {
171
+ p.wakeSent.thesis = true;
172
+ wake('thesis_warning', `liquidity down ${liqDrop.toFixed(0)}% from its window high (exit at ${tb.liquidityDropPct}%)`);
173
+ }
174
+ // 8. Ladder adds (scale-in tranches), each still clamped by the runner.
175
+ p.ladder.forEach((tr, i) => {
176
+ if (tr.usd <= 0)
177
+ return;
178
+ if (k.t > tr.deadline) {
179
+ tr.usd = 0;
180
+ return;
181
+ }
182
+ const first = p.fills[0].price;
183
+ const move = ((k.price - first) / first) * 100;
184
+ if (tr.atPct < 0 ? move <= tr.atPct : move >= tr.atPct) {
185
+ out.push({ kind: 'add', index: i, usd: tr.usd, detail: `tranche ${i + 1}: ${pct(move)} from the first fill (trigger ${tr.atPct}%)` });
186
+ tr.usd = 0;
187
+ }
188
+ });
189
+ return out;
190
+ }
@@ -0,0 +1,137 @@
1
+ // dexterm autotrade — candidate and position sessions on a LOCAL model (task-1543, Ollama).
2
+ //
3
+ // The same protocol as the claude sessions (sessions.ts, AUTOTRADE-SPEC §4): one conversation per coin, the
4
+ // DOSSIER first, then DELTA / ENGINE / EVENT messages, and every reply ends in one `DECISION {json}` line that
5
+ // session.ts parses against the closed schema and the engine clamps. What differs is only who answers:
6
+ //
7
+ // * the session is TOOL-LESS by construction: no `tools` are ever sent to /api/chat (the test asserts the
8
+ // request body), so the model can only answer;
9
+ // * the reply is constrained with Ollama structured output ({ comment, decision }) and rendered back into the
10
+ // exact text form the claude path produces, so parseDecision, the clamp, the caps and the ledger are shared;
11
+ // * a reply later than decideWithinSec aborts the request and resolves as no answer (no late result exists);
12
+ // * cost is $0 (the user's own machine); the day turn / session caps of the SessionGate still apply.
13
+ //
14
+ // The restriction profile is checked when the factory is built: autotrade on a local engine needs `paper`
15
+ // (`full` for anything live). Nothing here sees a key, a wallet or the signer.
16
+ import { OllamaError, atLeast, requiredLevel, stripThink } from '../../mcp/src/engine/index.js';
17
+ import { ACTIONS, parseDecision } from "./session.js";
18
+ /** The structured-output schema of one reply: a short comment and the decision object (the closed §4 shape). */
19
+ export const OLLAMA_DECISION_FORMAT = {
20
+ type: 'object',
21
+ properties: {
22
+ comment: { type: 'string' },
23
+ decision: {
24
+ type: 'object',
25
+ properties: {
26
+ action: { type: 'string', enum: [...ACTIONS] },
27
+ profile: { type: 'string' },
28
+ sizePct: { type: 'number' },
29
+ confidence: { type: 'number' },
30
+ fraction: { type: 'number' },
31
+ reason: { type: 'string' },
32
+ },
33
+ required: ['action', 'reason'],
34
+ },
35
+ },
36
+ required: ['comment', 'decision'],
37
+ };
38
+ /** Render a structured reply as the claude-path text: comment lines, then one DECISION line. Raw text is kept as is. */
39
+ export function renderOllamaReply(content) {
40
+ const s = stripThink(content).trim();
41
+ let v;
42
+ try {
43
+ v = JSON.parse(s);
44
+ }
45
+ catch {
46
+ return s;
47
+ }
48
+ if (!v || typeof v !== 'object' || Array.isArray(v))
49
+ return s;
50
+ const o = v;
51
+ if (!o.decision || typeof o.decision !== 'object')
52
+ return s;
53
+ // Drop keys the model set to null (optional fields), keep everything else for the closed-schema check to judge.
54
+ const d = Object.fromEntries(Object.entries(o.decision).filter(([, x]) => x !== null));
55
+ const comment = typeof o.comment === 'string' ? o.comment.replace(/\n+/g, ' ').trim().slice(0, 400) : '';
56
+ return `${comment}\nDECISION ${JSON.stringify(d)}`.trim();
57
+ }
58
+ class OllamaSession {
59
+ id;
60
+ kind = 'ollama';
61
+ model;
62
+ o;
63
+ messages;
64
+ turns = 0;
65
+ closed = false;
66
+ why = null;
67
+ inflight = null;
68
+ constructor(id, o) {
69
+ this.id = id;
70
+ this.o = o;
71
+ this.model = o.model;
72
+ this.messages = [{ role: 'system', content: o.systemPrompt }];
73
+ }
74
+ costUsd() { return 0; }
75
+ endReason() { return this.why; }
76
+ turnsUsed() { return this.turns; }
77
+ async send(text) {
78
+ if (this.closed || this.why)
79
+ return null;
80
+ if (this.turns >= this.o.maxTurns)
81
+ return null;
82
+ this.turns++;
83
+ const t0 = Date.now();
84
+ this.messages.push({ role: 'user', content: text });
85
+ const ac = new AbortController();
86
+ this.inflight = ac;
87
+ const timer = setTimeout(() => ac.abort(), this.o.decideWithinMs);
88
+ timer.unref?.();
89
+ try {
90
+ // Tool-less: the request never carries `tools`.
91
+ const r = await this.o.client.chat({ model: this.o.model, messages: this.messages, format: OLLAMA_DECISION_FORMAT, options: { num_ctx: this.o.numCtx } }, { signal: ac.signal, timeoutMs: this.o.decideWithinMs + 1000 });
92
+ this.o.onRaw?.(JSON.stringify({ engine: 'ollama', model: r.model ?? this.o.model, content: r.content.slice(0, 4000), promptTokens: r.promptTokens, evalTokens: r.evalTokens }));
93
+ const reply = renderOllamaReply(r.content);
94
+ this.messages.push({ role: 'assistant', content: reply });
95
+ return { text: reply, decision: parseDecision(reply), costUsd: 0, ms: Date.now() - t0 };
96
+ }
97
+ catch (e) {
98
+ // No answer in time (or the server went away): the turn resolves as no answer, never as a late decision.
99
+ this.messages.push({ role: 'assistant', content: '(no answer in time)' });
100
+ if (e instanceof OllamaError && (e.kind === 'unreachable' || e.kind === 'model-missing'))
101
+ this.why = e.kind === 'unreachable' ? 'ollama-unreachable' : 'model-missing';
102
+ return null;
103
+ }
104
+ finally {
105
+ clearTimeout(timer);
106
+ if (this.inflight === ac)
107
+ this.inflight = null;
108
+ }
109
+ }
110
+ async close(reason) {
111
+ this.closed = true;
112
+ try {
113
+ this.inflight?.abort();
114
+ }
115
+ catch { /* ignore */ }
116
+ this.why ??= reason;
117
+ return { turns: this.turns, costUsd: 0, reason };
118
+ }
119
+ }
120
+ /** Throws when the profile does not allow autotrade on a local engine (the run refuses to start). */
121
+ export function assertAutotradeAllowed(restriction, live = false) {
122
+ const need = requiredLevel('autotrade', { live });
123
+ if (!atLeast(restriction, need))
124
+ throw new Error(`the local engine's restriction profile is ${restriction}; ${live ? 'a live' : 'an autotrade'} run needs ${need} (dexterm ai restrictions ${need}${need === 'full' ? ', or dexterm ai engine ollama --full' : ''})`);
125
+ }
126
+ /** The runner's session factory on the user's local Ollama: same signature and protocol as claudeSessionFactory. */
127
+ export function ollamaSessionFactory(f) {
128
+ assertAutotradeAllowed(f.restriction, f.live);
129
+ return (id, s, ctx) => {
130
+ f.log('session_init', { sessionId: id, sessionKind: ctx.kind, engine: 'ollama', model: f.model, host: f.client.host.local ? 'local' : 'remote', tools: 0, mcpServers: 0, restriction: f.restriction, costUsd: 0, ok: true });
131
+ return new OllamaSession(id, {
132
+ client: f.client, model: f.model, numCtx: f.numCtx, systemPrompt: f.systemPrompt(s),
133
+ maxTurns: s.agent.maxTurns, decideWithinMs: s.agent.decideWithinSec * 1000,
134
+ onRaw: f.onRaw ? (line) => f.onRaw(id, line) : undefined,
135
+ });
136
+ };
137
+ }