dexterm-cli 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/agents/src/schema.js +22 -1
- package/dist/packages/agents/src/story.js +11 -2
- package/dist/packages/agents/src/tiers.js +27 -2
- package/dist/packages/agents/src/tools.js +30 -2
- package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -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/src/account.js +74 -0
- package/dist/packages/autotrade/src/backtest.js +357 -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 +93 -0
- package/dist/packages/autotrade/src/chat/connector.js +24 -0
- package/dist/packages/autotrade/src/chat/control.js +103 -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 +292 -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 +99 -0
- package/dist/packages/autotrade/src/chat/service.js +248 -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 +387 -0
- package/dist/packages/autotrade/src/execution-port.js +97 -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 +639 -0
- package/dist/packages/autotrade/src/index.js +53 -0
- package/dist/packages/autotrade/src/ledger.js +108 -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/paper-accounts.js +72 -0
- package/dist/packages/autotrade/src/replay.js +198 -0
- package/dist/packages/autotrade/src/runner-config.js +99 -0
- package/dist/packages/autotrade/src/runner.js +872 -0
- package/dist/packages/autotrade/src/safety.js +243 -0
- package/dist/packages/autotrade/src/session.js +269 -0
- package/dist/packages/autotrade/src/sessions.js +503 -0
- package/dist/packages/autotrade/src/store.js +250 -0
- package/dist/packages/autotrade/src/strategy.js +256 -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 +82 -15
- package/dist/packages/cli/src/analyst/codex.js +14 -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 +44 -8
- package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
- package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
- package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
- package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
- package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/run.js +413 -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 +168 -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 +288 -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 +158 -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-keys.js +169 -0
- package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
- package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
- package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
- package/dist/packages/cli/src/screens/autotrade-worker.js +89 -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 +451 -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-parallel.md +42 -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 +217 -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 +49 -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 +37 -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 +35 -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-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 +43 -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-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-live-trading-changed-in-0-1-7.md +26 -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 +33 -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-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 +38 -0
- package/dist/packages/mcp/src/app-help.js +1 -1
- package/dist/packages/mcp/src/autotrade-tools.js +944 -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 +346 -0
- package/dist/packages/mcp/src/paper-agent.js +2 -0
- package/dist/packages/mcp/src/server.js +127 -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 +21 -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,288 @@
|
|
|
1
|
+
// `dexterm ledger <sub>` (task-1530): the unified TRADE LEDGER. One row per round trip from every place dexterm books a
|
|
2
|
+
// trade: the paper ticket (paper wallet), the agent paper desk (its journal), autotrade runs (their hash-chained run
|
|
3
|
+
// ledgers) and the live signer (its trade ledger). Each row carries the strategy id + version + hash, the declared exit
|
|
4
|
+
// profile and entry reason (and where each came from), the exit reason, size and % of book, entry / exit price AND
|
|
5
|
+
// market cap, fees, impact, hold, rug flag, wallet mode and the model sessions that made it.
|
|
6
|
+
//
|
|
7
|
+
// Reads never write: every command folds $DEXTERM_HOME/ledger/trades.jsonl plus what the sources hold that the file
|
|
8
|
+
// does not yet (shown as "pending"). `sync` appends those to the file (append-only, sha256-chained; a broken file is
|
|
9
|
+
// refused, never rewritten) and `verify` recomputes the chain. No network, no model, no key, no signer.
|
|
10
|
+
import { writeFileSync } from 'node:fs';
|
|
11
|
+
export const LEDGER_HELP = `dexterm ledger: the unified trade ledger (paper, agent paper desk, autotrade runs, live). Read-only unless sync.
|
|
12
|
+
|
|
13
|
+
dexterm ledger trades [filters] [--limit N] [--json] one row per round trip, newest first
|
|
14
|
+
dexterm ledger positions [filters] [--json] the open trades (cost, % of book, held, profile)
|
|
15
|
+
dexterm ledger summary [filters] [--json] win rate (95% interval), P/L, drawdown, rug rate, by profile / exit / strategy / source / mode
|
|
16
|
+
dexterm ledger equity [filters] [--json] the realized equity curve and its max drawdown
|
|
17
|
+
dexterm ledger compare [filters] [--json] one row per strategy version: the forward-test table
|
|
18
|
+
dexterm ledger export csv|json|parquet-lite [filters] [--out FILE] (or --format csv|json|parquet-lite)
|
|
19
|
+
dexterm ledger card <tradeId> [--square] [--price] [--exact] [--sol-usd N] a PnL card (market cap at entry / exit)
|
|
20
|
+
dexterm ledger sync [--json] append what the sources hold to the ledger file
|
|
21
|
+
dexterm ledger verify [--json] recompute the ledger file's sha256 chain
|
|
22
|
+
|
|
23
|
+
filters: --source paper|desk|autotrade|live --mode paper|devnet|live --strategy ID[@VERSION|@HASH] --profile P
|
|
24
|
+
--exit REASON --mint MINT --run RUN_ID --account ID --days N --status open|closed --wallet-db PATH
|
|
25
|
+
|
|
26
|
+
The file is $DEXTERM_HOME/ledger/trades.jsonl (default ~/.dexterm). PAPER rows are simulated; filter by --mode
|
|
27
|
+
before comparing results. Not financial advice.`;
|
|
28
|
+
const VALUE_FLAGS = new Set(['source', 'mode', 'strategy', 'profile', 'exit', 'mint', 'run', 'account', 'days', 'status', 'limit', 'out', 'sol-usd', 'wallet-db', 'format']);
|
|
29
|
+
function parse(argv) {
|
|
30
|
+
const o = { _: [] };
|
|
31
|
+
for (let i = 0; i < argv.length; i++) {
|
|
32
|
+
const a = argv[i];
|
|
33
|
+
if (!a.startsWith('--')) {
|
|
34
|
+
o._.push(a);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const eq = a.indexOf('=');
|
|
38
|
+
const k = eq > 0 ? a.slice(2, eq) : a.slice(2);
|
|
39
|
+
if (VALUE_FLAGS.has(k)) {
|
|
40
|
+
const v = eq > 0 ? a.slice(eq + 1) : argv[++i];
|
|
41
|
+
if (v === undefined || v === '')
|
|
42
|
+
return `--${k} needs a value`;
|
|
43
|
+
o[k] = v;
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
o[k] = true;
|
|
47
|
+
}
|
|
48
|
+
return o;
|
|
49
|
+
}
|
|
50
|
+
/** CLI flags → the MCP tool args shape, validated by the same filterFromArgs the ledger tools use. */
|
|
51
|
+
async function filterOf(o, now) {
|
|
52
|
+
const { filterFromArgs } = await import('../../mcp/src/index.js');
|
|
53
|
+
const a = {};
|
|
54
|
+
for (const [flag, key] of [['source', 'source'], ['mode', 'mode'], ['strategy', 'strategy'], ['profile', 'profile'], ['exit', 'exit_reason'], ['mint', 'mint'], ['run', 'run_id'], ['account', 'account'], ['status', 'status']]) {
|
|
55
|
+
if (typeof o[flag] === 'string')
|
|
56
|
+
a[key] = o[flag];
|
|
57
|
+
}
|
|
58
|
+
if (typeof o.days === 'string')
|
|
59
|
+
a.days = Number(o.days);
|
|
60
|
+
return filterFromArgs(a, now);
|
|
61
|
+
}
|
|
62
|
+
async function loadView(io, o, now) {
|
|
63
|
+
const at = await import('../../autotrade/src/index.js');
|
|
64
|
+
return at.loadLedgerView({ env: io.env, now, ...(typeof o['wallet-db'] === 'string' ? { walletDb: o['wallet-db'] } : {}) });
|
|
65
|
+
}
|
|
66
|
+
// ───────────────────────────── formatting ─────────────────────────────
|
|
67
|
+
const pad = (s, n) => (s.length >= n ? s.slice(0, n) : s + ' '.repeat(n - s.length));
|
|
68
|
+
const lpad = (s, n) => (s.length >= n ? s : ' '.repeat(n - s.length) + s);
|
|
69
|
+
const num = (x, d = 2) => (x === null || x === undefined || !Number.isFinite(x) ? '—' : x.toFixed(d));
|
|
70
|
+
const pct = (x) => (x === null || x === undefined || !Number.isFinite(x) ? '—' : `${x >= 0 ? '+' : ''}${x.toFixed(1)}%`);
|
|
71
|
+
const frac = (x) => (x === null || x === undefined ? '—' : `${(x * 100).toFixed(0)}%`);
|
|
72
|
+
export function fmtCap(v) {
|
|
73
|
+
if (v === null || v === undefined || !Number.isFinite(v))
|
|
74
|
+
return '—';
|
|
75
|
+
if (v >= 1e9)
|
|
76
|
+
return `$${(v / 1e9).toFixed(2)}B`;
|
|
77
|
+
if (v >= 1e6)
|
|
78
|
+
return `$${(v / 1e6).toFixed(2)}M`;
|
|
79
|
+
if (v >= 1e3)
|
|
80
|
+
return `$${(v / 1e3).toFixed(1)}K`;
|
|
81
|
+
return `$${v.toFixed(0)}`;
|
|
82
|
+
}
|
|
83
|
+
const hold = (sec) => (sec < 90 ? `${Math.round(sec)}s` : sec < 5400 ? `${Math.round(sec / 60)}m` : sec < 172800 ? `${(sec / 3600).toFixed(1)}h` : `${(sec / 86400).toFixed(1)}d`);
|
|
84
|
+
const when = (t) => new Date(t).toISOString().slice(0, 16).replace('T', ' ');
|
|
85
|
+
const stratOf = (t) => (t.strategy ? `${t.strategy.id}@${t.strategy.version ?? (t.strategy.hash ?? '').slice(0, 8)}` : 'manual');
|
|
86
|
+
const tagOf = (id, source) => (id ? (source === 'structured' || source === 'rule' ? id : `${id}*`) : '—');
|
|
87
|
+
function banner(v, rows) {
|
|
88
|
+
const out = [];
|
|
89
|
+
const f = v.file;
|
|
90
|
+
out.push(f.exists ? (f.check.ok ? `ledger ${v.path}: ${f.events.length} events, chain verified` : `ledger ${v.path}: FAILS its check at line ${f.check.at}: ${f.check.error}`) : `ledger ${v.path}: not written yet (\`dexterm ledger sync\` writes it)`);
|
|
91
|
+
if (v.pending.length)
|
|
92
|
+
out.push(` + ${v.pending.length} events from the sources not in the file yet (shown here; \`dexterm ledger sync\` appends them)`);
|
|
93
|
+
for (const s of v.sources)
|
|
94
|
+
if (s.problem)
|
|
95
|
+
out.push(` ! ${s.problem}`);
|
|
96
|
+
const modes = [...new Set(rows.map((t) => t.walletMode))];
|
|
97
|
+
if (modes.length > 1)
|
|
98
|
+
out.push(' PAPER, DEVNET and LIVE trades are mixed here: use --mode before comparing results.');
|
|
99
|
+
else if (modes[0] === 'paper')
|
|
100
|
+
out.push(' PAPER (simulated) trades.');
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
function tradesTable(rows) {
|
|
104
|
+
const head = `${pad('opened', 16)} ${pad('symbol', 8)} ${pad('src', 9)} ${pad('mode', 6)} ${pad('strategy', 18)} ${pad('profile', 10)} ${pad('exit', 14)} ${lpad('size', 8)} ${lpad('%book', 6)} ${lpad('entry mc', 9)} ${lpad('exit mc', 9)} ${lpad('P/L', 8)} ${lpad('hold', 6)} ${lpad('MFE', 7)} ${lpad('MAE', 7)} id`;
|
|
105
|
+
const out = [head];
|
|
106
|
+
for (const t of rows) {
|
|
107
|
+
const size = t.sizeSol !== null ? `${num(t.sizeSol, 3)}◎` : t.sizeUsd !== null ? `$${num(t.sizeUsd, 0)}` : '—';
|
|
108
|
+
out.push([
|
|
109
|
+
pad(when(t.openedAt), 16), ' ', pad(t.symbol, 8), ' ', pad(t.source, 9), ' ', pad(t.walletMode, 6), ' ', pad(stratOf(t), 18), ' ',
|
|
110
|
+
pad(tagOf(t.profile.id, t.profile.source), 10), ' ', pad(t.status === 'open' ? 'open' : `${t.exitReason.code ?? 'untagged'}${t.rug ? ' RUG' : ''}`, 14), ' ',
|
|
111
|
+
lpad(size, 8), ' ', lpad(t.pctOfBook === null ? '—' : `${t.pctOfBook.toFixed(0)}%`, 6), ' ', lpad(fmtCap(t.entryMcapUsd), 9), ' ', lpad(fmtCap(t.exitMcapUsd), 9), ' ',
|
|
112
|
+
lpad(t.status === 'open' ? '—' : pct(t.pnlPct), 8), ' ', lpad(hold(t.holdSec), 6), ' ', lpad(pct(t.mfePct), 7), ' ', lpad(pct(t.maePct), 7), ' ', t.tradeId,
|
|
113
|
+
].join(''));
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
// ───────────────────────────── subcommands ─────────────────────────────
|
|
118
|
+
export async function ledgerCommand(argv, io) {
|
|
119
|
+
const sub = argv[0];
|
|
120
|
+
if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
|
|
121
|
+
io.stdout.write(`${LEDGER_HELP}\n`);
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
const o = parse(argv.slice(1));
|
|
125
|
+
if (typeof o === 'string') {
|
|
126
|
+
io.stderr.write(`dexterm ledger: ${o}\n`);
|
|
127
|
+
return 2;
|
|
128
|
+
}
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
const json = o.json === true;
|
|
131
|
+
const out = (s) => io.stdout.write(s.endsWith('\n') ? s : `${s}\n`);
|
|
132
|
+
try {
|
|
133
|
+
if (sub === 'verify') {
|
|
134
|
+
const { readTradeLedger, tradeLedgerPath } = await import('../../data/src/trade/ledger/index.js');
|
|
135
|
+
const path = tradeLedgerPath(io.env);
|
|
136
|
+
const r = readTradeLedger(path);
|
|
137
|
+
if (json)
|
|
138
|
+
out(JSON.stringify({ path, exists: r.exists, ...r.check }));
|
|
139
|
+
else if (!r.exists)
|
|
140
|
+
out(`${path}: not written yet (nothing to verify)`);
|
|
141
|
+
else
|
|
142
|
+
out(r.check.ok ? `${path}: ${r.check.events} events, chain verified, head ${r.check.head.slice(0, 16)}` : `${path}: FAILS at line ${r.check.at}: ${r.check.error}`);
|
|
143
|
+
return r.exists && !r.check.ok ? 1 : 0;
|
|
144
|
+
}
|
|
145
|
+
if (sub === 'sync') {
|
|
146
|
+
const at = await import('../../autotrade/src/index.js');
|
|
147
|
+
const r = at.syncLedger({ env: io.env, now, ...(typeof o['wallet-db'] === 'string' ? { walletDb: o['wallet-db'] } : {}) });
|
|
148
|
+
const problems = r.view.sources.filter((s) => s.problem).map((s) => s.problem);
|
|
149
|
+
if (json)
|
|
150
|
+
out(JSON.stringify({ path: r.view.path, added: r.added, skipped: r.skipped, events: r.events, head: r.head, sources: r.view.sources }));
|
|
151
|
+
else {
|
|
152
|
+
out(`${r.view.path}: +${r.added} events (${r.skipped} already there), ${r.events} total, head ${r.head.slice(0, 16)}`);
|
|
153
|
+
for (const s of r.view.sources)
|
|
154
|
+
out(` ${pad(s.source, 9)} ${s.label}: ${s.events} source events, ${s.pending} new${s.note ? ` · ${s.note}` : ''}`);
|
|
155
|
+
for (const p of problems)
|
|
156
|
+
out(` ! ${p}`);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
const fr = await filterOf(o, now);
|
|
161
|
+
if (!fr.ok) {
|
|
162
|
+
io.stderr.write(`dexterm ledger: ${fr.error}\n`);
|
|
163
|
+
return 2;
|
|
164
|
+
}
|
|
165
|
+
const { filterTrades, summarize, equityCurve, compareStrategies, exportTrades, EXPORT_FORMATS } = await import('../../data/src/trade/ledger/index.js');
|
|
166
|
+
const v = await loadView(io, o, now);
|
|
167
|
+
const filter = sub === 'positions' ? { ...fr.filter, status: 'open' } : fr.filter;
|
|
168
|
+
const rows = filterTrades(v.trades, filter).sort((a, b) => b.openedAt - a.openedAt);
|
|
169
|
+
switch (sub) {
|
|
170
|
+
case 'trades':
|
|
171
|
+
case 'positions': {
|
|
172
|
+
const limit = typeof o.limit === 'string' ? Math.max(1, Math.min(10_000, Number.parseInt(o.limit, 10) || 50)) : sub === 'positions' ? 10_000 : 50;
|
|
173
|
+
const shown = rows.slice(0, limit);
|
|
174
|
+
if (json) {
|
|
175
|
+
out(JSON.stringify({ count: rows.length, trades: shown }));
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
for (const l of banner(v, rows))
|
|
179
|
+
out(l);
|
|
180
|
+
if (!rows.length) {
|
|
181
|
+
out(sub === 'positions' ? 'no open trades' : 'no trades match');
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
for (const l of tradesTable(shown))
|
|
185
|
+
out(l);
|
|
186
|
+
out(`${shown.length} of ${rows.length} shown · profile/exit with * = not declared structurally (tag line, late, default)`);
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
case 'summary': {
|
|
190
|
+
const s = summarize(rows);
|
|
191
|
+
if (json) {
|
|
192
|
+
out(JSON.stringify(s));
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
for (const l of banner(v, rows))
|
|
196
|
+
out(l);
|
|
197
|
+
const u = s.unit === 'SOL' ? '◎' : '$';
|
|
198
|
+
out(`trades ${s.trades} closed · ${s.open} open · wins ${s.wins} · losses ${s.losses}`);
|
|
199
|
+
out(`win rate ${s.winRate ? `${frac(s.winRate.value)} (95% ${frac(s.winRate.lo)}–${frac(s.winRate.hi)})` : '—'} · P/L ${num(s.pnl, s.unit === 'SOL' ? 3 : 2)}${u} · avg ${pct(s.avgPnlPct)} · median ${pct(s.medianPnlPct)} · best ${pct(s.bestPct)} · worst ${pct(s.worstPct)}`);
|
|
200
|
+
out(`profit factor ${num(s.profitFactor)} · max drawdown ${num(s.maxDrawdown, 3)}${u}${s.maxDrawdownPct !== null ? ` (${num(s.maxDrawdownPct, 1)}%)` : ''} · rugs ${s.rugs} (${frac(s.rugRate)}) · avg hold ${s.avgHoldSec === null ? '—' : hold(s.avgHoldSec)} · MFE ${pct(s.avgMfePct)} · MAE ${pct(s.avgMaePct)}`);
|
|
201
|
+
out(`fees ${num(s.feesSol, 4)}◎ · model cost $${num(s.modelCostUsd)} · declared profile ${frac(s.tagCoverage.profileDeclared)} · declared exit ${frac(s.tagCoverage.exitDeclared)}`);
|
|
202
|
+
for (const [title, gs] of [['by profile', s.byProfile], ['by exit reason', s.byExitReason], ['by strategy', s.byStrategy], ['by source', s.bySource], ['by mode', s.byMode]]) {
|
|
203
|
+
if (!gs.length)
|
|
204
|
+
continue;
|
|
205
|
+
out(`\n${title}`);
|
|
206
|
+
for (const g of gs)
|
|
207
|
+
out(` ${pad(g.key, 22)} ${lpad(String(g.trades), 4)} closed ${lpad(String(g.open), 3)} open win ${lpad(frac(g.winRate), 4)} P/L ${lpad(num(g.pnl, s.unit === 'SOL' ? 3 : 2), 9)}${u} avg ${lpad(pct(g.avgPnlPct), 7)} rugs ${g.rugs}`);
|
|
208
|
+
}
|
|
209
|
+
if (s.trades < 30)
|
|
210
|
+
out(`\n${s.trades} closed trades: a small sample says little (see the interval).`);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
case 'equity': {
|
|
214
|
+
const c = equityCurve(rows);
|
|
215
|
+
if (json) {
|
|
216
|
+
out(JSON.stringify(c));
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
for (const l of banner(v, rows))
|
|
220
|
+
out(l);
|
|
221
|
+
const u = c.unit === 'SOL' ? '◎' : '$';
|
|
222
|
+
out(`realized equity (${c.unit}), ${c.points.length} closes · final ${num(c.final, 3)}${u} · max drawdown ${num(c.maxDrawdown, 3)}${u}${c.maxDrawdownPct !== null ? ` (${num(c.maxDrawdownPct, 1)}%)` : ''}`);
|
|
223
|
+
const step = Math.max(1, Math.ceil(c.points.length / 40));
|
|
224
|
+
for (const [i, p] of c.points.entries())
|
|
225
|
+
if (i % step === 0 || i === c.points.length - 1)
|
|
226
|
+
out(` ${when(p.t)} ${pad(p.symbol, 8)} ${lpad(num(p.pnl, 3), 9)} cum ${lpad(num(p.cum, 3), 9)} dd ${lpad(num(p.drawdown, 3), 8)}`);
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
case 'compare': {
|
|
230
|
+
const c = compareStrategies(rows);
|
|
231
|
+
if (json) {
|
|
232
|
+
out(JSON.stringify(c));
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
for (const l of banner(v, rows))
|
|
236
|
+
out(l);
|
|
237
|
+
const u = c.unit === 'SOL' ? '◎' : '$';
|
|
238
|
+
out(`${pad('strategy', 24)} ${pad('modes', 12)} ${lpad('trades', 6)} ${lpad('open', 4)} ${lpad('win', 4)} ${pad(' (95%)', 10)} ${lpad(`P/L ${u}`, 9)} ${lpad('avg', 7)} ${lpad('maxDD', 8)} ${lpad('rugs', 5)} ${lpad('hold', 6)} ${lpad('MFE', 7)} ${lpad('MAE', 7)} ${lpad('model $', 8)}`);
|
|
239
|
+
for (const r of c.rows)
|
|
240
|
+
out(`${pad(r.key, 24)} ${pad(r.modes.join(','), 12)} ${lpad(String(r.trades), 6)} ${lpad(String(r.open), 4)} ${lpad(frac(r.winRate?.value), 4)} ${pad(r.winRate ? ` ${frac(r.winRate.lo)}–${frac(r.winRate.hi)}` : '', 10)} ${lpad(num(r.pnl, 3), 9)} ${lpad(pct(r.avgPnlPct), 7)} ${lpad(num(r.maxDrawdown, 3), 8)} ${lpad(frac(r.rugRate), 5)} ${lpad(r.avgHoldSec === null ? '—' : hold(r.avgHoldSec), 6)} ${lpad(pct(r.avgMfePct), 7)} ${lpad(pct(r.avgMaePct), 7)} ${lpad(num(r.modelCostUsd), 8)}`);
|
|
241
|
+
return 0;
|
|
242
|
+
}
|
|
243
|
+
case 'export': {
|
|
244
|
+
const fmt = o._[0] ?? (typeof o.format === 'string' ? o.format : undefined);
|
|
245
|
+
if (!fmt || !EXPORT_FORMATS.includes(fmt)) {
|
|
246
|
+
io.stderr.write(`dexterm ledger export: the format is ${EXPORT_FORMATS.join(' | ')}\n`);
|
|
247
|
+
return 2;
|
|
248
|
+
}
|
|
249
|
+
const text = exportTrades(rows, fmt, { exportedAt: now, ledgerHead: v.file.check.ok ? v.file.check.head : null, unit: summarize(rows).unit, filters: { ...filter } });
|
|
250
|
+
if (typeof o.out === 'string') {
|
|
251
|
+
writeFileSync(o.out, text, { mode: 0o600 });
|
|
252
|
+
io.stderr.write(`wrote ${rows.length} trades to ${o.out}\n`);
|
|
253
|
+
}
|
|
254
|
+
else
|
|
255
|
+
io.stdout.write(text);
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
case 'card': {
|
|
259
|
+
const id = o._[0];
|
|
260
|
+
const t = id ? v.trades.find((x) => x.tradeId === id) : undefined;
|
|
261
|
+
if (!t) {
|
|
262
|
+
io.stderr.write(`dexterm ledger card: ${id ? `no trade ${id}` : 'pass a tradeId (from `dexterm ledger trades`)'}\n`);
|
|
263
|
+
return 2;
|
|
264
|
+
}
|
|
265
|
+
const card = await import("./pnl-card.js");
|
|
266
|
+
const solUsd = typeof o['sol-usd'] === 'string' ? Number(o['sol-usd']) : null;
|
|
267
|
+
if (solUsd !== null && !(solUsd > 0)) {
|
|
268
|
+
io.stderr.write('dexterm ledger card: --sol-usd is a positive number\n');
|
|
269
|
+
return 2;
|
|
270
|
+
}
|
|
271
|
+
const input = card.ledgerCardInput(t, { solUsd: solUsd ? { usd: solUsd, source: '--sol-usd' } : null });
|
|
272
|
+
const s = card.computeCard(input, now);
|
|
273
|
+
const opts = { aspect: o.square === true ? 'square' : 'wide', exactSize: o.exact === true, ...(o.price === true ? { basis: 'price' } : {}) };
|
|
274
|
+
const files = card.saveCard(s, opts, { env: io.env, now });
|
|
275
|
+
out(card.cardText(s, opts));
|
|
276
|
+
out(`saved ${files.wide}\n ${files.square}\n ${files.txt}`);
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
default:
|
|
280
|
+
io.stderr.write(`dexterm ledger: unknown subcommand ${JSON.stringify(sub)}\n\n${LEDGER_HELP}\n`);
|
|
281
|
+
return 2;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
io.stderr.write(`dexterm ledger: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const abs = (n) => (n < 0n ? -n : n);
|
|
2
|
+
const fin = (n) => typeof n === 'number' && Number.isFinite(n) && n > 0;
|
|
3
|
+
/** A paper ledger row → a chart fill (its own USD price when the paper engine recorded one). */
|
|
4
|
+
export function paperChartFill(r) {
|
|
5
|
+
return {
|
|
6
|
+
t: r.t, side: r.side, source: 'paper',
|
|
7
|
+
priceUsd: fin(r.priceUsd) ? r.priceUsd : null,
|
|
8
|
+
tokens: Number(abs(r.tokenDelta)) / 10 ** r.decimals,
|
|
9
|
+
sol: Number(abs(r.grossLamports)) / 1e9,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** How many paper rows are scanned for one mint (the wallet has no per-mint read; older history drops off first). */
|
|
13
|
+
export const CHART_FILLS_PAPER_SCAN = 2000;
|
|
14
|
+
/** How long one mint's fills are reused (the render loop asks every frame; the popup up to ~20×/s). */
|
|
15
|
+
export const CHART_FILLS_MEMO_MS = 1000;
|
|
16
|
+
/** Every paper fill for one mint, oldest first. A ledger that cannot be read contributes nothing. */
|
|
17
|
+
export function chartFillsFor(mint, rd) {
|
|
18
|
+
const out = [];
|
|
19
|
+
try {
|
|
20
|
+
for (const r of rd.paper?.(CHART_FILLS_PAPER_SCAN) ?? [])
|
|
21
|
+
if (r.mint === mint)
|
|
22
|
+
out.push(paperChartFill(r));
|
|
23
|
+
}
|
|
24
|
+
catch { /* unreadable paper ledger: no paper marks */ }
|
|
25
|
+
return out.sort((a, b) => a.t - b.t);
|
|
26
|
+
}
|
|
27
|
+
/** chartFillsFor memoised per mint for CHART_FILLS_MEMO_MS (a fill shows on the chart within a second). */
|
|
28
|
+
export class ChartFills {
|
|
29
|
+
memo = new Map();
|
|
30
|
+
rd;
|
|
31
|
+
now;
|
|
32
|
+
constructor(rd, now = Date.now) { this.rd = rd; this.now = now; }
|
|
33
|
+
for(mint) {
|
|
34
|
+
const now = this.now();
|
|
35
|
+
const m = this.memo.get(mint);
|
|
36
|
+
if (m && now - m.at >= 0 && now - m.at < CHART_FILLS_MEMO_MS)
|
|
37
|
+
return m.v;
|
|
38
|
+
const v = chartFillsFor(mint, this.rd);
|
|
39
|
+
if (this.memo.size > 32)
|
|
40
|
+
this.memo.clear();
|
|
41
|
+
this.memo.set(mint, { at: now, v });
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
/** Forget every memo (after a fill this run booked). */
|
|
45
|
+
invalidate() { this.memo.clear(); }
|
|
46
|
+
}
|
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
// run, created over the screener feed's data layer so every screen shares the cache, limiter, health
|
|
3
3
|
// registry and (when the Helius key is configured) the credit meter. Screens stay pure renderers: the
|
|
4
4
|
// context owns the loaders and tells the UI loop when to redraw.
|
|
5
|
-
import { PumpPortalClient, PUMPPORTAL_WS, WatchlistStore, PaperWallet, PaperExecutor, FastLane, WalletLinkStore, WalletBridge, KillSwitch,
|
|
5
|
+
import { PumpPortalClient, PUMPPORTAL_WS, WatchlistStore, PaperWallet, PaperExecutor, FastLane, WalletLinkStore, WalletBridge, KillSwitch, defaultLinkPath, defaultPanicPath, shortPubkey, } from '../../../data/src/index.js';
|
|
6
6
|
import { SocialFeed, KolStore, socialDeps, chainSignalsFromToken, chainSignalsFromIntel, chainSignalToRaw, RSS_FEEDS, JUPITER_HOSTS } from '../../../data/src/index.js';
|
|
7
7
|
import { XSource, XApi, XOAuth, XTokenStore, XSpendMeter } from '../../../data/src/index.js';
|
|
8
8
|
import { TokenLive, INTERVALS } from "./token.js";
|
|
9
9
|
import { NewPairsLive } from "./newpairs.js";
|
|
10
10
|
import { WatchlistLive } from "./watchlist.js";
|
|
11
|
-
import { TradeTicketLive, PositionsLive,
|
|
11
|
+
import { TradeTicketLive, PositionsLive, POSITIONS_REFRESH_MS } from "./trade.js";
|
|
12
12
|
import { IntelLive } from "./intel.js";
|
|
13
13
|
import { LiveSignalsLive } from "./signals.js";
|
|
14
14
|
import { CopyLive } from "./copy.js";
|
|
15
|
+
import { ChartFills } from "./chart-fills.js";
|
|
15
16
|
export { TokenLive, INTERVALS, panelCaption, emptyView } from "./token.js";
|
|
16
17
|
export { NewPairsLive, latencyStats, percentile, RECENT_POLL_MS, NEW_TOKENS_POLL_MS, ENRICH_POLL_MS } from "./newpairs.js";
|
|
17
18
|
export { WatchlistLive, WATCH_REFRESH_MS } from "./watchlist.js";
|
|
18
|
-
export { TradeTicketLive, PositionsLive,
|
|
19
|
+
export { TradeTicketLive, PositionsLive, POSITIONS_REFRESH_MS } from "./trade.js";
|
|
19
20
|
export { IntelLive, popupBubbleVM, unavailableText, INTEL_AUTOSCAN_DELAY_MS } from "./intel.js";
|
|
20
21
|
export { LiveSignalsLive, buildObservation, SIGNALS_LOG_MAX } from "./signals.js";
|
|
21
22
|
export { renderTokenLive } from "./screens/token-live.js";
|
|
@@ -37,17 +38,14 @@ export class LiveContext {
|
|
|
37
38
|
watch;
|
|
38
39
|
/** PAPER trading (task-1310): the simulated wallet, its executor and the two screens' bindings. */
|
|
39
40
|
wallet;
|
|
40
|
-
/** The linked
|
|
41
|
+
/** The linked wallet's PUBLIC key, or null. Shown in the status bar (display only; nothing signs with it). */
|
|
41
42
|
linkedWallet = null;
|
|
42
43
|
executor;
|
|
43
44
|
positions;
|
|
44
|
-
/**
|
|
45
|
-
livePositions;
|
|
46
|
-
/** Paper + live behind the task-1323 PositionSource (token screen row, popup chart). */
|
|
45
|
+
/** The paper ledger behind the task-1323 PositionSource (token screen row, popup chart). */
|
|
47
46
|
positionSource;
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
signerFailed = null;
|
|
47
|
+
/** task-1483: every paper fill per mint (memoised 1 s) for the chart BUY/SELL markers. */
|
|
48
|
+
chartFills;
|
|
51
49
|
trade;
|
|
52
50
|
linkStore;
|
|
53
51
|
ticket = null;
|
|
@@ -68,8 +66,8 @@ export class LiveContext {
|
|
|
68
66
|
/** task-1360 copy trading Tier 1: the follow store + wallet tracker (alerts on a followed wallet's buys/sells). */
|
|
69
67
|
copy;
|
|
70
68
|
/**
|
|
71
|
-
* task-1340 trade moments: set by the UI loop; the ticket calls it once per SETTLED outcome (paper fill
|
|
72
|
-
*
|
|
69
|
+
* task-1340 trade moments: set by the UI loop; the ticket calls it once per SETTLED outcome (paper fill or
|
|
70
|
+
* failure) and never for a quote or an in-flight fill. Effects + sound hang off this only.
|
|
73
71
|
*/
|
|
74
72
|
onSettled = () => { };
|
|
75
73
|
/**
|
|
@@ -173,12 +171,8 @@ export class LiveContext {
|
|
|
173
171
|
this.linkedWallet = this.linkStore.get();
|
|
174
172
|
this.executor = new PaperExecutor(this.wallet, { clock: dl.clock });
|
|
175
173
|
this.positions = new PositionsLive(dl, this.wallet, { now: this.now, onChange: () => emit('positions') });
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.livePositions = this.trade
|
|
179
|
-
? new LivePositions(this.trade.paths?.liveLedger ?? defaultLiveLedgerPath(), { now: this.now, mark: (m) => this.positions.markOf(m), solUsd: () => this.positions.solUsd() ?? this.solUsd })
|
|
180
|
-
: null;
|
|
181
|
-
this.positionSource = new CombinedPositions(this.positions, this.livePositions);
|
|
174
|
+
this.positionSource = this.positions;
|
|
175
|
+
this.chartFills = new ChartFills({ paper: (n) => this.wallet.trades(n) }, this.now);
|
|
182
176
|
this.intel = new IntelLive(dl, { now: this.now, onChange: () => { this.syncIntelRows(); this.observeSignals(); emit('intel'); }, isWatched: (mint) => this.watch.has(mint) });
|
|
183
177
|
// task-1404: the live holder/rug signals engine. A fired signal sets a transient notice and redraws; the
|
|
184
178
|
// token screen shows the recent signals, and the rules engine reads `signals.fields()` so each is an alert trigger.
|
|
@@ -186,64 +180,33 @@ export class LiveContext {
|
|
|
186
180
|
// task-1360: followed wallets are polled only in a real live run (replay never spends credits); the store is always readable.
|
|
187
181
|
this.copy = new CopyLive(opts.mode === 'live' ? dl : null, { onChange: (r) => emit(r), solUsd: () => this.solUsd, env: opts.copy?.env, followsPath: opts.copy?.followsPath, history: opts.copy?.history, pollMs: opts.copy?.pollMs });
|
|
188
182
|
}
|
|
189
|
-
/** Open a trade ticket
|
|
190
|
-
openTicket(seed, side
|
|
191
|
-
const live = this.trade ? {
|
|
192
|
-
signer: () => this.ensureSigner(),
|
|
193
|
-
linked: () => this.linkedWallet,
|
|
194
|
-
onFill: () => { this.livePositions?.invalidate(); this.onChange('positions'); },
|
|
195
|
-
} : undefined;
|
|
183
|
+
/** Open a PAPER trade ticket. Replaces any open ticket. */
|
|
184
|
+
openTicket(seed, side) {
|
|
196
185
|
const t = new TradeTicketLive(this.dl, this.wallet, this.executor, { mint: seed.mint, symbol: seed.symbol, decimals: seed.decimals, side, mode: 'paper' }, {
|
|
197
|
-
now: this.now, onChange: () => this.onChange('ticket'),
|
|
198
|
-
livePositionRaw: (mint) => { const p = this.livePositions?.positions().find((x) => x.mint === mint); return p?.tokensRaw ?? 0n; },
|
|
186
|
+
now: this.now, onChange: () => this.onChange('ticket'), onSettled: (tx) => this.onSettled(tx),
|
|
199
187
|
});
|
|
200
188
|
this.ticket = t;
|
|
201
|
-
if (mode === 'live')
|
|
202
|
-
t.setMode('live');
|
|
203
189
|
return t;
|
|
204
190
|
}
|
|
205
|
-
/**
|
|
206
|
-
* The signer process (task-1328), started on first use with the run's caps argv. A signer that
|
|
207
|
-
* failed to start or died is never respawned: LIVE stays off until dexterm restarts (fail closed).
|
|
208
|
-
*/
|
|
209
|
-
ensureSigner() {
|
|
210
|
-
if (!this.trade || this.signerFailed)
|
|
211
|
-
return this.signer;
|
|
212
|
-
if (!this.signer) {
|
|
213
|
-
try {
|
|
214
|
-
this.signer = this.trade.createSigner ? this.trade.createSigner() : new SignerClient({ argv: this.trade.signerArgv ?? [], log: this.onLog ? (l) => this.onLog?.(` signer ${l}\n`) : undefined });
|
|
215
|
-
}
|
|
216
|
-
catch (e) {
|
|
217
|
-
this.signerFailed = e instanceof Error ? e.message : String(e);
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return this.signer;
|
|
222
|
-
}
|
|
223
191
|
/** `/wallet …` typed in the app (task-1328). Returns the line to show. */
|
|
224
192
|
async walletCommand(cmd) {
|
|
225
193
|
const paths = this.trade?.paths ?? {};
|
|
226
194
|
switch (cmd) {
|
|
227
195
|
case 'status': {
|
|
228
|
-
const linked = this.linkedWallet ? `linked: ${shortPubkey(this.linkedWallet.publicKey)}` : 'no wallet linked (/wallet link)';
|
|
196
|
+
const linked = this.linkedWallet ? `linked: ${shortPubkey(this.linkedWallet.publicKey)} (public key only)` : 'no wallet linked (/wallet link)';
|
|
229
197
|
const ks = new KillSwitch(paths.panic ?? defaultPanicPath()).engaged();
|
|
230
|
-
|
|
231
|
-
if (s) {
|
|
232
|
-
const caps = s.caps;
|
|
233
|
-
return `${linked} · signer ${s.network.toUpperCase()} (${s.rpcHost}) · $${caps.perTradeUsd}/trade $${caps.dailyUsd}/day · spent $${s.spentTodayUsd.toFixed(2)}${s.panic || s.ledgerLocked ? ' · ⛔ PANICKED' : ''}`;
|
|
234
|
-
}
|
|
235
|
-
return `${linked} · signer ${this.signer ? (this.signerFailed ?? 'stopped') : 'not started (starts on the first LIVE plan)'}${ks ? ' · ⛔ PANICKED' : ''}${this.trade ? '' : ' · live trading off in this run'}`;
|
|
198
|
+
return `${linked}${ks ? ' · ⛔ PANICKED' : ''} · dexterm holds no key and signs nothing`;
|
|
236
199
|
}
|
|
237
200
|
case 'link': {
|
|
238
201
|
if (!this.trade)
|
|
239
|
-
return '
|
|
240
|
-
const bridge = new WalletBridge(this.trade
|
|
202
|
+
return 'wallet link is off in this run (replay / offline)';
|
|
203
|
+
const bridge = new WalletBridge(this.trade?.open ? { open: this.trade.open } : {});
|
|
241
204
|
try {
|
|
242
205
|
const res = await bridge.link();
|
|
243
|
-
const rec = this.linkStore.set(res.publicKey
|
|
206
|
+
const rec = this.linkStore.set(res.publicKey);
|
|
244
207
|
this.linkedWallet = rec;
|
|
245
208
|
this.onChange('wallet');
|
|
246
|
-
return `linked: ${shortPubkey(rec.publicKey)} (public key only
|
|
209
|
+
return `linked: ${shortPubkey(rec.publicKey)} (public key only; dexterm never signs with it)`;
|
|
247
210
|
}
|
|
248
211
|
catch (e) {
|
|
249
212
|
return `wallet link failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
@@ -252,40 +215,15 @@ export class LiveContext {
|
|
|
252
215
|
case 'unlink': {
|
|
253
216
|
const had = this.linkStore.clear();
|
|
254
217
|
this.linkedWallet = null;
|
|
255
|
-
if (this.ticket?.ticket.mode === 'live')
|
|
256
|
-
this.ticket.setMode('paper');
|
|
257
218
|
this.onChange('wallet');
|
|
258
219
|
return had ? 'unlinked' : 'no wallet was linked';
|
|
259
220
|
}
|
|
260
221
|
case 'panic': {
|
|
261
|
-
//
|
|
222
|
+
// The kill-switch FILE every autotrade runner and the paper agent desk re-read.
|
|
262
223
|
const ks = new KillSwitch(paths.panic ?? defaultPanicPath());
|
|
263
224
|
ks.engage('panic from dexterm UI', this.now());
|
|
264
|
-
try {
|
|
265
|
-
const l = new CapLedger({ path: paths.capLedger ?? defaultCapLedgerPath(), clock: this.dl.clock });
|
|
266
|
-
try {
|
|
267
|
-
l.lock('panic from dexterm UI', this.now());
|
|
268
|
-
}
|
|
269
|
-
finally {
|
|
270
|
-
l.close();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
catch { /* the file alone disables trading */ }
|
|
274
|
-
if (this.signer?.alive)
|
|
275
|
-
await this.signer.panic('panic from dexterm UI').catch(() => { });
|
|
276
|
-
if (this.ticket?.ticket.mode === 'live') {
|
|
277
|
-
this.ticket.ticket.phase = 'refused';
|
|
278
|
-
this.ticket.ticket.message = '⛔ PANIC — live signing disabled';
|
|
279
|
-
}
|
|
280
225
|
this.onChange('wallet');
|
|
281
|
-
return '⛔ PANIC:
|
|
282
|
-
}
|
|
283
|
-
case 'probe': {
|
|
284
|
-
if (!this.trade)
|
|
285
|
-
return 'live trading is off in this run';
|
|
286
|
-
const t = this.openTicket({ mint: this.WSOL, symbol: 'SOL', decimals: 9 }, 'buy');
|
|
287
|
-
const why = t.startProbe();
|
|
288
|
-
return why ?? 'devnet probe: review the plan, then Y to sign the wrap-and-unwrap in Phantom (fees only)';
|
|
226
|
+
return '⛔ PANIC: autotrade runs and the paper agent desk halted · clear it only from a shell: dexterm wallet resume';
|
|
289
227
|
}
|
|
290
228
|
}
|
|
291
229
|
return '';
|
|
@@ -560,10 +498,6 @@ export class LiveContext {
|
|
|
560
498
|
this.social.close();
|
|
561
499
|
}
|
|
562
500
|
catch { /* ignore */ }
|
|
563
|
-
try {
|
|
564
|
-
this.signer?.close();
|
|
565
|
-
}
|
|
566
|
-
catch { /* ignore */ }
|
|
567
501
|
try {
|
|
568
502
|
this.intel.close();
|
|
569
503
|
}
|
|
@@ -19,7 +19,7 @@ import { launchIntelCells, holderTags } from "../intel.js";
|
|
|
19
19
|
import { devBookOf, devRows } from "../dev-history.js";
|
|
20
20
|
import { EX_POOL_TAGS } from '../../../../data/src/index.js';
|
|
21
21
|
import { layoutFor } from "../../custom.js";
|
|
22
|
-
import { chartMode, chartTitle, priceModeOf, chartAxisOf, mcapAt, positionPriceCells } from "../../display.js";
|
|
22
|
+
import { chartMode, chartTitle, priceModeOf, chartAxisOf, chartMarkersOn, mcapAt, positionPriceCells, levelCell, supplyRatio } from "../../display.js";
|
|
23
23
|
const dash = '—';
|
|
24
24
|
const EX_POOL_TAG_SET = EX_POOL_TAGS;
|
|
25
25
|
/** task-1367 holder-class badge colours: venues/escrows accent, the creator/linked warn, the rest muted. */
|
|
@@ -139,8 +139,11 @@ export function renderTokenLive(c, state, v) {
|
|
|
139
139
|
let tx = cb.x + cb.w - 2 - (chartCap.length + 4) - INTERVALS.reduce((s, f) => s + f.length + 1, 0);
|
|
140
140
|
for (const f of INTERVALS)
|
|
141
141
|
tx = c.text(tx, cb.y - 1, f, f === v.interval ? { fg: t.accent, bg: cb.bg, b: true } : { fg: t.dim, bg: cb.bg }) + 1;
|
|
142
|
+
// task-1483: BUY / SELL markers for every paper + live fill of this coin (the ledgers, no fetch) and the open position's
|
|
143
|
+
// average entry as a dashed line (the position row's own figure); `display.chartMarkers off` hides them.
|
|
144
|
+
const trades = chartMarkersOn(state) ? { fills: state.live?.chartFills?.for(v.mint) ?? [], avgEntry: prow && prow.avgEntryUsd !== null ? { usd: prow.avgEntryUsd, source: held?.kind ?? src?.kind ?? 'paper' } : null } : null;
|
|
142
145
|
if (v.candles.length)
|
|
143
|
-
candleChart(c, cb.x + 1, cb.y, cb.w - 2, cb.h, v.candles, { bg: cb.bg, interval: v.interval, scale: cm.scale ?? undefined, axisFmt: cm.axisFmt });
|
|
146
|
+
candleChart(c, cb.x + 1, cb.y, cb.w - 2, cb.h, v.candles, { bg: cb.bg, interval: v.interval, scale: cm.scale ?? undefined, axisFmt: cm.axisFmt, trades });
|
|
144
147
|
else
|
|
145
148
|
c.textC(cb.x, cb.y + Math.floor(cb.h / 2), cb.w, cp.state === 'loading' ? 'loading candles…' : `no candles · ${cp.reason ?? 'source did not answer'}`, { fg: t.muted, bg: cb.bg, i: true });
|
|
146
149
|
y += chartH;
|
|
@@ -322,7 +325,7 @@ export function renderTokenLive(c, state, v) {
|
|
|
322
325
|
c.text(hb.x + 1, hy, 'TAPE', { fg: t.dim, bg: hb.bg });
|
|
323
326
|
c.textR(hb.x + 1, hy, hb.w - 2, tp.state === 'ok' || tp.fetchedAt !== null ? `geckoterminal · 20s poll${tp.stale || tp.state === 'error' ? ' · stale' : ''}` : panelCaption(tp, now, 'tape unavailable'), { fg: tp.state === 'error' && tp.fetchedAt === null ? t.warn : t.dim, bg: hb.bg });
|
|
324
327
|
hy += 1;
|
|
325
|
-
hy += tradeTape(c, hb.x + 1, hy, hb.w - 2, hb.y + hb.h - hy, v.trades, now, { bg: hb.bg });
|
|
328
|
+
hy += tradeTape(c, hb.x + 1, hy, hb.w - 2, hb.y + hb.h - hy, v.trades, now, { bg: hb.bg, priceFmt: (p) => levelCell(priceModeOf(state), supplyRatio(v.price, v.mcap), p) }); // task-1537
|
|
326
329
|
}
|
|
327
330
|
else if (flow) {
|
|
328
331
|
c.text(hb.x + 1, hy, 'BUY/SELL FLOW', { fg: t.dim, bg: hb.bg });
|
|
@@ -337,7 +340,7 @@ export function renderTokenLive(c, state, v) {
|
|
|
337
340
|
}
|
|
338
341
|
if (showAi) {
|
|
339
342
|
// task-1473: in an auto mode (following an agent / AI scan) the panel is the BRAIN VIEW; otherwise the analyst
|
|
340
|
-
const facts = factsFromView(v, { top10: brainTop10, dev: brainDev, larpVerdict: state.larpByMint?.get(v.mint)?.verdict ?? null, price: shownPx });
|
|
343
|
+
const facts = factsFromView(v, { top10: brainTop10, dev: brainDev, larpVerdict: state.larpByMint?.get(v.mint)?.verdict ?? null, price: shownPx, fills: trades?.fills ?? null });
|
|
341
344
|
const brain = brainFor(state, facts, isCalm(state.vfx), state.live?.scanMeterLabel?.() ?? '');
|
|
342
345
|
if (brain)
|
|
343
346
|
renderBrainPanel(c, mainW, top, aiW, bottom - top, brain.model, brain.opts);
|
|
@@ -394,9 +397,9 @@ function positionPanel(c, x, y, w, r, kind, mode = 'price', supply = null) {
|
|
|
394
397
|
if (r.realizedSol !== 0)
|
|
395
398
|
cols.push({ label: 'REALIZED', value: usd && r.realizedUsd !== null ? signedUsd(r.realizedUsd) : `${signedSol(r.realizedSol)} SOL`, st: { fg: pnlColor(r.realizedSol), bg }, prio: 2 });
|
|
396
399
|
if (r.stopUsd !== null)
|
|
397
|
-
cols.push({ label: 'STOP', value: `${
|
|
400
|
+
cols.push({ label: 'STOP', value: `${levelCell(mode, supply, r.stopUsd)}${r.stopDistPct !== null ? ` ${signedPctOf(r.stopDistPct)}` : ''}`, st: { fg: t.down, bg }, prio: 3 });
|
|
398
401
|
if (r.targetUsd !== null)
|
|
399
|
-
cols.push({ label: 'TARGET', value: `${
|
|
402
|
+
cols.push({ label: 'TARGET', value: `${levelCell(mode, supply, r.targetUsd)}${r.targetDistPct !== null ? ` ${signedPctOf(r.targetDistPct)}` : ''}`, st: { fg: t.up, bg }, prio: 3 });
|
|
400
403
|
const gap = 3;
|
|
401
404
|
const widthOf = (list) => list.reduce((s, k) => s + Math.max(k.label.length, k.value.length) + gap, -gap);
|
|
402
405
|
const inner = box.w - 2;
|