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
|
@@ -88,9 +88,94 @@ export function recordForToolCall(opts) {
|
|
|
88
88
|
agent: opts.agent, transport: 'mcp', tool: opts.tool,
|
|
89
89
|
summary: isErr ? `error: ${String(opts.result.error).slice(0, 80)}` : paperSummary(opts.tool, opts.args, opts.result) ?? `${opts.tool}${shortMint(opts.args)}`,
|
|
90
90
|
ms: opts.ms, dataAgeSec: isErr ? null : maxAgeSec(opts.result), nav, ok: !isErr,
|
|
91
|
+
...(() => { const d = detailFor(opts.tool, opts.args, opts.result); return d ? { detail: d } : {}; })(),
|
|
91
92
|
...(opts.token ? { token: opts.token } : {}),
|
|
92
93
|
};
|
|
93
94
|
}
|
|
95
|
+
const num = (x) => (typeof x === 'number' && Number.isFinite(x) ? x : null);
|
|
96
|
+
/** A firewalled field's inner text (`<untrusted …>X</untrusted>` → X; the inner text is already scrubbed upstream). */
|
|
97
|
+
const unwrap = (x) => x.replace(/^<untrusted\b[^>]*>([\s\S]*?)<\/untrusted>$/, '$1');
|
|
98
|
+
const str = (x, max = 16) => (typeof x === 'string' && x ? unwrap(x).slice(0, max) || null : null);
|
|
99
|
+
const MINT_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
100
|
+
const mintOf = (x) => (typeof x === 'string' && MINT_RE.test(x) ? x : null);
|
|
101
|
+
/**
|
|
102
|
+
* task-1466: the app-computed numbers behind one call, for the follow view (SpectatorDetail in @dexterm/agents).
|
|
103
|
+
* Copied from the tool's OWN result (a screener page, a verdict, a PAPER ticket / fill / position, the caps the
|
|
104
|
+
* desk replayed); agent-supplied args only name the coin. null when the call has nothing worth showing.
|
|
105
|
+
*/
|
|
106
|
+
export function detailFor(tool, args, result) {
|
|
107
|
+
const d = {};
|
|
108
|
+
if (typeof result.error === 'string') {
|
|
109
|
+
if (tool.startsWith('paper_') && typeof result.reason === 'string')
|
|
110
|
+
d.refusal = result.reason.slice(0, 40);
|
|
111
|
+
return Object.keys(d).length ? d : null;
|
|
112
|
+
}
|
|
113
|
+
if (tool === 'screener_query' && Array.isArray(result.rows)) {
|
|
114
|
+
d.rows = result.rows.slice(0, 12).flatMap((r) => {
|
|
115
|
+
const mint = mintOf(r.mint);
|
|
116
|
+
if (!mint)
|
|
117
|
+
return [];
|
|
118
|
+
const pc = (r.priceChange ?? {});
|
|
119
|
+
return [{ mint, symbol: str(r.symbol) ?? '?', chg: num(pc.h1) }];
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (tool === 'screen_state' && Array.isArray(result.visible)) {
|
|
123
|
+
d.rows = result.visible.slice(0, 12).flatMap((r) => {
|
|
124
|
+
const mint = mintOf(r.mint);
|
|
125
|
+
return mint ? [{ mint, symbol: str(r.symbol) ?? '?', chg: num(r.chg1h) }] : [];
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (tool === 'get_token') {
|
|
129
|
+
const tk = (result.token ?? {});
|
|
130
|
+
const pair = (result.pair ?? {});
|
|
131
|
+
const mint = mintOf(tk.mint) ?? mintOf(args.mint);
|
|
132
|
+
if (mint)
|
|
133
|
+
d.coin = { mint, symbol: str(tk.symbol), priceUsd: num(pair.priceUsd) };
|
|
134
|
+
}
|
|
135
|
+
else if (tool === 'analyze_token') {
|
|
136
|
+
const mint = mintOf(result.mint) ?? mintOf(args.mint);
|
|
137
|
+
if (mint)
|
|
138
|
+
d.coin = { mint, symbol: str(result.symbol) };
|
|
139
|
+
const label = str(result.deterministic_verdict, 18);
|
|
140
|
+
if (label)
|
|
141
|
+
d.verdict = { label, rug: num(result.rug_probability) };
|
|
142
|
+
}
|
|
143
|
+
else if (tool === 'larp_check') {
|
|
144
|
+
const mint = mintOf(result.mint) ?? mintOf(args.mint);
|
|
145
|
+
if (mint)
|
|
146
|
+
d.coin = { mint, symbol: str(result.symbol) };
|
|
147
|
+
const flags = Array.isArray(result.flags) ? result.flags.filter((f) => typeof f === 'string') : [];
|
|
148
|
+
d.larp = { flags: flags.length, items: flags.slice(0, 6).map((f) => f.slice(0, 60)) };
|
|
149
|
+
}
|
|
150
|
+
else if (['open_token', 'get_trades', 'get_candles', 'get_holders', 'rugcheck', 'get_dev_history', 'dev_history', 'get_dossier'].includes(tool)) {
|
|
151
|
+
const mint = mintOf(args.mint);
|
|
152
|
+
if (mint)
|
|
153
|
+
d.coin = { mint, symbol: str(args.symbol) };
|
|
154
|
+
}
|
|
155
|
+
if (tool === 'paper_open_ticket' || tool === 'paper_size_ticket') {
|
|
156
|
+
const t = (result.ticket ?? {});
|
|
157
|
+
const mint = mintOf(t.mint);
|
|
158
|
+
if (mint && (t.side === 'buy' || t.side === 'sell'))
|
|
159
|
+
d.ticket = { side: t.side, symbol: str(t.symbol) ?? '?', mint, usd: num(t.usd), fraction: num(t.fraction) };
|
|
160
|
+
}
|
|
161
|
+
if (tool === 'paper_confirm' || tool === 'paper_close') {
|
|
162
|
+
const f = (result.fill ?? {});
|
|
163
|
+
const mint = mintOf(f.mint);
|
|
164
|
+
if (mint && (f.side === 'buy' || f.side === 'sell') && num(f.solDelta) !== null && num(f.tokens) !== null) {
|
|
165
|
+
d.fill = { side: f.side, symbol: str(f.symbol) ?? '?', mint, sol: num(f.solDelta), qty: num(f.tokens), priceUsd: num(f.priceUsd), impactPct: num(f.priceImpactPct), realizedSol: num(f.realizedSol) };
|
|
166
|
+
}
|
|
167
|
+
const p = (result.position ?? null);
|
|
168
|
+
const pm = p ? mintOf(p.mint) : null;
|
|
169
|
+
if (p && pm && num(p.tokens) !== null && num(p.costSol) !== null)
|
|
170
|
+
d.position = { mint: pm, symbol: str(p.symbol) ?? '?', qty: num(p.tokens), costSol: num(p.costSol), realizedSol: num(p.realizedSol) ?? 0 };
|
|
171
|
+
}
|
|
172
|
+
if (tool === 'paper_pnl') {
|
|
173
|
+
const m = (result.mandate ?? null);
|
|
174
|
+
if (m && num(m.perTradeUsd) !== null && num(m.totalUsd) !== null)
|
|
175
|
+
d.caps = { perTradeUsd: num(m.perTradeUsd), totalUsd: num(m.totalUsd), maxPriceImpactPct: num(m.maxPriceImpactPct) };
|
|
176
|
+
}
|
|
177
|
+
return Object.keys(d).length ? d : null;
|
|
178
|
+
}
|
|
94
179
|
export function fileSpectatorSinkFromEnv(env) {
|
|
95
180
|
const file = env.DEXTERM_SPECTATOR_FILE;
|
|
96
181
|
if (!file)
|
|
@@ -87,7 +87,9 @@ export function runBacktest(doc, req, now = Date.now()) {
|
|
|
87
87
|
leakage_checks: { ...leakageOK },
|
|
88
88
|
};
|
|
89
89
|
if (req.kind === 'strategy') {
|
|
90
|
-
|
|
90
|
+
// task-1503: a strategy is scored by the @dexterm/autotrade engine over a replay tape (positions, fills,
|
|
91
|
+
// exits), not by this t0-features harness; `dexterm backtest --subject strategy` routes there.
|
|
92
|
+
return { n: rows.length, base_rate: 0, precision: null, recall: null, brier: null, brier_base_rate: null, auc: null, ...base, note: 'strategy/paper subject not implemented in this t0 harness: it runs on the autotrade engine over a replay tape (dexterm backtest --subject strategy --strategy <id>)' };
|
|
91
93
|
}
|
|
92
94
|
if (req.kind === 'watch_rule') {
|
|
93
95
|
return { n: rows.length, base_rate: 0, precision: null, recall: null, brier: null, brier_base_rate: null, auc: null, ...base, note: 'watch_rule subject not implemented in this tier (needs the rules engine, lane W-01)' };
|
|
@@ -18,5 +18,6 @@ export * from "./backtest.js";
|
|
|
18
18
|
// task-1359: LARP eval (recorded product verdicts vs outcome labels; counts only below n = 50).
|
|
19
19
|
export * from "./larp-eval.js";
|
|
20
20
|
export * from "./skill-backtest.js";
|
|
21
|
+
export * from "./larp-score-backtest.js";
|
|
21
22
|
// task-1414: skill care — rolling split, the proposal gate (new-coins + interval-noise + frozen canary), trend.
|
|
22
23
|
export * from "./skill-care.js";
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// LARP score backtest + calibration (task-1471). Pure: input is the task-1401 eval set (skills-eval.json).
|
|
2
|
+
//
|
|
3
|
+
// The eval set holds, per coin, the URL-free link features the coin advertised at t0 and the recorder's 72 h
|
|
4
|
+
// outcome (bad = rug or dead). That is the only part of the LARP score history can replay: pages, domains, GitHub
|
|
5
|
+
// and X accounts were not captured point-in-time (and looking them up now leaks the outcome, because dead coins'
|
|
6
|
+
// sites and accounts disappear). So this fits and scores the LINK-SHAPE flags only, and says so in every output.
|
|
7
|
+
//
|
|
8
|
+
// Pre-registered rule (written before the test split was scored):
|
|
9
|
+
// - fit on TRAIN only: for each link flag carried by ≥ LARP_SCORE_MIN_FLAG_N train coins,
|
|
10
|
+
// points = clamp(round(8 × ln OR), 0, 12), OR = Haldane-smoothed odds ratio of a bad outcome with vs without it;
|
|
11
|
+
// flags under that count keep their prior points;
|
|
12
|
+
// - bands are the fixed round thresholds 25 / 50 (LARP_SCORE_BANDS), never tuned;
|
|
13
|
+
// - report AUROC (ties count half) and P(bad) per band on train and on the held-out TEST split, with Wilson 95 %.
|
|
14
|
+
import { LARP_SCORE_BANDS, LARP_SCORE_WEIGHTS, bandOf, linkFlagIds, linkScore } from '../../data/src/index.js';
|
|
15
|
+
import { wilson } from "./larp-eval.js";
|
|
16
|
+
export const LARP_SCORE_BACKTEST_SCHEMA = 'dexterm/packs/larp-score-backtest/1';
|
|
17
|
+
export const LARP_SCORE_MIN_FLAG_N = 20;
|
|
18
|
+
const SCALE = 8;
|
|
19
|
+
const CAP = 12;
|
|
20
|
+
/** The link-shape flags the eval set can carry (in a stable order). */
|
|
21
|
+
export const LINK_FLAG_IDS = ['no_website', 'no_x', 'x_not_account', 'no_telegram', 'site_is_social', 'site_is_launchpad', 'site_linktree', 'site_free_host', 'domain_no_name_match', 'cheap_tld'];
|
|
22
|
+
/** Fit the link-flag points on train rows. Throws on a test row (the split is fixed before tuning). */
|
|
23
|
+
export function calibrateLinkPoints(train) {
|
|
24
|
+
if (train.some((r) => r.split !== 'train'))
|
|
25
|
+
throw new Error('calibrateLinkPoints: test rows are held out');
|
|
26
|
+
const N = train.length, B = train.filter((r) => r.bad).length;
|
|
27
|
+
return LINK_FLAG_IDS.map((id) => {
|
|
28
|
+
const has = train.filter((r) => linkFlagIds(r.link).includes(id));
|
|
29
|
+
const n = has.length, a = has.filter((r) => r.bad).length;
|
|
30
|
+
if (n < LARP_SCORE_MIN_FLAG_N)
|
|
31
|
+
return { id, n, bad: a, ln_or: null, points: LARP_SCORE_WEIGHTS[id].points, source: 'prior' };
|
|
32
|
+
const b = B - a, m = N - n;
|
|
33
|
+
const lnOr = Math.log(((a + 0.5) / (n - a + 0.5)) / ((b + 0.5) / (m - b + 0.5)));
|
|
34
|
+
return { id, n, bad: a, ln_or: Math.round(lnOr * 1000) / 1000, points: Math.max(0, Math.min(CAP, Math.round(SCALE * lnOr))), source: 'calibrated' };
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/** AUROC with ties counted half. Null when a class is empty. */
|
|
38
|
+
export function auroc(rows) {
|
|
39
|
+
const pos = rows.filter((r) => r.bad).map((r) => r.score), neg = rows.filter((r) => !r.bad).map((r) => r.score);
|
|
40
|
+
if (!pos.length || !neg.length)
|
|
41
|
+
return null;
|
|
42
|
+
let s = 0;
|
|
43
|
+
for (const p of pos)
|
|
44
|
+
for (const q of neg)
|
|
45
|
+
s += p > q ? 1 : p === q ? 0.5 : 0;
|
|
46
|
+
return s / (pos.length * neg.length);
|
|
47
|
+
}
|
|
48
|
+
function splitReport(rows, weights) {
|
|
49
|
+
const scored = rows.map((r) => ({ score: linkScore(r.link, weights), bad: r.bad }));
|
|
50
|
+
const by_band = { low: { n: 0, bad: 0, rate: null }, medium: { n: 0, bad: 0, rate: null }, high: { n: 0, bad: 0, rate: null } };
|
|
51
|
+
for (const s of scored) {
|
|
52
|
+
const b = by_band[bandOf(s.score)];
|
|
53
|
+
b.n++;
|
|
54
|
+
if (s.bad)
|
|
55
|
+
b.bad++;
|
|
56
|
+
}
|
|
57
|
+
for (const b of Object.values(by_band))
|
|
58
|
+
b.rate = b.n >= 20 ? wilson(b.bad, b.n) : null;
|
|
59
|
+
const B = scored.filter((s) => s.bad).length;
|
|
60
|
+
const at_threshold = [LARP_SCORE_BANDS.medium, LARP_SCORE_BANDS.high].map((t) => {
|
|
61
|
+
const f = scored.filter((s) => s.score >= t), tp = f.filter((s) => s.bad).length;
|
|
62
|
+
return { threshold: t, flagged: f.length, precision: f.length >= 20 ? wilson(tp, f.length) : null, recall: B ? wilson(tp, B) : null };
|
|
63
|
+
});
|
|
64
|
+
const sorted = scored.map((s) => s.score).sort((a, b) => a - b);
|
|
65
|
+
const q = (p) => sorted.length ? sorted[Math.min(sorted.length - 1, Math.floor(p * sorted.length))] : 0;
|
|
66
|
+
const a = auroc(scored);
|
|
67
|
+
return { n: rows.length, bad: B, auroc: a === null ? null : Math.round(a * 1000) / 1000, by_band, at_threshold, score_quantiles: { p10: q(0.1), p50: q(0.5), p90: q(0.9), max: sorted.at(-1) ?? 0 } };
|
|
68
|
+
}
|
|
69
|
+
/** Fit on train, score train and test with the fitted points. */
|
|
70
|
+
export function larpScoreBacktest(set) {
|
|
71
|
+
const train = set.rows.filter((r) => r.split === 'train'), test = set.rows.filter((r) => r.split === 'test');
|
|
72
|
+
const fit = calibrateLinkPoints(train);
|
|
73
|
+
const weights = Object.fromEntries(fit.map((f) => [f.id, f.points]));
|
|
74
|
+
return {
|
|
75
|
+
schema: LARP_SCORE_BACKTEST_SCHEMA,
|
|
76
|
+
eval: { built_at: set.built_at, horizon_sec: set.horizon_sec, rows: set.rows.length, label_rule: set.label_rule, split_rule: set.split_rule },
|
|
77
|
+
part: 'link-shape flags only (what the eval set holds point-in-time)',
|
|
78
|
+
method: [
|
|
79
|
+
`fit on the train split only (${train.length} coins): points = clamp(round(${SCALE} × ln odds ratio of a bad outcome), 0, ${CAP}) for a flag on ≥ ${LARP_SCORE_MIN_FLAG_N} train coins; rarer flags keep their prior points`,
|
|
80
|
+
`bands fixed before scoring: low < ${LARP_SCORE_BANDS.medium} ≤ medium < ${LARP_SCORE_BANDS.high} ≤ high (not tuned)`,
|
|
81
|
+
'AUROC with ties counted half; rates with 95% Wilson intervals, printed only for n ≥ 20',
|
|
82
|
+
'meaning: how well the link-shape points rank coins that went bad (a price outcome) above the rest, NOT whether a product is real',
|
|
83
|
+
],
|
|
84
|
+
fit,
|
|
85
|
+
shipped_matches_fit: fit.every((f) => LARP_SCORE_WEIGHTS[f.id].points === f.points && LARP_SCORE_WEIGHTS[f.id].source === f.source),
|
|
86
|
+
train: splitReport(train, weights),
|
|
87
|
+
test: splitReport(test, weights),
|
|
88
|
+
not_measured: [
|
|
89
|
+
'website page flags (default page, wallet connect, drainer markers, never names the token): pages were not captured at t0',
|
|
90
|
+
'domain flags (new domain, registrant privacy) and GitHub flags: not captured at t0',
|
|
91
|
+
"X-account flags (new account, tiny following, no posts, suspended, handle mismatch): not captured at t0, and a lookup today leaks the outcome (dead coins' accounts get deleted or suspended); the X sources' robots.txt also rule out bulk crawling them for a backfill",
|
|
92
|
+
'the high band: link shape alone tops out below it by design, so its precision is unmeasured',
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const pct = (r) => (r === null ? 'counts only (n < 20)' : `${(r.value * 100).toFixed(1)}% [${(r.lo * 100).toFixed(0)}–${(r.hi * 100).toFixed(0)}]`);
|
|
97
|
+
export function renderLarpScoreBacktestText(r) {
|
|
98
|
+
const L = [];
|
|
99
|
+
L.push(`LARP SCORE BACKTEST ${r.part}`);
|
|
100
|
+
L.push(` eval set built ${r.eval.built_at}, ${r.eval.rows} coins, horizon ${r.eval.horizon_sec}s; ${r.eval.label_rule}`);
|
|
101
|
+
L.push(' fitted points (train split):');
|
|
102
|
+
for (const f of r.fit)
|
|
103
|
+
L.push(` ${f.id.padEnd(22)} n=${String(f.n).padStart(4)} bad=${String(f.bad).padStart(4)} ln OR=${f.ln_or === null ? ' — ' : f.ln_or.toFixed(2).padStart(6)} → ${String(f.points).padStart(2)} pts (${f.source})`);
|
|
104
|
+
L.push(` shipped weights match this fit: ${r.shipped_matches_fit ? 'yes' : 'NO (re-tune or update LARP_SCORE_WEIGHTS)'}`);
|
|
105
|
+
for (const [name, s] of [['train', r.train], ['TEST (held out)', r.test]]) {
|
|
106
|
+
L.push(` ${name}: n=${s.n} bad=${s.bad} (${((s.bad / Math.max(1, s.n)) * 100).toFixed(1)}%) AUROC=${s.auroc === null ? '—' : s.auroc.toFixed(3)} score p10/p50/p90/max=${s.score_quantiles.p10}/${s.score_quantiles.p50}/${s.score_quantiles.p90}/${s.score_quantiles.max}`);
|
|
107
|
+
for (const [b, v] of Object.entries(s.by_band))
|
|
108
|
+
L.push(` band ${b.padEnd(6)} n=${String(v.n).padStart(4)} bad=${String(v.bad).padStart(4)} P(bad)=${pct(v.rate)}`);
|
|
109
|
+
for (const t of s.at_threshold)
|
|
110
|
+
L.push(` score ≥ ${t.threshold}: flagged ${t.flagged} precision=${pct(t.precision)} recall=${t.recall ? pct(t.recall) : '—'}`);
|
|
111
|
+
}
|
|
112
|
+
L.push(' method:');
|
|
113
|
+
for (const m of r.method)
|
|
114
|
+
L.push(` - ${m}`);
|
|
115
|
+
L.push(' not measured:');
|
|
116
|
+
for (const m of r.not_measured)
|
|
117
|
+
L.push(` - ${m}`);
|
|
118
|
+
return L.join('\n') + '\n';
|
|
119
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// dexterm popup — chart v2 pure helpers (task-1325). No DOM, no network, no globals besides the export.
|
|
2
2
|
// Inlined into the chart page (before chart.js) under the page's CSP nonce; test/chart-lib.test.ts loads the
|
|
3
3
|
// same file through node:vm to unit-test the math. Everything here works on OUR data only: the ChartVM's
|
|
4
|
-
// candles (from the terminal's cached/derived series), its trade tape and
|
|
4
|
+
// candles (from the terminal's cached/derived series), its trade tape and the ledgers' fills (task-1483 markers).
|
|
5
5
|
(function (root) {
|
|
6
6
|
'use strict';
|
|
7
7
|
const INTERVALS = ['1m', '5m', '15m', '1h', '4h', '1d'];
|
|
@@ -121,28 +121,141 @@
|
|
|
121
121
|
return out.sort((x, y) => x.time - y.time);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
function
|
|
124
|
+
// ---- task-1483 BUY / SELL markers: a mirror of @dexterm/tui markers.ts (test/chart-lib parity-tests the two). Fills
|
|
125
|
+
// are bucketed to the bars on screen by their REAL time (bar.t, UTC ms); a fill older than the first bar, newer than
|
|
126
|
+
// the last, or in a period with no bar is omitted and counted, never moved onto a neighbour.
|
|
127
|
+
const EPS = 1e-9;
|
|
128
|
+
function cleanFills(fills) {
|
|
129
129
|
const out = [];
|
|
130
|
-
let lastBar = -Infinity;
|
|
131
|
-
for (const t of barTimes) if (t > lastBar) lastBar = t;
|
|
132
130
|
for (const f of fills || []) {
|
|
133
|
-
if (!f || !fin(f.t) || (f.side !== 'buy' && f.side !== 'sell')) continue;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// a trading chart shows a fresh execution — up to 3 periods ahead; older gaps are skipped.
|
|
137
|
-
if (!barTimes.has(time) && time > lastBar && time - lastBar <= (3 * periodMs) / 1000) time = lastBar;
|
|
138
|
-
if (!barTimes.has(time)) continue;
|
|
139
|
-
const text = `${f.side === 'buy' ? 'BUY' : 'SELL'} ${fin(f.sol) ? fmtSol(f.sol) : ''}`.trim();
|
|
140
|
-
const m = { time, shape: f.side === 'buy' ? 'arrowUp' : 'arrowDown', color: f.side === 'buy' ? colors.buy : colors.sell, text, size: 1.4, id: `fill-${f.side}-${f.t}` };
|
|
141
|
-
if (fin(f.priceUsd) && f.priceUsd > 0) { m.position = f.side === 'buy' ? 'atPriceBottom' : 'atPriceTop'; m.price = f.priceUsd; }
|
|
142
|
-
else m.position = f.side === 'buy' ? 'belowBar' : 'aboveBar';
|
|
143
|
-
out.push(m);
|
|
131
|
+
if (!f || !fin(f.t) || (f.side !== 'buy' && f.side !== 'sell') || (f.source !== 'paper' && f.source !== 'live')) continue;
|
|
132
|
+
if (!fin(f.tokens) || f.tokens <= 0) continue;
|
|
133
|
+
out.push({ t: f.t, side: f.side, source: f.source, priceUsd: fin(f.priceUsd) && f.priceUsd > 0 ? f.priceUsd : null, tokens: f.tokens, sol: fin(f.sol) && f.sol >= 0 ? f.sol : null });
|
|
144
134
|
}
|
|
145
|
-
return out.sort((
|
|
135
|
+
return out.map((f, i) => [f, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([f]) => f);
|
|
136
|
+
}
|
|
137
|
+
/** Replay per source (average-cost ledger rule): partial / closing flags, the open average entry, closed round trips. */
|
|
138
|
+
function replayFills(fills) {
|
|
139
|
+
const st = { paper: { held: 0, cost: 0, costOk: true, trip: null }, live: { held: 0, cost: 0, costOk: true, trip: null } };
|
|
140
|
+
const out = [], trips = [];
|
|
141
|
+
for (const f of cleanFills(fills)) {
|
|
142
|
+
const s = st[f.source]; const before = s.held;
|
|
143
|
+
if (f.side === 'buy') {
|
|
144
|
+
if (s.held <= EPS) { s.held = 0; s.cost = 0; s.costOk = true; s.trip = { openT: f.t, bTok: 0, bUsd: 0, bOk: true, sTok: 0, sUsd: 0, sOk: true }; }
|
|
145
|
+
s.held += f.tokens;
|
|
146
|
+
if (f.priceUsd === null) s.costOk = false; else s.cost += f.tokens * f.priceUsd;
|
|
147
|
+
if (s.trip) { s.trip.bTok += f.tokens; if (f.priceUsd === null) s.trip.bOk = false; else s.trip.bUsd += f.tokens * f.priceUsd; }
|
|
148
|
+
out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: before > EPS, closes: false });
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const sold = Math.min(f.tokens, s.held);
|
|
152
|
+
const frac = s.held > EPS ? sold / s.held : 1;
|
|
153
|
+
s.cost -= s.cost * frac;
|
|
154
|
+
s.held = Math.max(0, s.held - f.tokens);
|
|
155
|
+
const closes = s.held <= f.tokens * 1e-9 || s.held <= EPS;
|
|
156
|
+
if (closes) s.held = 0;
|
|
157
|
+
if (s.trip) { s.trip.sTok += f.tokens; if (f.priceUsd === null) s.trip.sOk = false; else s.trip.sUsd += f.tokens * f.priceUsd; }
|
|
158
|
+
out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: !closes, closes });
|
|
159
|
+
if (closes) {
|
|
160
|
+
if (s.trip) trips.push({ source: f.source, openT: s.trip.openT, closeT: f.t, entryUsd: s.trip.bOk && s.trip.bTok > 0 ? s.trip.bUsd / s.trip.bTok : null, exitUsd: s.trip.sOk && s.trip.sTok > 0 ? s.trip.sUsd / s.trip.sTok : null });
|
|
161
|
+
s.trip = null; s.cost = 0; s.costOk = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const avg = (src) => { const s = st[src]; return s.held > EPS && s.costOk && s.cost > 0 ? s.cost / s.held : null; };
|
|
165
|
+
return { fills: out, avgEntry: { paper: avg('paper'), live: avg('live') }, trips };
|
|
166
|
+
}
|
|
167
|
+
function indexIn(times, t) { let lo = 0, hi = times.length - 1; while (lo <= hi) { const mid = (lo + hi) >> 1; const v = times[mid]; if (v === t) return mid; if (v < t) lo = mid + 1; else hi = mid - 1; } return -1; }
|
|
168
|
+
/** Markers for the bars (`barTimes` = the bars' real UTC ms, ascending): one per (bar, side, source). */
|
|
169
|
+
function chartMarkers(fills, periodMs, barTimes) {
|
|
170
|
+
const rp = replayFills(fills);
|
|
171
|
+
const res = { markers: [], before: 0, after: 0, gap: 0, avgEntry: rp.avgEntry, trips: [] };
|
|
172
|
+
if (!(periodMs > 0) || !barTimes.length) { res.before = rp.fills.length; return res; }
|
|
173
|
+
const first = barTimes[0], last = barTimes[barTimes.length - 1];
|
|
174
|
+
const by = new Map();
|
|
175
|
+
for (const f of rp.fills) {
|
|
176
|
+
const b = Math.floor(f.t / periodMs) * periodMs;
|
|
177
|
+
if (b < first) { res.before++; continue; }
|
|
178
|
+
if (b > last) { res.after++; continue; }
|
|
179
|
+
const index = indexIn(barTimes, b);
|
|
180
|
+
if (index < 0) { res.gap++; continue; }
|
|
181
|
+
const key = `${b}|${f.side}|${f.source}`;
|
|
182
|
+
let m = by.get(key);
|
|
183
|
+
if (!m) { m = { side: f.side, source: f.source, bucket: b, index, t: f.t, tLast: f.t, priceUsd: null, tokens: 0, sol: 0, count: 0, partial: false, closes: false, pTok: 0, pUsd: 0, solOk: true }; by.set(key, m); }
|
|
184
|
+
m.count++; m.tLast = f.t; m.tokens += f.tokens;
|
|
185
|
+
if (f.sol === null) m.solOk = false; else m.sol += f.sol;
|
|
186
|
+
if (f.priceUsd !== null) { m.pTok += f.tokens; m.pUsd += f.tokens * f.priceUsd; }
|
|
187
|
+
if (f.partial) m.partial = true;
|
|
188
|
+
if (f.closes) m.closes = true;
|
|
189
|
+
}
|
|
190
|
+
const order = (m) => (m.side === 'buy' ? 0 : 1) * 2 + (m.source === 'live' ? 1 : 0);
|
|
191
|
+
res.markers = [...by.values()].map(({ pTok, pUsd, solOk, ...m }) => ({ ...m, priceUsd: pTok > 0 ? pUsd / pTok : null, sol: solOk ? m.sol : null }))
|
|
192
|
+
.sort((a, b) => a.index - b.index || order(a) - order(b));
|
|
193
|
+
res.trips = rp.trips.filter((r) => Math.floor(r.closeT / periodMs) * periodMs >= first && Math.floor(r.openT / periodMs) * periodMs <= last);
|
|
194
|
+
return res;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Re-bucket on zoom: when bars are narrower than `minPx`, markers of the same side + source in each run of `k` bars
|
|
198
|
+
* merge into one (token-weighted price, summed size and count, at the run's last bar), so glyphs never pile up.
|
|
199
|
+
*/
|
|
200
|
+
function clusterMarkers(markers, barSpacingPx, minPx = 14) {
|
|
201
|
+
const k = fin(barSpacingPx) && barSpacingPx > 0 ? Math.max(1, Math.ceil(minPx / barSpacingPx)) : 1;
|
|
202
|
+
if (k === 1) return markers.map((m) => ({ ...m, span: 1 }));
|
|
203
|
+
const by = new Map();
|
|
204
|
+
for (const m of markers) {
|
|
205
|
+
const g = Math.floor(m.index / k);
|
|
206
|
+
const key = `${g}|${m.side}|${m.source}`;
|
|
207
|
+
const c = by.get(key);
|
|
208
|
+
if (!c) { by.set(key, { ...m, span: k, pTok: m.priceUsd !== null ? m.tokens : 0, pUsd: m.priceUsd !== null ? m.tokens * m.priceUsd : 0 }); continue; }
|
|
209
|
+
c.index = Math.max(c.index, m.index); c.bucket = Math.max(c.bucket, m.bucket);
|
|
210
|
+
c.t = Math.min(c.t, m.t); c.tLast = Math.max(c.tLast, m.tLast);
|
|
211
|
+
c.count += m.count; c.tokens += m.tokens; c.sol = c.sol === null || m.sol === null ? null : c.sol + m.sol;
|
|
212
|
+
if (m.priceUsd !== null) { c.pTok += m.tokens; c.pUsd += m.tokens * m.priceUsd; }
|
|
213
|
+
c.partial = c.partial || m.partial; c.closes = c.closes || m.closes;
|
|
214
|
+
}
|
|
215
|
+
return [...by.values()].map(({ pTok, pUsd, ...m }) => ({ ...m, priceUsd: pTok > 0 ? pUsd / pTok : null })).sort((a, b) => a.index - b.index);
|
|
216
|
+
}
|
|
217
|
+
/** Solid ▲ / ▼ = live, hollow △ / ▽ = paper; B / S always (reads without colour). */
|
|
218
|
+
function markerGlyph(side, source) { return source === 'paper' ? (side === 'buy' ? '△' : '▽') : (side === 'buy' ? '▲' : '▼'); }
|
|
219
|
+
function markerLetter(side) { return side === 'buy' ? 'B' : 'S'; }
|
|
220
|
+
/**
|
|
221
|
+
* Pixel geometry of one marker: the triangle's tip sits ON the fill price (a buy points up from below, a sell points
|
|
222
|
+
* down from above); with no price it sits against the bar (below the low / above the high). `x` = the bar's centre.
|
|
223
|
+
*/
|
|
224
|
+
function markerShape(side, x, y, size = 6) {
|
|
225
|
+
const s = size, buy = side === 'buy';
|
|
226
|
+
const base = buy ? y + s * 1.6 : y - s * 1.6;
|
|
227
|
+
const tri = [[x, y], [x - s, base], [x + s, base]];
|
|
228
|
+
const labelY = buy ? base + 9 : base - 9;
|
|
229
|
+
return { tri, labelX: x, labelY, hit: { x0: x - s - 3, x1: x + s + 3, y0: Math.min(y, labelY) - 7, y1: Math.max(y, labelY) + 7 } };
|
|
230
|
+
}
|
|
231
|
+
/** The hover tooltip lines for a marker (time, price — mcap and price on an mcap axis — size, source). */
|
|
232
|
+
function markerTooltip(m, display, timeFmt) {
|
|
233
|
+
const f = axisFormatter(display);
|
|
234
|
+
const px = m.priceUsd === null ? 'price not recorded' : display && display.mode === 'mcap' ? `MC ${f(m.priceUsd)} (${fmtPrice(m.priceUsd)})` : fmtPrice(m.priceUsd);
|
|
235
|
+
const when = m.count > 1 ? `${timeFmt(m.t)} – ${timeFmt(m.tLast)}` : timeFmt(m.t);
|
|
236
|
+
const kind = m.side === 'buy' ? (m.partial ? 'BUY (added)' : 'BUY') : (m.closes ? 'SELL (closed)' : 'SELL (partial)');
|
|
237
|
+
return [
|
|
238
|
+
`${markerGlyph(m.side, m.source)}${markerLetter(m.side)} ${kind}${m.count > 1 ? ` ×${m.count}` : ''}`,
|
|
239
|
+
when,
|
|
240
|
+
`${m.count > 1 && m.priceUsd !== null ? 'avg ' : ''}${px}`,
|
|
241
|
+
`${fmtTokens(m.tokens)} tokens${m.sol !== null ? ` · ${fmtSol(m.sol)}` : ''}`,
|
|
242
|
+
m.source === 'paper' ? 'PAPER · simulated ledger' : 'LIVE · confirmed on-chain fill',
|
|
243
|
+
];
|
|
244
|
+
}
|
|
245
|
+
function fmtTokens(n) { return !fin(n) ? '—' : n >= 1e12 ? `${(n / 1e12).toFixed(2)}T` : n >= 1e9 ? `${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e4 ? `${(n / 1e3).toFixed(1)}K` : n >= 1 ? String(Math.round(n * 100) / 100) : n.toPrecision(3); }
|
|
246
|
+
/** "△B ▽S paper ▲B ▼S live" for the sources on screen; "+N fills off-chart (…)" for what was omitted. */
|
|
247
|
+
function markerLegend(markers) {
|
|
248
|
+
const has = (s) => markers.some((m) => m.source === s);
|
|
249
|
+
return [has('paper') ? '△B ▽S paper' : '', has('live') ? '▲B ▼S live' : ''].filter(Boolean).join(' ');
|
|
250
|
+
}
|
|
251
|
+
function omittedNote(s) {
|
|
252
|
+
const n = s.before + s.after + s.gap;
|
|
253
|
+
if (!n) return '';
|
|
254
|
+
const why = [];
|
|
255
|
+
if (s.before) why.push(`${s.before} earlier`);
|
|
256
|
+
if (s.after) why.push(`${s.after} newer than the last candle`);
|
|
257
|
+
if (s.gap) why.push(`${s.gap} in a period with no candle`);
|
|
258
|
+
return `+${n} fill${n === 1 ? '' : 's'} off-chart (${why.join(', ')})`;
|
|
146
259
|
}
|
|
147
260
|
|
|
148
261
|
/** LWC price format for a price magnitude: enough decimals for memecoin prices (down to 1e-12). */
|
|
@@ -180,22 +293,43 @@
|
|
|
180
293
|
if (a >= 1e3) return `${s}$${(a / 1e3).toFixed(1)}K`;
|
|
181
294
|
return `${s}$${a.toFixed(0)}`;
|
|
182
295
|
}
|
|
183
|
-
// ---- task-
|
|
184
|
-
|
|
185
|
-
|
|
296
|
+
// ---- task-1472 the one chart number formatter (mirrors @dexterm/tui fmtCompact, parity-tested): lowercase k, then
|
|
297
|
+
// M/B/T; one decimal while < 10 of the unit, none after; the unit is promoted after rounding (999 900 → 1M); below 1
|
|
298
|
+
// the subscript price form (0.0₅493): never scientific notation. `extra` (0..3) keeps two axis ticks apart.
|
|
299
|
+
const CU = ['', 'k', 'M', 'B', 'T'];
|
|
300
|
+
const trimZeros = (t) => (t.includes('.') ? t.replace(/0+$/, '').replace(/\.$/, '') : t);
|
|
301
|
+
function tinyBare(p) {
|
|
302
|
+
if (p >= 0.01) return p.toFixed(4);
|
|
303
|
+
const m = p.toFixed(12).match(/^0\.(0*)(\d+)/);
|
|
304
|
+
if (!m) return String(p);
|
|
305
|
+
const zeros = m[1].length;
|
|
306
|
+
const digits = m[2].replace(/0+$/, '').slice(0, 4) || '0';
|
|
307
|
+
if (zeros < 3) return `0.${m[1]}${digits}`;
|
|
308
|
+
return `0.0${String(zeros).split('').map((c) => SUB[Number(c)]).join('')}${digits}`;
|
|
309
|
+
}
|
|
310
|
+
function fmtCompact(n, extra = 0) {
|
|
186
311
|
if (!fin(n)) return '—';
|
|
312
|
+
if (n === 0) return '0';
|
|
187
313
|
const sign = n < 0 ? '-' : '';
|
|
188
|
-
|
|
189
|
-
if (a
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
314
|
+
const a = Math.abs(n);
|
|
315
|
+
if (a < 1) return sign + trimZeros(tinyBare(a));
|
|
316
|
+
const x = Math.max(0, Math.min(3, Math.floor(extra) || 0));
|
|
317
|
+
const round = (q) => Number(q.toFixed((q < 10 ? 1 : 0) + x));
|
|
318
|
+
let u = 0, v = a;
|
|
319
|
+
while (u < CU.length - 1 && v >= 1000) { v /= 1000; u++; }
|
|
320
|
+
let r = round(v);
|
|
321
|
+
if (r >= 1000 && u < CU.length - 1) { u++; r = round(r / 1000); }
|
|
322
|
+
return `${sign}${trimZeros(r.toFixed((r < 10 ? 1 : 0) + x))}${CU[u]}`;
|
|
323
|
+
}
|
|
324
|
+
/** `$4.9k`, `-$1.2k` (volume pane, legend volume). */
|
|
325
|
+
function fmtCompactUsd(n, extra = 0) {
|
|
326
|
+
const b = fmtCompact(n, extra);
|
|
327
|
+
return b === '—' ? b : b.startsWith('-') ? `-$${b.slice(1)}` : `$${b}`;
|
|
196
328
|
}
|
|
329
|
+
/** task-1366 name kept: the mcap axis label is now the compact formatter. */
|
|
330
|
+
const fmtMcapAxis = (n) => fmtCompact(n);
|
|
197
331
|
const MCAP_LABEL = 'mcap (current supply)';
|
|
198
|
-
const MCAP_FALLBACK = 'supply unknown · showing price'
|
|
332
|
+
const MCAP_FALLBACK = 'price (mcap unavailable)'; // task-1472: the card's wording (was 'supply unknown · showing price')
|
|
199
333
|
/**
|
|
200
334
|
* How the page shows numbers for a VM: `mcap` (with the supply multiplier) only when the terminal is in mcap mode AND
|
|
201
335
|
* sent a usable supply; mcap mode without one is `price` + a visible note. Never invents a supply.
|
|
@@ -207,7 +341,7 @@
|
|
|
207
341
|
if (supply) return { mode: 'mcap', supply, label: MCAP_LABEL, key: `mcap:${Number(supply.toPrecision(4))}` };
|
|
208
342
|
return { mode: 'price', supply: null, label: wantMcap ? MCAP_FALLBACK : null, key: wantMcap ? 'price:fallback' : 'price' };
|
|
209
343
|
}
|
|
210
|
-
/** The Y-axis / crosshair label formatter for a display: mcap = price × supply in
|
|
344
|
+
/** The Y-axis / crosshair label formatter for a display: mcap = price × supply in compact k/M/B, else the price label. */
|
|
211
345
|
function axisFormatter(d) {
|
|
212
346
|
if (d && d.mode === 'mcap' && fin(d.supply)) { const s = d.supply; return (p) => fmtMcapAxis(p * s); }
|
|
213
347
|
return fmtAxis;
|
|
@@ -225,7 +359,7 @@
|
|
|
225
359
|
const chg = base > 0 ? (bar.close - base) / base : null;
|
|
226
360
|
const f = axisFormatter(display);
|
|
227
361
|
const mc = !!display && display.mode === 'mcap';
|
|
228
|
-
return { o: f(bar.open), h: f(bar.high), l: f(bar.low), c: mc ? `${f(bar.close)} (${fmtPrice(bar.close)})` : f(bar.close), v:
|
|
362
|
+
return { o: f(bar.open), h: f(bar.high), l: f(bar.low), c: mc ? `${f(bar.close)} (${fmtPrice(bar.close)})` : f(bar.close), v: fmtCompactUsd(bar.volume), chg: fmtPct(chg), up: bar.close >= bar.open };
|
|
229
363
|
}
|
|
230
364
|
|
|
231
365
|
/** Index of the bar at `time` (binary search), or -1. */
|
|
@@ -257,6 +391,6 @@
|
|
|
257
391
|
return bars[i].time + f * (bars[i + 1].time - bars[i].time);
|
|
258
392
|
}
|
|
259
393
|
|
|
260
|
-
const api = { setTzOffset, chartTime, INTERVALS, PERIOD_MS, INDICATORS, STYLES, SCALES, toBars, mergeTail, ema, sma, vwap, volumes, barTimeOf, tapeMarkers,
|
|
394
|
+
const api = { setTzOffset, chartTime, INTERVALS, PERIOD_MS, INDICATORS, STYLES, SCALES, toBars, mergeTail, ema, sma, vwap, volumes, barTimeOf, tapeMarkers, cleanFills, replayFills, chartMarkers, clusterMarkers, markerGlyph, markerLetter, markerShape, markerTooltip, markerLegend, omittedNote, priceFormatFor, fmtPrice, fmtAxis, fmtUsd, fmtPct, fmtSol, legendFor, fmtMcapAxis, fmtCompact, fmtCompactUsd, displayOf, axisFormatter, MCAP_LABEL, MCAP_FALLBACK, indexOfTime, logicalOf, timeOfLogical };
|
|
261
395
|
root.DTChart = Object.freeze(api);
|
|
262
396
|
})(typeof window !== 'undefined' ? window : globalThis);
|
|
@@ -28,6 +28,15 @@
|
|
|
28
28
|
.ohlc .row1 b:first-child { margin-left: 0; }
|
|
29
29
|
.ohlc .up { color: var(--up); } .ohlc .down { color: var(--down); }
|
|
30
30
|
.ohlc .ind { margin-right: 10px; }
|
|
31
|
+
.ohlc .row3 { color: var(--dim); }
|
|
32
|
+
.ohlc .row3 .paper { color: var(--warn); }
|
|
33
|
+
/* task-1483: the BUY / SELL marker hover card (textContent only; positioned by chart.js) */
|
|
34
|
+
.mtip { position: absolute; z-index: 6; pointer-events: none; font-size: 11px; line-height: 1.45; padding: 5px 8px; border-radius: 4px;
|
|
35
|
+
background: var(--panel); border: 1px solid var(--border); color: var(--text); white-space: nowrap; box-shadow: 0 2px 10px rgba(0,0,0,.45); }
|
|
36
|
+
.mtip .h { font-weight: 700; }
|
|
37
|
+
.mtip .h.buy { color: var(--up); } .mtip .h.sell { color: var(--down); }
|
|
38
|
+
.mtip .src { color: var(--dim); }
|
|
39
|
+
.mtip .src.paper { color: var(--warn); }
|
|
31
40
|
.hint { position: absolute; right: 70px; top: 6px; z-index: 5; font-size: 11px; color: var(--warn); pointer-events: none; }
|
|
32
41
|
.pos { border-top: 1px solid var(--border); padding: 3px 10px 4px; font-size: 11px; background: rgba(245,255,45,.025); }
|
|
33
42
|
.pos-head { display: flex; gap: 10px; }
|
|
@@ -48,3 +57,12 @@
|
|
|
48
57
|
/* LWC attribution logo: the library injects this exact rule too (allowed by CSP hash); repeated here so the
|
|
49
58
|
logo stays positioned even if a future build changes its inline style text. */
|
|
50
59
|
a#tv-attr-logo { position: absolute; left: 10px; bottom: 10px; height: 19px; width: 35px; margin: 0; padding: 0; border: 0; z-index: 3; }
|
|
60
|
+
/* task-1473 r2: the persistent POSITION PnL chip (the held coin, which need not be the charted coin). Display only. */
|
|
61
|
+
.pnlchip { display: flex; flex-wrap: nowrap; gap: 0; padding: 2px 10px; font-size: 11px; white-space: nowrap; overflow: hidden; border-bottom: 1px solid var(--border); background: rgba(255,45,240,.07); color: var(--muted); }
|
|
62
|
+
.pnlchip .seg + .seg::before { content: ' │ '; color: var(--border); }
|
|
63
|
+
.pnlchip .head { color: var(--pump); font-weight: 700; }
|
|
64
|
+
.pnlchip .pnl { font-weight: 700; }
|
|
65
|
+
.pnlchip .pnl.up, .pnlchip .peak.up { color: var(--up); } .pnlchip .pnl.down, .pnlchip .peak.down { color: var(--down); }
|
|
66
|
+
.pnlchip .dd { color: var(--warn); }
|
|
67
|
+
.pnlchip .pnl.flash { background: var(--up); color: var(--bg, #000); padding: 0 3px; border-radius: 2px; }
|
|
68
|
+
@media (prefers-reduced-motion: reduce) { .pnlchip .pnl.flash { background: none; color: var(--up); padding: 0; } }
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
<div id="toolbar" class="toolbar">
|
|
24
24
|
<span class="grp" id="tf"></span>
|
|
25
25
|
<span class="sep"></span>
|
|
26
|
+
<span class="grp" id="axis"></span>
|
|
27
|
+
<span class="sep"></span>
|
|
28
|
+
<span class="grp" id="marks"></span>
|
|
29
|
+
<span class="sep"></span>
|
|
26
30
|
<span class="grp" id="styles"></span>
|
|
27
31
|
<span class="sep"></span>
|
|
28
32
|
<span class="grp" id="scales"></span>
|
|
@@ -31,6 +35,7 @@
|
|
|
31
35
|
<span class="sep"></span>
|
|
32
36
|
<span class="grp" id="draws"></span>
|
|
33
37
|
</div>
|
|
38
|
+
<div id="pnlchip" class="pnlchip" hidden title="position PnL (display only)"></div>
|
|
34
39
|
<div id="banner" class="banner" hidden></div>
|
|
35
40
|
<div class="stage">
|
|
36
41
|
<canvas id="rain"></canvas>
|
|
@@ -44,7 +49,7 @@
|
|
|
44
49
|
<div id="pos-cells" class="pos-cells"></div>
|
|
45
50
|
</div>
|
|
46
51
|
<div id="foot" class="foot">
|
|
47
|
-
<span class="keys">1–6 tf · c style · a/l/p scale · e ema · w vwap · m vol·ma · v vol · b tape · h/t draw · f fit · esc close</span>
|
|
52
|
+
<span class="keys">1–6 tf · A/y mcap⇄price · k markers · c style · a/l/p scale · e ema · w vwap · m vol·ma · v vol · b tape · h/t draw · f fit · esc close</span>
|
|
48
53
|
<span class="attr" title="TradingView Lightweight Charts v__LWC_VERSION__ (Apache-2.0), self-hosted"><span id="notice">__LWC_NOTICE__</span> · <a id="tv-link" href="https://www.tradingview.com/" target="_blank" rel="noopener noreferrer">https://www.tradingview.com/</a></span>
|
|
49
54
|
</div>
|
|
50
55
|
</div>
|