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
|
@@ -302,7 +302,13 @@ const USAGE = `dexterm mcp [<command>]
|
|
|
302
302
|
--print only print the snippet, never write
|
|
303
303
|
--node emit a "node <path> mcp" command (when dexterm is not on PATH)
|
|
304
304
|
--yes write without the confirmation prompt
|
|
305
|
-
doctor spawn the server, do the MCP handshake, and list the tools/prompts/resources
|
|
305
|
+
doctor spawn the server, do the MCP handshake, and list the tools/prompts/resources
|
|
306
|
+
|
|
307
|
+
for agent authors: \`dexterm mcp doctor\` lists every tool with its description. Reads (market data, the trade
|
|
308
|
+
ledger: ledger_trades / positions / summary / equity / compare, candidate_dossier, outcomes_lookup, trading_status,
|
|
309
|
+
telegram_status) never change anything; strategy_propose_* and backtest_request only file a proposal the user applies
|
|
310
|
+
or runs; the paper_* tools trade the SIMULATED wallet and only inside a mandate the user armed. No tool can sign, send,
|
|
311
|
+
enable live trading or raise a cap. Docs: https://dexterm.xyz/docs/agents/`;
|
|
306
312
|
/** `dexterm mcp …`. With no subcommand (or an arg the server consumes, e.g. --replay) it runs the server. */
|
|
307
313
|
export async function runMcp(rest, io) {
|
|
308
314
|
const sub = rest[0];
|
|
@@ -253,7 +253,7 @@ export async function runMandate(rest, io) {
|
|
|
253
253
|
}
|
|
254
254
|
out(row.paperOnly
|
|
255
255
|
? `Arm PAPER-ONLY mandate ${id}? This lets the agent paper desk book simulated fills under these caps. No plan, copy trade or signer can use it.`
|
|
256
|
-
: `Arm standing mandate ${id}? This lets armed plans fire at machine speed
|
|
256
|
+
: `Arm standing mandate ${id}? This lets armed plans fire at machine speed on PAPER (simulated fills; dexterm places no live order).`);
|
|
257
257
|
out(` total ${fmtUsd(row.totalUsd)} · per-trade ${fmtUsd(row.perTradeUsd)} · per-coin ${fmtUsd(row.perCoinUsd)} · maxTier ${row.maxTier}`);
|
|
258
258
|
out('Hold any key to confirm (physical keypress). A pipe or agent cannot arm. Waiting 15s…');
|
|
259
259
|
const key = await ttyKeypress(io.stdin, 15_000);
|
|
@@ -4,17 +4,26 @@
|
|
|
4
4
|
// positions → every coin with fills (newest activity first; ← → cycle), LIVE or PAPER as above
|
|
5
5
|
// follows → the selected followed wallet's coins (TRACKED), newest first
|
|
6
6
|
// In the preview: x share to X · t copy as text · s save only · a show/hide wallet · z exact size/band ·
|
|
7
|
-
//
|
|
7
|
+
// m market cap ⇄ SOL price (task-1518; mcap is the default when the coin's supply is known) · f 16:9 ⇄ 1:1 ·
|
|
8
|
+
// ← → other coin · esc close. Share = save + clipboard image + X compose intent in the user's own
|
|
8
9
|
// browser (pnl-card.ts shareCard); dexterm posts nothing. Nothing here trades, signs or edits a number.
|
|
9
10
|
import { mix } from '../../tui/src/index.js';
|
|
10
|
-
import { computeCard, renderCard, previewCanvas, cardText, paperCardInput,
|
|
11
|
+
import { computeCard, renderCard, previewCanvas, cardText, paperCardInput, trackedCardInput, cardSupply, mcapView, CardError, } from "./pnl-card.js";
|
|
11
12
|
const MARK_VIA = 'dexscreener';
|
|
12
13
|
function coinMeta(state, mint) {
|
|
13
14
|
const tv = state.live?.token?.view;
|
|
14
15
|
if (tv && tv.mint === mint)
|
|
15
|
-
return { symbol: tv.symbol, name: tv.name, image: tv.image };
|
|
16
|
+
return { symbol: tv.symbol, name: tv.name, image: tv.image, launchpad: tv.launchpad ?? null, fdv: tv.fdv ?? null, price: tv.price ?? null };
|
|
16
17
|
const row = state.screener.rows.find((r) => r.mint === mint);
|
|
17
|
-
return { symbol: row?.symbol ?? null, name: row?.name ?? null, image: row?.image ?? null };
|
|
18
|
+
return { symbol: row?.symbol ?? null, name: row?.name ?? null, image: row?.image ?? null, launchpad: row?.launchpad ?? null, fdv: row?.fdv ?? null, price: row?.price ?? null };
|
|
19
|
+
}
|
|
20
|
+
/** The market-cap inputs for a coin: its recorded supply (FDV ÷ price, else pump.fun's 1B) and today's SOL/USD. */
|
|
21
|
+
function mcapInputs(state, mint, meta) {
|
|
22
|
+
const solUsd = state.live?.positions.solUsd() ?? null;
|
|
23
|
+
return {
|
|
24
|
+
supply: cardSupply({ mint, launchpad: meta.launchpad, fdv: meta.fdv, price: meta.price, via: 'market data' }),
|
|
25
|
+
usdPerSol: solUsd && solUsd > 0 ? { usd: solUsd, source: `$${solUsd.toFixed(2)}/SOL now (display conversion)` } : null,
|
|
26
|
+
};
|
|
18
27
|
}
|
|
19
28
|
function imageOf(state, url) {
|
|
20
29
|
if (!url || !state.images)
|
|
@@ -35,7 +44,7 @@ export function markFor(state, mint) {
|
|
|
35
44
|
return { priceSol: tv.price / solUsd, at: m.fetchedAt, via: `${m.via ?? 'market'} ÷ SOL/USD` };
|
|
36
45
|
return null;
|
|
37
46
|
}
|
|
38
|
-
/** Coins with paper
|
|
47
|
+
/** Coins with paper fills, newest activity first. */
|
|
39
48
|
function tradedCoins(state) {
|
|
40
49
|
const live = state.live;
|
|
41
50
|
if (!live)
|
|
@@ -45,8 +54,6 @@ function tradedCoins(state) {
|
|
|
45
54
|
last.set(mint, { t, symbol }); };
|
|
46
55
|
for (const r of live.wallet.trades(1000))
|
|
47
56
|
see(r.mint, r.t, r.symbol);
|
|
48
|
-
for (const r of live.livePositions?.records(1000) ?? [])
|
|
49
|
-
see(r.mint, r.t, r.symbol);
|
|
50
57
|
return [...last.entries()].sort((a, b) => b[1].t - a[1].t).map(([mint, v]) => ({ mint, symbol: v.symbol }));
|
|
51
58
|
}
|
|
52
59
|
/** Open the preview for the current screen. Returns a reason when there is nothing to card. */
|
|
@@ -75,15 +82,15 @@ export function openPnl(state) {
|
|
|
75
82
|
const mint = state.live.token?.view?.mint ?? state.token?.mint ?? null;
|
|
76
83
|
if (!mint)
|
|
77
84
|
return 'open a coin first';
|
|
78
|
-
const has =
|
|
85
|
+
const has = state.live.wallet.trades(1000).some((r) => r.mint === mint);
|
|
79
86
|
if (!has)
|
|
80
|
-
return 'no paper
|
|
87
|
+
return 'no paper fills for this coin yet: PnL cards come from your own recorded trades';
|
|
81
88
|
state.pnl = { origin: 'token', choices: [{ mint, symbol: coinMeta(state, mint).symbol ?? mint.slice(0, 4) }], wallet: null, ...base };
|
|
82
89
|
}
|
|
83
90
|
else if (state.screen === 'positions') {
|
|
84
91
|
const choices = tradedCoins(state);
|
|
85
92
|
if (!choices.length)
|
|
86
|
-
return 'no paper
|
|
93
|
+
return 'no paper fills yet: B / S on a token to paper-trade, then H here';
|
|
87
94
|
state.pnl = { origin: 'positions', choices, wallet: null, ...base };
|
|
88
95
|
}
|
|
89
96
|
else
|
|
@@ -111,16 +118,12 @@ export async function pnlInput(state, ui) {
|
|
|
111
118
|
const solUsd = live.positions.solUsd();
|
|
112
119
|
if (!mark && m.priceUsd && m.priceAt && solUsd)
|
|
113
120
|
mark = { priceSol: m.priceUsd / solUsd, at: m.priceAt, via: `${MARK_VIA} ÷ SOL/USD` };
|
|
114
|
-
return trackedCardInput(copy.trades(ui.wallet), {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
const liveRows = live.livePositions?.fills(ch.mint) ?? [];
|
|
120
|
-
if (liveRows.length) {
|
|
121
|
-
return liveCardInput(liveRows, { mint: ch.mint, symbol: meta.symbol ?? undefined, name: meta.name, wallet: live.linkedWallet?.publicKey ?? null, mark: markFor(state, ch.mint), image });
|
|
121
|
+
return { ...trackedCardInput(copy.trades(ui.wallet), {
|
|
122
|
+
wallet: ui.wallet, mint: ch.mint, symbol: meta.symbol ?? m.symbol ?? ch.symbol, name: meta.name, decimals, mark,
|
|
123
|
+
historyAt: st.lastPollAt, complete: stats.sample.complete, image,
|
|
124
|
+
}), ...mcapInputs(state, ch.mint, meta) };
|
|
122
125
|
}
|
|
123
|
-
return paperCardInput(live.wallet.trades(1000), { mint: ch.mint, symbol: meta.symbol ?? undefined, name: meta.name, mark: markFor(state, ch.mint), image });
|
|
126
|
+
return { ...paperCardInput(live.wallet.trades(1000), { mint: ch.mint, symbol: meta.symbol ?? undefined, name: meta.name, mark: markFor(state, ch.mint), image }), ...mcapInputs(state, ch.mint, meta) };
|
|
124
127
|
}
|
|
125
128
|
/** (Re)build the card for the current choice; on refusal the preview shows the reason instead of a card. */
|
|
126
129
|
export async function loadPnl(state, now = Date.now()) {
|
|
@@ -167,6 +170,13 @@ export function pnlKey(ui, key) {
|
|
|
167
170
|
ui.notice = ui.opts.exactSize ? 'exact SOL size SHOWN on the card' : 'size shown as a band';
|
|
168
171
|
return { kind: 'none' };
|
|
169
172
|
}
|
|
173
|
+
if (ch === 'm') {
|
|
174
|
+
const now = ui.stats ? mcapView(ui.stats, ui.opts).basis : (ui.opts.basis ?? 'mcap');
|
|
175
|
+
ui.opts.basis = now === 'mcap' ? 'price' : 'mcap';
|
|
176
|
+
const v = ui.stats ? mcapView(ui.stats, ui.opts) : null;
|
|
177
|
+
ui.notice = ui.opts.basis === 'price' ? 'entry / exit as SOL price' : v?.fallback ?? 'entry / exit as market cap';
|
|
178
|
+
return { kind: 'none' };
|
|
179
|
+
}
|
|
170
180
|
if (ch === 'f') {
|
|
171
181
|
ui.opts.aspect = ui.opts.aspect === 'wide' ? 'square' : 'wide';
|
|
172
182
|
return { kind: 'none' };
|
|
@@ -181,7 +191,7 @@ export function pnlKey(ui, key) {
|
|
|
181
191
|
return { kind: 'share', action: 'save' };
|
|
182
192
|
return { kind: 'none' };
|
|
183
193
|
}
|
|
184
|
-
const HINTS = 'x share to X · t copy text · s save · a wallet · z size · f 16:9/1:1 · ←→ coin · esc';
|
|
194
|
+
const HINTS = 'x share to X · t copy text · s save · a wallet · z size · m mcap/price · f 16:9/1:1 · ←→ coin · esc';
|
|
185
195
|
/** The preview overlay: the card itself at cell size when the terminal fits it, else its text; then the keys. */
|
|
186
196
|
export function renderPnlOverlay(c, state) {
|
|
187
197
|
const ui = state.pnl;
|
|
@@ -191,7 +201,7 @@ export function renderPnlOverlay(c, state) {
|
|
|
191
201
|
c.noRain(0, 0, c.w, c.h);
|
|
192
202
|
c.fill(0, 0, c.w, c.h, ' ', { bg: mix(t.bg, '#000000', 0.5) });
|
|
193
203
|
const ch = ui.choices[ui.idx];
|
|
194
|
-
const head = `PnL CARD · $${ch?.symbol ?? '?'}${ui.choices.length > 1 ? ` (${ui.idx + 1}/${ui.choices.length})` : ''} · ${ui.opts.aspect === 'wide' ? '16:9' : '1:1'} · wallet ${ui.opts.showWallet ? 'SHOWN' : 'hidden'} · size ${ui.opts.exactSize ? 'EXACT' : 'band'}`;
|
|
204
|
+
const head = `PnL CARD · $${ch?.symbol ?? '?'}${ui.choices.length > 1 ? ` (${ui.idx + 1}/${ui.choices.length})` : ''} · ${ui.opts.aspect === 'wide' ? '16:9' : '1:1'} · wallet ${ui.opts.showWallet ? 'SHOWN' : 'hidden'} · size ${ui.opts.exactSize ? 'EXACT' : 'band'}${ui.stats ? ` · ${mcapView(ui.stats, ui.opts).basis === 'mcap' ? 'mcap' : 'SOL price'}` : ''}`;
|
|
195
205
|
c.text(1, 0, head, { fg: t.text, bg: t.headBg, b: true }, c.w - 2);
|
|
196
206
|
c.fill(0, c.h - 1, c.w, 1, ' ', { bg: t.headBg });
|
|
197
207
|
c.text(1, c.h - 1, ui.busy ? 'working… (esc closes)' : (ui.notice ?? HINTS), { fg: ui.notice ? t.warn : t.muted, bg: t.headBg }, c.w - 2);
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
// Honesty rules, enforced by construction:
|
|
8
8
|
// - The stamp (PAPER / LIVE / TRACKED WALLET) comes from `CardInput.source` and is drawn by the renderer in three
|
|
9
9
|
// places (the stamp block, the frame colour, the stamp strip). No option turns it off; an unknown source throws.
|
|
10
|
-
// - Numbers come only from fills (the paper ledger, the
|
|
10
|
+
// - Numbers come only from fills (the paper ledger, the trade ledger, a tracked wallet's decoded history).
|
|
11
11
|
// There is no API that takes a PnL, a price or a size; every figure is computed here from those fills, with
|
|
12
12
|
// the time window, the fee basis, what was excluded and the data ages printed on the card.
|
|
13
13
|
// - Wallet addresses (and LIVE tx signatures, which point at the wallet) are hidden unless `showWallet`; size is a
|
|
14
14
|
// band unless `exactSize`; no balance, no key, no file path is ever drawn.
|
|
15
15
|
// - Footer: "not financial advice" and the dexterm URL. No performance claims beyond this one position's fills.
|
|
16
|
+
// - Market cap (task-1518): ENTRY / EXIT are shown as market cap when the token supply is known (`basis: 'mcap'`,
|
|
17
|
+
// the default then; `basis: 'price'` keeps the SOL price view). Each mcap is a fill-derived price × the supply,
|
|
18
|
+
// never a quoted mcap, and the card names the supply source (recorded FDV ÷ price, or pump.fun's fixed 1B) and
|
|
19
|
+
// the USD conversion. Exit mcap is the blended average of every sell; the last sell's mcap is its own labelled row.
|
|
16
20
|
//
|
|
17
21
|
// Rendering reuses the task-1401 exporter stack: a cell Canvas rasterized by @dexterm/tui (the 5×9 bitmap font) and
|
|
18
22
|
// encoded by @dexterm/data. Big type (the stamp, the ticker, the PnL %) is the same font rasterized at a larger
|
|
@@ -20,7 +24,7 @@
|
|
|
20
24
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
21
25
|
import { join } from 'node:path';
|
|
22
26
|
import { spawn } from 'node:child_process';
|
|
23
|
-
import { Canvas, THEMES, rasterizeCanvas, fmtPriceBare, mix } from '../../tui/src/index.js';
|
|
27
|
+
import { Canvas, THEMES, rasterizeCanvas, fmtPriceBare, fmtUsd, fmtNum, mix } from '../../tui/src/index.js';
|
|
24
28
|
import { encodePng, lamportsToSol } from '../../data/src/index.js';
|
|
25
29
|
import { exportsDir, fmtAge } from "./export.js";
|
|
26
30
|
import { cleanText } from "./screener/rows.js";
|
|
@@ -41,18 +45,6 @@ export function paperCardInput(records, o) {
|
|
|
41
45
|
excluded: [], fees: 'simulated network fee included', image: o.image ?? null,
|
|
42
46
|
};
|
|
43
47
|
}
|
|
44
|
-
/** LIVE: the signer's ledger rows (confirmed on-chain fills, swaps only) for one mint. */
|
|
45
|
-
export function liveCardInput(rows, o) {
|
|
46
|
-
const rs = rows.filter((r) => r.mint === o.mint && r.kind === 'swap');
|
|
47
|
-
const nets = new Set(rs.map((r) => r.network));
|
|
48
|
-
const network = nets.size > 1 ? 'mixed' : rs[0]?.network === 'devnet' ? 'devnet' : 'mainnet';
|
|
49
|
-
return {
|
|
50
|
-
source: 'live', network, mint: o.mint, symbol: o.symbol ?? rs[0]?.symbol ?? '?', name: o.name ?? null, decimals: rs[0]?.decimals ?? null,
|
|
51
|
-
fills: rs.map((r) => ({ t: r.t, side: r.side === 'sell' ? 'sell' : 'buy', solDelta: lamportsToSol(BigInt(r.solDelta)), tokenDelta: BigInt(r.tokenDelta), feeSol: lamportsToSol(BigInt(r.feeLamports)), sig: r.signature })),
|
|
52
|
-
ledger: 'signer live ledger', wallet: o.wallet ?? null, mark: o.mark ?? null,
|
|
53
|
-
excluded: network === 'mixed' ? ['devnet and mainnet fills are both in the ledger for this coin'] : [], fees: 'network + priority fees included', image: o.image ?? null,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
48
|
/** TRACKED: a followed wallet's decoded swaps for one mint (read-only public history). */
|
|
57
49
|
export function trackedCardInput(trades, o) {
|
|
58
50
|
const all = trades.filter((t) => t.mint === o.mint && t.wallet === o.wallet);
|
|
@@ -99,6 +91,78 @@ export function trackedCardInput(trades, o) {
|
|
|
99
91
|
excluded, fees: 'network fees + tips included (balance deltas)', image: o.image ?? null,
|
|
100
92
|
};
|
|
101
93
|
}
|
|
94
|
+
/** pump.fun mints a fixed 1,000,000,000 supply per coin (none is minted later), so its market cap is price × 1B. */
|
|
95
|
+
export const PUMP_FUN_SUPPLY = 1_000_000_000;
|
|
96
|
+
/**
|
|
97
|
+
* The supply for the market-cap view: the token's recorded FDV ÷ price when both are known (total supply, so on a
|
|
98
|
+
* non-pump coin this is a fully diluted cap and says so), else pump.fun's fixed 1B for a pump.fun coin, else null
|
|
99
|
+
* (the card then stays on the price view; no supply is ever guessed).
|
|
100
|
+
*/
|
|
101
|
+
export function cardSupply(o) {
|
|
102
|
+
if (o.fdv && o.price && Number.isFinite(o.fdv / o.price) && o.fdv > 0 && o.price > 0) {
|
|
103
|
+
return { tokens: o.fdv / o.price, source: `recorded FDV ÷ price${o.via ? ` (${o.via})` : ''}` };
|
|
104
|
+
}
|
|
105
|
+
if (o.launchpad === 'pump.fun' || ((!o.launchpad || o.launchpad === 'unknown') && o.mint.endsWith('pump'))) {
|
|
106
|
+
return { tokens: PUMP_FUN_SUPPLY, source: 'pump.fun fixed supply' };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* AUTOTRADE PAPER: one position's fills from an autotrade run ledger. The desk books in USD, so each fill's SOL leg
|
|
112
|
+
* is its USD at `solUsd` (said on the card); the market caps come straight from the USD fills × supply, so the SOL
|
|
113
|
+
* rate changes only the size band, never a cap or the return.
|
|
114
|
+
*/
|
|
115
|
+
export function autotradeCardInput(events, o) {
|
|
116
|
+
if (!(o.solUsd.usd > 0))
|
|
117
|
+
throw new CardError('an autotrade card needs a SOL/USD rate to show the USD desk in SOL');
|
|
118
|
+
const fs = events.filter((e) => e.kind === 'fill' && e.positionId === o.positionId && (e.side === 'buy' || e.side === 'sell') && (e.usd ?? 0) > 0 && (e.tokens ?? 0) > 0);
|
|
119
|
+
const mint = fs[0]?.mint ?? '';
|
|
120
|
+
const closed = events.find((e) => e.kind === 'trade_closed' && e.trade?.id === o.positionId);
|
|
121
|
+
const decimals = o.decimals ?? 6;
|
|
122
|
+
return {
|
|
123
|
+
source: 'paper', mint, symbol: o.symbol ?? closed?.trade?.symbol ?? mint.slice(0, 4), name: o.name ?? null, decimals,
|
|
124
|
+
fills: fs.map((e) => ({
|
|
125
|
+
t: e.t, side: e.side, solDelta: (e.side === 'buy' ? -1 : 1) * e.usd / o.solUsd.usd,
|
|
126
|
+
tokenDelta: BigInt(Math.round(e.tokens * 10 ** decimals)) * (e.side === 'buy' ? 1n : -1n), feeSol: null, sig: null,
|
|
127
|
+
})),
|
|
128
|
+
ledger: o.runId ? `autotrade paper run ${o.runId}` : 'autotrade paper ledger', wallet: null, mark: o.mark ?? null, excluded: [],
|
|
129
|
+
fees: `USD paper desk, impact in fills · SOL $${o.solUsd.usd.toFixed(2)} (${o.solUsd.source})`, image: o.image ?? null,
|
|
130
|
+
supply: o.supply === undefined ? cardSupply({ mint }) : o.supply,
|
|
131
|
+
usdPerSol: { usd: o.solUsd.usd, source: 'the desk\'s USD fills', native: true },
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* TRADE LEDGER (task-1530): one round trip of the unified ledger as a card. The stamp follows the wallet mode
|
|
136
|
+
* (paper → PAPER; devnet / live → LIVE with its network), and ENTRY / EXIT use the market-cap basis whenever the
|
|
137
|
+
* ledger recorded a supply (the same mcap code as every other card). A fill with no SOL leg (the autotrade USD desk)
|
|
138
|
+
* needs `solUsd`; a fill with no token amount refuses the card rather than guess.
|
|
139
|
+
*/
|
|
140
|
+
export function ledgerCardInput(row, o = {}) {
|
|
141
|
+
// task-1655: a real-mode row is dexterm's model of a trade it signalled; the real fill is the executor's receipt.
|
|
142
|
+
// A card would stamp it PAPER or LIVE, and both are wrong, so there is none.
|
|
143
|
+
if (row.walletMode === 'real')
|
|
144
|
+
throw new CardError('a real-mode trade: dexterm only modelled this fill (the signal it sent your executor); your executor\'s receipt is the record, so dexterm makes no card for it');
|
|
145
|
+
const decimals = row.decimals ?? 6;
|
|
146
|
+
const usdDesk = row.fills.some((f) => f.sol === null);
|
|
147
|
+
if (usdDesk && !(o.solUsd && o.solUsd.usd > 0))
|
|
148
|
+
throw new CardError('this trade was booked in USD: a card needs a SOL/USD rate (--sol-usd)');
|
|
149
|
+
const fills = row.fills.map((f) => {
|
|
150
|
+
if (!(f.tokens !== null && f.tokens > 0))
|
|
151
|
+
throw new CardError(`fill ${f.t} has no token amount in the ledger`);
|
|
152
|
+
const sol = f.sol !== null && !usdDesk ? f.sol : (f.usd ?? 0) / o.solUsd.usd;
|
|
153
|
+
return { t: f.t, side: f.side, solDelta: (f.side === 'buy' ? -1 : 1) * Math.abs(sol), tokenDelta: BigInt(Math.round(f.tokens * 10 ** decimals)) * (f.side === 'buy' ? 1n : -1n), feeSol: f.feeSol, sig: row.walletMode === 'paper' ? null : f.txSig };
|
|
154
|
+
});
|
|
155
|
+
const where = row.source === 'autotrade' ? `autotrade paper run ${row.runId ?? ''}`.trim() : row.source === 'desk' ? 'agent paper desk' : row.source === 'live' ? 'signer live ledger' : 'dexterm paper ledger';
|
|
156
|
+
return {
|
|
157
|
+
source: row.walletMode === 'paper' ? 'paper' : 'live', network: row.walletMode === 'paper' ? null : row.walletMode === 'devnet' ? 'devnet' : 'mainnet',
|
|
158
|
+
mint: row.mint, symbol: row.symbol, name: o.name ?? null, decimals, fills,
|
|
159
|
+
ledger: `trade ledger · ${where} · ${row.tradeId}`, wallet: null, mark: o.mark ?? null, excluded: [],
|
|
160
|
+
fees: usdDesk ? `USD paper desk, impact in fills · SOL $${o.solUsd.usd.toFixed(2)} (${o.solUsd.source})` : row.walletMode === 'paper' ? 'simulated network fee included' : 'network + priority fees included',
|
|
161
|
+
image: o.image ?? null,
|
|
162
|
+
supply: row.supply ?? cardSupply({ mint: row.mint }),
|
|
163
|
+
...(usdDesk ? { usdPerSol: { usd: o.solUsd.usd, source: 'the desk\'s USD fills', native: true } } : o.solUsd ? { usdPerSol: o.solUsd } : {}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
102
166
|
/** Cost-basis maths over the fills (average cost, the paper and live ledgers' own rule). Throws CardError when a card would mislead. */
|
|
103
167
|
export function computeCard(input, now) {
|
|
104
168
|
if (input.source !== 'paper' && input.source !== 'live' && input.source !== 'tracked')
|
|
@@ -109,7 +173,7 @@ export function computeCard(input, now) {
|
|
|
109
173
|
if (!fills.some((f) => f.side === 'buy'))
|
|
110
174
|
throw new CardError('no recorded buy for this coin, so there is no cost basis to measure against');
|
|
111
175
|
let held = 0n, peak = 0n, cost = 0, costIn = 0, proceeds = 0, realized = 0, boughtRaw = 0n, soldRaw = 0n, buys = 0, sells = 0, unmatched = 0;
|
|
112
|
-
let fees = 0, feesKnown = false, firstBuy = Infinity, lastSell = -Infinity;
|
|
176
|
+
let fees = 0, feesKnown = false, firstBuy = Infinity, lastSell = -Infinity, lastSellAvg = null;
|
|
113
177
|
for (const f of fills) {
|
|
114
178
|
if (f.feeSol !== null) {
|
|
115
179
|
fees += f.feeSol;
|
|
@@ -147,6 +211,8 @@ export function computeCard(input, now) {
|
|
|
147
211
|
cost -= removed;
|
|
148
212
|
held -= matched;
|
|
149
213
|
soldRaw += matched;
|
|
214
|
+
if (input.decimals !== null)
|
|
215
|
+
lastSellAvg = got / (Number(matched) / 10 ** input.decimals);
|
|
150
216
|
}
|
|
151
217
|
}
|
|
152
218
|
if (!buys)
|
|
@@ -174,7 +240,7 @@ export function computeCard(input, now) {
|
|
|
174
240
|
input, buys, sells, firstT: fills[0].t, lastT: fills[fills.length - 1].t, open,
|
|
175
241
|
costInSol: costIn, proceedsSol: proceeds, realizedSol: realized, unrealizedSol: unrealized, pnlSol: pnl, pnlPct: costIn > 0 ? pnl / costIn : 0,
|
|
176
242
|
kind: !open ? 'realized' : sells ? 'realized + unrealized' : 'unrealized',
|
|
177
|
-
entryAvg: boughtW ? costIn / boughtW : null, exitAvg: soldW ? proceeds / soldW : null,
|
|
243
|
+
entryAvg: boughtW ? costIn / boughtW : null, exitAvg: soldW ? proceeds / soldW : null, lastSellAvg,
|
|
178
244
|
holdMs: Math.max(0, (open ? now : lastSell === -Infinity ? now : lastSell) - (firstBuy === Infinity ? fills[0].t : firstBuy)),
|
|
179
245
|
feesSol: feesKnown ? fees : null, now,
|
|
180
246
|
};
|
|
@@ -217,6 +283,66 @@ export function fmtWhen(t) {
|
|
|
217
283
|
}
|
|
218
284
|
const signedSol = (n) => `${n >= 0 ? '+' : '-'}${Math.abs(n).toFixed(4)} SOL`;
|
|
219
285
|
const short = (a) => (a.length > 10 ? `${a.slice(0, 4)}…${a.slice(-4)}` : a);
|
|
286
|
+
/** `1B` / `998.8M` / `21,000,000` → a short supply figure for the provenance line. */
|
|
287
|
+
const fmtSupply = (n) => (n === PUMP_FUN_SUPPLY ? '1B' : fmtNum(n));
|
|
288
|
+
/** Resolve the basis and the market caps for a card: every figure is a fill-derived SOL price × supply (× USD/SOL). */
|
|
289
|
+
export function mcapView(s, o = {}) {
|
|
290
|
+
const sup = s.input.supply;
|
|
291
|
+
const want = o.basis ?? (sup ? 'mcap' : 'price');
|
|
292
|
+
const none = { basis: 'price', fallback: null, unit: 'SOL', entry: null, exitBlended: null, lastSell: null, mark: null };
|
|
293
|
+
if (want === 'price')
|
|
294
|
+
return none;
|
|
295
|
+
if (!sup || !(sup.tokens > 0))
|
|
296
|
+
return { ...none, fallback: 'market cap not shown: no token supply recorded, so prices are in SOL' };
|
|
297
|
+
if (s.input.decimals === null)
|
|
298
|
+
return { ...none, fallback: 'market cap not shown: token decimals unknown, so prices are in SOL' };
|
|
299
|
+
const k = sup.tokens * (s.input.usdPerSol && s.input.usdPerSol.usd > 0 ? s.input.usdPerSol.usd : 1);
|
|
300
|
+
const m = (p) => (p === null || p === undefined || !Number.isFinite(p) ? null : p * k);
|
|
301
|
+
return {
|
|
302
|
+
basis: 'mcap', fallback: null, unit: s.input.usdPerSol && s.input.usdPerSol.usd > 0 ? 'USD' : 'SOL',
|
|
303
|
+
entry: m(s.entryAvg), exitBlended: m(s.exitAvg), lastSell: m(s.lastSellAvg), mark: s.open ? m(s.input.mark?.priceSol) : null,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/** `$69.7K` / `$1.23M` in USD, `412.3 SOL` / `1.20K SOL` in SOL. */
|
|
307
|
+
export function fmtMcap(v, unit) {
|
|
308
|
+
if (v === null)
|
|
309
|
+
return '—';
|
|
310
|
+
return unit === 'USD' ? fmtUsd(v) : `${fmtNum(v)} SOL`;
|
|
311
|
+
}
|
|
312
|
+
/** The ENTRY / EXIT rows for the market-cap view (closed: entry, blended exit, last sell when there are 2+ sells). */
|
|
313
|
+
function mcapRows(s, v) {
|
|
314
|
+
const f = (x) => fmtMcap(x, v.unit);
|
|
315
|
+
const rows = [['ENTRY mcap', `${f(v.entry)} (avg fill)`]];
|
|
316
|
+
if (s.open)
|
|
317
|
+
rows.push(['MARK mcap', f(v.mark)]);
|
|
318
|
+
else
|
|
319
|
+
rows.push(['EXIT mcap', s.sells > 1 ? `${f(v.exitBlended)} (blended)` : f(v.exitBlended)]);
|
|
320
|
+
if (s.sells > 1 || (s.open && s.sells))
|
|
321
|
+
rows.push(['LAST SELL', `${f(v.lastSell)} mcap`]);
|
|
322
|
+
return rows;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The card's own statement of where the caps come from, as two stat rows (the provenance block is full on a LIVE
|
|
326
|
+
* card with the wallet shown, and OPENED / CLOSED repeat its window line, so these two take their place).
|
|
327
|
+
*/
|
|
328
|
+
function mcapSourceRows(s, v) {
|
|
329
|
+
const sup = s.input.supply, usd = s.input.usdPerSol;
|
|
330
|
+
const src = sup.source.startsWith('pump.fun') ? 'pump.fun fixed' : sup.source.startsWith('recorded FDV') ? 'FDV/price' : cleanText(sup.source, 12);
|
|
331
|
+
return [
|
|
332
|
+
['SUPPLY', `${fmtSupply(sup.tokens)} · ${src}`],
|
|
333
|
+
['MCAP IN', v.unit === 'SOL' ? 'SOL (no USD rate)' : usd?.native ? 'USD (desk fills)' : `USD at $${usd.usd.toFixed(2)}/SOL`],
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
/** The provenance line of the market-cap view (or of a fallback to the price view). */
|
|
337
|
+
function mcapLine(s, v) {
|
|
338
|
+
if (v.fallback)
|
|
339
|
+
return v.fallback;
|
|
340
|
+
if (v.basis !== 'mcap' || !s.input.supply)
|
|
341
|
+
return null;
|
|
342
|
+
const sup = s.input.supply, usd = s.input.usdPerSol;
|
|
343
|
+
const conv = v.unit === 'USD' ? ` · USD: ${usd.source}` : ' · in SOL';
|
|
344
|
+
return `mcap = fill price × ${fmtSupply(sup.tokens)} supply (${sup.source})${conv}`;
|
|
345
|
+
}
|
|
220
346
|
/** The stamp of a source. Exhaustive: anything else throws, so a card can never be drawn without one. */
|
|
221
347
|
export function stampOf(s, t) {
|
|
222
348
|
const src = s.input.source;
|
|
@@ -251,6 +377,10 @@ export function provenance(s, o, mode = 'text') {
|
|
|
251
377
|
if (mode === 'text')
|
|
252
378
|
ages.push(`card made ${fmtWhen(s.now)}`);
|
|
253
379
|
L.push(`data ages: ${ages.join(' · ')}`);
|
|
380
|
+
// the card states the market-cap source in its stat rows (mcapSourceRows); the text spells it out here
|
|
381
|
+
const ml = mode === 'text' ? mcapLine(s, mcapView(s, o)) : null;
|
|
382
|
+
if (ml)
|
|
383
|
+
L.push(ml);
|
|
254
384
|
if (o.showWallet && i.wallet)
|
|
255
385
|
L.push(`wallet: ${i.wallet}`);
|
|
256
386
|
if (i.source === 'live' && o.showWallet) {
|
|
@@ -351,15 +481,22 @@ export function renderCard(s, o = {}) {
|
|
|
351
481
|
: 'return on SOL spent · net of fees · size shown as a band';
|
|
352
482
|
const dec = s.input.decimals !== null;
|
|
353
483
|
const px = (p) => (p === null ? (dec ? '—' : 'n/a (decimals unknown)') : `${fmtPriceBare(p)} SOL`);
|
|
484
|
+
const mv = mcapView(s, o);
|
|
354
485
|
const stats = [
|
|
355
|
-
|
|
356
|
-
|
|
486
|
+
...(mv.basis === 'mcap' ? mcapRows(s, mv) : [
|
|
487
|
+
['ENTRY avg', px(s.entryAvg)],
|
|
488
|
+
s.open ? ['MARK', px(s.input.mark?.priceSol ?? null)] : ['EXIT avg', px(s.exitAvg)],
|
|
489
|
+
]),
|
|
357
490
|
['HOLD', `${fmtHold(s.holdMs)}${s.open ? ' (open)' : ''}`],
|
|
358
491
|
['SIZE', o.exactSize ? `${s.costInSol.toFixed(4)} SOL in` : `${sizeBand(s.costInSol)} (band)`],
|
|
359
492
|
['FILLS', `${s.buys} buy${s.buys === 1 ? '' : 's'} · ${s.sells} sell${s.sells === 1 ? '' : 's'}`],
|
|
360
|
-
|
|
361
|
-
|
|
493
|
+
...(mv.basis === 'mcap' ? mcapSourceRows(s, mv) : [
|
|
494
|
+
['OPENED', fmtWhen(s.firstT)],
|
|
495
|
+
['CLOSED', s.open ? 'still open' : fmtWhen(s.lastT)],
|
|
496
|
+
]),
|
|
362
497
|
];
|
|
498
|
+
if (mv.fallback)
|
|
499
|
+
stats.push(['MCAP', mv.fallback.startsWith('market cap not shown: no token supply') ? 'n/a: no supply known' : 'n/a: decimals unknown']);
|
|
363
500
|
const prov = [...provenance(s, o, 'card'), stamp.note];
|
|
364
501
|
const coinTile = (x, y, w, h) => {
|
|
365
502
|
if (s.input.image && s.input.image.w > 0 && s.input.image.h > 0) {
|
|
@@ -510,7 +647,11 @@ export function cardText(s, o = {}) {
|
|
|
510
647
|
L.push(`${s.kind} PnL ${fmtPnlPct(s.pnlPct)} (net of fees)${o.exactSize ? ` · ${signedSol(s.pnlSol)} on ${s.costInSol.toFixed(4)} SOL in` : ` · size ${sizeBand(s.costInSol)} (band)`}`);
|
|
511
648
|
const dec = s.input.decimals !== null;
|
|
512
649
|
const p = (v) => (v === null ? (dec ? '—' : 'n/a') : `${fmtPriceBare(v)} SOL`);
|
|
513
|
-
|
|
650
|
+
const mv = mcapView(s, o);
|
|
651
|
+
const head = mv.basis === 'mcap'
|
|
652
|
+
? mcapRows(s, mv).map(([k, v]) => `${k.toLowerCase()} ${v}`).join(' · ')
|
|
653
|
+
: `entry avg ${p(s.entryAvg)} · ${s.open ? `mark ${p(s.input.mark?.priceSol ?? null)}` : `exit avg ${p(s.exitAvg)}`}`;
|
|
654
|
+
L.push(`${head} · hold ${fmtHold(s.holdMs)}${s.open ? ' (open)' : ''} · ${s.buys} buys · ${s.sells} sells`);
|
|
514
655
|
L.push(...provenance(s, o));
|
|
515
656
|
L.push(st.note + ' Trading in dexterm is experimental.');
|
|
516
657
|
L.push(`not financial advice · ${DEXTERM_URL}`);
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
// changed, otherwise just the last two bars ("tail"), so a live tick costs the page one `series.update`.
|
|
10
10
|
// When the coin is held (task-1323 PositionSource) the VM carries the position row + entry/stop/target levels
|
|
11
11
|
// and the fills of the current position from the paper ledger. Read-only: nothing here can trade.
|
|
12
|
-
import { createPopupManager, loadConfig, nativeShellAvailable, TerminalDock, CHART_INTERVALS } from '../../popup/src/index.js';
|
|
12
|
+
import { createPopupManager, loadConfig, nativeShellAvailable, TerminalDock, CHART_INTERVALS, CHART_MARKS_MAX } from '../../popup/src/index.js';
|
|
13
13
|
import { fmtPrice, fmtNum } from '../../tui/src/index.js';
|
|
14
14
|
import { positionRow } from "./live/trade.js";
|
|
15
15
|
import { signedUsd, signedPctOf, fmtUsdFine, sol as fmtSol, signedSol } from "./screens/positions.js";
|
|
16
|
-
import { supplyRatio, positionPriceCells } from "./display.js";
|
|
16
|
+
import { supplyRatio, positionPriceCells, levelCell } from "./display.js";
|
|
17
17
|
import { chipSegments } from "./screens/pnl-chip.js";
|
|
18
18
|
/** Map the app's active Theme to the popup protocol's theme-token subset. */
|
|
19
19
|
export function popupThemeFrom(t) {
|
|
@@ -26,6 +26,11 @@ export function popupThemeFrom(t) {
|
|
|
26
26
|
/** Most bars one VM carries (the 1m base is 1000 bars; this only guards a future bigger base). */
|
|
27
27
|
export const CHART_MAX_BARS = 1500;
|
|
28
28
|
export const DEFAULT_CHART_PREFS = { style: 'candles', scale: 'auto', indicators: ['volume', 'tape', 'ema9', 'ema21'] };
|
|
29
|
+
/** task-1483: the VM's markers layer: the newest CHART_MARKS_MAX fills (oldest first), and how many older ones were left out. */
|
|
30
|
+
export function chartMarksFrom(on, fills) {
|
|
31
|
+
const keep = fills.length > CHART_MARKS_MAX ? fills.slice(-CHART_MARKS_MAX) : fills;
|
|
32
|
+
return { on, fills: keep.map((f) => ({ t: f.t, side: f.side, source: f.source, priceUsd: f.priceUsd, tokens: f.tokens, sol: f.sol })), truncated: fills.length - keep.length };
|
|
33
|
+
}
|
|
29
34
|
const finitePos = (n) => typeof n === 'number' && Number.isFinite(n) && n > 0;
|
|
30
35
|
/** Token amounts up to trillions (same rule as the terminal's position row). */
|
|
31
36
|
function fmtAmt(n) { return n >= 1e12 ? `${(n / 1e12).toFixed(2)}T` : fmtNum(n); }
|
|
@@ -57,9 +62,9 @@ export function positionCells(r, mode = 'price', supply = null) {
|
|
|
57
62
|
if (r.realizedSol !== 0)
|
|
58
63
|
cells.push({ label: 'REALIZED', value: usd && r.realizedUsd !== null ? signedUsd(r.realizedUsd) : `${signedSol(r.realizedSol)} SOL`, tone: tone(r.realizedSol) });
|
|
59
64
|
if (r.stopUsd !== null)
|
|
60
|
-
cells.push({ label: 'STOP', value: `${
|
|
65
|
+
cells.push({ label: 'STOP', value: `${levelCell(mode, supply, r.stopUsd)}${r.stopDistPct !== null ? ` ${signedPctOf(r.stopDistPct)}` : ''}`, tone: 'down' });
|
|
61
66
|
if (r.targetUsd !== null)
|
|
62
|
-
cells.push({ label: 'TARGET', value: `${
|
|
67
|
+
cells.push({ label: 'TARGET', value: `${levelCell(mode, supply, r.targetUsd)}${r.targetDistPct !== null ? ` ${signedPctOf(r.targetDistPct)}` : ''}`, tone: 'up' });
|
|
63
68
|
return cells;
|
|
64
69
|
}
|
|
65
70
|
/** The chart's position model from the shared task-1323 selector (PositionSource → positionRow). */
|
|
@@ -138,7 +143,7 @@ export function chartVMFromView(v, x = {}) {
|
|
|
138
143
|
vm.feed = x.feed ?? 'POLL';
|
|
139
144
|
vm.livePrice = x.feed === 'LIVE' && finitePos(x.livePrice) ? x.livePrice : null;
|
|
140
145
|
vm.trades = v.trades.map((t) => ({ t: t.t, side: t.side, usd: t.usd, price: t.price })); // no wallet addresses
|
|
141
|
-
const mode = x.priceMode === '
|
|
146
|
+
const mode = x.priceMode === 'price' ? 'price' : 'mcap'; // task-1537: market cap unless the host says price
|
|
142
147
|
const supply = mode === 'mcap' ? supplyRatio(v.price, v.mcap) : null;
|
|
143
148
|
vm.position = x.held ? chartPositionFrom(x.held, tick, x.solUsd ?? null, x.positionKind ?? 'paper', x.fills ?? [], mode, supply) : null;
|
|
144
149
|
vm.prefs = x.prefs ?? DEFAULT_CHART_PREFS;
|
|
@@ -147,6 +152,8 @@ export function chartVMFromView(v, x = {}) {
|
|
|
147
152
|
// task-1366: display only — the bars stay USD prices; the page formats the axis as price × supply in mcap mode
|
|
148
153
|
vm.priceMode = mode;
|
|
149
154
|
vm.supply = supply;
|
|
155
|
+
if (x.marks)
|
|
156
|
+
vm.marks = chartMarksFrom(x.marks.on, x.marks.fills); // task-1483
|
|
150
157
|
return vm;
|
|
151
158
|
}
|
|
152
159
|
/** Build the token-detail popup view model from the live TokenView (reused by the openPopup interface / Jarvis). */
|
|
@@ -447,6 +454,14 @@ export class TokenPopups {
|
|
|
447
454
|
this.push(this.lastView);
|
|
448
455
|
return;
|
|
449
456
|
}
|
|
457
|
+
case 'chart.markers': { // task-1483: a display preference the terminal saves; nothing else
|
|
458
|
+
if (typeof p.on !== 'boolean')
|
|
459
|
+
return;
|
|
460
|
+
this.source?.setChartMarkers?.(p.on);
|
|
461
|
+
if (this.lastView)
|
|
462
|
+
this.push(this.lastView);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
450
465
|
case 'chart.painted': {
|
|
451
466
|
const ms = Number(p.ms);
|
|
452
467
|
if (p.kind === 'first' && this.metrics.openedAt !== null && this.metrics.openToFirstPaintMs === null)
|
|
@@ -535,7 +550,8 @@ export class TokenPopups {
|
|
|
535
550
|
prefs: this.prefs,
|
|
536
551
|
now: this.now(),
|
|
537
552
|
image: src.imagePath?.(view) ?? null,
|
|
538
|
-
priceMode: src.chartAxis?.() ?? src.priceMode?.() ?? '
|
|
553
|
+
priceMode: src.chartAxis?.() ?? src.priceMode?.() ?? 'mcap', // task-1472: the chart axis drives the popup
|
|
554
|
+
marks: src.markFills ? { on: src.chartMarkers?.() ?? true, fills: src.markFills(view.mint) } : null, // task-1483
|
|
539
555
|
});
|
|
540
556
|
vm.chip = chartChipFrom(src.pnlChip?.() ?? null); // task-1473 r2
|
|
541
557
|
const key = vm.seriesKey ?? null;
|