dexterm-cli 0.1.5 → 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/index.js +2 -0
- package/dist/packages/agents/src/schema.js +22 -1
- package/dist/packages/agents/src/spectator.js +54 -0
- package/dist/packages/agents/src/story.js +532 -0
- package/dist/packages/agents/src/tiers.js +28 -2
- package/dist/packages/agents/src/tools.js +31 -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/REGISTRY.json +1 -1
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +15 -8
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
- package/dist/packages/cli/src/agent-cmd.js +6 -4
- package/dist/packages/cli/src/agent-spectator.js +17 -1
- package/dist/packages/cli/src/ai-cmd.js +412 -0
- package/dist/packages/cli/src/analyst/backend.js +105 -15
- package/dist/packages/cli/src/analyst/codex.js +14 -9
- package/dist/packages/cli/src/analyst/larp-gauge.js +60 -0
- package/dist/packages/cli/src/analyst/ollama.js +263 -0
- package/dist/packages/cli/src/analyst/session.js +17 -3
- package/dist/packages/cli/src/app-help-gen.js +51 -15
- 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/backtest-cmd.js +13 -2
- package/dist/packages/cli/src/display.js +208 -23
- 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/dev-history.js +31 -15
- package/dist/packages/cli/src/live/index.js +28 -90
- package/dist/packages/cli/src/live/screens/newpairs-live.js +1 -1
- package/dist/packages/cli/src/live/screens/token-live.js +48 -15
- package/dist/packages/cli/src/live/screens/watchlist-live.js +1 -1
- package/dist/packages/cli/src/live/trade.js +30 -453
- package/dist/packages/cli/src/live/watchlist.js +1 -1
- package/dist/packages/cli/src/main.js +290 -199
- package/dist/packages/cli/src/mcp-cmd.js +7 -1
- package/dist/packages/cli/src/mcp-follow.js +103 -6
- package/dist/packages/cli/src/plan-cmd.js +15 -5
- 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 +38 -6
- package/dist/packages/cli/src/record-cmd.js +141 -20
- package/dist/packages/cli/src/scan/ui.js +7 -1
- package/dist/packages/cli/src/screens/agent-hud.js +773 -0
- package/dist/packages/cli/src/screens/analyst-session.js +11 -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 +634 -0
- package/dist/packages/cli/src/screens/newpairs.js +1 -1
- 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 +144 -0
- package/dist/packages/cli/src/screens/positions.js +6 -28
- package/dist/packages/cli/src/screens/screener.js +1 -0
- package/dist/packages/cli/src/screens/ticket.js +38 -205
- package/dist/packages/cli/src/screens/token.js +18 -9
- package/dist/packages/cli/src/screens/trending.js +1 -1
- package/dist/packages/cli/src/state.js +88 -18
- 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 +41 -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 +2 -14
- package/dist/packages/data/src/larp/score.js +220 -0
- package/dist/packages/data/src/larp/x-account.js +316 -0
- package/dist/packages/data/src/store.js +14 -5
- package/dist/packages/data/src/trade/agent/paper-desk.js +300 -23
- 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/engine.js +7 -0
- package/dist/packages/data/src/trade/plans/index.js +0 -1
- package/dist/packages/data/src/trade/plans/mandate.js +28 -15
- package/dist/packages/data/src/trade/plans/service.js +4 -28
- 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/dev-history.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/larp-check.md +5 -1
- 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/scan-mode.md +1 -1
- 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 +6 -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/spectate-an-agent.md +2 -1
- 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 +7 -3
- 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-starting-a-run.md +2 -0
- 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 +32 -0
- 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 +9 -7
- 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-running-the-tests.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-the-larp-score.md +60 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +0 -2
- package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-larp.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 +4 -13
- 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 +40 -0
- 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-market-cap-axis.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-dev-history.md +10 -7
- 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-follow-over-mcp-in-the-running-terminal.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-agent-hud-spectator-v2.md +58 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +46 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-spectator.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 +5 -2
- package/dist/packages/mcp/src/server.js +285 -36
- package/dist/packages/mcp/src/spectator-sink.js +85 -0
- package/dist/packages/packs/src/backtest.js +3 -1
- package/dist/packages/packs/src/index.js +1 -0
- package/dist/packages/packs/src/larp-score-backtest.js +119 -0
- package/dist/packages/popup/src/pages/chart-lib.js +169 -35
- package/dist/packages/popup/src/pages/chart.css +18 -0
- package/dist/packages/popup/src/pages/chart.html +6 -1
- package/dist/packages/popup/src/pages/chart.js +136 -10
- package/dist/packages/popup/src/pages/spectator.css +45 -0
- package/dist/packages/popup/src/pages/spectator.html +1 -0
- package/dist/packages/popup/src/pages/spectator.js +69 -0
- package/dist/packages/popup/src/protocol.js +4 -0
- package/dist/packages/popup/src/server.js +9 -1
- package/dist/packages/recorder/src/dev-history.js +7 -3
- 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/canvas.js +7 -0
- package/dist/packages/tui/src/fmt.js +53 -0
- package/dist/packages/tui/src/index.js +1 -0
- package/dist/packages/tui/src/keymap.js +25 -1
- package/dist/packages/tui/src/markers.js +219 -0
- package/dist/packages/tui/src/widgets.js +158 -6
- 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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { creatorOf, devHistoryLine, queryDevHistory } from '../../../recorder/src/index.js';
|
|
1
|
+
import { ageShort, creatorOf, devHistoryLine, queryDevHistory } from '../../../recorder/src/index.js';
|
|
2
2
|
export const DEV_TTL_MS = 60_000;
|
|
3
3
|
/** Short creator/funder address for the panel (never a full address in a 40-column cell). */
|
|
4
4
|
export const shortAddr = (a) => (a ? `${a.slice(0, 4)}…${a.slice(-4)}` : '—');
|
|
@@ -14,9 +14,10 @@ export class DevHistoryBook {
|
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* The entry for `mint` (cached for `ttlMs`). `creatorHint` is the dev wallet a source named on screen (Jupiter
|
|
17
|
-
* `dev`) for a coin the recorder has not linked yet; `intel` is the coin's settled wallet-intel scan, if any
|
|
17
|
+
* `dev`) for a coin the recorder has not linked yet; `intel` is the coin's settled wallet-intel scan, if any, and
|
|
18
|
+
* `intelAt` when that scan settled (the linked row's age).
|
|
18
19
|
*/
|
|
19
|
-
forMint(mint, creatorHint, intel) {
|
|
20
|
+
forMint(mint, creatorHint, intel, intelAt) {
|
|
20
21
|
const now = this.now();
|
|
21
22
|
const hit = this.cache.get(mint);
|
|
22
23
|
if (hit && now - hit.at < this.ttlMs && (hit.creator || !creatorHint) && (hit.linked || !intel))
|
|
@@ -36,7 +37,7 @@ export class DevHistoryBook {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
if (creator && intel)
|
|
39
|
-
linked = linkedFrom(intel, creator.creator, db);
|
|
40
|
+
linked = linkedFrom(intel, creator.creator, db, intelAt ?? null);
|
|
40
41
|
const line = !db ? 'unknown: the history recorder is off (--no-recorder / DEXTERM_RECORDER=0)'
|
|
41
42
|
: !creator ? 'unknown: no source named the creator yet'
|
|
42
43
|
: report ? devHistoryLine(report) : 'unknown: dev history unreadable';
|
|
@@ -53,11 +54,11 @@ export class DevHistoryBook {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
/** Linked wallets from a settled wallet-intel scan: the creator's funder and any multi-wallet cluster it sits in. */
|
|
56
|
-
export function linkedFrom(a, creator, db) {
|
|
57
|
+
export function linkedFrom(a, creator, db, at = null) {
|
|
57
58
|
const h = a.holders.find((x) => x.owner === creator);
|
|
58
59
|
const fundedBy = h?.funder?.address ?? a.funding[creator]?.funder ?? null;
|
|
59
60
|
const c = a.clusters.find((x) => x.members.includes(creator) && x.members.length > 1) ?? null;
|
|
60
|
-
let linkedCreators = 0, linkedRugs = 0;
|
|
61
|
+
let linkedCreators = 0, linkedRugs = 0, linkedNewestT = null;
|
|
61
62
|
if (c && db) {
|
|
62
63
|
try {
|
|
63
64
|
const others = c.members.filter((m) => m !== creator).slice(0, 50);
|
|
@@ -66,25 +67,40 @@ export function linkedFrom(a, creator, db) {
|
|
|
66
67
|
if (Number(launched.get(w).n) <= 0)
|
|
67
68
|
continue;
|
|
68
69
|
linkedCreators++;
|
|
69
|
-
|
|
70
|
+
const r = queryDevHistory(db, { creator: w, devExit: false });
|
|
71
|
+
linkedRugs += r.labelled.rug;
|
|
72
|
+
if (r.labelled.newestT !== null)
|
|
73
|
+
linkedNewestT = Math.max(linkedNewestT ?? 0, r.labelled.newestT);
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
catch { /* an old DB without the table: nothing linked */ }
|
|
73
77
|
}
|
|
74
|
-
return { fundedBy, cluster: c ? { name: c.name, kind: c.kind, members: c.members.length, pct: c.pct } : null, linkedCreators, linkedRugs, source: h || c || a.funding[creator] ? 'wallet-intel scan' : null };
|
|
78
|
+
return { fundedBy, cluster: c ? { name: c.name, kind: c.kind, members: c.members.length, pct: c.pct } : null, linkedCreators, linkedRugs, linkedNewestT, source: h || c || a.funding[creator] ? 'wallet-intel scan' : null, at };
|
|
75
79
|
}
|
|
76
|
-
/**
|
|
77
|
-
|
|
80
|
+
/** Short source tags for a panel cell: `rec` = dexterm's recorder, `scan` = the wallet-intel scan; creator links by feed. */
|
|
81
|
+
const LINK_TAG = { 'jupiter-tokens': 'jup', pumpportal: 'pump', rugcheck: 'rc' };
|
|
82
|
+
const linkTag = (src) => (src ? LINK_TAG[src] ?? src : '?');
|
|
83
|
+
const ageAt = (t, now) => ageShort(t === null || t === undefined ? null : Math.max(0, Math.round((now - t) / 1000)));
|
|
84
|
+
/**
|
|
85
|
+
* Rows for the token screen's DEV block: [label, value, level]. Every value names its source, sample and age (or
|
|
86
|
+
* says unknown): `rec` = dexterm's recorder, `scan` = the wallet-intel scan, `dev:jup` = which feed named the creator.
|
|
87
|
+
* Ages are measured from `now` (default: when the entry was read): the newest label / sighting / exit / scan behind it.
|
|
88
|
+
*/
|
|
89
|
+
export function devRows(e, now = e.at) {
|
|
78
90
|
const rows = [];
|
|
79
91
|
const r = e.report;
|
|
80
92
|
const rugLevel = () => !r || r.labelled.n === 0 ? 'na' : r.serialRugger ? 'bad' : r.labelled.rug > 0 || (r.badRate ?? 0) >= 0.5 ? 'warn' : 'ok';
|
|
81
|
-
rows.push(['Dev rugs
|
|
82
|
-
rows.push(['Dev launches
|
|
93
|
+
rows.push(['Dev rugs', r && r.labelled.n ? `${r.labelled.rug}/${r.labelled.n} · n=${r.labelled.n} · rec ${ageAt(r.labelled.newestT, now)}` : 'unknown', rugLevel()]);
|
|
94
|
+
rows.push(['Dev launches', r && r.status === 'ok' ? `n=${r.launches.n} · rec ${ageAt(r.launches.newestT, now)} · dev:${linkTag(e.creator?.source)}` : 'unknown', 'na']);
|
|
83
95
|
if (r && r.devExit.n)
|
|
84
|
-
rows.push(['Dev sold
|
|
96
|
+
rows.push(['Dev sold bag', `med ${Math.round(r.devExit.medianMin)}m · n=${r.devExit.n} · rec ${ageAt(r.devExit.newestT, now)}`, r.devExit.medianMin <= 60 ? 'warn' : 'ok']);
|
|
85
97
|
const l = e.linked;
|
|
86
|
-
if (l && (l.fundedBy || l.cluster))
|
|
87
|
-
|
|
98
|
+
if (l && (l.fundedBy || l.cluster)) {
|
|
99
|
+
const scan = `scan ${ageAt(l.at, now)}`;
|
|
100
|
+
rows.push(['Dev linked', l.cluster ? `${l.cluster.kind} ${l.cluster.members}w · ${scan}` : `funded by ${shortAddr(l.fundedBy)} · ${scan}`, 'na']);
|
|
101
|
+
if (l.linkedCreators)
|
|
102
|
+
rows.push(['Linked dev rugs', `${l.linkedRugs} · n=${l.linkedCreators} devs · rec ${ageAt(l.linkedNewestT, now)}`, l.linkedRugs > 0 ? 'bad' : 'na']);
|
|
103
|
+
}
|
|
88
104
|
return rows;
|
|
89
105
|
}
|
|
90
106
|
/** The app's one book (created on first use), reading the live recorder's DB. Null when there is no recorder. */
|