dexterm-cli 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/agents/src/schema.js +22 -1
- package/dist/packages/agents/src/story.js +11 -2
- package/dist/packages/agents/src/tiers.js +27 -2
- package/dist/packages/agents/src/tools.js +30 -2
- package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
- package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
- package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
- package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
- package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
- package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
- package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
- package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
- package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
- package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
- package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
- package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
- package/dist/packages/autotrade/src/account.js +74 -0
- package/dist/packages/autotrade/src/backtest.js +357 -0
- package/dist/packages/autotrade/src/basis-text.js +32 -0
- package/dist/packages/autotrade/src/chat/approvals.js +484 -0
- package/dist/packages/autotrade/src/chat/audit.js +140 -0
- package/dist/packages/autotrade/src/chat/config.js +93 -0
- package/dist/packages/autotrade/src/chat/connector.js +24 -0
- package/dist/packages/autotrade/src/chat/control.js +103 -0
- package/dist/packages/autotrade/src/chat/fake.js +100 -0
- package/dist/packages/autotrade/src/chat/format.js +71 -0
- package/dist/packages/autotrade/src/chat/hub.js +187 -0
- package/dist/packages/autotrade/src/chat/index.js +18 -0
- package/dist/packages/autotrade/src/chat/notify.js +292 -0
- package/dist/packages/autotrade/src/chat/pairing.js +46 -0
- package/dist/packages/autotrade/src/chat/redact.js +48 -0
- package/dist/packages/autotrade/src/chat/secrets.js +99 -0
- package/dist/packages/autotrade/src/chat/service.js +248 -0
- package/dist/packages/autotrade/src/chat/telegram.js +290 -0
- package/dist/packages/autotrade/src/desk-executor.js +188 -0
- package/dist/packages/autotrade/src/engine.js +387 -0
- package/dist/packages/autotrade/src/execution-port.js +97 -0
- package/dist/packages/autotrade/src/fleet-config.js +169 -0
- package/dist/packages/autotrade/src/fleet-control.js +276 -0
- package/dist/packages/autotrade/src/fleet.js +639 -0
- package/dist/packages/autotrade/src/index.js +53 -0
- package/dist/packages/autotrade/src/ledger.js +108 -0
- package/dist/packages/autotrade/src/live-source.js +182 -0
- package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
- package/dist/packages/autotrade/src/monitor.js +190 -0
- package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
- package/dist/packages/autotrade/src/paper-accounts.js +72 -0
- package/dist/packages/autotrade/src/replay.js +198 -0
- package/dist/packages/autotrade/src/runner-config.js +99 -0
- package/dist/packages/autotrade/src/runner.js +872 -0
- package/dist/packages/autotrade/src/safety.js +243 -0
- package/dist/packages/autotrade/src/session.js +269 -0
- package/dist/packages/autotrade/src/sessions.js +503 -0
- package/dist/packages/autotrade/src/store.js +250 -0
- package/dist/packages/autotrade/src/strategy.js +256 -0
- package/dist/packages/autotrade/src/trade-ledger.js +241 -0
- package/dist/packages/autotrade/src/yolo.js +140 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
- package/dist/packages/cli/src/ai-cmd.js +412 -0
- package/dist/packages/cli/src/analyst/backend.js +82 -15
- package/dist/packages/cli/src/analyst/codex.js +14 -9
- package/dist/packages/cli/src/analyst/ollama.js +263 -0
- package/dist/packages/cli/src/analyst/session.js +5 -2
- package/dist/packages/cli/src/app-help-gen.js +44 -8
- package/dist/packages/cli/src/autotrade-cmd/audit.js +57 -0
- package/dist/packages/cli/src/autotrade-cmd/backtest.js +199 -0
- package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
- package/dist/packages/cli/src/autotrade-cmd/fleet.js +397 -0
- package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/run.js +413 -0
- package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
- package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
- package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
- package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
- package/dist/packages/cli/src/autotrade-cmd.js +168 -0
- package/dist/packages/cli/src/display.js +156 -17
- package/dist/packages/cli/src/doctor.js +62 -3
- package/dist/packages/cli/src/export.js +10 -2
- package/dist/packages/cli/src/index.js +1 -1
- package/dist/packages/cli/src/ledger-cmd.js +288 -0
- package/dist/packages/cli/src/live/chart-fills.js +46 -0
- package/dist/packages/cli/src/live/index.js +23 -89
- package/dist/packages/cli/src/live/screens/token-live.js +9 -6
- package/dist/packages/cli/src/live/trade.js +12 -457
- package/dist/packages/cli/src/live/watchlist.js +1 -1
- package/dist/packages/cli/src/main.js +147 -190
- package/dist/packages/cli/src/mcp-cmd.js +7 -1
- package/dist/packages/cli/src/plan-cmd.js +1 -1
- package/dist/packages/cli/src/pnl-app.js +31 -21
- package/dist/packages/cli/src/pnl-card.js +158 -21
- package/dist/packages/cli/src/popup.js +22 -6
- package/dist/packages/cli/src/record-cmd.js +141 -20
- package/dist/packages/cli/src/screens/agent-hud.js +34 -2
- package/dist/packages/cli/src/screens/analyst-session.js +7 -4
- package/dist/packages/cli/src/screens/analyst.js +3 -2
- package/dist/packages/cli/src/screens/autotrade-keys.js +169 -0
- package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
- package/dist/packages/cli/src/screens/autotrade-screen.js +725 -0
- package/dist/packages/cli/src/screens/autotrade-ui.js +640 -0
- package/dist/packages/cli/src/screens/autotrade-worker.js +89 -0
- package/dist/packages/cli/src/screens/brain.js +37 -11
- package/dist/packages/cli/src/screens/overlay.js +5 -2
- package/dist/packages/cli/src/screens/plans.js +5 -17
- package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
- package/dist/packages/cli/src/screens/positions.js +6 -28
- package/dist/packages/cli/src/screens/ticket.js +38 -205
- package/dist/packages/cli/src/screens/token.js +3 -3
- package/dist/packages/cli/src/state.js +25 -19
- package/dist/packages/cli/src/telegram-cmd.js +451 -0
- package/dist/packages/cli/src/trade.js +3 -128
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/cli/src/wallet-cmd.js +39 -340
- package/dist/packages/cli/src/wallet-command.js +8 -11
- package/dist/packages/cli/src/widgets.js +0 -12
- package/dist/packages/custom/src/config.js +20 -2
- package/dist/packages/custom/src/schema.js +35 -10
- package/dist/packages/custom/src/workspace.js +3 -3
- package/dist/packages/data/src/basis.js +191 -0
- package/dist/packages/data/src/detail.js +53 -2
- package/dist/packages/data/src/index.js +0 -14
- package/dist/packages/data/src/store.js +14 -5
- package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
- package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
- package/dist/packages/data/src/trade/bridge.js +4 -102
- package/dist/packages/data/src/trade/ledger/export.js +101 -0
- package/dist/packages/data/src/trade/ledger/file.js +135 -0
- package/dist/packages/data/src/trade/ledger/fold.js +191 -0
- package/dist/packages/data/src/trade/ledger/index.js +9 -0
- package/dist/packages/data/src/trade/ledger/schema.js +83 -0
- package/dist/packages/data/src/trade/ledger/sources.js +307 -0
- package/dist/packages/data/src/trade/ledger/stats.js +123 -0
- package/dist/packages/data/src/trade/ledger/tags.js +116 -0
- package/dist/packages/data/src/trade/ledger/view.js +51 -0
- package/dist/packages/data/src/trade/plans/index.js +0 -1
- package/dist/packages/data/src/trade/plans/service.js +3 -27
- package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
- package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
- package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
- package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +217 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
- package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
- package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +49 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
- package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
- package/dist/packages/mcp/assets/kb/entries/autotrade.md +38 -0
- package/dist/packages/mcp/src/app-help.js +1 -1
- package/dist/packages/mcp/src/autotrade-tools.js +944 -0
- package/dist/packages/mcp/src/backtest-requests.js +71 -0
- package/dist/packages/mcp/src/engine/config.js +159 -0
- package/dist/packages/mcp/src/engine/index.js +10 -0
- package/dist/packages/mcp/src/engine/loop.js +101 -0
- package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
- package/dist/packages/mcp/src/engine/ollama.js +258 -0
- package/dist/packages/mcp/src/engine/probe.js +110 -0
- package/dist/packages/mcp/src/engine/reason.js +35 -0
- package/dist/packages/mcp/src/engine/restrictions.js +111 -0
- package/dist/packages/mcp/src/index.js +7 -1
- package/dist/packages/mcp/src/ledger-tools.js +346 -0
- package/dist/packages/mcp/src/paper-agent.js +2 -0
- package/dist/packages/mcp/src/server.js +127 -19
- package/dist/packages/packs/src/backtest.js +3 -1
- package/dist/packages/popup/src/pages/chart-lib.js +134 -21
- package/dist/packages/popup/src/pages/chart.css +9 -0
- package/dist/packages/popup/src/pages/chart.html +3 -1
- package/dist/packages/popup/src/pages/chart.js +104 -7
- package/dist/packages/popup/src/protocol.js +2 -0
- package/dist/packages/popup/src/server.js +4 -0
- package/dist/packages/recorder/src/index.js +2 -0
- package/dist/packages/recorder/src/recorder.js +63 -6
- package/dist/packages/recorder/src/retention.js +100 -0
- package/dist/packages/recorder/src/schema.js +34 -1
- package/dist/packages/recorder/src/ticks.js +237 -0
- package/dist/packages/tui/src/index.js +1 -0
- package/dist/packages/tui/src/keymap.js +21 -1
- package/dist/packages/tui/src/markers.js +219 -0
- package/dist/packages/tui/src/widgets.js +149 -2
- package/package.json +2 -1
- package/dist/packages/data/src/trade/caps.js +0 -323
- package/dist/packages/data/src/trade/executor.js +0 -933
- package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
- package/dist/packages/data/src/trade/rpc-exec.js +0 -229
- package/dist/packages/data/src/trade/signer/client.js +0 -197
- package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
- package/dist/packages/data/src/trade/signer/host.js +0 -97
- package/dist/packages/data/src/trade/signer/main.js +0 -121
- package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
- package/dist/packages/data/src/trade/signer/protocol.js +0 -92
- package/dist/packages/data/src/trade/signer/service.js +0 -400
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
- package/dist/packages/data/src/trade/sol-usd.js +0 -86
- package/dist/packages/data/src/trade/swap-build.js +0 -108
- package/dist/packages/data/src/trade/token-delta.js +0 -201
- package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
// (observe snapshots + feed membership, the 1 h/2 h/6 h/24 h/72 h outcome sweeps, labels, retention) with no
|
|
5
5
|
// TUI, to be driven by a user-level launchd LaunchAgent on the Mac mini.
|
|
6
6
|
//
|
|
7
|
-
// dexterm record [run] [--interval S] [--ticks N | --once] [--maintain S] [--max-db-mb N] [--
|
|
8
|
-
//
|
|
7
|
+
// dexterm record [run] [--interval S] [--ticks N | --once] [--maintain S] [--max-db-mb N] [--retention-mb N] [--retention-days D]
|
|
8
|
+
// [--focus-ticks] [--no-newpairs] [--db PATH] [--verbose]
|
|
9
|
+
// dexterm record install [--interval S] [--max-db-mb N] [--retention-mb N] [--retention-days D] [--focus-ticks] [--load] [--dry-run]
|
|
9
10
|
// dexterm record uninstall [--unload]
|
|
10
11
|
// dexterm record status [--db PATH]
|
|
11
12
|
//
|
|
@@ -15,13 +16,18 @@
|
|
|
15
16
|
// the data layer; only the provider name is ever logged). Modest CPU: one feed refresh per `--interval` (60 s
|
|
16
17
|
// default) and recorder maintenance at most every `--maintain` seconds. A soft disk cap pauses OBSERVATION
|
|
17
18
|
// (the growth source) above `--max-db-mb` while labels + retention keep running, so the DB cannot run away.
|
|
19
|
+
// task-1504 (AUTOTRADE-SPEC §3, §10 Q4), both opt-in so an installed collector behaves as before until it is
|
|
20
|
+
// re-installed with them: `--retention-mb N` replaces that pause with eviction of the oldest history (observation
|
|
21
|
+
// never pauses), `--retention-days D` is the age rule for snapshot rows, and `--focus-ticks` runs the autotrade
|
|
22
|
+
// tick lane (5–10 s DexScreener samples of watchlist / held / candidate / open coins, plus 1m candles).
|
|
18
23
|
import { mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs';
|
|
19
24
|
import { spawnSync } from 'node:child_process';
|
|
20
25
|
import { dirname, join, resolve } from 'node:path';
|
|
21
26
|
import { fileURLToPath } from 'node:url';
|
|
22
27
|
import { defaultDbPath, resolveRpcUrl, extraRpcUrls, redactSecrets } from '../../data/src/index.js';
|
|
23
28
|
import { PumpPortalClient } from '../../data/src/index.js';
|
|
24
|
-
import { Recorder, recorderEnabled } from '../../recorder/src/index.js';
|
|
29
|
+
import { Recorder, recorderEnabled, dbFocus, focusFilePath, readFocusFile, DEFAULT_TICKS } from '../../recorder/src/index.js';
|
|
30
|
+
import { dextermHome } from "./boot-state.js";
|
|
25
31
|
import { createLiveFeed } from "./feed.js";
|
|
26
32
|
import { NewPairsLive } from "./live/index.js";
|
|
27
33
|
/** The LaunchAgent label (reverse-DNS). One collector per user. */
|
|
@@ -41,7 +47,17 @@ export function buildLiveEngine(env, opts) {
|
|
|
41
47
|
dbPath: opts.dbPath, rpcUrl: rpc.url, rpcFallbackUrl: rpc.fallbackUrl, rpcPublicPool: true, rpcExtraUrls: extraRpcUrls(env),
|
|
42
48
|
onRequest: opts.verbose ? (req, meta, o) => opts.onLog?.(` ${meta.source.padEnd(14)} ${String(o.status ?? 'ERR').padStart(3)} ${String(o.latencyMs).padStart(5)}ms ${redactSecrets(req.url).slice(0, 110)}\n`) : undefined,
|
|
43
49
|
});
|
|
44
|
-
|
|
50
|
+
// Focus coins for the tick lane: the watchlist + paper positions in this DB, and whatever the autotrade runner /
|
|
51
|
+
// the app publish in the focus file. Read on every poll (two indexed reads + one small file).
|
|
52
|
+
const focusFile = focusFilePath(dextermHome(env));
|
|
53
|
+
const recorder = new Recorder({
|
|
54
|
+
path: opts.dbPath, onLog: opts.verbose ? opts.onLog : undefined,
|
|
55
|
+
// The cap reclaims the data layer's expired cache entries (its own rule: a day past their TTL) before it evicts
|
|
56
|
+
// any history; `feed` is built above, so the hook can reach it.
|
|
57
|
+
...(opts.retentionBytes ? { cap: { maxBytes: opts.retentionBytes, reclaim: () => feed.dl.cache.prune() } } : {}),
|
|
58
|
+
...(opts.retentionDays ? { retention: { columnsDays: opts.retentionDays } } : {}),
|
|
59
|
+
...(opts.focusTicks ? { ticks: { focus: () => [...dbFocus(recorder.database), ...readFocusFile(focusFile, Date.now())] } } : {}),
|
|
60
|
+
});
|
|
45
61
|
recorder.attach(feed.dl.store);
|
|
46
62
|
let newpairs = null;
|
|
47
63
|
if (!opts.noNewpairs) {
|
|
@@ -66,6 +82,8 @@ export function buildLiveEngine(env, opts) {
|
|
|
66
82
|
recorder.prune();
|
|
67
83
|
}
|
|
68
84
|
catch { /* logged inside; never fatal */ } },
|
|
85
|
+
enforceCap() { return recorder.enforceCap(); },
|
|
86
|
+
...(opts.focusTicks ? { pollTicks: () => recorder.pollTicks(dl) } : {}),
|
|
69
87
|
dbBytes() {
|
|
70
88
|
try {
|
|
71
89
|
const db = recorder.database;
|
|
@@ -101,7 +119,7 @@ export function buildLiveEngine(env, opts) {
|
|
|
101
119
|
* Never throws out of the loop: an engine error is logged and the loop continues to the next tick.
|
|
102
120
|
*/
|
|
103
121
|
export async function runCollectorLoop(engine, opts) {
|
|
104
|
-
const res = { ticks: 0, observePasses: 0, rowsWritten: 0, maintainPasses: 0, cappedTicks: 0 };
|
|
122
|
+
const res = { ticks: 0, observePasses: 0, rowsWritten: 0, maintainPasses: 0, cappedTicks: 0, evictions: 0 };
|
|
105
123
|
let lastMaintain = -Infinity;
|
|
106
124
|
for (let i = 0; i < opts.ticks && !opts.aborted(); i++) {
|
|
107
125
|
if (i > 0) {
|
|
@@ -111,7 +129,19 @@ export async function runCollectorLoop(engine, opts) {
|
|
|
111
129
|
}
|
|
112
130
|
res.ticks++;
|
|
113
131
|
let over = false;
|
|
114
|
-
if (opts.
|
|
132
|
+
if (opts.capMode === 'evict') {
|
|
133
|
+
try {
|
|
134
|
+
const c = engine.enforceCap?.() ?? null;
|
|
135
|
+
if (c?.evicted)
|
|
136
|
+
res.evictions++;
|
|
137
|
+
if (c?.over)
|
|
138
|
+
opts.onLog?.(`record: DB still at ${(c.usedAfter / 1e6).toFixed(0)} MB used after evicting all but the newest hour (other tables are large); observation continues\n`);
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
opts.onLog?.(`record: retention failed: ${redactSecrets(e.message)}\n`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (opts.maxDbBytes !== null) {
|
|
115
145
|
const b = engine.dbBytes();
|
|
116
146
|
if (b !== null && b >= opts.maxDbBytes) {
|
|
117
147
|
engine.prune();
|
|
@@ -146,6 +176,29 @@ export async function runCollectorLoop(engine, opts) {
|
|
|
146
176
|
}
|
|
147
177
|
return res;
|
|
148
178
|
}
|
|
179
|
+
/** task-1504: drive the tick lane every `everyMs` until aborted. Never throws (a failed poll is logged). */
|
|
180
|
+
export async function runTickLoop(engine, opts) {
|
|
181
|
+
const res = { polls: 0, calls: 0, written: 0 };
|
|
182
|
+
if (!engine.pollTicks)
|
|
183
|
+
return res;
|
|
184
|
+
while (!opts.aborted()) {
|
|
185
|
+
try {
|
|
186
|
+
const r = await engine.pollTicks();
|
|
187
|
+
res.polls++;
|
|
188
|
+
res.calls += r.calls;
|
|
189
|
+
res.written += r.written;
|
|
190
|
+
if (r.error)
|
|
191
|
+
opts.onLog?.(`record: tick poll failed: ${redactSecrets(r.error)}\n`);
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
opts.onLog?.(`record: tick poll failed: ${redactSecrets(e.message)}\n`);
|
|
195
|
+
}
|
|
196
|
+
if (opts.aborted())
|
|
197
|
+
break;
|
|
198
|
+
await opts.sleep(opts.everyMs);
|
|
199
|
+
}
|
|
200
|
+
return res;
|
|
201
|
+
}
|
|
149
202
|
const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
150
203
|
/**
|
|
151
204
|
* A user-level LaunchAgent (no sudo). KeepAlive so a crash or a reboot-after-login brings it back, RunAtLoad,
|
|
@@ -155,7 +208,7 @@ const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g,
|
|
|
155
208
|
* collector to run; a true pre-login daemon would be a LaunchDaemon under sudo, which this task does not use.
|
|
156
209
|
*/
|
|
157
210
|
export function renderCollectorPlist(o) {
|
|
158
|
-
const args = [o.nodePath, o.binPath, 'record', 'run', '--interval', String(Math.round(o.intervalMs / 1000)), '--max-db-mb', String(o.maxDbMb)];
|
|
211
|
+
const args = [o.nodePath, o.binPath, 'record', 'run', '--interval', String(Math.round(o.intervalMs / 1000)), '--max-db-mb', String(o.maxDbMb), ...(o.extraArgs ?? [])];
|
|
159
212
|
const argXml = args.map((a) => ` <string>${esc(a)}</string>`).join('\n');
|
|
160
213
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
161
214
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -202,10 +255,37 @@ function dataDir(env, db) {
|
|
|
202
255
|
function sourceBin() {
|
|
203
256
|
return fileURLToPath(new URL('../bin/dexterm.mjs', import.meta.url));
|
|
204
257
|
}
|
|
258
|
+
/** task-1504: the shared `--retention-mb` / `--retention-days` / `--focus-ticks` parser (run + install). */
|
|
259
|
+
function parseRetention(t, next, a) {
|
|
260
|
+
if (t === '--focus-ticks') {
|
|
261
|
+
a.focusTicks = true;
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (t === '--retention-mb') {
|
|
265
|
+
const n = Number(next());
|
|
266
|
+
if (!Number.isFinite(n) || n < 1)
|
|
267
|
+
return '--retention-mb needs a number ≥ 1';
|
|
268
|
+
a.retentionBytes = Math.round(n * 1_000_000);
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
if (t === '--retention-days') {
|
|
272
|
+
const n = Number(next());
|
|
273
|
+
if (!Number.isFinite(n) || n < 1)
|
|
274
|
+
return '--retention-days needs a number of days ≥ 1';
|
|
275
|
+
a.retentionDays = n;
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
205
280
|
export function parseRun(argv) {
|
|
206
|
-
const a = { intervalMs: DEFAULT_INTERVAL_MS, ticks: Infinity, maintainMs: DEFAULT_MAINTAIN_MS, maxDbBytes: DEFAULT_MAX_DB_MB * 1_000_000, noNewpairs: false, db: null, verbose: false };
|
|
281
|
+
const a = { intervalMs: DEFAULT_INTERVAL_MS, ticks: Infinity, maintainMs: DEFAULT_MAINTAIN_MS, maxDbBytes: DEFAULT_MAX_DB_MB * 1_000_000, noNewpairs: false, db: null, verbose: false, retentionBytes: null, retentionDays: null, focusTicks: false };
|
|
207
282
|
for (let i = 0; i < argv.length; i++) {
|
|
208
283
|
const t = argv[i];
|
|
284
|
+
const r = parseRetention(t, () => argv[++i], a);
|
|
285
|
+
if (typeof r === 'string')
|
|
286
|
+
return { error: r };
|
|
287
|
+
if (r === null)
|
|
288
|
+
continue;
|
|
209
289
|
if (t === '--verbose' || t === '-v')
|
|
210
290
|
a.verbose = true;
|
|
211
291
|
else if (t === '--no-newpairs')
|
|
@@ -248,15 +328,20 @@ export function parseRun(argv) {
|
|
|
248
328
|
}
|
|
249
329
|
export const RECORD_USAGE = `dexterm record: keep the history recorder fed with no TUI open (task-1414).
|
|
250
330
|
|
|
251
|
-
dexterm record [run] [--interval S] [--ticks N | --once] [--maintain S] [--max-db-mb N] [--
|
|
252
|
-
|
|
331
|
+
dexterm record [run] [--interval S] [--ticks N | --once] [--maintain S] [--max-db-mb N] [--retention-mb N] [--retention-days D]
|
|
332
|
+
[--focus-ticks] [--no-newpairs] [--db PATH] [--verbose]
|
|
333
|
+
dexterm record install [--interval S] [--max-db-mb N] [--retention-mb N] [--retention-days D] [--focus-ticks] [--load] [--dry-run]
|
|
253
334
|
dexterm record uninstall [--unload]
|
|
254
335
|
dexterm record status [--db PATH]
|
|
255
336
|
|
|
256
337
|
run the collector loop: one keyless feed refresh per --interval (default 60 s) feeds the recorder
|
|
257
338
|
(snapshots + feed membership + new pairs), recorder maintenance (outcome sweeps, labels, retention)
|
|
258
339
|
every --maintain seconds. --max-db-mb is a soft cap (default 1024): above it observation pauses while
|
|
259
|
-
labels + retention keep running. --
|
|
340
|
+
labels + retention keep running. --retention-mb N replaces that pause: the oldest history is evicted
|
|
341
|
+
to stay under N MB and observation never pauses. --retention-days D drops snapshot rows (sweep rows
|
|
342
|
+
too) older than D days (default 90). --focus-ticks samples watchlist / held / candidate / open coins
|
|
343
|
+
every 5-10 s (DexScreener batches of 30, a daily call ceiling) with 1m candles; autotrade backtests
|
|
344
|
+
read them. --no-newpairs drops the PumpPortal stream. Ctrl-C stops cleanly
|
|
260
345
|
(labels whatever is due). Requests honour the data layer's rate limits, health gate and sweep budgets.
|
|
261
346
|
install write a user-level launchd LaunchAgent (no sudo) that runs \`record run\`; prints the launchctl
|
|
262
347
|
command. --load also runs launchctl (off by default). --dry-run prints the plist, writes nothing.
|
|
@@ -300,47 +385,77 @@ async function run(argv, io, deps) {
|
|
|
300
385
|
return 2;
|
|
301
386
|
}
|
|
302
387
|
// SIGINT/SIGTERM-aware cancellable sleeper (launchd sends SIGTERM to stop the agent).
|
|
388
|
+
// Several loops may sleep at once (the collector + the tick lane), so every sleeper is woken on abort.
|
|
303
389
|
const stop = (() => {
|
|
304
390
|
let aborted = false;
|
|
305
|
-
|
|
391
|
+
const wakes = new Set();
|
|
306
392
|
return {
|
|
307
393
|
get aborted() { return aborted; },
|
|
308
|
-
abort() { aborted = true;
|
|
309
|
-
|
|
394
|
+
abort() { aborted = true; for (const w of [...wakes])
|
|
395
|
+
w(); },
|
|
396
|
+
sleep(ms) {
|
|
397
|
+
return new Promise((r) => {
|
|
398
|
+
const done = () => { clearTimeout(t); wakes.delete(done); r(); };
|
|
399
|
+
const t = setTimeout(done, ms);
|
|
400
|
+
t.unref?.();
|
|
401
|
+
wakes.add(done);
|
|
402
|
+
});
|
|
403
|
+
},
|
|
310
404
|
};
|
|
311
405
|
})();
|
|
312
406
|
const log = (s) => io.stderr.write(s);
|
|
313
|
-
const engine = deps.engine ?? buildLiveEngine(io.env, { dbPath, noNewpairs: parsed.noNewpairs, verbose: parsed.verbose, onLog: log });
|
|
407
|
+
const engine = deps.engine ?? buildLiveEngine(io.env, { dbPath, noNewpairs: parsed.noNewpairs, verbose: parsed.verbose, onLog: log, retentionBytes: parsed.retentionBytes, retentionDays: parsed.retentionDays, focusTicks: parsed.focusTicks });
|
|
314
408
|
const own = !deps.engine;
|
|
315
409
|
const onSig = () => { io.stderr.write('\ndexterm record: stopping…\n'); stop.abort(); };
|
|
316
410
|
if (own) {
|
|
317
411
|
process.on('SIGINT', onSig);
|
|
318
412
|
process.on('SIGTERM', onSig);
|
|
319
413
|
}
|
|
414
|
+
// The sleeps are unref'd; without the PumpPortal socket (--no-newpairs) nothing else would keep the event loop
|
|
415
|
+
// alive between passes and node would exit mid-run (task-1504 smoke). Hold it open until the loops finish.
|
|
416
|
+
const hold = own ? setInterval(() => { }, 1 << 30) : null;
|
|
320
417
|
if (parsed.verbose || parsed.ticks !== Infinity)
|
|
321
|
-
io.stderr.write(`dexterm record: db ${dbPath} · interval ${Math.round(parsed.intervalMs / 1000)}s · maintain ${Math.round(parsed.maintainMs / 1000)}s · cap ${parsed.maxDbBytes === null ? 'off' : `${Math.round(parsed.maxDbBytes / 1e6)} MB`} · newpairs ${parsed.noNewpairs ? 'off' : 'on'}\n`);
|
|
418
|
+
io.stderr.write(`dexterm record: db ${dbPath} · interval ${Math.round(parsed.intervalMs / 1000)}s · maintain ${Math.round(parsed.maintainMs / 1000)}s · cap ${parsed.maxDbBytes === null ? 'off' : `${Math.round(parsed.maxDbBytes / 1e6)} MB`} · newpairs ${parsed.noNewpairs ? 'off' : 'on'}${parsed.retentionBytes !== null ? ` · retention ${Math.round(parsed.retentionBytes / 1e6)} MB (evict, never pause)` : ''}${parsed.retentionDays !== null ? ` · rows ${parsed.retentionDays} d` : ''} · focus ticks ${parsed.focusTicks ? 'on' : 'off'}\n`);
|
|
322
419
|
let result;
|
|
420
|
+
let tickRes = { polls: 0, calls: 0, written: 0 };
|
|
323
421
|
try {
|
|
324
|
-
|
|
422
|
+
let mainDone = false;
|
|
423
|
+
const main = runCollectorLoop(engine, {
|
|
325
424
|
intervalMs: parsed.intervalMs, ticks: parsed.ticks, maintainEveryMs: parsed.maintainMs, maxDbBytes: parsed.maxDbBytes,
|
|
425
|
+
capMode: parsed.retentionBytes !== null ? 'evict' : 'pause',
|
|
326
426
|
now, sleep: (ms) => stop.sleep(ms), aborted: () => stop.aborted, onLog: log,
|
|
327
|
-
});
|
|
427
|
+
}).finally(() => { mainDone = true; if (parsed.ticks !== Infinity)
|
|
428
|
+
stop.abort(); });
|
|
429
|
+
const lane = parsed.focusTicks && engine.pollTicks
|
|
430
|
+
? runTickLoop(engine, { everyMs: DEFAULT_TICKS.fastEveryMs, sleep: (ms) => stop.sleep(ms), aborted: () => stop.aborted || mainDone, onLog: log })
|
|
431
|
+
: Promise.resolve(tickRes);
|
|
432
|
+
[result, tickRes] = await Promise.all([main, lane]);
|
|
328
433
|
}
|
|
329
434
|
finally {
|
|
330
435
|
if (own) {
|
|
331
436
|
process.removeListener('SIGINT', onSig);
|
|
332
437
|
process.removeListener('SIGTERM', onSig);
|
|
333
438
|
}
|
|
439
|
+
if (hold)
|
|
440
|
+
clearInterval(hold);
|
|
334
441
|
engine.close();
|
|
335
442
|
}
|
|
336
443
|
if (parsed.verbose || parsed.ticks !== Infinity)
|
|
337
|
-
io.stdout.write(`record: ${result.observePasses} observe pass(es), ${result.rowsWritten} snapshot row(s), ${result.maintainPasses} maintenance pass(es)${result.cappedTicks ? `, ${result.cappedTicks} tick(s) paused at the disk cap` : ''}\n`);
|
|
444
|
+
io.stdout.write(`record: ${result.observePasses} observe pass(es), ${result.rowsWritten} snapshot row(s), ${result.maintainPasses} maintenance pass(es)${result.cappedTicks ? `, ${result.cappedTicks} tick(s) paused at the disk cap` : ''}${result.evictions ? `, ${result.evictions} retention eviction(s)` : ''}${tickRes.polls ? `, ${tickRes.polls} focus-tick poll(s) (${tickRes.calls} call(s), ${tickRes.written} tick row(s))` : ''}\n`);
|
|
338
445
|
return 0;
|
|
339
446
|
}
|
|
340
447
|
function install(argv, io) {
|
|
341
448
|
let intervalMs = DEFAULT_INTERVAL_MS, maxDbMb = DEFAULT_MAX_DB_MB, load = false, dry = false;
|
|
449
|
+
const ret = { retentionBytes: null, retentionDays: null, focusTicks: false };
|
|
342
450
|
for (let i = 0; i < argv.length; i++) {
|
|
343
451
|
const t = argv[i];
|
|
452
|
+
const r = parseRetention(t, () => argv[++i], ret);
|
|
453
|
+
if (typeof r === 'string') {
|
|
454
|
+
io.stderr.write(`dexterm record: ${r}\n`);
|
|
455
|
+
return 2;
|
|
456
|
+
}
|
|
457
|
+
if (r === null)
|
|
458
|
+
continue;
|
|
344
459
|
if (t === '--load')
|
|
345
460
|
load = true;
|
|
346
461
|
else if (t === '--dry-run')
|
|
@@ -369,7 +484,12 @@ function install(argv, io) {
|
|
|
369
484
|
const path = plistPath(io.env);
|
|
370
485
|
const logPath = join(dataDir(io.env), 'collector.log');
|
|
371
486
|
const bin = sourceBin();
|
|
372
|
-
const
|
|
487
|
+
const extraArgs = [
|
|
488
|
+
...(ret.retentionBytes !== null ? ['--retention-mb', String(Math.round(ret.retentionBytes / 1e6))] : []),
|
|
489
|
+
...(ret.retentionDays !== null ? ['--retention-days', String(ret.retentionDays)] : []),
|
|
490
|
+
...(ret.focusTicks ? ['--focus-ticks'] : []),
|
|
491
|
+
];
|
|
492
|
+
const plist = renderCollectorPlist({ label: COLLECTOR_LABEL, nodePath: process.execPath, binPath: bin, intervalMs, maxDbMb, extraArgs, logPath, workingDir: resolve(dirname(bin), '..', '..', '..') });
|
|
373
493
|
if (dry) {
|
|
374
494
|
io.stdout.write(plist);
|
|
375
495
|
io.stderr.write(`\ndexterm record: dry run — would write ${path}\n`);
|
|
@@ -427,6 +547,7 @@ function status(argv, io) {
|
|
|
427
547
|
`snapshots ${s.snapshots} (live ${s.live}, sweep ${s.sweep}, t0 anchors ${s.t0})`,
|
|
428
548
|
`outcomes ${s.outcomes}${s.outcomes ? ` (${Object.entries(s.outcomesByLabel).map(([k, v]) => `${k} ${v}`).join(', ')})` : ''}`,
|
|
429
549
|
`sweeps ${s.sweeps} runs · ${s.sweepCallsToday} DexScreener calls today`,
|
|
550
|
+
`focus ticks ${s.ticks} rows · ${s.candles} 1m candles · ${s.tickCallsToday} calls today`,
|
|
430
551
|
`span ${when(s.oldestT)} → ${when(s.newestT)}`,
|
|
431
552
|
].join('\n') + '\n');
|
|
432
553
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// Pure: (canvas, StoryView, opts) → cells. Every number comes from the story (app-computed); nothing here can act.
|
|
12
12
|
// Reduced motion (calm mode / DEXTERM_REDUCED_MOTION) drops every sweep, flash, glitch and typewriter: the same
|
|
13
13
|
// information appears at once. NO_COLOR keeps meaning in glyphs (▸ active phase, ✓ ! ✗ chips, ▶ cursor).
|
|
14
|
-
import { mix, hash3, KATAKANA, decryptText, fmtCompactUsd } from '../../../tui/src/index.js';
|
|
14
|
+
import { mix, hash3, KATAKANA, decryptText, fmtCompactUsd, markerGlyph, markerLetter } from '../../../tui/src/index.js';
|
|
15
15
|
import { STORY_RAIL, fmtQty, fmtPx } from '../../../agents/src/index.js';
|
|
16
16
|
import { pnlChipVM, renderPnlChip } from "./pnl-chip.js";
|
|
17
17
|
/** Rows the dock takes at the bottom of the terminal. */
|
|
@@ -550,7 +550,8 @@ function ticketCard(c, v, arc, o, screenH) {
|
|
|
550
550
|
['SIZE', arc.sizeUsd != null ? `$${arc.sizeUsd.toFixed(2)}${v.caps ? ` cap $${v.caps.perTradeUsd.toFixed(2)}/trade · $${v.caps.totalUsd.toFixed(0)} total` : ''}` : 'sizing…', arc.sizeUsd != null ? t.text : t.dim],
|
|
551
551
|
['GATE', 'YOUR keypress armed the mandate · tier hands-free', t.muted],
|
|
552
552
|
['', 'no keypress per trade inside it · desk enforces caps', mix(bg, t.dim, 0.85)],
|
|
553
|
-
|
|
553
|
+
// task-1537: the fill reads as the market cap it filled at when the HUD's cap scale knows the supply (like the position card)
|
|
554
|
+
['ENTRY', fill ? `${Math.abs(fill.sol).toFixed(4)} SOL → ${fmtQty(fill.qty)} $${arc.symbol}${fill.priceUsd != null ? ` @ ${((sc) => (sc ? `MC ${fmtCompactUsd(fill.priceUsd * sc)}` : `$${fmtPx(fill.priceUsd)}`))(o.mcapScale?.(arc.mint) ?? null)}` : ''}` : refused ? '—' : 'awaiting confirm · fresh quote at fill', fill ? t.text : t.dim],
|
|
554
555
|
['IMPACT', fill?.impactPct != null ? `${fill.impactPct.toFixed(2)}%${v.caps?.maxPriceImpactPct != null ? ` max ${v.caps.maxPriceImpactPct}%` : ''}` : '—', t.muted],
|
|
555
556
|
['EXITS', 'agent-managed · no resting orders on PAPER', t.muted],
|
|
556
557
|
];
|
|
@@ -585,6 +586,26 @@ function ticketCard(c, v, arc, o, screenH) {
|
|
|
585
586
|
c.text(x + Math.floor((w - s.length) / 2), sy, s, { fg: mix(bg, t.meteora, pulse), bg });
|
|
586
587
|
}
|
|
587
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* task-1483: where the arc's fills sit on the PnL trace: the column of the first sample at or after the fill (the sample
|
|
591
|
+
* that first reflects it). A fill before the first SHOWN sample when older samples were cut off, or after the last
|
|
592
|
+
* sample, gets no column (it is not moved onto another one). Columns are 0-based into `shownT`.
|
|
593
|
+
*/
|
|
594
|
+
export function traceFillCols(allT, shownFrom, fills) {
|
|
595
|
+
const by = new Map();
|
|
596
|
+
for (const f of fills) {
|
|
597
|
+
const i = allT.findIndex((t) => t >= f.t);
|
|
598
|
+
if (i < 0 || i < shownFrom)
|
|
599
|
+
continue;
|
|
600
|
+
const key = `${i}|${f.side}`;
|
|
601
|
+
const e = by.get(key);
|
|
602
|
+
if (e)
|
|
603
|
+
e.count++;
|
|
604
|
+
else
|
|
605
|
+
by.set(key, { col: i - shownFrom, side: f.side, count: 1 });
|
|
606
|
+
}
|
|
607
|
+
return [...by.values()].sort((a, b) => a.col - b.col);
|
|
608
|
+
}
|
|
588
609
|
function positionCard(c, v, arc, o, screenH) {
|
|
589
610
|
const t = c.theme;
|
|
590
611
|
const w = Math.min(62, c.w - 4);
|
|
@@ -619,6 +640,17 @@ function positionCard(c, v, arc, o, screenH) {
|
|
|
619
640
|
const lv = Math.round(((s.pct - lo) / span) * 7);
|
|
620
641
|
c.put(x + 2 + i, y + 6, LV[lv], { fg: s.pct >= 0 ? t.up : t.down, bg, soft: true });
|
|
621
642
|
});
|
|
643
|
+
// task-1483: the agent's PAPER fills under the trace (△B / ▽S, hollow = paper), at the sample that first shows them
|
|
644
|
+
const used = new Set();
|
|
645
|
+
for (const m of traceFillCols(arc.trace.map((s) => s.t), arc.trace.length - tr.length, arc.fills)) {
|
|
646
|
+
const cx = x + 2 + m.col;
|
|
647
|
+
if (used.has(m.col) || used.has(m.col + 1) || cx + 1 >= x + w - 1)
|
|
648
|
+
continue;
|
|
649
|
+
used.add(m.col);
|
|
650
|
+
used.add(m.col + 1);
|
|
651
|
+
const col = m.side === 'buy' ? t.up : t.down;
|
|
652
|
+
c.text(cx, y + 7, `${markerGlyph(m.side, 'paper')}${markerLetter(m.side)}`, { fg: col, bg: mix(bg, col, 0.2), b: true, hard: true });
|
|
653
|
+
}
|
|
622
654
|
}
|
|
623
655
|
else
|
|
624
656
|
c.text(x + 2, y + 6, '· waiting for the first mark tick', { fg: t.dim, bg });
|
|
@@ -20,7 +20,7 @@ export function renderAnalystSession(c, x, y, w, h, s, opts) {
|
|
|
20
20
|
// Narrow panels (token screen) drop the model so cost · latency · tools still fits beside the title.
|
|
21
21
|
const full = s.status === 'error' ? 'analyst unavailable' : s.footer;
|
|
22
22
|
// 0.1.5: a codex conversation says so in the title; claude panels keep the original default.
|
|
23
|
-
const title = opts.title ?? (s.engine === 'codex' ? '✦ ANALYST · codex' : '✦ ANALYST · claude');
|
|
23
|
+
const title = opts.title ?? (s.engine === 'codex' ? '✦ ANALYST · codex' : s.engine === 'ollama' ? '✦ ANALYST · ollama' : '✦ ANALYST · claude');
|
|
24
24
|
const caption = title.length + full.length + 10 <= w ? full : full.split(' · ').slice(0, 3).join(' · ');
|
|
25
25
|
const capFg = s.status === 'error' ? t.warn : t.dim;
|
|
26
26
|
const box = c.box(x, y, w, h, { title, titleFg: t.ai, caption, captionFg: capFg, border: bc });
|
|
@@ -183,7 +183,7 @@ export function renderAnalystSession(c, x, y, w, h, s, opts) {
|
|
|
183
183
|
// mcap/age + the analyst's one-line reason), navigable with ↑↓ and ⏎-to-jump. The numbers come from
|
|
184
184
|
// live data (resolved by the caller from the app's data layer, never from the model's text).
|
|
185
185
|
if (opts.results?.length && cy < inputY - 1) {
|
|
186
|
-
cy = renderResultsTable(c, box, bg, cy, inputY, inner, opts.results, opts.resultsCursor ?? 0, opts.resultsActive ?? false);
|
|
186
|
+
cy = renderResultsTable(c, box, bg, cy, inputY, inner, opts.results, opts.resultsCursor ?? 0, opts.resultsActive ?? false, opts.priceMode !== 'price');
|
|
187
187
|
}
|
|
188
188
|
if (s.candidates.length && cy < inputY - 1) {
|
|
189
189
|
cy += 1;
|
|
@@ -251,14 +251,17 @@ function fmtAge(min) {
|
|
|
251
251
|
* so it stays legible instead of wrapping into prose. The highlighted row (cursor) is tinted; when the
|
|
252
252
|
* table is active a `▸` marks it and the footer hint changes. Returns the new y.
|
|
253
253
|
*/
|
|
254
|
-
function renderResultsTable(c, box, bg, startY, inputY, inner, rows, cursor, active) {
|
|
254
|
+
function renderResultsTable(c, box, bg, startY, inputY, inner, rows, cursor, active, mcapFirst = true) {
|
|
255
255
|
const t = c.theme;
|
|
256
256
|
let cy = startY + 1;
|
|
257
257
|
if (cy >= inputY - 1)
|
|
258
258
|
return cy;
|
|
259
259
|
// Column plan: [symbol, price, 24h, liq, vol, mcap, age] then the reason fills the rest. Drop from
|
|
260
260
|
// the right as width shrinks so a narrow panel still shows symbol + price + change + a reason.
|
|
261
|
-
|
|
261
|
+
// task-1537: in the market-cap basis MCAP takes PRICE's slot (the headline number) and PRICE moves to MCAP's
|
|
262
|
+
const full = mcapFirst
|
|
263
|
+
? [['SYMBOL', 10], ['MCAP', 9], ['24H', 8], ['LIQ', 8], ['VOL', 8], ['PRICE', 8], ['AGE', 5]]
|
|
264
|
+
: [['SYMBOL', 10], ['PRICE', 9], ['24H', 8], ['LIQ', 8], ['VOL', 8], ['MCAP', 8], ['AGE', 5]];
|
|
262
265
|
let cols = full.slice();
|
|
263
266
|
// keep trimming trailing columns until the header + a minimal reason (12) fits
|
|
264
267
|
while (cols.length > 3 && cols.reduce((a, [, w]) => a + w + 1, 0) + 12 > inner - 2)
|
|
@@ -6,6 +6,7 @@ import { Canvas, sparkline, heatPct, decryptText, spinnerGlyph, fmtUsd, fmtPrice
|
|
|
6
6
|
import { headerModel } from "../state.js";
|
|
7
7
|
import { screenerView } from "../screener/view.js";
|
|
8
8
|
import { renderAnalystSession } from "./analyst-session.js";
|
|
9
|
+
import { priceModeOf } from "../display.js";
|
|
9
10
|
import { resolveAnalystResults } from "../analyst/results.js";
|
|
10
11
|
export function renderAnalyst(c, state) {
|
|
11
12
|
const t = c.theme;
|
|
@@ -28,7 +29,7 @@ export function renderAnalyst(c, state) {
|
|
|
28
29
|
frame: state.vfx.frame, code: state.vfx.code, calm: isCalm(state.vfx),
|
|
29
30
|
input: state.aiInput ?? '', inputFocused: focused,
|
|
30
31
|
scroll: state.aiScroll, revealAll: state.aiRevealAll,
|
|
31
|
-
results, resultsCursor: state.analyst.resultsCursor, resultsActive,
|
|
32
|
+
results, resultsCursor: state.analyst.resultsCursor, resultsActive, priceMode: priceModeOf(state), // task-1537
|
|
32
33
|
inputHint: focused ? 'type your question… ⏎ send · esc blur · analysis not advice'
|
|
33
34
|
: results.length ? 'i ask · ↑↓ pick coin · ⏎ open · o ticket · esc back'
|
|
34
35
|
: 'i ask · ⏎ focus · ↑↓ pick coin · PgUp/Dn scroll',
|
|
@@ -43,7 +44,7 @@ export function renderAnalyst(c, state) {
|
|
|
43
44
|
if (state.screener.source !== 'fixtures') {
|
|
44
45
|
const live = state.aiFocused === true;
|
|
45
46
|
const eng = state.analystEngine ?? 'claude'; // 0.1.5: the user's own CLI that will answer (claude | codex)
|
|
46
|
-
const b = c.box(0, top, chatW, bottom - top, { title: `✦ ANALYST · ${eng}`, titleFg: t.ai, caption: `read-only · local ${eng} CLI · on demand`, captionFg: t.dim, border: bc });
|
|
47
|
+
const b = c.box(0, top, chatW, bottom - top, { title: `✦ ANALYST · ${eng}`, titleFg: t.ai, caption: `read-only · local ${eng === 'ollama' ? 'ollama model' : `${eng} CLI`} · on demand`, captionFg: t.dim, border: bc });
|
|
47
48
|
const bbg = b.bg;
|
|
48
49
|
const bin = b.w - 2;
|
|
49
50
|
let by = b.y + 1;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// dexterm — the AUTOTRADE screen's key handling (`Q`, task-1508). The loop calls autotradeKey() first while the
|
|
2
|
+
// screen captures typed keys (an id / file line, a field value, the flatten confirm), and autotradeAction() for
|
|
3
|
+
// every resolved action while the screen is open. Both only change this screen's state and the user's own
|
|
4
|
+
// strategy files; nothing here reaches a wallet, a key, a mandate or the signer.
|
|
5
|
+
import { INPUT_MAX, backtestSubject, beginFieldEdit, beginImport, beginNew, commitInput, commitTyping, confirmKey, exportSelected, nextView, openEditor, reloadList, refreshRunner, revalidateTyping, runnerVerb, saveEditor, selectedEntry, startBacktest, startCompareBacktest, startSweep, toggleMark, undoEditor, } from "./autotrade-ui.js";
|
|
6
|
+
import { openReview, reviewKey } from "./autotrade-proposals.js";
|
|
7
|
+
const PRINTABLE = (k) => (k.name === 'space' ? ' ' : k.name === 'char' && k.ch && !k.ctrl && !k.alt && !/[\u0000-\u001f\u007f]/.test(k.ch) ? k.ch : null);
|
|
8
|
+
/** A captured key (input line, field value or confirm). Returns true when the key was consumed. */
|
|
9
|
+
export function autotradeKey(ui, key) {
|
|
10
|
+
// task-1507: a chat proposal's review card owns the keyboard (y applies, a LOOSER edit needs a second y, n dismisses)
|
|
11
|
+
if (ui.review) {
|
|
12
|
+
ui.notice = reviewKey(ui, key);
|
|
13
|
+
if (!ui.review)
|
|
14
|
+
reloadList(ui);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (ui.confirm) {
|
|
18
|
+
ui.notice = confirmKey(ui, key.name === 'char' && key.ch === 'y' && !key.ctrl && !key.alt);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (ui.input) {
|
|
22
|
+
const inp = ui.input;
|
|
23
|
+
if (key.name === 'esc') {
|
|
24
|
+
ui.input = null;
|
|
25
|
+
ui.notice = null;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (key.name === 'enter') {
|
|
29
|
+
ui.notice = commitInput(ui);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (key.name === 'backspace') {
|
|
33
|
+
inp.text = inp.text.slice(0, -1);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (key.ctrl && key.ch === 'u') {
|
|
37
|
+
inp.text = '';
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
const ch = PRINTABLE(key);
|
|
41
|
+
// an id is lowercase letters, digits and dashes only, so it can never carry a path
|
|
42
|
+
if (ch && (inp.kind === 'import' ? inp.text.length < INPUT_MAX : /^[a-z0-9-]$/.test(ch) && inp.text.length < 32))
|
|
43
|
+
inp.text += ch;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const ty = ui.view === 'editor' ? ui.editor?.typing : null;
|
|
47
|
+
if (ty) {
|
|
48
|
+
if (key.name === 'esc') {
|
|
49
|
+
ui.editor.typing = null;
|
|
50
|
+
ui.notice = null;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (key.name === 'enter') {
|
|
54
|
+
ui.notice = commitTyping(ui);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (key.name === 'backspace')
|
|
58
|
+
ty.text = ty.text.slice(0, -1);
|
|
59
|
+
else if (key.ctrl && key.ch === 'u')
|
|
60
|
+
ty.text = '';
|
|
61
|
+
else {
|
|
62
|
+
const ch = PRINTABLE(key);
|
|
63
|
+
if (ch && ty.text.length < INPUT_MAX)
|
|
64
|
+
ty.text += ch;
|
|
65
|
+
else
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
revalidateTyping(ui);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function move(ui, delta) {
|
|
74
|
+
if (ui.view === 'list')
|
|
75
|
+
ui.selected = Math.max(0, Math.min(ui.entries.length - 1, ui.selected + delta));
|
|
76
|
+
else if (ui.view === 'editor' && ui.editor)
|
|
77
|
+
ui.editor.selected = Math.max(0, Math.min(ui.editor.fields.length - 1, ui.editor.selected + delta));
|
|
78
|
+
else if (ui.view === 'compare' && ui.compare)
|
|
79
|
+
ui.compare.scroll = Math.max(0, ui.compare.scroll + delta);
|
|
80
|
+
}
|
|
81
|
+
const PAGE = 10;
|
|
82
|
+
/**
|
|
83
|
+
* One resolved action on the AUTOTRADE screen. Returns true when it was handled here (the loop then does
|
|
84
|
+
* nothing else with it); false lets the loop's own handling run (esc on the list pops the screen stack, `q`
|
|
85
|
+
* quits, the global screen keys still navigate away).
|
|
86
|
+
*/
|
|
87
|
+
export function autotradeAction(ui, action) {
|
|
88
|
+
const say = (s) => { ui.notice = s ?? null; return true; };
|
|
89
|
+
const e = selectedEntry(ui);
|
|
90
|
+
switch (action) {
|
|
91
|
+
case 'move.down':
|
|
92
|
+
move(ui, 1);
|
|
93
|
+
return true;
|
|
94
|
+
case 'move.up':
|
|
95
|
+
move(ui, -1);
|
|
96
|
+
return true;
|
|
97
|
+
case 'move.pagedown':
|
|
98
|
+
move(ui, PAGE);
|
|
99
|
+
return true;
|
|
100
|
+
case 'move.pageup':
|
|
101
|
+
move(ui, -PAGE);
|
|
102
|
+
return true;
|
|
103
|
+
case 'move.top':
|
|
104
|
+
move(ui, -1e9);
|
|
105
|
+
return true;
|
|
106
|
+
case 'move.bottom':
|
|
107
|
+
move(ui, 1e9);
|
|
108
|
+
return true;
|
|
109
|
+
case 'back':
|
|
110
|
+
if (ui.view === 'list')
|
|
111
|
+
return false;
|
|
112
|
+
ui.view = 'list';
|
|
113
|
+
return say(null);
|
|
114
|
+
case 'autotrade.view':
|
|
115
|
+
nextView(ui);
|
|
116
|
+
if (ui.view === 'runner')
|
|
117
|
+
refreshRunner(ui);
|
|
118
|
+
return say(null);
|
|
119
|
+
case 'open':
|
|
120
|
+
case 'autotrade.edit':
|
|
121
|
+
if (ui.view === 'editor')
|
|
122
|
+
return say(beginFieldEdit(ui));
|
|
123
|
+
if (ui.view !== 'list')
|
|
124
|
+
return action === 'open' ? true : say('e edits from the strategies list');
|
|
125
|
+
return say(e ? openEditor(ui, e.id) : 'no strategy selected');
|
|
126
|
+
case 'autotrade.new': return say(ui.view === 'list' ? beginNew(ui, false) : 'n works on the strategies list (esc)');
|
|
127
|
+
case 'autotrade.dup': return say(ui.view === 'list' ? beginNew(ui, true) : 'd works on the strategies list (esc)');
|
|
128
|
+
case 'autotrade.import':
|
|
129
|
+
if (ui.view !== 'list')
|
|
130
|
+
return say('i works on the strategies list (esc)');
|
|
131
|
+
beginImport(ui);
|
|
132
|
+
return say(null);
|
|
133
|
+
case 'autotrade.export': return say(ui.view === 'list' ? exportSelected(ui) : 'x works on the strategies list (esc)');
|
|
134
|
+
case 'autotrade.compare': return say(ui.view === 'list' ? toggleMark(ui) : 'c marks strategies on the list (esc)');
|
|
135
|
+
case 'autotrade.save': return say(ui.view === 'editor' ? saveEditor(ui) : 's saves in the editor');
|
|
136
|
+
case 'autotrade.undo': return say(ui.view === 'editor' ? undoEditor(ui) : 'u undoes in the editor');
|
|
137
|
+
case 'autotrade.backtest': {
|
|
138
|
+
if (ui.view === 'compare')
|
|
139
|
+
return say(startCompareBacktest(ui));
|
|
140
|
+
if (ui.view === 'editor' && ui.editor)
|
|
141
|
+
return say(`${startBacktest(ui, ui.editor.id)}${ui.editor.edits.length ? ' (the saved version; s saves your edits first)' : ''}`);
|
|
142
|
+
if (ui.view === 'backtest') {
|
|
143
|
+
const s = backtestSubject(ui);
|
|
144
|
+
return say(s ? startBacktest(ui, s.id) : 'no strategy selected');
|
|
145
|
+
}
|
|
146
|
+
return say(e ? startBacktest(ui, e.id) : 'no strategy selected');
|
|
147
|
+
}
|
|
148
|
+
case 'autotrade.sweep': {
|
|
149
|
+
const id = ui.view === 'editor' && ui.editor ? ui.editor.id : ui.view === 'backtest' ? backtestSubject(ui)?.id : e?.id;
|
|
150
|
+
return say(id ? startSweep(ui, id) : 'no strategy selected');
|
|
151
|
+
}
|
|
152
|
+
case 'autotrade.proposal': return say(ui.view === 'list' ? openReview(ui) || null : 'o reviews chat proposals on the strategies list (esc)');
|
|
153
|
+
case 'autotrade.run':
|
|
154
|
+
ui.view = 'runner';
|
|
155
|
+
refreshRunner(ui);
|
|
156
|
+
reloadList(ui);
|
|
157
|
+
return say('starting and stopping a paper run ships with the runner (not in this build yet) · this view shows a runner as soon as one writes its status file');
|
|
158
|
+
case 'autotrade.kill':
|
|
159
|
+
refreshRunner(ui);
|
|
160
|
+
return say(runnerVerb(ui, 'kill'));
|
|
161
|
+
case 'autotrade.pause':
|
|
162
|
+
refreshRunner(ui);
|
|
163
|
+
return say(runnerVerb(ui, 'pause'));
|
|
164
|
+
case 'autotrade.flatten':
|
|
165
|
+
refreshRunner(ui);
|
|
166
|
+
return say(runnerVerb(ui, 'flatten'));
|
|
167
|
+
default: return false;
|
|
168
|
+
}
|
|
169
|
+
}
|