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.
- package/dist/packages/agents/src/schema.js +22 -1
- package/dist/packages/agents/src/spectator.js +3 -1
- package/dist/packages/agents/src/story.js +11 -2
- package/dist/packages/agents/src/tiers.js +36 -2
- package/dist/packages/agents/src/tools.js +35 -2
- package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
- package/dist/packages/autotrade/assets/limits.schema.json +569 -0
- package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
- package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
- package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
- package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
- package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
- package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
- package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
- package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
- package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
- package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
- package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
- package/dist/packages/autotrade/examples/executor/README.md +70 -0
- package/dist/packages/autotrade/examples/executor/jsonl-tail-executor.mjs +57 -0
- package/dist/packages/autotrade/examples/executor/kit.mjs +144 -0
- package/dist/packages/autotrade/examples/executor/signal-cmd-executor.mjs +25 -0
- package/dist/packages/autotrade/examples/executor/telegram-bot-recipe.md +47 -0
- package/dist/packages/autotrade/examples/executor/webhook-executor.mjs +47 -0
- package/dist/packages/autotrade/src/account.js +74 -0
- package/dist/packages/autotrade/src/backtest.js +358 -0
- package/dist/packages/autotrade/src/basis-text.js +32 -0
- package/dist/packages/autotrade/src/chat/approvals.js +484 -0
- package/dist/packages/autotrade/src/chat/audit.js +140 -0
- package/dist/packages/autotrade/src/chat/config.js +95 -0
- package/dist/packages/autotrade/src/chat/connector.js +24 -0
- package/dist/packages/autotrade/src/chat/control.js +104 -0
- package/dist/packages/autotrade/src/chat/fake.js +100 -0
- package/dist/packages/autotrade/src/chat/format.js +71 -0
- package/dist/packages/autotrade/src/chat/hub.js +187 -0
- package/dist/packages/autotrade/src/chat/index.js +18 -0
- package/dist/packages/autotrade/src/chat/notify.js +302 -0
- package/dist/packages/autotrade/src/chat/pairing.js +46 -0
- package/dist/packages/autotrade/src/chat/redact.js +48 -0
- package/dist/packages/autotrade/src/chat/secrets.js +106 -0
- package/dist/packages/autotrade/src/chat/service.js +251 -0
- package/dist/packages/autotrade/src/chat/telegram.js +290 -0
- package/dist/packages/autotrade/src/desk-executor.js +188 -0
- package/dist/packages/autotrade/src/engine.js +401 -0
- package/dist/packages/autotrade/src/execution-port.js +97 -0
- package/dist/packages/autotrade/src/executor-health.js +217 -0
- package/dist/packages/autotrade/src/fleet-config.js +169 -0
- package/dist/packages/autotrade/src/fleet-control.js +276 -0
- package/dist/packages/autotrade/src/fleet.js +642 -0
- package/dist/packages/autotrade/src/forward.js +355 -0
- package/dist/packages/autotrade/src/index.js +72 -0
- package/dist/packages/autotrade/src/journey.js +477 -0
- package/dist/packages/autotrade/src/ledger.js +108 -0
- package/dist/packages/autotrade/src/limits.js +682 -0
- package/dist/packages/autotrade/src/live-source.js +182 -0
- package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
- package/dist/packages/autotrade/src/monitor.js +190 -0
- package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
- package/dist/packages/autotrade/src/outbox.js +394 -0
- package/dist/packages/autotrade/src/paper-accounts.js +72 -0
- package/dist/packages/autotrade/src/paper-limits.js +87 -0
- package/dist/packages/autotrade/src/real-mode.js +688 -0
- package/dist/packages/autotrade/src/receipts.js +330 -0
- package/dist/packages/autotrade/src/replay.js +198 -0
- package/dist/packages/autotrade/src/report.js +168 -0
- package/dist/packages/autotrade/src/runner-config.js +99 -0
- package/dist/packages/autotrade/src/runner.js +923 -0
- package/dist/packages/autotrade/src/safety.js +243 -0
- package/dist/packages/autotrade/src/service-guard.js +125 -0
- package/dist/packages/autotrade/src/service-install.js +213 -0
- package/dist/packages/autotrade/src/service-recover.js +232 -0
- package/dist/packages/autotrade/src/service.js +289 -0
- package/dist/packages/autotrade/src/session.js +269 -0
- package/dist/packages/autotrade/src/sessions.js +503 -0
- package/dist/packages/autotrade/src/signal-channels.js +312 -0
- package/dist/packages/autotrade/src/signal.js +196 -0
- package/dist/packages/autotrade/src/store.js +250 -0
- package/dist/packages/autotrade/src/strategy.js +258 -0
- package/dist/packages/autotrade/src/trade-ledger.js +241 -0
- package/dist/packages/autotrade/src/yolo.js +140 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
- package/dist/packages/cli/src/ai-cmd.js +412 -0
- package/dist/packages/cli/src/analyst/backend.js +83 -15
- package/dist/packages/cli/src/analyst/codex.js +15 -9
- package/dist/packages/cli/src/analyst/ollama.js +263 -0
- package/dist/packages/cli/src/analyst/session.js +5 -2
- package/dist/packages/cli/src/app-help-gen.js +56 -8
- package/dist/packages/cli/src/autotrade-cmd/audit.js +74 -0
- package/dist/packages/cli/src/autotrade-cmd/backtest.js +216 -0
- package/dist/packages/cli/src/autotrade-cmd/executor.js +91 -0
- package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
- package/dist/packages/cli/src/autotrade-cmd/fleet.js +401 -0
- package/dist/packages/cli/src/autotrade-cmd/forward.js +283 -0
- package/dist/packages/cli/src/autotrade-cmd/journey.js +208 -0
- package/dist/packages/cli/src/autotrade-cmd/limits.js +393 -0
- package/dist/packages/cli/src/autotrade-cmd/receipt.js +63 -0
- package/dist/packages/cli/src/autotrade-cmd/report.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/run.js +657 -0
- package/dist/packages/cli/src/autotrade-cmd/service.js +873 -0
- package/dist/packages/cli/src/autotrade-cmd/signal.js +824 -0
- package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
- package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
- package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
- package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
- package/dist/packages/cli/src/autotrade-cmd.js +214 -0
- package/dist/packages/cli/src/display.js +156 -17
- package/dist/packages/cli/src/doctor.js +62 -3
- package/dist/packages/cli/src/export.js +10 -2
- package/dist/packages/cli/src/index.js +1 -1
- package/dist/packages/cli/src/ledger-cmd.js +290 -0
- package/dist/packages/cli/src/live/chart-fills.js +46 -0
- package/dist/packages/cli/src/live/index.js +23 -89
- package/dist/packages/cli/src/live/screens/token-live.js +9 -6
- package/dist/packages/cli/src/live/trade.js +12 -457
- package/dist/packages/cli/src/live/watchlist.js +1 -1
- package/dist/packages/cli/src/main.js +147 -190
- package/dist/packages/cli/src/mcp-cmd.js +7 -1
- package/dist/packages/cli/src/plan-cmd.js +1 -1
- package/dist/packages/cli/src/pnl-app.js +31 -21
- package/dist/packages/cli/src/pnl-card.js +162 -21
- package/dist/packages/cli/src/popup.js +22 -6
- package/dist/packages/cli/src/record-cmd.js +141 -20
- package/dist/packages/cli/src/screens/agent-hud.js +34 -2
- package/dist/packages/cli/src/screens/analyst-session.js +7 -4
- package/dist/packages/cli/src/screens/analyst.js +3 -2
- package/dist/packages/cli/src/screens/autotrade-journey.js +134 -0
- package/dist/packages/cli/src/screens/autotrade-keys.js +182 -0
- package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
- package/dist/packages/cli/src/screens/autotrade-screen.js +776 -0
- package/dist/packages/cli/src/screens/autotrade-ui.js +650 -0
- package/dist/packages/cli/src/screens/autotrade-worker.js +91 -0
- package/dist/packages/cli/src/screens/brain.js +37 -11
- package/dist/packages/cli/src/screens/overlay.js +5 -2
- package/dist/packages/cli/src/screens/plans.js +5 -17
- package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
- package/dist/packages/cli/src/screens/positions.js +6 -28
- package/dist/packages/cli/src/screens/ticket.js +38 -205
- package/dist/packages/cli/src/screens/token.js +3 -3
- package/dist/packages/cli/src/state.js +25 -19
- package/dist/packages/cli/src/telegram-cmd.js +454 -0
- package/dist/packages/cli/src/trade.js +3 -128
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/cli/src/wallet-cmd.js +39 -340
- package/dist/packages/cli/src/wallet-command.js +8 -11
- package/dist/packages/cli/src/widgets.js +0 -12
- package/dist/packages/custom/src/config.js +20 -2
- package/dist/packages/custom/src/schema.js +35 -10
- package/dist/packages/custom/src/workspace.js +3 -3
- package/dist/packages/data/src/basis.js +191 -0
- package/dist/packages/data/src/detail.js +53 -2
- package/dist/packages/data/src/index.js +0 -14
- package/dist/packages/data/src/store.js +14 -5
- package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
- package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
- package/dist/packages/data/src/trade/bridge.js +4 -102
- package/dist/packages/data/src/trade/ledger/export.js +101 -0
- package/dist/packages/data/src/trade/ledger/file.js +135 -0
- package/dist/packages/data/src/trade/ledger/fold.js +191 -0
- package/dist/packages/data/src/trade/ledger/index.js +9 -0
- package/dist/packages/data/src/trade/ledger/schema.js +83 -0
- package/dist/packages/data/src/trade/ledger/sources.js +307 -0
- package/dist/packages/data/src/trade/ledger/stats.js +123 -0
- package/dist/packages/data/src/trade/ledger/tags.js +116 -0
- package/dist/packages/data/src/trade/ledger/view.js +51 -0
- package/dist/packages/data/src/trade/plans/index.js +0 -1
- package/dist/packages/data/src/trade/plans/service.js +3 -27
- package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-executor-kit.md +44 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-journey.md +46 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-limits.md +48 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-real-signal.md +45 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
- package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
- package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
- package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +229 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
- package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +51 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-always-on-service.md +57 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-1-ask-for-a-strategy.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-2-backtest-it-held-out-split.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-3-forward-test-it-on-live-data.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-4-promote-it-to-always-on-paper.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-5-real-only-if-you-choose.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-ladder.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-report.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-what-the-analyst-knows.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat.md +24 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-executor-health.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-paper-vs-real.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-digest.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-executor-kit.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-whole-path.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-what-dexterm-does-and-never-does.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-your-limits.md +77 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real.md +24 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-real-mode-your-strategy-s-trades-to-your.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-receipts-what-your-executor-did.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-safety.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-set-it-up.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-channels.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-signal.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-is.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-will-not-do.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-where-you-see-it.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
- package/dist/packages/mcp/assets/kb/entries/autotrade.md +40 -0
- package/dist/packages/mcp/src/app-help.js +1 -1
- package/dist/packages/mcp/src/autotrade-tools.js +1107 -0
- package/dist/packages/mcp/src/backtest-requests.js +71 -0
- package/dist/packages/mcp/src/engine/config.js +159 -0
- package/dist/packages/mcp/src/engine/index.js +10 -0
- package/dist/packages/mcp/src/engine/loop.js +101 -0
- package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
- package/dist/packages/mcp/src/engine/ollama.js +258 -0
- package/dist/packages/mcp/src/engine/probe.js +110 -0
- package/dist/packages/mcp/src/engine/reason.js +35 -0
- package/dist/packages/mcp/src/engine/restrictions.js +111 -0
- package/dist/packages/mcp/src/index.js +7 -1
- package/dist/packages/mcp/src/ledger-tools.js +494 -0
- package/dist/packages/mcp/src/paper-agent.js +2 -0
- package/dist/packages/mcp/src/server.js +138 -19
- package/dist/packages/packs/src/backtest.js +3 -1
- package/dist/packages/popup/src/pages/chart-lib.js +134 -21
- package/dist/packages/popup/src/pages/chart.css +9 -0
- package/dist/packages/popup/src/pages/chart.html +3 -1
- package/dist/packages/popup/src/pages/chart.js +104 -7
- package/dist/packages/popup/src/protocol.js +2 -0
- package/dist/packages/popup/src/server.js +4 -0
- package/dist/packages/recorder/src/index.js +2 -0
- package/dist/packages/recorder/src/recorder.js +63 -6
- package/dist/packages/recorder/src/retention.js +100 -0
- package/dist/packages/recorder/src/schema.js +34 -1
- package/dist/packages/recorder/src/ticks.js +237 -0
- package/dist/packages/tui/src/index.js +1 -0
- package/dist/packages/tui/src/keymap.js +28 -1
- package/dist/packages/tui/src/markers.js +219 -0
- package/dist/packages/tui/src/widgets.js +149 -2
- package/package.json +2 -1
- package/dist/packages/data/src/trade/caps.js +0 -323
- package/dist/packages/data/src/trade/executor.js +0 -933
- package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
- package/dist/packages/data/src/trade/rpc-exec.js +0 -229
- package/dist/packages/data/src/trade/signer/client.js +0 -197
- package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
- package/dist/packages/data/src/trade/signer/host.js +0 -97
- package/dist/packages/data/src/trade/signer/main.js +0 -121
- package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
- package/dist/packages/data/src/trade/signer/protocol.js +0 -92
- package/dist/packages/data/src/trade/signer/service.js +0 -400
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
- package/dist/packages/data/src/trade/sol-usd.js +0 -86
- package/dist/packages/data/src/trade/swap-build.js +0 -108
- package/dist/packages/data/src/trade/token-delta.js +0 -201
- package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// dexterm history recorder: position-grade ticks for autotrade (task-1504, AUTOTRADE-SPEC §3 "Data today vs what
|
|
2
|
+
// must be recorded"). The change-driven `token_snapshots` rows land every ~2 min at best and only on a ≥ 5 % move,
|
|
3
|
+
// which hides the intrabar path a stop or a take-profit lives on. This lane samples the FEW coins that matter to a
|
|
4
|
+
// position every 5–10 s and writes every sample:
|
|
5
|
+
//
|
|
6
|
+
// focus held > open > candidate > watch, one role per mint (the highest wins), at most `maxCoins`
|
|
7
|
+
// (default 60 = two calls). Sources are injected: the collector reads the watchlist and the paper
|
|
8
|
+
// wallet from the DB and the autotrade focus file (`focus.json`, written by the runner / the app).
|
|
9
|
+
// cadence held + open coins every `fastEveryMs` (5 s), candidate + watch every `slowEveryMs` (10 s). A call
|
|
10
|
+
// that goes out anyway is topped up with coins not yet due (oldest sample first): a batch of 30
|
|
11
|
+
// costs the same one request.
|
|
12
|
+
// budget DexScreener `tokens/v1` batches of 30 only (never a per-coin keyless Jupiter call), through the
|
|
13
|
+
// data layer's limiter and health gate; 'normal' priority when a held/open coin is in the call,
|
|
14
|
+
// 'low' otherwise; skipped while the bucket is paused after a 429; a per-UTC-day call ceiling.
|
|
15
|
+
// candles every sample with a price folds into `autotrade_candles_1m` (sampled OHLC: high/low are of the
|
|
16
|
+
// samples, not of every trade).
|
|
17
|
+
//
|
|
18
|
+
// No change detection and no raw JSON: a tick row is ~100 bytes. Retention (retention.ts) keeps ticks 14 days.
|
|
19
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
21
|
+
import { withPriority, systemClock } from '../../data/src/index.js';
|
|
22
|
+
/** Priority order: earlier roles are sampled first and faster. */
|
|
23
|
+
export const FOCUS_ROLES = ['held', 'open', 'candidate', 'watch'];
|
|
24
|
+
export const DEFAULT_TICKS = { fastEveryMs: 5_000, slowEveryMs: 10_000, maxCoins: 60, batch: 30, maxCallsPerDay: 36_000 };
|
|
25
|
+
const BASE58 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
26
|
+
const MIN = 60_000;
|
|
27
|
+
const fin = (x) => (typeof x === 'number' && Number.isFinite(x) ? x : null);
|
|
28
|
+
const isFast = (r) => r === 'held' || r === 'open';
|
|
29
|
+
/** One role per mint (the highest-priority role wins), invalid mints dropped, in priority order. */
|
|
30
|
+
export function mergeFocus(entries) {
|
|
31
|
+
const best = new Map();
|
|
32
|
+
for (const e of entries) {
|
|
33
|
+
if (!e || typeof e.mint !== 'string' || !BASE58.test(e.mint) || !FOCUS_ROLES.includes(e.role))
|
|
34
|
+
continue;
|
|
35
|
+
const cur = best.get(e.mint);
|
|
36
|
+
if (!cur || FOCUS_ROLES.indexOf(e.role) < FOCUS_ROLES.indexOf(cur))
|
|
37
|
+
best.set(e.mint, e.role);
|
|
38
|
+
}
|
|
39
|
+
return [...best].map(([mint, role]) => ({ mint, role })).sort((a, b) => FOCUS_ROLES.indexOf(a.role) - FOCUS_ROLES.indexOf(b.role));
|
|
40
|
+
}
|
|
41
|
+
/** The sampler. Owns no connection: it writes through the recorder's DB handle (same file, same thread). */
|
|
42
|
+
export class TickLane {
|
|
43
|
+
policy;
|
|
44
|
+
db;
|
|
45
|
+
focus;
|
|
46
|
+
clock;
|
|
47
|
+
onLog;
|
|
48
|
+
/** mint -> time of its last sample this process took. */
|
|
49
|
+
last = new Map();
|
|
50
|
+
polling = false;
|
|
51
|
+
day = '';
|
|
52
|
+
dayCalls = 0;
|
|
53
|
+
counters = { polls: 0, calls: 0, written: 0 };
|
|
54
|
+
stmts;
|
|
55
|
+
constructor(db, opts) {
|
|
56
|
+
this.db = db;
|
|
57
|
+
this.focus = opts.focus;
|
|
58
|
+
this.policy = { ...DEFAULT_TICKS, ...opts.policy };
|
|
59
|
+
this.clock = opts.clock ?? systemClock;
|
|
60
|
+
this.onLog = opts.onLog;
|
|
61
|
+
this.stmts = {
|
|
62
|
+
tick: db.prepare(`INSERT OR IGNORE INTO autotrade_ticks (mint, t, role, price_usd, liquidity_usd, mcap, fdv, vol_m5, vol_h1, buys_m5, sells_m5, chg_m5, chg_h1, pair_address, pair_created_at)
|
|
63
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
|
64
|
+
candle: db.prepare(`INSERT INTO autotrade_candles_1m (mint, t, o, h, l, c, liq_c, mcap_c, n) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)
|
|
65
|
+
ON CONFLICT(mint, t) DO UPDATE SET h = MAX(h, excluded.h), l = MIN(l, excluded.l), c = excluded.c, liq_c = excluded.liq_c, mcap_c = excluded.mcap_c, n = n + 1`),
|
|
66
|
+
meta: db.prepare("INSERT OR REPLACE INTO recorder_meta (key, value) VALUES ('tick_calls', ?)"),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Tick calls issued in the current UTC day (persisted in recorder_meta, so a restart keeps the count). */
|
|
70
|
+
callsToday(now = this.clock.now()) {
|
|
71
|
+
const day = new Date(now).toISOString().slice(0, 10);
|
|
72
|
+
if (day !== this.day) {
|
|
73
|
+
this.day = day;
|
|
74
|
+
this.dayCalls = 0;
|
|
75
|
+
const r = this.db.prepare("SELECT value FROM recorder_meta WHERE key = 'tick_calls'").get();
|
|
76
|
+
try {
|
|
77
|
+
const v = r ? JSON.parse(r.value) : null;
|
|
78
|
+
if (v && v.day === day)
|
|
79
|
+
this.dayCalls = Number(v.calls) || 0;
|
|
80
|
+
}
|
|
81
|
+
catch { /* reset */ }
|
|
82
|
+
}
|
|
83
|
+
return this.dayCalls;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Which coins this poll samples: every due coin in priority order up to `maxCoins`, then the last batch topped
|
|
87
|
+
* up with coins not yet due (oldest sample first). `overCap` counts focus coins past `maxCoins`.
|
|
88
|
+
*/
|
|
89
|
+
plan(now = this.clock.now(), maxCalls = Math.ceil(this.policy.maxCoins / this.policy.batch)) {
|
|
90
|
+
const focus = mergeFocus(this.focus());
|
|
91
|
+
const cap = Math.min(this.policy.maxCoins, maxCalls * this.policy.batch);
|
|
92
|
+
const inCap = focus.slice(0, cap);
|
|
93
|
+
const overCap = focus.length - inCap.length;
|
|
94
|
+
const isDue = (e) => now - (this.last.get(e.mint) ?? -Infinity) >= (isFast(e.role) ? this.policy.fastEveryMs : this.policy.slowEveryMs);
|
|
95
|
+
const due = inCap.filter(isDue);
|
|
96
|
+
if (!due.length)
|
|
97
|
+
return { coins: [], due: 0, overCap, focus: focus.length };
|
|
98
|
+
const room = Math.ceil(due.length / this.policy.batch) * this.policy.batch - due.length;
|
|
99
|
+
const ride = inCap.filter((e) => !isDue(e)).sort((a, b) => (this.last.get(a.mint) ?? 0) - (this.last.get(b.mint) ?? 0)).slice(0, room);
|
|
100
|
+
return { coins: [...due, ...ride], due: due.length, overCap, focus: focus.length };
|
|
101
|
+
}
|
|
102
|
+
/** One poll. The host calls this every `fastEveryMs` (the collector's tick loop). Never throws. */
|
|
103
|
+
async poll(deps, now = this.clock.now()) {
|
|
104
|
+
const none = (skipped, overCap = 0) => ({ coins: 0, calls: 0, answered: 0, written: 0, overCap, skipped });
|
|
105
|
+
if (this.polling)
|
|
106
|
+
return none('in-flight');
|
|
107
|
+
let budget = this.policy.maxCallsPerDay - this.callsToday(now);
|
|
108
|
+
if (budget <= 0)
|
|
109
|
+
return none('day-cap');
|
|
110
|
+
let planned;
|
|
111
|
+
try {
|
|
112
|
+
planned = this.plan(now, Math.min(budget, Math.ceil(this.policy.maxCoins / this.policy.batch)));
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
this.log(`focus failed: ${msg(err)}`);
|
|
116
|
+
return none('no-focus');
|
|
117
|
+
}
|
|
118
|
+
if (!planned.coins.length)
|
|
119
|
+
return none(planned.focus ? 'not-due' : 'no-focus', planned.overCap);
|
|
120
|
+
if (deps.limiter.bucket('dexscreener').stats().pausedUntil > now)
|
|
121
|
+
return none('paused', planned.overCap);
|
|
122
|
+
this.polling = true;
|
|
123
|
+
this.counters.polls++;
|
|
124
|
+
const res = { coins: planned.coins.length, calls: 0, answered: 0, written: 0, overCap: planned.overCap, skipped: null };
|
|
125
|
+
try {
|
|
126
|
+
for (let i = 0; i < planned.coins.length && budget > 0; i += this.policy.batch) {
|
|
127
|
+
const chunk = planned.coins.slice(i, i + this.policy.batch);
|
|
128
|
+
const priority = chunk.some((e) => isFast(e.role)) ? 'normal' : 'low';
|
|
129
|
+
res.calls++;
|
|
130
|
+
budget--;
|
|
131
|
+
this.dayCalls++;
|
|
132
|
+
this.stmts.meta.run(JSON.stringify({ day: this.day, calls: this.dayCalls }));
|
|
133
|
+
const views = await withPriority(priority, () => deps.dexscreener.tokens(chunk.map((e) => e.mint)));
|
|
134
|
+
const t = this.clock.now();
|
|
135
|
+
const w = this.write(chunk, views, t);
|
|
136
|
+
res.answered += w.answered;
|
|
137
|
+
res.written += w.written;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
res.error = msg(err);
|
|
142
|
+
this.log(`tick poll failed: ${res.error}`);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
this.polling = false;
|
|
146
|
+
this.counters.calls += res.calls;
|
|
147
|
+
this.counters.written += res.written;
|
|
148
|
+
}
|
|
149
|
+
return res;
|
|
150
|
+
}
|
|
151
|
+
/** Write one answered batch (one transaction): a tick per coin (NULL price when no pair came back) + candles. */
|
|
152
|
+
write(chunk, views, t) {
|
|
153
|
+
let answered = 0, written = 0;
|
|
154
|
+
this.db.exec('BEGIN');
|
|
155
|
+
try {
|
|
156
|
+
for (const e of chunk) {
|
|
157
|
+
this.last.set(e.mint, t);
|
|
158
|
+
const p = views.get(e.mint)?.pair;
|
|
159
|
+
if (p)
|
|
160
|
+
answered++;
|
|
161
|
+
const price = p ? fin(p.priceUsd) : null;
|
|
162
|
+
const liq = p ? fin(p.liquidityUsd) : null;
|
|
163
|
+
const mcap = p ? fin(p.mcap) : null;
|
|
164
|
+
const r = this.stmts.tick.run(e.mint, t, e.role, price, liq, mcap, p ? fin(p.fdv) : null, p ? fin(p.volume.m5) : null, p ? fin(p.volume.h1) : null, p ? fin(p.txns.m5?.buys) : null, p ? fin(p.txns.m5?.sells) : null, p ? fin(p.priceChange.m5) : null, p ? fin(p.priceChange.h1) : null, p?.pairAddress || null, p ? fin(p.createdAt) : null);
|
|
165
|
+
if (Number(r.changes) > 0)
|
|
166
|
+
written++;
|
|
167
|
+
if (price !== null && price > 0 && Number(r.changes) > 0)
|
|
168
|
+
this.stmts.candle.run(e.mint, Math.floor(t / MIN) * MIN, price, price, price, price, liq, mcap);
|
|
169
|
+
}
|
|
170
|
+
this.db.exec('COMMIT');
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
this.db.exec('ROLLBACK');
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
// Forget coins that left the focus long ago (bounded memory over a long run).
|
|
177
|
+
if (this.last.size > 4 * this.policy.maxCoins)
|
|
178
|
+
for (const [m, lt] of this.last)
|
|
179
|
+
if (t - lt > 3_600_000)
|
|
180
|
+
this.last.delete(m);
|
|
181
|
+
return { answered, written };
|
|
182
|
+
}
|
|
183
|
+
log(line) {
|
|
184
|
+
this.onLog?.(`recorder: ${line}\n`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// ---------------------------------------------------------------- focus sources
|
|
188
|
+
/**
|
|
189
|
+
* Focus coins the app's own DB already knows: every watchlist mint ('watch') and every paper position with a
|
|
190
|
+
* token balance ('held'). Tables that do not exist yet (a fresh DB) contribute nothing.
|
|
191
|
+
*/
|
|
192
|
+
export function dbFocus(db) {
|
|
193
|
+
const has = (name) => !!db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(name);
|
|
194
|
+
const out = [];
|
|
195
|
+
if (has('watchlist'))
|
|
196
|
+
for (const r of db.prepare('SELECT mint FROM watchlist').all())
|
|
197
|
+
out.push({ mint: r.mint, role: 'watch' });
|
|
198
|
+
if (has('paper_positions'))
|
|
199
|
+
for (const r of db.prepare("SELECT mint FROM paper_positions WHERE tokens_raw NOT IN ('0', '')").all())
|
|
200
|
+
out.push({ mint: r.mint, role: 'held' });
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
/** The rendezvous file other processes (the autotrade runner, the app) publish focus coins in. */
|
|
204
|
+
export const FOCUS_FILE_SCHEMA = 'dexterm-autotrade-focus/1';
|
|
205
|
+
/** A focus file not rewritten for this long is ignored (a dead writer must not pin coins to the fast lane). */
|
|
206
|
+
export const FOCUS_FILE_STALE_MS = 30 * MIN;
|
|
207
|
+
/** `<dexterm home>/autotrade/focus.json`. */
|
|
208
|
+
export const focusFilePath = (home) => join(home, 'autotrade', 'focus.json');
|
|
209
|
+
/** Read a focus file: missing, garbled, stale or expired entries contribute nothing. Never throws. */
|
|
210
|
+
export function readFocusFile(path, now) {
|
|
211
|
+
let doc;
|
|
212
|
+
try {
|
|
213
|
+
doc = JSON.parse(readFileSync(path, 'utf8'));
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
if (!doc || doc.schema !== FOCUS_FILE_SCHEMA || !Array.isArray(doc.entries) || !(now - Number(doc.updatedAt) <= FOCUS_FILE_STALE_MS))
|
|
219
|
+
return [];
|
|
220
|
+
return mergeFocus(doc.entries.filter((e) => e && (e.until === undefined || Number(e.until) >= now)).map((e) => ({ mint: e.mint, role: e.role })));
|
|
221
|
+
}
|
|
222
|
+
/** Write a focus file atomically (tmp + rename, mode 0600). */
|
|
223
|
+
export function writeFocusFile(path, entries, now) {
|
|
224
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
225
|
+
const doc = { schema: FOCUS_FILE_SCHEMA, updatedAt: now, entries: [...entries] };
|
|
226
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
227
|
+
writeFileSync(tmp, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 });
|
|
228
|
+
renameSync(tmp, path);
|
|
229
|
+
}
|
|
230
|
+
/** Sampled 1m candles for one mint, oldest first (read-only use). */
|
|
231
|
+
export function candlesFromDb(db, mint, from = 0, to = Number.MAX_SAFE_INTEGER) {
|
|
232
|
+
return db.prepare('SELECT t, o, h, l, c, liq_c, mcap_c, n FROM autotrade_candles_1m WHERE mint = ? AND t >= ? AND t <= ? ORDER BY t').all(mint, from, to)
|
|
233
|
+
.map((r) => ({ t: Number(r.t), o: Number(r.o), h: Number(r.h), l: Number(r.l), c: Number(r.c), liq: r.liq_c ?? null, mcap: r.mcap_c ?? null, n: Number(r.n) }));
|
|
234
|
+
}
|
|
235
|
+
function msg(err) {
|
|
236
|
+
return err instanceof Error ? err.message : String(err);
|
|
237
|
+
}
|
|
@@ -339,7 +339,6 @@ export const DEFAULT_KEYMAP = new Keymap([
|
|
|
339
339
|
{ keys: ['!'], action: 'plans.panic', label: 'PANIC', screen: 'plans' },
|
|
340
340
|
{ keys: ['M'], action: 'plans.popup', label: 'popup', screen: 'plans' },
|
|
341
341
|
{ keys: ['G'], action: 'plans.gate', label: 'hands-free gate', screen: 'plans' },
|
|
342
|
-
{ keys: ['L'], action: 'plans.live', label: 'fire path paper/live', screen: 'plans' },
|
|
343
342
|
// task-1360 copy trading. `U` opens the FOLLOWED WALLETS screen from anywhere (free capital: not B/S/P/W/V/X/T/L/C/F/Z/R/
|
|
344
343
|
// K/M/N/D/O/G/I). On it: `i` types a wallet address (+ optional label) to follow, `d` unfollows the selected wallet,
|
|
345
344
|
// `r` (global refresh) polls now. Nothing on this screen trades: a buy/sell alert is information only.
|
|
@@ -368,4 +367,32 @@ export const DEFAULT_KEYMAP = new Keymap([
|
|
|
368
367
|
{ keys: ['d'], action: 'rules.remove', label: 'delete rule', screen: 'rules' },
|
|
369
368
|
{ keys: ['tab'], action: 'rules.tab', label: 'rules ⇄ proposals', screen: 'rules' },
|
|
370
369
|
{ keys: ['x'], action: 'rules.dismiss', label: 'dismiss proposal', screen: 'rules' },
|
|
370
|
+
// task-1508 AUTOTRADE (paper). `Q` (free capital everywhere) opens the screen; tab cycles its five views (strategies,
|
|
371
|
+
// editor, compare, backtest, runner). The lowercase verbs below override the global w/b/x/p/i/s on this screen only.
|
|
372
|
+
// `!` soft-stops the paper runner, `p` pauses its new entries, `f` flattens after a y confirm. Edits only touch the
|
|
373
|
+
// user's own strategy files (validated, journaled); there is no key here for live mode, mandates or the signer.
|
|
374
|
+
{ keys: ['Q'], action: 'screen.autotrade', label: 'autotrade (paper)' },
|
|
375
|
+
{ keys: ['tab'], action: 'autotrade.view', label: 'next view', screen: 'autotrade' },
|
|
376
|
+
{ keys: ['n'], action: 'autotrade.new', label: 'new from preset', screen: 'autotrade' },
|
|
377
|
+
{ keys: ['d'], action: 'autotrade.dup', label: 'duplicate', screen: 'autotrade' },
|
|
378
|
+
{ keys: ['e'], action: 'autotrade.edit', label: 'edit', screen: 'autotrade' },
|
|
379
|
+
{ keys: ['c'], action: 'autotrade.compare', label: 'mark to compare', screen: 'autotrade' },
|
|
380
|
+
{ keys: ['b'], action: 'autotrade.backtest', label: 'backtest', screen: 'autotrade' },
|
|
381
|
+
{ keys: ['w'], action: 'autotrade.sweep', label: 'sweep (held-out)', screen: 'autotrade' },
|
|
382
|
+
{ keys: ['x'], action: 'autotrade.export', label: 'export file', screen: 'autotrade' },
|
|
383
|
+
{ keys: ['i'], action: 'autotrade.import', label: 'import file', screen: 'autotrade' },
|
|
384
|
+
{ keys: ['r'], action: 'autotrade.run', label: 'runner', screen: 'autotrade' },
|
|
385
|
+
{ keys: ['s'], action: 'autotrade.save', label: 'save (new version)', screen: 'autotrade' },
|
|
386
|
+
{ keys: ['u'], action: 'autotrade.undo', label: 'undo', screen: 'autotrade' },
|
|
387
|
+
{ keys: ['!'], action: 'autotrade.kill', label: 'kill (soft stop)', screen: 'autotrade' },
|
|
388
|
+
{ keys: ['p'], action: 'autotrade.pause', label: 'pause new entries', screen: 'autotrade' },
|
|
389
|
+
{ keys: ['f'], action: 'autotrade.flatten', label: 'flatten (confirm)', screen: 'autotrade' },
|
|
390
|
+
{ keys: ['o'], action: 'autotrade.proposal', label: 'review chat proposal', screen: 'autotrade' },
|
|
391
|
+
// task-1647: a strategy's JOURNEY (draft → backtested → forward-tested → always-on → real). F starts a 24 h paper forward
|
|
392
|
+
// test (live data, the strategy's own bankroll), X stops it, A opens a pending always-on promotion (y applies). Real is
|
|
393
|
+
// never applied from a key: it needs the user's caps and a typed phrase on a terminal.
|
|
394
|
+
{ keys: ['J'], action: 'autotrade.journey', label: 'journey (ladder)', screen: 'autotrade' },
|
|
395
|
+
{ keys: ['F'], action: 'autotrade.forward', label: 'forward test (paper)', screen: 'autotrade' },
|
|
396
|
+
{ keys: ['X'], action: 'autotrade.forwardstop', label: 'stop forward test', screen: 'autotrade' },
|
|
397
|
+
{ keys: ['A'], action: 'autotrade.promote', label: 'review promotion', screen: 'autotrade' },
|
|
371
398
|
]);
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// @dexterm/tui — task-1483 BUY / SELL markers: the pure model every chart draws from (terminal candles, the agent
|
|
2
|
+
// views' traces, and — mirrored in packages/popup/src/pages/chart-lib.js, parity-tested — the chart popup).
|
|
3
|
+
//
|
|
4
|
+
// Input: the fills the app already holds (the paper ledger's trades and the live ledger's confirmed swaps), never a
|
|
5
|
+
// fetch. Output: one marker per (bar, side, source) for the bars on screen, the open position's average entry per
|
|
6
|
+
// source, and the closed round trips. Display only: nothing here can arm, size, sign or confirm anything.
|
|
7
|
+
//
|
|
8
|
+
// Placement rules (the card's "never clamped to a wrong place"):
|
|
9
|
+
// - a fill belongs to the bar whose period contains its time (bar open = floor(t / period) · period);
|
|
10
|
+
// - a fill older than the first bar on screen, newer than the last bar, or in a period with no bar (a quiet coin
|
|
11
|
+
// can skip a minute) is OMITTED and counted, never moved onto a neighbouring bar;
|
|
12
|
+
// - the price is the fill's own USD price (token-weighted when one bar holds several fills of a side); with no
|
|
13
|
+
// known price the marker is drawn against the bar (below it for a buy, above it for a sell) and says so.
|
|
14
|
+
// The axis never changes a marker's position: bars and markers are both laid out in USD price; an mcap axis only
|
|
15
|
+
// relabels (price × the current supply, task-1366/1472).
|
|
16
|
+
/** Bar periods by the app's timeframe names. */
|
|
17
|
+
export const MARKER_PERIOD_MS = { '1m': 60e3, '5m': 300e3, '15m': 900e3, '1h': 3600e3, '4h': 14400e3, '1d': 86400e3 };
|
|
18
|
+
/** The open time of the bar a time falls in. */
|
|
19
|
+
export function bucketOf(t, periodMs) { return Math.floor(t / periodMs) * periodMs; }
|
|
20
|
+
const fin = (n) => typeof n === 'number' && Number.isFinite(n);
|
|
21
|
+
const EPS = 1e-9;
|
|
22
|
+
/** Valid fills only (finite time, a side, a source, a positive token amount), oldest first; stable for equal times. */
|
|
23
|
+
export function cleanFills(fills) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const f of fills ?? []) {
|
|
26
|
+
if (!f || !fin(f.t) || (f.side !== 'buy' && f.side !== 'sell') || (f.source !== 'paper' && f.source !== 'live'))
|
|
27
|
+
continue;
|
|
28
|
+
if (!fin(f.tokens) || f.tokens <= 0)
|
|
29
|
+
continue;
|
|
30
|
+
out.push({ t: f.t, side: f.side, source: f.source, priceUsd: fin(f.priceUsd) && f.priceUsd > 0 ? f.priceUsd : null, tokens: f.tokens, sol: fin(f.sol) && f.sol >= 0 ? f.sol : null });
|
|
31
|
+
}
|
|
32
|
+
return out.map((f, i) => [f, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([f]) => f);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Replay the fills per source (oldest first) the way the ledgers book them: a buy adds tokens and cost, a sell
|
|
36
|
+
* removes tokens at the average cost. A sell bigger than what the replay holds (the history was truncated) is
|
|
37
|
+
* treated as closing. Returns every fill annotated, the open position's average entry, and the closed trips.
|
|
38
|
+
*/
|
|
39
|
+
export function replayFills(fills) {
|
|
40
|
+
const st = {
|
|
41
|
+
paper: { held: 0, cost: 0, costOk: true, trip: null },
|
|
42
|
+
live: { held: 0, cost: 0, costOk: true, trip: null },
|
|
43
|
+
};
|
|
44
|
+
const out = [];
|
|
45
|
+
const trips = [];
|
|
46
|
+
for (const f of cleanFills(fills)) {
|
|
47
|
+
const s = st[f.source];
|
|
48
|
+
const before = s.held;
|
|
49
|
+
if (f.side === 'buy') {
|
|
50
|
+
if (s.held <= EPS) {
|
|
51
|
+
s.held = 0;
|
|
52
|
+
s.cost = 0;
|
|
53
|
+
s.costOk = true;
|
|
54
|
+
s.trip = { openT: f.t, bTok: 0, bUsd: 0, bOk: true, sTok: 0, sUsd: 0, sOk: true };
|
|
55
|
+
}
|
|
56
|
+
s.held += f.tokens;
|
|
57
|
+
if (f.priceUsd === null)
|
|
58
|
+
s.costOk = false;
|
|
59
|
+
else
|
|
60
|
+
s.cost += f.tokens * f.priceUsd;
|
|
61
|
+
if (s.trip) {
|
|
62
|
+
s.trip.bTok += f.tokens;
|
|
63
|
+
if (f.priceUsd === null)
|
|
64
|
+
s.trip.bOk = false;
|
|
65
|
+
else
|
|
66
|
+
s.trip.bUsd += f.tokens * f.priceUsd;
|
|
67
|
+
}
|
|
68
|
+
out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: before > EPS, closes: false });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const sold = Math.min(f.tokens, s.held);
|
|
72
|
+
const frac = s.held > EPS ? sold / s.held : 1;
|
|
73
|
+
s.cost -= s.cost * frac;
|
|
74
|
+
s.held = Math.max(0, s.held - f.tokens);
|
|
75
|
+
const closes = s.held <= f.tokens * 1e-9 || s.held <= EPS;
|
|
76
|
+
if (closes)
|
|
77
|
+
s.held = 0;
|
|
78
|
+
if (s.trip) {
|
|
79
|
+
s.trip.sTok += f.tokens;
|
|
80
|
+
if (f.priceUsd === null)
|
|
81
|
+
s.trip.sOk = false;
|
|
82
|
+
else
|
|
83
|
+
s.trip.sUsd += f.tokens * f.priceUsd;
|
|
84
|
+
}
|
|
85
|
+
out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: !closes, closes });
|
|
86
|
+
if (closes) {
|
|
87
|
+
if (s.trip)
|
|
88
|
+
trips.push({ source: f.source, openT: s.trip.openT, closeT: f.t, entryUsd: s.trip.bOk && s.trip.bTok > 0 ? s.trip.bUsd / s.trip.bTok : null, exitUsd: s.trip.sOk && s.trip.sTok > 0 ? s.trip.sUsd / s.trip.sTok : null });
|
|
89
|
+
s.trip = null;
|
|
90
|
+
s.cost = 0;
|
|
91
|
+
s.costOk = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const avg = (src) => { const s = st[src]; return s.held > EPS && s.costOk && s.cost > 0 ? s.cost / s.held : null; };
|
|
95
|
+
return { fills: out, avgEntry: { paper: avg('paper'), live: avg('live') }, trips };
|
|
96
|
+
}
|
|
97
|
+
/** Index of `t` in an ascending list, or -1 (binary search). */
|
|
98
|
+
function indexOf(times, t) {
|
|
99
|
+
let lo = 0, hi = times.length - 1;
|
|
100
|
+
while (lo <= hi) {
|
|
101
|
+
const mid = (lo + hi) >> 1;
|
|
102
|
+
const v = times[mid];
|
|
103
|
+
if (v === t)
|
|
104
|
+
return mid;
|
|
105
|
+
if (v < t)
|
|
106
|
+
lo = mid + 1;
|
|
107
|
+
else
|
|
108
|
+
hi = mid - 1;
|
|
109
|
+
}
|
|
110
|
+
return -1;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The markers for a window of bars: one per (bar, side, source), oldest first; buys before sells within a bar.
|
|
114
|
+
* Fills outside the window (or in a period with no bar) are counted in before / after / gap, never placed.
|
|
115
|
+
*/
|
|
116
|
+
export function chartMarkers(fills, win) {
|
|
117
|
+
const rp = replayFills(fills);
|
|
118
|
+
const times = win.barTimes;
|
|
119
|
+
const empty = () => ({ markers: [], before: 0, after: 0, gap: 0, avgEntry: rp.avgEntry, trips: [] });
|
|
120
|
+
if (!(win.periodMs > 0) || !times.length) {
|
|
121
|
+
const e = empty();
|
|
122
|
+
e.before = rp.fills.length;
|
|
123
|
+
return e;
|
|
124
|
+
}
|
|
125
|
+
const first = times[0], last = times[times.length - 1];
|
|
126
|
+
const res = empty();
|
|
127
|
+
const by = new Map();
|
|
128
|
+
for (const f of rp.fills) {
|
|
129
|
+
const b = bucketOf(f.t, win.periodMs);
|
|
130
|
+
if (b < first) {
|
|
131
|
+
res.before++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (b > last) {
|
|
135
|
+
res.after++;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const index = indexOf(times, b);
|
|
139
|
+
if (index < 0) {
|
|
140
|
+
res.gap++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const key = `${b}|${f.side}|${f.source}`;
|
|
144
|
+
let m = by.get(key);
|
|
145
|
+
if (!m) {
|
|
146
|
+
m = { side: f.side, source: f.source, bucket: b, index, t: f.t, tLast: f.t, priceUsd: null, tokens: 0, sol: 0, count: 0, partial: false, closes: false, pTok: 0, pUsd: 0, solOk: true };
|
|
147
|
+
by.set(key, m);
|
|
148
|
+
}
|
|
149
|
+
m.count++;
|
|
150
|
+
m.tLast = f.t;
|
|
151
|
+
m.tokens += f.tokens;
|
|
152
|
+
if (f.sol === null)
|
|
153
|
+
m.solOk = false;
|
|
154
|
+
else
|
|
155
|
+
m.sol = (m.sol ?? 0) + f.sol;
|
|
156
|
+
if (f.priceUsd !== null) {
|
|
157
|
+
m.pTok += f.tokens;
|
|
158
|
+
m.pUsd += f.tokens * f.priceUsd;
|
|
159
|
+
}
|
|
160
|
+
if (f.partial)
|
|
161
|
+
m.partial = true;
|
|
162
|
+
if (f.closes)
|
|
163
|
+
m.closes = true;
|
|
164
|
+
}
|
|
165
|
+
const order = (m) => (m.side === 'buy' ? 0 : 1) * 2 + (m.source === 'live' ? 1 : 0);
|
|
166
|
+
res.markers = [...by.values()].map(({ pTok, pUsd, solOk, ...m }) => ({ ...m, priceUsd: pTok > 0 ? pUsd / pTok : null, sol: solOk ? m.sol : null }))
|
|
167
|
+
.sort((a, b) => a.index - b.index || order(a) - order(b));
|
|
168
|
+
res.trips = rp.trips.filter((r) => bucketOf(r.closeT, win.periodMs) >= first && bucketOf(r.openT, win.periodMs) <= last);
|
|
169
|
+
return res;
|
|
170
|
+
}
|
|
171
|
+
/** The glyph for a marker: solid ▲ / ▼ for live fills, hollow △ / ▽ for paper. Reads without colour (NO_COLOR). */
|
|
172
|
+
export function markerGlyph(side, source) {
|
|
173
|
+
if (source === 'paper')
|
|
174
|
+
return side === 'buy' ? '△' : '▽';
|
|
175
|
+
return side === 'buy' ? '▲' : '▼';
|
|
176
|
+
}
|
|
177
|
+
/** The letter for a marker (always shown with the glyph, so BUY / SELL never depends on colour). */
|
|
178
|
+
export function markerLetter(side) { return side === 'buy' ? 'B' : 'S'; }
|
|
179
|
+
/** "△ ▽ paper ▲ ▼ live" — the legend for the sources actually on screen (empty when none). */
|
|
180
|
+
export function markerLegend(markers, avgShown = false) {
|
|
181
|
+
const has = (s) => markers.some((m) => m.source === s);
|
|
182
|
+
const parts = [];
|
|
183
|
+
if (has('paper'))
|
|
184
|
+
parts.push('△B ▽S paper');
|
|
185
|
+
if (has('live'))
|
|
186
|
+
parts.push('▲B ▼S live');
|
|
187
|
+
if (avgShown)
|
|
188
|
+
parts.push('┄ avg entry');
|
|
189
|
+
return parts.join(' ');
|
|
190
|
+
}
|
|
191
|
+
/** "+2 fills off-chart (1 earlier, 1 newer than the last candle)" — what the window omitted, or ''. */
|
|
192
|
+
export function omittedNote(s) {
|
|
193
|
+
const n = s.before + s.after + s.gap;
|
|
194
|
+
if (!n)
|
|
195
|
+
return '';
|
|
196
|
+
const why = [];
|
|
197
|
+
if (s.before)
|
|
198
|
+
why.push(`${s.before} earlier`);
|
|
199
|
+
if (s.after)
|
|
200
|
+
why.push(`${s.after} newer than the last candle`);
|
|
201
|
+
if (s.gap)
|
|
202
|
+
why.push(`${s.gap} in a period with no candle`);
|
|
203
|
+
return `+${n} fill${n === 1 ? '' : 's'} off-chart (${why.join(', ')})`;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The plot row of a price on a terminal chart laid out between `lo` and `hi` over `plotH` rows (row 0 = `hi`), the same
|
|
207
|
+
* mapping candleChart uses for candle bodies. Null when the price is outside the plotted range (omitted, not clamped).
|
|
208
|
+
*/
|
|
209
|
+
export function markerRow(priceUsd, lo, hi, plotH) {
|
|
210
|
+
if (!fin(priceUsd) || !(hi > lo) || plotH < 1)
|
|
211
|
+
return null;
|
|
212
|
+
const r = Math.floor(((hi - priceUsd) / (hi - lo)) * plotH);
|
|
213
|
+
return r >= 0 && r < plotH ? r : null;
|
|
214
|
+
}
|
|
215
|
+
/** "B 41.2k" / "S×2 0.0₄123": the compact terminal label (axis value = price × scale, formatted by the axis). */
|
|
216
|
+
export function markerLabel(m, fmt, scale = 1) {
|
|
217
|
+
const head = `${markerLetter(m.side)}${m.count > 1 ? `×${m.count}` : ''}`;
|
|
218
|
+
return m.priceUsd === null ? head : `${head} ${fmt(m.priceUsd * (scale > 0 ? scale : 1))}`;
|
|
219
|
+
}
|