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
|
@@ -268,6 +268,10 @@ export const DEFAULT_KEYMAP = new Keymap([
|
|
|
268
268
|
// the open coin. (`P` is already the global positions key, so chart uses `C`.) Collision-free with the
|
|
269
269
|
// token screen's other capitals (B/S trade, W/X/T/L links, V vet).
|
|
270
270
|
{ keys: ['C'], action: 'popup.chart', label: 'chart popup', screen: 'token' },
|
|
271
|
+
// task-1472: `A` (axis) on the token screen flips the chart Y axis MARKET CAP ⇄ PRICE (both charts; saved as
|
|
272
|
+
// display.chartAxis). Free capital on the token screen (A is only auto-mirror on the follows screen); deliberately
|
|
273
|
+
// not Y, which confirms inside the PAPER ticket and the modals. The chart popup uses the same `A`.
|
|
274
|
+
{ keys: ['A'], action: 'display.chartAxis', label: 'chart axis mcap ⇄ price', screen: 'token' },
|
|
271
275
|
// task-1359: `I` on the token screen opens/toggles the TOKEN-DETAIL HUD (identity, market, risk, snipers/bundled
|
|
272
276
|
// from the wallet-intel scan); `M` on the trending screen opens/toggles the FEEDS HUD (the keyless social feed +
|
|
273
277
|
// official news, newest first; ⏎ in the window opens that coin here). Both are read-only windows. `I` is unused
|
|
@@ -335,7 +339,6 @@ export const DEFAULT_KEYMAP = new Keymap([
|
|
|
335
339
|
{ keys: ['!'], action: 'plans.panic', label: 'PANIC', screen: 'plans' },
|
|
336
340
|
{ keys: ['M'], action: 'plans.popup', label: 'popup', screen: 'plans' },
|
|
337
341
|
{ keys: ['G'], action: 'plans.gate', label: 'hands-free gate', screen: 'plans' },
|
|
338
|
-
{ keys: ['L'], action: 'plans.live', label: 'fire path paper/live', screen: 'plans' },
|
|
339
342
|
// task-1360 copy trading. `U` opens the FOLLOWED WALLETS screen from anywhere (free capital: not B/S/P/W/V/X/T/L/C/F/Z/R/
|
|
340
343
|
// K/M/N/D/O/G/I). On it: `i` types a wallet address (+ optional label) to follow, `d` unfollows the selected wallet,
|
|
341
344
|
// `r` (global refresh) polls now. Nothing on this screen trades: a buy/sell alert is information only.
|
|
@@ -364,4 +367,25 @@ export const DEFAULT_KEYMAP = new Keymap([
|
|
|
364
367
|
{ keys: ['d'], action: 'rules.remove', label: 'delete rule', screen: 'rules' },
|
|
365
368
|
{ keys: ['tab'], action: 'rules.tab', label: 'rules ⇄ proposals', screen: 'rules' },
|
|
366
369
|
{ keys: ['x'], action: 'rules.dismiss', label: 'dismiss proposal', screen: 'rules' },
|
|
370
|
+
// task-1508 AUTOTRADE (paper). `Q` (free capital everywhere) opens the screen; tab cycles its five views (strategies,
|
|
371
|
+
// editor, compare, backtest, runner). The lowercase verbs below override the global w/b/x/p/i/s on this screen only.
|
|
372
|
+
// `!` soft-stops the paper runner, `p` pauses its new entries, `f` flattens after a y confirm. Edits only touch the
|
|
373
|
+
// user's own strategy files (validated, journaled); there is no key here for live mode, mandates or the signer.
|
|
374
|
+
{ keys: ['Q'], action: 'screen.autotrade', label: 'autotrade (paper)' },
|
|
375
|
+
{ keys: ['tab'], action: 'autotrade.view', label: 'next view', screen: 'autotrade' },
|
|
376
|
+
{ keys: ['n'], action: 'autotrade.new', label: 'new from preset', screen: 'autotrade' },
|
|
377
|
+
{ keys: ['d'], action: 'autotrade.dup', label: 'duplicate', screen: 'autotrade' },
|
|
378
|
+
{ keys: ['e'], action: 'autotrade.edit', label: 'edit', screen: 'autotrade' },
|
|
379
|
+
{ keys: ['c'], action: 'autotrade.compare', label: 'mark to compare', screen: 'autotrade' },
|
|
380
|
+
{ keys: ['b'], action: 'autotrade.backtest', label: 'backtest', screen: 'autotrade' },
|
|
381
|
+
{ keys: ['w'], action: 'autotrade.sweep', label: 'sweep (held-out)', screen: 'autotrade' },
|
|
382
|
+
{ keys: ['x'], action: 'autotrade.export', label: 'export file', screen: 'autotrade' },
|
|
383
|
+
{ keys: ['i'], action: 'autotrade.import', label: 'import file', screen: 'autotrade' },
|
|
384
|
+
{ keys: ['r'], action: 'autotrade.run', label: 'runner', screen: 'autotrade' },
|
|
385
|
+
{ keys: ['s'], action: 'autotrade.save', label: 'save (new version)', screen: 'autotrade' },
|
|
386
|
+
{ keys: ['u'], action: 'autotrade.undo', label: 'undo', screen: 'autotrade' },
|
|
387
|
+
{ keys: ['!'], action: 'autotrade.kill', label: 'kill (soft stop)', screen: 'autotrade' },
|
|
388
|
+
{ keys: ['p'], action: 'autotrade.pause', label: 'pause new entries', screen: 'autotrade' },
|
|
389
|
+
{ keys: ['f'], action: 'autotrade.flatten', label: 'flatten (confirm)', screen: 'autotrade' },
|
|
390
|
+
{ keys: ['o'], action: 'autotrade.proposal', label: 'review chat proposal', screen: 'autotrade' },
|
|
367
391
|
]);
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// @dexterm/tui — task-1483 BUY / SELL markers: the pure model every chart draws from (terminal candles, the agent
|
|
2
|
+
// views' traces, and — mirrored in packages/popup/src/pages/chart-lib.js, parity-tested — the chart popup).
|
|
3
|
+
//
|
|
4
|
+
// Input: the fills the app already holds (the paper ledger's trades and the live ledger's confirmed swaps), never a
|
|
5
|
+
// fetch. Output: one marker per (bar, side, source) for the bars on screen, the open position's average entry per
|
|
6
|
+
// source, and the closed round trips. Display only: nothing here can arm, size, sign or confirm anything.
|
|
7
|
+
//
|
|
8
|
+
// Placement rules (the card's "never clamped to a wrong place"):
|
|
9
|
+
// - a fill belongs to the bar whose period contains its time (bar open = floor(t / period) · period);
|
|
10
|
+
// - a fill older than the first bar on screen, newer than the last bar, or in a period with no bar (a quiet coin
|
|
11
|
+
// can skip a minute) is OMITTED and counted, never moved onto a neighbouring bar;
|
|
12
|
+
// - the price is the fill's own USD price (token-weighted when one bar holds several fills of a side); with no
|
|
13
|
+
// known price the marker is drawn against the bar (below it for a buy, above it for a sell) and says so.
|
|
14
|
+
// The axis never changes a marker's position: bars and markers are both laid out in USD price; an mcap axis only
|
|
15
|
+
// relabels (price × the current supply, task-1366/1472).
|
|
16
|
+
/** Bar periods by the app's timeframe names. */
|
|
17
|
+
export const MARKER_PERIOD_MS = { '1m': 60e3, '5m': 300e3, '15m': 900e3, '1h': 3600e3, '4h': 14400e3, '1d': 86400e3 };
|
|
18
|
+
/** The open time of the bar a time falls in. */
|
|
19
|
+
export function bucketOf(t, periodMs) { return Math.floor(t / periodMs) * periodMs; }
|
|
20
|
+
const fin = (n) => typeof n === 'number' && Number.isFinite(n);
|
|
21
|
+
const EPS = 1e-9;
|
|
22
|
+
/** Valid fills only (finite time, a side, a source, a positive token amount), oldest first; stable for equal times. */
|
|
23
|
+
export function cleanFills(fills) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const f of fills ?? []) {
|
|
26
|
+
if (!f || !fin(f.t) || (f.side !== 'buy' && f.side !== 'sell') || (f.source !== 'paper' && f.source !== 'live'))
|
|
27
|
+
continue;
|
|
28
|
+
if (!fin(f.tokens) || f.tokens <= 0)
|
|
29
|
+
continue;
|
|
30
|
+
out.push({ t: f.t, side: f.side, source: f.source, priceUsd: fin(f.priceUsd) && f.priceUsd > 0 ? f.priceUsd : null, tokens: f.tokens, sol: fin(f.sol) && f.sol >= 0 ? f.sol : null });
|
|
31
|
+
}
|
|
32
|
+
return out.map((f, i) => [f, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([f]) => f);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Replay the fills per source (oldest first) the way the ledgers book them: a buy adds tokens and cost, a sell
|
|
36
|
+
* removes tokens at the average cost. A sell bigger than what the replay holds (the history was truncated) is
|
|
37
|
+
* treated as closing. Returns every fill annotated, the open position's average entry, and the closed trips.
|
|
38
|
+
*/
|
|
39
|
+
export function replayFills(fills) {
|
|
40
|
+
const st = {
|
|
41
|
+
paper: { held: 0, cost: 0, costOk: true, trip: null },
|
|
42
|
+
live: { held: 0, cost: 0, costOk: true, trip: null },
|
|
43
|
+
};
|
|
44
|
+
const out = [];
|
|
45
|
+
const trips = [];
|
|
46
|
+
for (const f of cleanFills(fills)) {
|
|
47
|
+
const s = st[f.source];
|
|
48
|
+
const before = s.held;
|
|
49
|
+
if (f.side === 'buy') {
|
|
50
|
+
if (s.held <= EPS) {
|
|
51
|
+
s.held = 0;
|
|
52
|
+
s.cost = 0;
|
|
53
|
+
s.costOk = true;
|
|
54
|
+
s.trip = { openT: f.t, bTok: 0, bUsd: 0, bOk: true, sTok: 0, sUsd: 0, sOk: true };
|
|
55
|
+
}
|
|
56
|
+
s.held += f.tokens;
|
|
57
|
+
if (f.priceUsd === null)
|
|
58
|
+
s.costOk = false;
|
|
59
|
+
else
|
|
60
|
+
s.cost += f.tokens * f.priceUsd;
|
|
61
|
+
if (s.trip) {
|
|
62
|
+
s.trip.bTok += f.tokens;
|
|
63
|
+
if (f.priceUsd === null)
|
|
64
|
+
s.trip.bOk = false;
|
|
65
|
+
else
|
|
66
|
+
s.trip.bUsd += f.tokens * f.priceUsd;
|
|
67
|
+
}
|
|
68
|
+
out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: before > EPS, closes: false });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const sold = Math.min(f.tokens, s.held);
|
|
72
|
+
const frac = s.held > EPS ? sold / s.held : 1;
|
|
73
|
+
s.cost -= s.cost * frac;
|
|
74
|
+
s.held = Math.max(0, s.held - f.tokens);
|
|
75
|
+
const closes = s.held <= f.tokens * 1e-9 || s.held <= EPS;
|
|
76
|
+
if (closes)
|
|
77
|
+
s.held = 0;
|
|
78
|
+
if (s.trip) {
|
|
79
|
+
s.trip.sTok += f.tokens;
|
|
80
|
+
if (f.priceUsd === null)
|
|
81
|
+
s.trip.sOk = false;
|
|
82
|
+
else
|
|
83
|
+
s.trip.sUsd += f.tokens * f.priceUsd;
|
|
84
|
+
}
|
|
85
|
+
out.push({ ...f, heldBefore: before, heldAfter: s.held, partial: !closes, closes });
|
|
86
|
+
if (closes) {
|
|
87
|
+
if (s.trip)
|
|
88
|
+
trips.push({ source: f.source, openT: s.trip.openT, closeT: f.t, entryUsd: s.trip.bOk && s.trip.bTok > 0 ? s.trip.bUsd / s.trip.bTok : null, exitUsd: s.trip.sOk && s.trip.sTok > 0 ? s.trip.sUsd / s.trip.sTok : null });
|
|
89
|
+
s.trip = null;
|
|
90
|
+
s.cost = 0;
|
|
91
|
+
s.costOk = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const avg = (src) => { const s = st[src]; return s.held > EPS && s.costOk && s.cost > 0 ? s.cost / s.held : null; };
|
|
95
|
+
return { fills: out, avgEntry: { paper: avg('paper'), live: avg('live') }, trips };
|
|
96
|
+
}
|
|
97
|
+
/** Index of `t` in an ascending list, or -1 (binary search). */
|
|
98
|
+
function indexOf(times, t) {
|
|
99
|
+
let lo = 0, hi = times.length - 1;
|
|
100
|
+
while (lo <= hi) {
|
|
101
|
+
const mid = (lo + hi) >> 1;
|
|
102
|
+
const v = times[mid];
|
|
103
|
+
if (v === t)
|
|
104
|
+
return mid;
|
|
105
|
+
if (v < t)
|
|
106
|
+
lo = mid + 1;
|
|
107
|
+
else
|
|
108
|
+
hi = mid - 1;
|
|
109
|
+
}
|
|
110
|
+
return -1;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The markers for a window of bars: one per (bar, side, source), oldest first; buys before sells within a bar.
|
|
114
|
+
* Fills outside the window (or in a period with no bar) are counted in before / after / gap, never placed.
|
|
115
|
+
*/
|
|
116
|
+
export function chartMarkers(fills, win) {
|
|
117
|
+
const rp = replayFills(fills);
|
|
118
|
+
const times = win.barTimes;
|
|
119
|
+
const empty = () => ({ markers: [], before: 0, after: 0, gap: 0, avgEntry: rp.avgEntry, trips: [] });
|
|
120
|
+
if (!(win.periodMs > 0) || !times.length) {
|
|
121
|
+
const e = empty();
|
|
122
|
+
e.before = rp.fills.length;
|
|
123
|
+
return e;
|
|
124
|
+
}
|
|
125
|
+
const first = times[0], last = times[times.length - 1];
|
|
126
|
+
const res = empty();
|
|
127
|
+
const by = new Map();
|
|
128
|
+
for (const f of rp.fills) {
|
|
129
|
+
const b = bucketOf(f.t, win.periodMs);
|
|
130
|
+
if (b < first) {
|
|
131
|
+
res.before++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (b > last) {
|
|
135
|
+
res.after++;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const index = indexOf(times, b);
|
|
139
|
+
if (index < 0) {
|
|
140
|
+
res.gap++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const key = `${b}|${f.side}|${f.source}`;
|
|
144
|
+
let m = by.get(key);
|
|
145
|
+
if (!m) {
|
|
146
|
+
m = { side: f.side, source: f.source, bucket: b, index, t: f.t, tLast: f.t, priceUsd: null, tokens: 0, sol: 0, count: 0, partial: false, closes: false, pTok: 0, pUsd: 0, solOk: true };
|
|
147
|
+
by.set(key, m);
|
|
148
|
+
}
|
|
149
|
+
m.count++;
|
|
150
|
+
m.tLast = f.t;
|
|
151
|
+
m.tokens += f.tokens;
|
|
152
|
+
if (f.sol === null)
|
|
153
|
+
m.solOk = false;
|
|
154
|
+
else
|
|
155
|
+
m.sol = (m.sol ?? 0) + f.sol;
|
|
156
|
+
if (f.priceUsd !== null) {
|
|
157
|
+
m.pTok += f.tokens;
|
|
158
|
+
m.pUsd += f.tokens * f.priceUsd;
|
|
159
|
+
}
|
|
160
|
+
if (f.partial)
|
|
161
|
+
m.partial = true;
|
|
162
|
+
if (f.closes)
|
|
163
|
+
m.closes = true;
|
|
164
|
+
}
|
|
165
|
+
const order = (m) => (m.side === 'buy' ? 0 : 1) * 2 + (m.source === 'live' ? 1 : 0);
|
|
166
|
+
res.markers = [...by.values()].map(({ pTok, pUsd, solOk, ...m }) => ({ ...m, priceUsd: pTok > 0 ? pUsd / pTok : null, sol: solOk ? m.sol : null }))
|
|
167
|
+
.sort((a, b) => a.index - b.index || order(a) - order(b));
|
|
168
|
+
res.trips = rp.trips.filter((r) => bucketOf(r.closeT, win.periodMs) >= first && bucketOf(r.openT, win.periodMs) <= last);
|
|
169
|
+
return res;
|
|
170
|
+
}
|
|
171
|
+
/** The glyph for a marker: solid ▲ / ▼ for live fills, hollow △ / ▽ for paper. Reads without colour (NO_COLOR). */
|
|
172
|
+
export function markerGlyph(side, source) {
|
|
173
|
+
if (source === 'paper')
|
|
174
|
+
return side === 'buy' ? '△' : '▽';
|
|
175
|
+
return side === 'buy' ? '▲' : '▼';
|
|
176
|
+
}
|
|
177
|
+
/** The letter for a marker (always shown with the glyph, so BUY / SELL never depends on colour). */
|
|
178
|
+
export function markerLetter(side) { return side === 'buy' ? 'B' : 'S'; }
|
|
179
|
+
/** "△ ▽ paper ▲ ▼ live" — the legend for the sources actually on screen (empty when none). */
|
|
180
|
+
export function markerLegend(markers, avgShown = false) {
|
|
181
|
+
const has = (s) => markers.some((m) => m.source === s);
|
|
182
|
+
const parts = [];
|
|
183
|
+
if (has('paper'))
|
|
184
|
+
parts.push('△B ▽S paper');
|
|
185
|
+
if (has('live'))
|
|
186
|
+
parts.push('▲B ▼S live');
|
|
187
|
+
if (avgShown)
|
|
188
|
+
parts.push('┄ avg entry');
|
|
189
|
+
return parts.join(' ');
|
|
190
|
+
}
|
|
191
|
+
/** "+2 fills off-chart (1 earlier, 1 newer than the last candle)" — what the window omitted, or ''. */
|
|
192
|
+
export function omittedNote(s) {
|
|
193
|
+
const n = s.before + s.after + s.gap;
|
|
194
|
+
if (!n)
|
|
195
|
+
return '';
|
|
196
|
+
const why = [];
|
|
197
|
+
if (s.before)
|
|
198
|
+
why.push(`${s.before} earlier`);
|
|
199
|
+
if (s.after)
|
|
200
|
+
why.push(`${s.after} newer than the last candle`);
|
|
201
|
+
if (s.gap)
|
|
202
|
+
why.push(`${s.gap} in a period with no candle`);
|
|
203
|
+
return `+${n} fill${n === 1 ? '' : 's'} off-chart (${why.join(', ')})`;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The plot row of a price on a terminal chart laid out between `lo` and `hi` over `plotH` rows (row 0 = `hi`), the same
|
|
207
|
+
* mapping candleChart uses for candle bodies. Null when the price is outside the plotted range (omitted, not clamped).
|
|
208
|
+
*/
|
|
209
|
+
export function markerRow(priceUsd, lo, hi, plotH) {
|
|
210
|
+
if (!fin(priceUsd) || !(hi > lo) || plotH < 1)
|
|
211
|
+
return null;
|
|
212
|
+
const r = Math.floor(((hi - priceUsd) / (hi - lo)) * plotH);
|
|
213
|
+
return r >= 0 && r < plotH ? r : null;
|
|
214
|
+
}
|
|
215
|
+
/** "B 41.2k" / "S×2 0.0₄123": the compact terminal label (axis value = price × scale, formatted by the axis). */
|
|
216
|
+
export function markerLabel(m, fmt, scale = 1) {
|
|
217
|
+
const head = `${markerLetter(m.side)}${m.count > 1 ? `×${m.count}` : ''}`;
|
|
218
|
+
return m.priceUsd === null ? head : `${head} ${fmt(m.priceUsd * (scale > 0 ? scale : 1))}`;
|
|
219
|
+
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
// background (hard cells); glow is only ever applied to neighbouring blank cells or to bg tints.
|
|
6
6
|
import { Canvas } from "./canvas.js";
|
|
7
7
|
import { mix, luminance } from "./color.js";
|
|
8
|
-
import { fmtUsd, fmtPct, fmtNum, fmtPrice, fmtAgeSec, shortAddr, clamp } from "./fmt.js";
|
|
8
|
+
import { fmtUsd, fmtPct, fmtNum, fmtPrice, fmtPriceBare, fmtCompactUsd, compactDecimalsFor, fmtAgeSec, shortAddr, clamp } from "./fmt.js";
|
|
9
9
|
import { launchpadColor } from "./theme.js";
|
|
10
|
+
import { chartMarkers, markerGlyph, markerLetter, markerLabel, markerLegend, markerRow, omittedNote, MARKER_PERIOD_MS } from "./markers.js";
|
|
10
11
|
export const BLOCKS = '▁▂▃▄▅▆▇█';
|
|
11
12
|
// braille dot bits: column 0 rows 0..3, column 1 rows 0..3 (row 0 = top)
|
|
12
13
|
const BR_L = [0x01, 0x02, 0x04, 0x40];
|
|
@@ -107,6 +108,7 @@ export function brailleLine(c, x, y, w, h, values, opts = {}) {
|
|
|
107
108
|
c.put(x + col, y + r, String.fromCharCode(0x2800 + bits), { fg: neon(color, col / (w - 1), bg), bg, soft: true });
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
/** task-1472: the price axis never prints scientific notation: subscript zeros below 0.01 (0.0₅493), like fmtPrice. */
|
|
110
112
|
function fmtPriceAxis(p) {
|
|
111
113
|
if (p >= 1000)
|
|
112
114
|
return p.toFixed(0);
|
|
@@ -114,7 +116,7 @@ function fmtPriceAxis(p) {
|
|
|
114
116
|
return p.toFixed(3);
|
|
115
117
|
if (p >= 0.001)
|
|
116
118
|
return p.toFixed(5);
|
|
117
|
-
return p
|
|
119
|
+
return fmtPriceBare(p);
|
|
118
120
|
}
|
|
119
121
|
/**
|
|
120
122
|
* Candlestick chart with right-hand price axis, last-price tag, volume strip and time axis.
|
|
@@ -130,20 +132,37 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
|
130
132
|
const plotW = w - axisW;
|
|
131
133
|
const plotH = h - volH - timeH;
|
|
132
134
|
if (plotW < 10 || plotH < 4 || candles.length === 0)
|
|
133
|
-
return;
|
|
135
|
+
return null;
|
|
134
136
|
const CW = 4; // 3-col body with centered wick + 1-col gap
|
|
135
137
|
const n = Math.min(candles.length, Math.floor(plotW / CW));
|
|
136
138
|
const data = candles.slice(-n);
|
|
137
139
|
// task-1366: the bars are laid out in price units either way (identical glyphs); only the labels are scaled
|
|
138
140
|
const s = opts.scale && Number.isFinite(opts.scale) && opts.scale > 0 ? opts.scale : 1;
|
|
139
141
|
const baseFmt = opts.axisFmt ?? fmtPriceAxis;
|
|
140
|
-
const fmtAxis = s === 1 ? baseFmt : (p) => baseFmt(p * s);
|
|
141
142
|
let lo = Infinity, hi = -Infinity, vmax = 0;
|
|
142
143
|
for (const k of data) {
|
|
143
144
|
lo = Math.min(lo, k.l);
|
|
144
145
|
hi = Math.max(hi, k.h);
|
|
145
146
|
vmax = Math.max(vmax, k.v);
|
|
146
147
|
}
|
|
148
|
+
// task-1483: the trade marks for the bars on screen. Their prices (and the avg entry) widen the range when they sit
|
|
149
|
+
// within 3× of the last close, like the popup's levels; a farther price is left off the plot rather than squashing it.
|
|
150
|
+
const tm = opts.trades && (opts.trades.fills.length || opts.trades.avgEntry) ? opts.trades : null;
|
|
151
|
+
const period = MARKER_PERIOD_MS[String(opts.interval || '1h')] ?? 3600e3;
|
|
152
|
+
const set = tm ? chartMarkers(tm.fills, { periodMs: period, barTimes: data.map((k) => k.t) }) : null;
|
|
153
|
+
const lastC = data[data.length - 1].c;
|
|
154
|
+
const near = (p) => typeof p === 'number' && Number.isFinite(p) && p > 0 && lastC > 0 && p / lastC < 3 && lastC / p < 3;
|
|
155
|
+
if (set)
|
|
156
|
+
for (const m of set.markers)
|
|
157
|
+
if (near(m.priceUsd)) {
|
|
158
|
+
lo = Math.min(lo, m.priceUsd);
|
|
159
|
+
hi = Math.max(hi, m.priceUsd);
|
|
160
|
+
}
|
|
161
|
+
const avgUsd = tm?.avgEntry && near(tm.avgEntry.usd) ? tm.avgEntry.usd : null;
|
|
162
|
+
if (avgUsd !== null) {
|
|
163
|
+
lo = Math.min(lo, avgUsd);
|
|
164
|
+
hi = Math.max(hi, avgUsd);
|
|
165
|
+
}
|
|
147
166
|
const pad = (hi - lo) * 0.06 || hi * 0.01;
|
|
148
167
|
lo -= pad;
|
|
149
168
|
hi += pad;
|
|
@@ -152,6 +171,9 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
|
152
171
|
const gridStyle = { fg: mix(bg, t.border, 0.9), bg, soft: true };
|
|
153
172
|
const labelStyle = { fg: t.muted, bg };
|
|
154
173
|
const ticks = Math.max(2, Math.min(6, Math.floor(plotH / 3)));
|
|
174
|
+
// task-1472: one more decimal only when two neighbouring ticks would otherwise print the same compact label
|
|
175
|
+
const extra = opts.axisFmt ? compactDecimalsFor(((hi + lo) / 2) * s, (range / ticks) * s) : 0;
|
|
176
|
+
const fmtAxis = (p) => baseFmt(p * s, extra);
|
|
155
177
|
for (let i = 0; i <= ticks; i++) {
|
|
156
178
|
const row = Math.round((i / ticks) * (plotH - 1));
|
|
157
179
|
const price = hi - (row / (plotH - 1)) * range;
|
|
@@ -159,6 +181,30 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
|
159
181
|
c.put(x + k, y + row, '·', gridStyle);
|
|
160
182
|
c.textR(x + plotW, y + row, axisW, ' ' + fmtAxis(price), labelStyle);
|
|
161
183
|
}
|
|
184
|
+
// task-1483: closed round trips, entry bar → exit bar, as a faint wash behind the candles (green = sold above the entry)
|
|
185
|
+
if (set && tm?.band !== false) {
|
|
186
|
+
for (const r of set.trips) {
|
|
187
|
+
const a = Math.max(0, data.findIndex((k) => k.t + period > r.openT));
|
|
188
|
+
let b = n - 1;
|
|
189
|
+
for (let i = n - 1; i >= 0; i--)
|
|
190
|
+
if (data[i].t <= r.closeT) {
|
|
191
|
+
b = i;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
const win = r.entryUsd !== null && r.exitUsd !== null ? r.exitUsd >= r.entryUsd : null;
|
|
195
|
+
const tint = mix(bg, win === null ? t.muted : win ? t.up : t.down, 0.07);
|
|
196
|
+
for (let i = a; i <= b; i++)
|
|
197
|
+
for (let q = 0; q < CW; q++)
|
|
198
|
+
for (let row = 0; row < plotH; row++) {
|
|
199
|
+
const cx = x + i * CW + q;
|
|
200
|
+
if (cx >= x + plotW)
|
|
201
|
+
continue;
|
|
202
|
+
const gi = c.idx(cx, y + row);
|
|
203
|
+
if (!c.hard[gi])
|
|
204
|
+
c.bg[gi] = tint;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
162
208
|
// last price line + tag
|
|
163
209
|
const last = data[data.length - 1];
|
|
164
210
|
const lastRow = Math.round(rowsOf(last.c));
|
|
@@ -226,6 +272,7 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
|
226
272
|
}
|
|
227
273
|
}
|
|
228
274
|
}
|
|
275
|
+
const info = set ? drawTradeMarks(c, { x, y, plotW, plotH, n, CW, bg, lo, hi, axisW, fmtAxis, lastRow: lastRow >= 0 && lastRow < plotH ? lastRow : null, data, set, avgUsd, avgSource: tm?.avgEntry?.source ?? 'paper', scale: s }) : null;
|
|
229
276
|
if (volH) {
|
|
230
277
|
const vy = y + plotH;
|
|
231
278
|
for (let i = 0; i < n; i++) {
|
|
@@ -245,7 +292,7 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
|
245
292
|
}
|
|
246
293
|
}
|
|
247
294
|
c.text(x + plotW + 1, vy, 'vol', labelStyle);
|
|
248
|
-
c.text(x + plotW + 1, vy + 1,
|
|
295
|
+
c.text(x + plotW + 1, vy + 1, fmtCompactUsd(last.v), { fg: t.text, bg }); // task-1472: volume stays USD, compact k/M
|
|
249
296
|
}
|
|
250
297
|
const ty = y + h - 1;
|
|
251
298
|
const labels = Math.max(2, Math.floor(plotW / 18));
|
|
@@ -257,6 +304,108 @@ export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
|
257
304
|
c.text(cx, ty, lab, labelStyle);
|
|
258
305
|
}
|
|
259
306
|
c.textR(x + plotW, ty, axisW, (opts.interval || '1h') + ' UTC', { fg: t.dim, bg });
|
|
307
|
+
return info;
|
|
308
|
+
}
|
|
309
|
+
const freeCell = (c, cx, cy) => { const ch = c.ch[c.idx(cx, cy)]; return ch === ' ' || ch === '·' || ch === '┄'; };
|
|
310
|
+
/**
|
|
311
|
+
* task-1483: the avg-entry line, the B / S markers and their legend, on top of the drawn candles. Glyph + letter on
|
|
312
|
+
* every marker (△B ▽S paper, ▲B ▼S live), so NO_COLOR and calm read the same; colour only adds to it.
|
|
313
|
+
*/
|
|
314
|
+
function drawTradeMarks(c, g) {
|
|
315
|
+
const t = c.theme;
|
|
316
|
+
const { x, y, plotW, plotH, CW, bg } = g;
|
|
317
|
+
const rowsOf = (p) => ((g.hi - p) / (g.hi - g.lo)) * plotH;
|
|
318
|
+
// the open position's average entry: a dashed line through the empty cells (candles stay on top) + an axis tag
|
|
319
|
+
let avgRow = null;
|
|
320
|
+
let avgShown = false;
|
|
321
|
+
if (g.avgUsd !== null) {
|
|
322
|
+
avgRow = markerRow(g.avgUsd, g.lo, g.hi, plotH);
|
|
323
|
+
if (avgRow !== null) {
|
|
324
|
+
const col = g.avgSource === 'paper' ? t.warn : t.text;
|
|
325
|
+
for (let k = 0; k < plotW; k++)
|
|
326
|
+
if (freeCell(c, x + k, y + avgRow)) {
|
|
327
|
+
c.put(x + k, y + avgRow, '┄', { fg: mix(bg, col, 0.8), bg, soft: true });
|
|
328
|
+
avgShown = true;
|
|
329
|
+
}
|
|
330
|
+
// `avg` at the line's left end so it reads without colour (also over the last-price dashes when both share a row),
|
|
331
|
+
// and the value as an axis tag
|
|
332
|
+
const labelOk = (cx) => freeCell(c, cx, y + avgRow) || c.ch[c.idx(cx, y + avgRow)] === '╌';
|
|
333
|
+
if (labelOk(x) && labelOk(x + 1) && labelOk(x + 2)) {
|
|
334
|
+
c.text(x, y + avgRow, 'avg', { fg: col, bg, b: true, hard: true });
|
|
335
|
+
avgShown = true;
|
|
336
|
+
}
|
|
337
|
+
if (avgRow !== g.lastRow) {
|
|
338
|
+
c.fill(x + plotW, y + avgRow, g.axisW, 1, ' ', { bg }); // the grid label under it goes, not half of it
|
|
339
|
+
c.textR(x + plotW, y + avgRow, g.axisW, ' ' + g.fmtAxis(g.avgUsd), { fg: '#000000', bg: col, b: true });
|
|
340
|
+
avgShown = true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const used = new Set();
|
|
345
|
+
const drawn = [];
|
|
346
|
+
let offPlot = 0;
|
|
347
|
+
for (const m of g.set.markers) {
|
|
348
|
+
const k = g.data[m.index];
|
|
349
|
+
const col0 = x + m.index * CW;
|
|
350
|
+
const buy = m.side === 'buy';
|
|
351
|
+
// the fill's own price; no price → against the bar (below the low for a buy, above the high for a sell)
|
|
352
|
+
let row = m.priceUsd !== null ? markerRow(m.priceUsd, g.lo, g.hi, plotH) : Math.floor(rowsOf(buy ? k.l : k.h)) + (buy ? 1 : -1);
|
|
353
|
+
if (row === null || row < 0 || row >= plotH) {
|
|
354
|
+
offPlot++;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
// two marks in one bar on the same row (buy + sell, or paper + live): step away from the bar, never across it
|
|
358
|
+
while (row >= 0 && row < plotH && (used.has((row << 12) | m.index)))
|
|
359
|
+
row += buy ? 1 : -1;
|
|
360
|
+
if (row < 0 || row >= plotH) {
|
|
361
|
+
offPlot++;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
used.add((row << 12) | m.index);
|
|
365
|
+
const sideCol = buy ? t.up : t.down;
|
|
366
|
+
const st = m.source === 'live' ? { fg: '#000000', bg: sideCol, b: true, hard: true } : { fg: sideCol, bg: mix(bg, sideCol, 0.2), b: true, hard: true };
|
|
367
|
+
c.put(col0 + 1, y + row, markerGlyph(m.side, m.source), st);
|
|
368
|
+
c.put(col0 + 2, y + row, markerLetter(m.side), st);
|
|
369
|
+
if (m.count > 1 && col0 + 3 < x + plotW)
|
|
370
|
+
c.put(col0 + 3, y + row, m.count > 9 ? '+' : String(m.count), st);
|
|
371
|
+
drawn.push({ marker: m, col: col0 + 1, row });
|
|
372
|
+
}
|
|
373
|
+
// the newest marker gets a compact label (B 41k) where the row has room, right of it first, else left
|
|
374
|
+
const newest = drawn[drawn.length - 1];
|
|
375
|
+
if (newest) {
|
|
376
|
+
const label = markerLabel(newest.marker, g.fmtAxis, 1);
|
|
377
|
+
const fits = (sx) => sx >= x && sx + label.length <= x + plotW && [...label].every((_, i) => freeCell(c, sx + i, y + newest.row));
|
|
378
|
+
const right = newest.col + 3 + (newest.marker.count > 1 ? 1 : 0);
|
|
379
|
+
const sx = fits(right) ? right : fits(newest.col - 2 - label.length) ? newest.col - 2 - label.length : null;
|
|
380
|
+
if (sx !== null)
|
|
381
|
+
c.text(sx, y + newest.row, label, { fg: newest.marker.side === 'buy' ? t.up : t.down, bg, hard: true });
|
|
382
|
+
}
|
|
383
|
+
// legend + what was left off, on the top plot row where it is empty (else the bottom row), never over a candle
|
|
384
|
+
const note = [markerLegend(drawn.map((d) => d.marker), avgShown), omittedNote(g.set)].filter(Boolean).join(' · ');
|
|
385
|
+
if (note) {
|
|
386
|
+
const text = note.length > plotW - 2 ? note.slice(0, Math.max(0, plotW - 3)) + '…' : note;
|
|
387
|
+
// the outermost rows first (top, bottom, then inwards); in a row, the leftmost run of empty cells long enough
|
|
388
|
+
const rows = [];
|
|
389
|
+
for (let i = 0; i < Math.ceil(plotH / 2); i++) {
|
|
390
|
+
rows.push(i);
|
|
391
|
+
if (plotH - 1 - i !== i)
|
|
392
|
+
rows.push(plotH - 1 - i);
|
|
393
|
+
}
|
|
394
|
+
const len = [...text].length;
|
|
395
|
+
place: for (const row of rows) {
|
|
396
|
+
if (row === avgRow || row === g.lastRow || drawn.some((d) => d.row === row))
|
|
397
|
+
continue;
|
|
398
|
+
let run = 0;
|
|
399
|
+
for (let cx = x + 1; cx < x + plotW; cx++) {
|
|
400
|
+
run = freeCell(c, cx, y + row) ? run + 1 : 0;
|
|
401
|
+
if (run === len) {
|
|
402
|
+
c.text(cx - len + 1, y + row, text, { fg: t.dim, bg, i: true });
|
|
403
|
+
break place;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return { set: g.set, drawn, offPlot, avgRow };
|
|
260
409
|
}
|
|
261
410
|
/** Horizontal progress bar: gradient fill, hot head cell, one-cell ▓ glow past the head, ░ empty. pct 0..100 */
|
|
262
411
|
export function bar(c, x, y, w, pct, opts = {}) {
|
|
@@ -386,6 +535,8 @@ export function table(c, x, y, w, h, spec) {
|
|
|
386
535
|
const selected = ri === spec.selected;
|
|
387
536
|
const rowBg = selected ? t.selBg : (r % 2 === 1 && spec.zebra !== false ? mix(bg, t.panelAlt, 1) : bg);
|
|
388
537
|
c.fill(x, ry, w, 1, ' ', { bg: rowBg });
|
|
538
|
+
if (spec.rowKey)
|
|
539
|
+
c.rowMarks.push({ key: spec.rowKey(row), x, y: ry, w });
|
|
389
540
|
if (selected)
|
|
390
541
|
c.put(x - 1 >= 0 ? x - 1 : x, ry, '▎', { fg: t.accent, bg: rowBg, soft: true });
|
|
391
542
|
cx = x;
|
|
@@ -478,7 +629,8 @@ export function tradeTape(c, x, y, w, h, trades, now, opts = {}) {
|
|
|
478
629
|
continue;
|
|
479
630
|
}
|
|
480
631
|
cx = c.textR(cx + 1, yy, 8, fmtUsd(tr.usd), { fg: t.text, bg });
|
|
481
|
-
|
|
632
|
+
// task-1537: the caller's price basis (a market cap at the coin's supply by default); plain price without one
|
|
633
|
+
cx = c.text(cx + 2, yy, tr.price > 0 ? (opts.priceFmt ?? fmtPrice)(tr.price) : '—', { fg: t.muted, bg }, Math.max(0, x + w - cx - 2));
|
|
482
634
|
c.text(cx + 2, yy, shortAddr(tr.wallet, 3), { fg: t.dim, bg }, Math.max(0, x + w - cx - 2));
|
|
483
635
|
n += 1;
|
|
484
636
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dexterm-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"mcpName": "io.github.dexterm-xyz/dexterm",
|
|
5
5
|
"description": "dexterm: terminal DEX screener with a local Claude CLI analyst",
|
|
6
6
|
"author": "The dexterm authors",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@dexterm/agents": "0.0.1",
|
|
28
|
+
"@dexterm/autotrade": "0.0.1",
|
|
28
29
|
"@dexterm/custom": "0.0.1",
|
|
29
30
|
"@dexterm/data": "0.0.1",
|
|
30
31
|
"@dexterm/mcp": "0.0.1",
|