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
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
const chipEl = $('pnlchip');
|
|
16
16
|
const imgSlot = $('coin-img-slot');
|
|
17
17
|
const chartEl = $('chart'), drawCv = $('draw'), dctx = drawCv.getContext('2d');
|
|
18
|
+
// task-1483: the BUY / SELL marker hover card (built with textContent only)
|
|
19
|
+
const tipEl = document.createElement('div'); tipEl.className = 'mtip'; tipEl.hidden = true; chartEl.parentNode.appendChild(tipEl);
|
|
18
20
|
const STALE_MS = 20_000;
|
|
19
21
|
const DEFAULT_IND = ['volume', 'tape', 'ema9', 'ema21'];
|
|
20
22
|
/** Exposed for the headless harness (measure/popup-v2-1325): first paint + setData → repaint latency. */
|
|
@@ -26,6 +28,8 @@
|
|
|
26
28
|
draw: null, tPending: null, hover: null, drawings: [], lastDataAt: 0, firstPainted: false, fitNext: true,
|
|
27
29
|
banners: { fallback: null, data: null },
|
|
28
30
|
display: C.displayOf(null), // task-1366: price | mcap (from the VM's priceMode + supply)
|
|
31
|
+
marks: null, // task-1483: { set, on } — the BUY / SELL markers for the bars on screen (chart-lib chartMarkers)
|
|
32
|
+
mouse: null, // task-1483: crosshair point for the marker hover card
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
const banner = () => {
|
|
@@ -88,7 +92,8 @@
|
|
|
88
92
|
function levels() {
|
|
89
93
|
const vm = S.vm || {}, pos = vm.position || null, out = [];
|
|
90
94
|
const last = S.bars.length ? S.bars[S.bars.length - 1].close : null;
|
|
91
|
-
|
|
95
|
+
const mk = S.marks && S.marks.on ? S.marks.set.markers.map((m) => m.priceUsd) : []; // task-1483: keep the fills in view
|
|
96
|
+
for (const v of [pos && pos.avgEntryUsd, pos && pos.stopUsd, pos && pos.targetUsd, vm.feed === 'LIVE' ? vm.livePrice : null, ...mk]) {
|
|
92
97
|
if (typeof v === 'number' && v > 0 && last && v / last < 3 && last / v < 3) out.push(v);
|
|
93
98
|
}
|
|
94
99
|
return out;
|
|
@@ -160,7 +165,7 @@
|
|
|
160
165
|
DT.onTheme(applyChartTheme);
|
|
161
166
|
|
|
162
167
|
// ---------------------------------------------------------------- toolbar (buttons built with textContent only)
|
|
163
|
-
const btns = { tf: new Map(), axis: new Map(), styles: new Map(), scales: new Map(), inds: new Map(), draws: new Map() };
|
|
168
|
+
const btns = { tf: new Map(), axis: new Map(), marks: new Map(), styles: new Map(), scales: new Map(), inds: new Map(), draws: new Map() };
|
|
164
169
|
function mkBtn(group, id, label, title, onClick) {
|
|
165
170
|
const b = document.createElement('button'); b.type = 'button'; b.className = 'tb'; b.textContent = label; b.title = title;
|
|
166
171
|
b.addEventListener('click', (e) => { e.preventDefault(); onClick(); });
|
|
@@ -170,6 +175,8 @@
|
|
|
170
175
|
// task-1472: MCAP | PRICE asks the terminal to switch (and save) the chart axis; the next VM re-labels the page
|
|
171
176
|
mkBtn('axis', 'mcap', 'MCAP', 'Y axis: market cap (A / y toggles; saved by the terminal)', () => setAxis('mcap'));
|
|
172
177
|
mkBtn('axis', 'price', 'PRICE', 'Y axis: USD price (A / y toggles; saved by the terminal)', () => setAxis('price'));
|
|
178
|
+
// task-1483: MARKERS on/off asks the terminal to switch (and save) display.chartMarkers; the next VM carries it
|
|
179
|
+
mkBtn('marks', 'on', 'MARKERS', 'BUY / SELL markers of your paper + live fills (k toggles; saved by the terminal)', () => setMarkers(!(S.vm && S.vm.marks && S.vm.marks.on)));
|
|
173
180
|
mkBtn('styles', 'candles', 'candles', 'candlesticks (c cycles)', () => setStyle('candles', true));
|
|
174
181
|
mkBtn('styles', 'line', 'line', 'line (c cycles)', () => setStyle('line', true));
|
|
175
182
|
mkBtn('styles', 'area', 'area', 'area (c cycles)', () => setStyle('area', true));
|
|
@@ -189,6 +196,8 @@
|
|
|
189
196
|
for (const [iv, b] of btns.tf) { b.classList.toggle('on', iv === S.interval); b.classList.toggle('pending', iv === S.pendingInterval && iv !== S.interval); }
|
|
190
197
|
const ax = S.vm && S.vm.priceMode === 'mcap' ? 'mcap' : 'price';
|
|
191
198
|
for (const [k, b] of btns.axis) b.classList.toggle('on', k === ax);
|
|
199
|
+
const mOn = !!(S.vm && S.vm.marks && S.vm.marks.on);
|
|
200
|
+
for (const [, b] of btns.marks) { b.classList.toggle('on', mOn); b.hidden = !(S.vm && S.vm.marks); }
|
|
192
201
|
for (const [k, b] of btns.styles) b.classList.toggle('on', k === S.style);
|
|
193
202
|
for (const [k, b] of btns.scales) b.classList.toggle('on', k === S.scale);
|
|
194
203
|
for (const [k, b] of btns.inds) b.classList.toggle('on', S.ind.has(k));
|
|
@@ -207,6 +216,11 @@
|
|
|
207
216
|
if (axis !== 'mcap' && axis !== 'price') return;
|
|
208
217
|
DT.send({ type: 'event', name: 'chart.axis', payload: { axis } });
|
|
209
218
|
}
|
|
219
|
+
/** task-1483: ask the terminal to show / hide the markers; nothing changes here until its next VM says so. */
|
|
220
|
+
function setMarkers(on) {
|
|
221
|
+
if (typeof on !== 'boolean') return;
|
|
222
|
+
DT.send({ type: 'event', name: 'chart.markers', payload: { on } });
|
|
223
|
+
}
|
|
210
224
|
function setStyle(st, user) {
|
|
211
225
|
if (!C.STYLES.includes(st) || st === S.style) return;
|
|
212
226
|
S.style = st;
|
|
@@ -271,9 +285,82 @@
|
|
|
271
285
|
const times = new Set(S.bars.map((b) => b.time));
|
|
272
286
|
const list = [];
|
|
273
287
|
if (S.ind.has('tape')) list.push(...C.tapeMarkers(vm.trades, periodMs(), times, { up: DT.rgba(T.up, 0.85), down: DT.rgba(T.down, 0.85) }));
|
|
274
|
-
if (vm.position && vm.position.fills) list.push(...C.fillMarkers(vm.position.fills, periodMs(), times, { buy: T.accent, sell: T.warn }));
|
|
275
288
|
list.sort((a, b) => a.time - b.time);
|
|
276
289
|
markers[S.style].setMarkers(list);
|
|
290
|
+
computeMarks();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ---------------------------------------------------------------- task-1483: BUY / SELL markers of the ledgers' fills
|
|
294
|
+
// Bucketed to THIS page's timeframe by the bars' real times (re-bucketed on every timeframe change), drawn on the
|
|
295
|
+
// overlay canvas so paper (hollow) and live (solid) read apart, re-clustered when zooming out, hover card per marker.
|
|
296
|
+
function computeMarks() {
|
|
297
|
+
const mk = S.vm && S.vm.marks;
|
|
298
|
+
if (!mk) { S.marks = null; return; }
|
|
299
|
+
S.marks = { on: !!mk.on, truncated: mk.truncated | 0, set: C.chartMarkers(mk.fills, periodMs(), S.bars.map((b) => b.t)) };
|
|
300
|
+
}
|
|
301
|
+
const MARK_PX = 6;
|
|
302
|
+
let markHits = [];
|
|
303
|
+
function drawMarks(ts, s, paneH) {
|
|
304
|
+
markHits = [];
|
|
305
|
+
const mk = S.marks;
|
|
306
|
+
if (!mk || !mk.on || !S.bars.length) return;
|
|
307
|
+
const x0 = ts.logicalToCoordinate(0), x1 = ts.logicalToCoordinate(1);
|
|
308
|
+
const spacing = x0 != null && x1 != null ? Math.abs(x1 - x0) : 7;
|
|
309
|
+
// closed round trips: a faint wash entry → exit (green when sold above the entry)
|
|
310
|
+
for (const r of mk.set.trips) {
|
|
311
|
+
const a = C.logicalOf(S.bars, C.chartTime(Math.floor(r.openT / periodMs()) * periodMs()), periodMs() / 1000);
|
|
312
|
+
const b = C.logicalOf(S.bars, C.chartTime(Math.floor(r.closeT / periodMs()) * periodMs()), periodMs() / 1000);
|
|
313
|
+
const xa = ts.logicalToCoordinate(a), xb = ts.logicalToCoordinate(b);
|
|
314
|
+
if (xa == null || xb == null) continue;
|
|
315
|
+
const win = r.entryUsd !== null && r.exitUsd !== null ? r.exitUsd >= r.entryUsd : null;
|
|
316
|
+
dctx.fillStyle = DT.rgba(win === null ? T.muted : win ? T.up : T.down, 0.07);
|
|
317
|
+
dctx.fillRect(Math.min(xa, xb) - spacing / 2, 0, Math.abs(xb - xa) + spacing, paneH);
|
|
318
|
+
}
|
|
319
|
+
const shown = C.clusterMarkers(mk.set.markers, spacing, 14);
|
|
320
|
+
const used = new Map();
|
|
321
|
+
dctx.font = 'bold 10px ui-monospace, Menlo, monospace'; dctx.textAlign = 'center'; dctx.textBaseline = 'middle';
|
|
322
|
+
for (const m of shown) {
|
|
323
|
+
const x = ts.logicalToCoordinate(m.index);
|
|
324
|
+
const bar = S.bars[m.index];
|
|
325
|
+
if (x == null || !bar) continue;
|
|
326
|
+
const buy = m.side === 'buy';
|
|
327
|
+
let y = m.priceUsd !== null ? s.priceToCoordinate(m.priceUsd) : s.priceToCoordinate(buy ? bar.low : bar.high);
|
|
328
|
+
if (y == null) continue;
|
|
329
|
+
if (m.priceUsd === null) y += buy ? 4 : -4; // no recorded price: against the bar, never at a made-up price
|
|
330
|
+
// a second marker on the same bar and side steps away from the bar, never across it
|
|
331
|
+
const k = `${m.index}|${m.side}`; const n = used.get(k) || 0; used.set(k, n + 1);
|
|
332
|
+
if (n) y += (buy ? 1 : -1) * n * (MARK_PX * 1.6 + 12);
|
|
333
|
+
if (y < -20 || y > paneH + 20) continue;
|
|
334
|
+
const g = C.markerShape(m.side, x, y, MARK_PX);
|
|
335
|
+
const col = buy ? T.up : T.down;
|
|
336
|
+
dctx.beginPath(); dctx.moveTo(g.tri[0][0], g.tri[0][1]); dctx.lineTo(g.tri[1][0], g.tri[1][1]); dctx.lineTo(g.tri[2][0], g.tri[2][1]); dctx.closePath();
|
|
337
|
+
// a dark halo first, so a green buy still reads on a green candle (and a red sell on a red one)
|
|
338
|
+
dctx.setLineDash([]); dctx.lineJoin = 'round'; dctx.lineWidth = 4; dctx.strokeStyle = DT.rgba(T.bg, 0.92); dctx.stroke();
|
|
339
|
+
if (m.source === 'live') { dctx.fillStyle = col; dctx.fill(); dctx.lineWidth = 1.2; dctx.strokeStyle = DT.rgba(T.text, 0.95); dctx.stroke(); } // solid + a light edge: never mistaken for a hollow paper mark on a same-colour candle
|
|
340
|
+
else { dctx.fillStyle = DT.rgba(T.bg, 0.85); dctx.fill(); dctx.fillStyle = DT.rgba(col, 0.22); dctx.fill(); dctx.lineWidth = 1.6; dctx.strokeStyle = col; dctx.stroke(); }
|
|
341
|
+
const txt = `${C.markerLetter(m.side)}${m.count > 1 ? '×' + m.count : ''}`;
|
|
342
|
+
dctx.lineWidth = 3; dctx.strokeStyle = DT.rgba(T.bg, 0.92); dctx.strokeText(txt, g.labelX, g.labelY);
|
|
343
|
+
dctx.fillStyle = col; dctx.fillText(txt, g.labelX, g.labelY);
|
|
344
|
+
markHits.push({ m, hit: g.hit, x, y });
|
|
345
|
+
}
|
|
346
|
+
hoverMarks();
|
|
347
|
+
}
|
|
348
|
+
/** The hover card for the marker under the crosshair (time, price, size, source). Text only. */
|
|
349
|
+
function hoverMarks() {
|
|
350
|
+
const p = S.mouse;
|
|
351
|
+
const hit = p ? markHits.find((h) => p.x >= h.hit.x0 && p.x <= h.hit.x1 && p.y >= h.hit.y0 && p.y <= h.hit.y1) : null;
|
|
352
|
+
if (!hit) { if (!tipEl.hidden) tipEl.hidden = true; return; }
|
|
353
|
+
const lines = C.markerTooltip(hit.m, S.display, (ms) => fmtTime(C.chartTime(ms)));
|
|
354
|
+
const key = lines.join('|');
|
|
355
|
+
if (tipEl.dataset.k !== key) {
|
|
356
|
+
tipEl.dataset.k = key;
|
|
357
|
+
while (tipEl.firstChild) tipEl.removeChild(tipEl.firstChild);
|
|
358
|
+
lines.forEach((t, i) => tipEl.appendChild(el('div', i === 0 ? `h ${hit.m.side}` : i === lines.length - 1 ? `src ${hit.m.source}` : null, t)));
|
|
359
|
+
}
|
|
360
|
+
tipEl.hidden = false;
|
|
361
|
+
const w = chartEl.clientWidth;
|
|
362
|
+
const left = hit.x + 14 + 190 > w ? hit.x - 14 - tipEl.offsetWidth : hit.x + 14;
|
|
363
|
+
tipEl.style.left = `${Math.max(4, left)}px`; tipEl.style.top = `${Math.max(4, hit.y - 20)}px`;
|
|
277
364
|
}
|
|
278
365
|
|
|
279
366
|
function renderOverlays() {
|
|
@@ -281,7 +368,8 @@
|
|
|
281
368
|
const pos = vm.position || null;
|
|
282
369
|
if (pos && typeof pos.avgEntryUsd === 'number' && pos.avgEntryUsd > 0) {
|
|
283
370
|
const pct = typeof pos.unrealizedPct === 'number' ? ` ${C.fmtPct(pos.unrealizedPct, 1)}` : '';
|
|
284
|
-
|
|
371
|
+
// task-1483: the average entry of the open position is a DASHED line (fills are the triangles)
|
|
372
|
+
setLine('entry', { price: pos.avgEntryUsd, color: pos.kind === 'paper' ? T.warn : T.text, lineWidth: 1, lineStyle: L.LineStyle.LargeDashed, axisLabelVisible: true, axisLabelColor: pos.kind === 'paper' ? T.warn : T.text, axisLabelTextColor: T.bg, title: `AVG ENTRY ${pos.sizeLabel}${pct}` });
|
|
285
373
|
} else setLine('entry', null);
|
|
286
374
|
setLine('stop', pos && typeof pos.stopUsd === 'number' && pos.stopUsd > 0 ? { price: pos.stopUsd, color: T.down, lineWidth: 1, lineStyle: L.LineStyle.Dashed, axisLabelVisible: true, title: 'STOP' } : null);
|
|
287
375
|
setLine('target', pos && typeof pos.targetUsd === 'number' && pos.targetUsd > 0 ? { price: pos.targetUsd, color: T.up, lineWidth: 1, lineStyle: L.LineStyle.Dashed, axisLabelVisible: true, title: 'TARGET' } : null);
|
|
@@ -342,8 +430,14 @@
|
|
|
342
430
|
r2.appendChild(s);
|
|
343
431
|
}
|
|
344
432
|
if (r2.firstChild) legendEl.appendChild(r2);
|
|
433
|
+
// task-1483: which markers are on screen, and what the window left off (never moved onto another bar)
|
|
434
|
+
if (S.marks && S.marks.on) {
|
|
435
|
+
const lg = C.markerLegend(S.marks.set.markers);
|
|
436
|
+
const om = C.omittedNote({ ...S.marks.set, before: S.marks.set.before + S.marks.truncated });
|
|
437
|
+
if (lg || om) { const r3 = el('div', 'row3'); if (lg) r3.appendChild(el('span', S.marks.set.markers.some((m) => m.source === 'paper') ? 'paper' : null, lg)); if (om) r3.appendChild(el('span', null, `${lg ? ' · ' : ''}${om}`)); legendEl.appendChild(r3); }
|
|
438
|
+
}
|
|
345
439
|
}
|
|
346
|
-
chart.subscribeCrosshairMove((p) => { S.hover = p && p.time != null ? p.time : null; renderLegend(S.hover); });
|
|
440
|
+
chart.subscribeCrosshairMove((p) => { S.hover = p && p.time != null ? p.time : null; S.mouse = p && p.point ? { x: p.point.x, y: p.point.y } : null; renderLegend(S.hover); });
|
|
347
441
|
|
|
348
442
|
// ---------------------------------------------------------------- header, LIVE badge, banner, position row
|
|
349
443
|
function renderHeader() {
|
|
@@ -437,7 +531,7 @@
|
|
|
437
531
|
// task-1366: a mode / supply change re-labels the axis in place (the bars are prices either way)
|
|
438
532
|
const disp = C.displayOf(vm);
|
|
439
533
|
if (disp.key !== S.display.key) { S.display = disp; applyPriceFormat(); }
|
|
440
|
-
if (tail && (!vm.seriesKey || vm.seriesKey !== S.key)) { S.vm = { ...vm, candles: [] }; renderHeader(); renderPosition(); renderChip(); renderOverlays(); return; }
|
|
534
|
+
if (tail && (!vm.seriesKey || vm.seriesKey !== S.key)) { S.vm = { ...vm, candles: [] }; renderHeader(); renderPosition(); renderChip(); renderOverlays(); computeMarks(); syncToolbar(); return; }
|
|
441
535
|
const intervalChanged = vm.interval !== S.interval;
|
|
442
536
|
S.interval = vm.interval;
|
|
443
537
|
const answered = S.pendingInterval === vm.interval ? S.reqAt : null;
|
|
@@ -520,10 +614,12 @@
|
|
|
520
614
|
dctx.clearRect(0, 0, w, h);
|
|
521
615
|
const segs = S.drawings.filter((d) => d.kind === 't').map((d) => [d.a, d.b]);
|
|
522
616
|
if (S.tPending && mouse) segs.push([S.tPending, mouse]);
|
|
523
|
-
if (!
|
|
617
|
+
if (!S.bars.length) return;
|
|
524
618
|
const ts = chart.timeScale(), s = active(), pS = periodMs() / 1000;
|
|
525
619
|
const paneH = chart.panes()[0] ? chart.panes()[0].getHeight() : h;
|
|
526
620
|
dctx.save(); dctx.beginPath(); dctx.rect(0, 0, ts.width(), paneH); dctx.clip();
|
|
621
|
+
drawMarks(ts, s, paneH); // task-1483
|
|
622
|
+
if (!segs.length) { dctx.restore(); return; }
|
|
527
623
|
dctx.strokeStyle = T.accent; dctx.fillStyle = T.accent; dctx.lineWidth = 1.5;
|
|
528
624
|
for (const [a, b] of segs) {
|
|
529
625
|
const xa = ts.logicalToCoordinate(C.logicalOf(S.bars, a.time, pS)), xb = ts.logicalToCoordinate(C.logicalOf(S.bars, b.time, pS));
|
|
@@ -552,6 +648,7 @@
|
|
|
552
648
|
case 'm': toggleInd('volma'); break;
|
|
553
649
|
case 'v': toggleInd('volume'); break;
|
|
554
650
|
case 'b': toggleInd('tape'); break;
|
|
651
|
+
case 'k': setMarkers(!(S.vm && S.vm.marks && S.vm.marks.on)); break; // task-1483
|
|
555
652
|
case 'h': armDraw('h'); break;
|
|
556
653
|
case 't': armDraw('t'); break;
|
|
557
654
|
case 'x': clearDrawings(); break;
|
|
@@ -30,4 +30,6 @@ export const CHART_STYLES = ['candles', 'line', 'area'];
|
|
|
30
30
|
export const CHART_SCALES = ['auto', 'log', 'percent'];
|
|
31
31
|
/** task-1472: what the chart's Y axis shows (the popup's MCAP | PRICE control / `A` ask the terminal to switch). */
|
|
32
32
|
export const CHART_AXES = ['mcap', 'price'];
|
|
33
|
+
/** The most fills one chart VM carries (newest kept). */
|
|
34
|
+
export const CHART_MARKS_MAX = 500;
|
|
33
35
|
export const PROTOCOL_VERSION = 'dexterm-popup/1';
|
|
@@ -50,6 +50,8 @@ export const POPUP_EVENTS = new Set([
|
|
|
50
50
|
'event:chart.interval', 'event:chart.indicator', 'event:chart.style', 'event:chart.scale', 'event:chart.painted',
|
|
51
51
|
// task-1472: the chart's Y axis (market cap ⇄ price): a display preference the terminal saves, nothing else
|
|
52
52
|
'event:chart.axis',
|
|
53
|
+
// task-1483: show / hide the chart's BUY / SELL markers (display.chartMarkers): a display preference, nothing else
|
|
54
|
+
'event:chart.markers',
|
|
53
55
|
// task-1345 agent roster: per-agent pause / kick, by agent name (the only writes this popup can send)
|
|
54
56
|
'event:roster.pause', 'event:roster.resume', 'event:roster.kick',
|
|
55
57
|
// task-1355 order plans: cancel a plan by id (the ONLY write the plans popup can send — never arm/cap/sign)
|
|
@@ -98,6 +100,8 @@ export function validateEventPayload(name, payload) {
|
|
|
98
100
|
return oneOf(CHART_SCALES, p.scale) ? { scale: p.scale } : null;
|
|
99
101
|
case 'chart.axis':
|
|
100
102
|
return oneOf(CHART_AXES, p.axis) ? { axis: p.axis } : null;
|
|
103
|
+
case 'chart.markers': // task-1483: show / hide the BUY / SELL markers (a display setting; nothing else)
|
|
104
|
+
return typeof p.on === 'boolean' ? { on: p.on } : null;
|
|
101
105
|
case 'chart.painted':
|
|
102
106
|
return (p.kind === 'first' || p.kind === 'data') && finiteIn(p.ms, 0, 600_000) && finiteIn(p.sentAt, 0, 1e15)
|
|
103
107
|
? { kind: p.kind, ms: p.ms, sentAt: p.sentAt } : null;
|
|
@@ -9,6 +9,8 @@ export * from "./open.js";
|
|
|
9
9
|
export * from "./verdicts.js";
|
|
10
10
|
export * from "./schedule.js";
|
|
11
11
|
export * from "./dev-history.js";
|
|
12
|
+
export * from "./ticks.js";
|
|
13
|
+
export * from "./retention.js";
|
|
12
14
|
export { recorderCli, RECORDER_HELP } from "./cli.js";
|
|
13
15
|
/** `--no-recorder` / `DEXTERM_RECORDER=0` resolution: the recorder is on in live mode unless either says no. */
|
|
14
16
|
export function recorderEnabled(env, flagOff) {
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
// day, skipped while the bucket is paused after a 429. The only requests this package makes.
|
|
8
8
|
// label code-only outcome labels per (t0 anchor, horizon) from the recorded numbers (labels.ts).
|
|
9
9
|
// prune retention: raw JSON 7 d (focus rows only), one row per mint per minute after 24 h,
|
|
10
|
-
// columns 90 d, outcomes/verdicts forever.
|
|
10
|
+
// columns 90 d, outcomes/verdicts forever; autotrade ticks 14 d, sampled candles 90 d.
|
|
11
|
+
// ticks (task-1504, opt-in) position-grade samples every 5–10 s for held / open / candidate / watch coins
|
|
12
|
+
// (ticks.ts), and a DB cap enforced by evicting the oldest history (retention.ts) instead of
|
|
13
|
+
// pausing observation.
|
|
11
14
|
// Everything is synchronous SQLite on the app's own DB file (node:sqlite, WAL), batched in transactions,
|
|
12
15
|
// so a refresh never awaits the recorder and a crash loses at most one batch.
|
|
13
16
|
import { mkdirSync } from 'node:fs';
|
|
@@ -18,8 +21,10 @@ import { ALL_HORIZONS_MASK, HORIZONS_SEC, SCHEMA_SQL, SCHEMA_VERSION } from "./s
|
|
|
18
21
|
import { COLUMN_NAMES, fromDsView, fromEnrichedRow, isMaterialChange } from "./snapshot.js";
|
|
19
22
|
import { LABELER_VERSION, labelAtHorizon } from "./labels.js";
|
|
20
23
|
import { dueBounds, selectDue } from "./sweep.js";
|
|
24
|
+
import { TickLane } from "./ticks.js";
|
|
25
|
+
import { DEFAULT_AGE, enforceCap, pruneTicksByAge } from "./retention.js";
|
|
21
26
|
export const DEFAULT_SWEEP = { everyMs: 60_000, maxCallsPerSweep: 6, maxCallsPerDay: 1500, batch: 30 };
|
|
22
|
-
export const DEFAULT_RETENTION = { rawDays: 7, columnsDays: 90, downsampleAfterHours: 24, everyMs: 3_600_000 };
|
|
27
|
+
export const DEFAULT_RETENTION = { rawDays: 7, columnsDays: 90, downsampleAfterHours: 24, everyMs: 3_600_000, ...DEFAULT_AGE };
|
|
23
28
|
/** Unchanged rows are re-written this often at most; the horizons are ≥ 1 h, so 30 min keeps every window observed. */
|
|
24
29
|
export const DEFAULT_HEARTBEAT_MS = 1_800_000;
|
|
25
30
|
export const DEFAULT_T0_EVERY_MS = 3_600_000;
|
|
@@ -43,6 +48,9 @@ export class Recorder {
|
|
|
43
48
|
membersEveryMs;
|
|
44
49
|
sweepPolicy;
|
|
45
50
|
retention;
|
|
51
|
+
/** task-1504: the tick lane (null when not configured) and the cap policy (null = no cap). */
|
|
52
|
+
tickLane;
|
|
53
|
+
cap;
|
|
46
54
|
onLog;
|
|
47
55
|
/**
|
|
48
56
|
* Per mint: the last WRITTEN row (columns + hash + time) and the newest observation time this process
|
|
@@ -92,6 +100,8 @@ export class Recorder {
|
|
|
92
100
|
lastLive: this.db.prepare(`SELECT t, hash, sources, ${cols} FROM token_snapshots WHERE mint = ? AND kind = 'live' ORDER BY t DESC LIMIT 1`),
|
|
93
101
|
insertCreator: this.db.prepare('INSERT OR IGNORE INTO creators (mint, creator, source, first_t, created_at) VALUES (?, ?, ?, ?, ?)'),
|
|
94
102
|
};
|
|
103
|
+
this.tickLane = opts.ticks ? new TickLane(this.db, { focus: opts.ticks.focus, policy: opts.ticks.policy, clock: this.clock, onLog: this.onLog }) : null;
|
|
104
|
+
this.cap = opts.cap && opts.cap.maxBytes ? opts.cap : null;
|
|
95
105
|
}
|
|
96
106
|
/** Attach as the data layer's store listener (and detach any previous one). */
|
|
97
107
|
attach(store) {
|
|
@@ -298,8 +308,38 @@ export class Recorder {
|
|
|
298
308
|
catch (err) {
|
|
299
309
|
this.log(`prune failed: ${msg(err)}`);
|
|
300
310
|
}
|
|
311
|
+
try {
|
|
312
|
+
this.enforceCap(now);
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
this.log(`cap failed: ${msg(err)}`);
|
|
316
|
+
}
|
|
301
317
|
return out;
|
|
302
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* task-1504: one tick-lane poll (ticks.ts). The host calls it every `fastEveryMs` (5 s); 'disabled' when the
|
|
321
|
+
* recorder is off or no tick lane was configured. The only other requests this package makes besides sweeps.
|
|
322
|
+
*/
|
|
323
|
+
async pollTicks(deps, now = this.clock.now()) {
|
|
324
|
+
if (!this.enabled || this.closed || !this.tickLane)
|
|
325
|
+
return { coins: 0, calls: 0, answered: 0, written: 0, overCap: 0, skipped: 'disabled' };
|
|
326
|
+
return this.tickLane.poll(deps, now);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* task-1504: keep the DB under the configured cap by evicting the oldest history (retention.ts). A no-op without
|
|
330
|
+
* a cap or below its high-water mark. Observation never pauses; the collector calls this before each pass.
|
|
331
|
+
*/
|
|
332
|
+
enforceCap(now = this.clock.now()) {
|
|
333
|
+
if (!this.cap || this.closed)
|
|
334
|
+
return null;
|
|
335
|
+
const r = enforceCap(this.db, this.cap, now);
|
|
336
|
+
if (r.evicted) {
|
|
337
|
+
this.log(`cap: used ${(r.usedBefore / 1e6).toFixed(1)} → ${(r.usedAfter / 1e6).toFixed(1)} MB (${r.reclaimed} reclaimed, ${r.slices} slice(s) of history), history now from ${r.cutoff === null ? '—' : new Date(r.cutoff).toISOString()}${r.over ? ' (still over: only the newest hour is left)' : ''}`);
|
|
338
|
+
// Forget cached last rows: an evicted mint is re-seeded from the DB (or written fresh) next time.
|
|
339
|
+
this.seen.clear();
|
|
340
|
+
}
|
|
341
|
+
return r;
|
|
342
|
+
}
|
|
303
343
|
/** Mints whose horizon window is open and unobserved (sweep.ts), soonest-closing first. */
|
|
304
344
|
due(now = this.clock.now(), max = this.sweepPolicy.maxCallsPerSweep * this.sweepPolicy.batch) {
|
|
305
345
|
const b = dueBounds(now);
|
|
@@ -455,7 +495,7 @@ export class Recorder {
|
|
|
455
495
|
* watermark in recorder_meta means each pass only looks at the hours that newly crossed the line.
|
|
456
496
|
*/
|
|
457
497
|
prune(now = this.clock.now()) {
|
|
458
|
-
const res = { rawCleared: 0, downsampled: 0, expired: 0, membersExpired: 0 };
|
|
498
|
+
const res = { rawCleared: 0, downsampled: 0, expired: 0, membersExpired: 0, ticksExpired: 0, candlesExpired: 0 };
|
|
459
499
|
if (this.closed)
|
|
460
500
|
return res;
|
|
461
501
|
const day = 86_400_000;
|
|
@@ -480,14 +520,17 @@ export class Recorder {
|
|
|
480
520
|
res.membersExpired = Number(this.db.prepare('DELETE FROM feed_members WHERE t < ?').run(colsBefore).changes);
|
|
481
521
|
this.db.prepare('DELETE FROM mints WHERE last_any_t < ?').run(colsBefore);
|
|
482
522
|
this.db.prepare('DELETE FROM sweeps WHERE started_at < ?').run(colsBefore);
|
|
523
|
+
const tk = pruneTicksByAge(this.db, { ticksDays: this.retention.ticksDays, candlesDays: this.retention.candlesDays }, now);
|
|
524
|
+
res.ticksExpired = tk.ticks;
|
|
525
|
+
res.candlesExpired = tk.candles;
|
|
483
526
|
this.db.exec('COMMIT');
|
|
484
527
|
}
|
|
485
528
|
catch (err) {
|
|
486
529
|
this.db.exec('ROLLBACK');
|
|
487
530
|
throw err;
|
|
488
531
|
}
|
|
489
|
-
if (res.rawCleared || res.downsampled || res.expired)
|
|
490
|
-
this.log(`prune: raw ${res.rawCleared}, downsampled ${res.downsampled}, expired ${res.expired} rows`);
|
|
532
|
+
if (res.rawCleared || res.downsampled || res.expired || res.ticksExpired)
|
|
533
|
+
this.log(`prune: raw ${res.rawCleared}, downsampled ${res.downsampled}, expired ${res.expired} rows, ${res.ticksExpired} ticks`);
|
|
491
534
|
return res;
|
|
492
535
|
}
|
|
493
536
|
// ---------------------------------------------------------------- read side
|
|
@@ -512,7 +555,7 @@ export class Recorder {
|
|
|
512
555
|
const d = one("SELECT SUM(pgsize) AS b FROM dbstat WHERE name IN ('token_snapshots', 'token_snapshots_mint_t', 'token_snapshots_t', 'token_snapshots_t0_pending')");
|
|
513
556
|
if (d.b !== null && Number(s.n) > 0)
|
|
514
557
|
bytesPerSnapshot = Number(d.b) / Number(s.n);
|
|
515
|
-
const all = one("SELECT SUM(pgsize) AS b FROM dbstat WHERE name IN ('recorder_meta', 'feed_members', 'feed_members_mint_t', 'token_snapshots', 'token_snapshots_mint_t', 'token_snapshots_t', 'token_snapshots_t0_pending', 'mints', 'mints_last_live', 'sweeps', 'chain_events', 'chain_events_mint_t', 'outcomes', 'outcomes_label', 'verdicts', 'verdicts_mint_t')");
|
|
558
|
+
const all = one("SELECT SUM(pgsize) AS b FROM dbstat WHERE name IN ('recorder_meta', 'feed_members', 'feed_members_mint_t', 'token_snapshots', 'token_snapshots_mint_t', 'token_snapshots_t', 'token_snapshots_t0_pending', 'mints', 'mints_last_live', 'sweeps', 'chain_events', 'chain_events_mint_t', 'outcomes', 'outcomes_label', 'verdicts', 'verdicts_mint_t', 'creators', 'creators_creator', 'autotrade_ticks', 'autotrade_ticks_t', 'autotrade_candles_1m', 'autotrade_candles_1m_t')");
|
|
516
559
|
recorderBytes = all.b === null ? null : Number(all.b);
|
|
517
560
|
}
|
|
518
561
|
catch {
|
|
@@ -533,6 +576,9 @@ export class Recorder {
|
|
|
533
576
|
outcomesByLabel: byLabel,
|
|
534
577
|
sweeps: Number(one('SELECT COUNT(*) AS n FROM sweeps').n),
|
|
535
578
|
sweepCallsToday: this.sweepCallsToday(now),
|
|
579
|
+
ticks: Number(one('SELECT COUNT(*) AS n FROM autotrade_ticks').n),
|
|
580
|
+
candles: Number(one('SELECT COUNT(*) AS n FROM autotrade_candles_1m').n),
|
|
581
|
+
tickCallsToday: this.tickLane ? this.tickLane.callsToday(now) : tickCallsOn(this.db, now),
|
|
536
582
|
dbBytes, recorderBytes, bytesPerSnapshot,
|
|
537
583
|
oldestT: s.oldest === null ? null : Number(s.oldest),
|
|
538
584
|
newestT: s.newest === null ? null : Number(s.newest),
|
|
@@ -571,6 +617,17 @@ export class Recorder {
|
|
|
571
617
|
this.onLog?.(`recorder: ${line}\n`);
|
|
572
618
|
}
|
|
573
619
|
}
|
|
620
|
+
/** Tick calls the lane recorded for `now`'s UTC day (recorder_meta), for a reader with no lane of its own. */
|
|
621
|
+
function tickCallsOn(db, now) {
|
|
622
|
+
const r = db.prepare("SELECT value FROM recorder_meta WHERE key = 'tick_calls'").get();
|
|
623
|
+
try {
|
|
624
|
+
const v = r ? JSON.parse(r.value) : null;
|
|
625
|
+
return v && v.day === new Date(now).toISOString().slice(0, 10) ? Number(v.calls) || 0 : 0;
|
|
626
|
+
}
|
|
627
|
+
catch {
|
|
628
|
+
return 0;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
574
631
|
function msg(err) {
|
|
575
632
|
return err instanceof Error ? err.message : String(err);
|
|
576
633
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// dexterm history recorder: retention under a DB cap (task-1504, AUTOTRADE-SPEC §3 + §10 Q4).
|
|
2
|
+
//
|
|
3
|
+
// Before this, the collector's only answer to a full disk was to PAUSE observation above `--max-db-mb`, while the
|
|
4
|
+
// outcome sweeps kept writing: the cap stopped exactly the rows autotrade needs and the DB still grew past it.
|
|
5
|
+
// This module keeps the DB under the cap by EVICTING the oldest recorder history instead, so observation never
|
|
6
|
+
// pauses:
|
|
7
|
+
//
|
|
8
|
+
// age ticks after `ticksDays` (14), sampled candles after `candlesDays` (90). Snapshot / feed-member /
|
|
9
|
+
// sweep rows keep the recorder's existing `columnsDays` rule (recorder.ts prune; Q4's "prune raw rows
|
|
10
|
+
// older than 14 days" is `columnsDays: 14`).
|
|
11
|
+
// reclaim (optional) once the cap is reached, the host's cheap space goes first: the collector passes the data
|
|
12
|
+
// layer's own expired-cache prune (`Cache.prune()`, entries a day past their TTL), measured on
|
|
13
|
+
// 2026-10-21 at ~470 MB of a 1,173 MB file that only held ~430 MB of recorder history.
|
|
14
|
+
// cap once the DB's USED bytes (pages minus the freelist) reach `highWater` × `maxBytes`, delete the
|
|
15
|
+
// oldest slice of history (`sliceMs`, all recorder tables: ticks, candles, snapshots incl. sweep rows,
|
|
16
|
+
// feed members; the small sweep ledger keeps its age rule) and repeat until used ≤ `lowWater` × `maxBytes`. Rows newer than
|
|
17
|
+
// `keepMs` are never evicted. Outcomes, verdicts and creators are tiny and stay forever.
|
|
18
|
+
//
|
|
19
|
+
// SQLite does not shrink the file on DELETE: freed pages go to the freelist and new rows reuse them. So the file
|
|
20
|
+
// stops growing at about the high-water mark; a file that is ALREADY above the cap stays that size (only
|
|
21
|
+
// `VACUUM`, with the collector stopped, gives the space back) but no longer grows.
|
|
22
|
+
export const DEFAULT_CAP = { maxBytes: null, highWater: 0.95, lowWater: 0.85, sliceMs: 3_600_000, keepMs: 3_600_000, maxSlices: 500 };
|
|
23
|
+
export const DEFAULT_AGE = { ticksDays: 14, candlesDays: 90 };
|
|
24
|
+
/** The Q4 default cap when retention-by-cap is switched on (3 GB). */
|
|
25
|
+
export const DEFAULT_CAP_BYTES = 3_000_000_000;
|
|
26
|
+
/** Tables the cap may evict from, each with its time column. */
|
|
27
|
+
const EVICTABLE = [
|
|
28
|
+
{ table: 'autotrade_ticks', col: 't' },
|
|
29
|
+
{ table: 'autotrade_candles_1m', col: 't' },
|
|
30
|
+
{ table: 'token_snapshots', col: 't' },
|
|
31
|
+
{ table: 'feed_members', col: 't' },
|
|
32
|
+
];
|
|
33
|
+
export function dbSize(db) {
|
|
34
|
+
const one = (sql) => Number(Object.values(db.prepare(sql).get())[0]);
|
|
35
|
+
const ps = one('PRAGMA page_size');
|
|
36
|
+
const pc = one('PRAGMA page_count');
|
|
37
|
+
const fl = one('PRAGMA freelist_count');
|
|
38
|
+
return { fileBytes: pc * ps, usedBytes: (pc - fl) * ps };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Enforce the cap: a no-op below the high-water mark; above it, delete the oldest `sliceMs` of history across every
|
|
42
|
+
* evictable table, one transaction per slice, until used ≤ the low-water mark. Never deletes rows newer than
|
|
43
|
+
* `now - keepMs`. Outcome / verdict / creator rows are never touched.
|
|
44
|
+
*/
|
|
45
|
+
export function enforceCap(db, policy, now) {
|
|
46
|
+
const p = { ...DEFAULT_CAP, ...policy };
|
|
47
|
+
const size0 = dbSize(db);
|
|
48
|
+
const res = { evicted: false, usedBefore: size0.usedBytes, usedAfter: size0.usedBytes, fileBytes: size0.fileBytes, slices: 0, reclaimed: 0, deleted: {}, cutoff: null, over: false };
|
|
49
|
+
if (p.maxBytes === null || p.maxBytes <= 0 || size0.usedBytes < p.highWater * p.maxBytes)
|
|
50
|
+
return res;
|
|
51
|
+
res.evicted = true;
|
|
52
|
+
const target = p.lowWater * p.maxBytes;
|
|
53
|
+
const floor = now - p.keepMs;
|
|
54
|
+
const tables = EVICTABLE.filter((e) => !!db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(e.table));
|
|
55
|
+
const oldestQ = tables.map((e) => ({ ...e, q: db.prepare(`SELECT MIN(${e.col}) AS m FROM ${e.table}`), del: db.prepare(`DELETE FROM ${e.table} WHERE ${e.col} < ?`) }));
|
|
56
|
+
const delMints = db.prepare('DELETE FROM mints WHERE last_any_t < ?');
|
|
57
|
+
let used = size0.usedBytes;
|
|
58
|
+
if (p.reclaim) {
|
|
59
|
+
res.reclaimed = p.reclaim();
|
|
60
|
+
used = dbSize(db).usedBytes;
|
|
61
|
+
}
|
|
62
|
+
while (used > target && res.slices < p.maxSlices) {
|
|
63
|
+
let oldest = Infinity;
|
|
64
|
+
for (const e of oldestQ) {
|
|
65
|
+
const m = e.q.get().m;
|
|
66
|
+
if (m !== null && Number(m) < oldest)
|
|
67
|
+
oldest = Number(m);
|
|
68
|
+
}
|
|
69
|
+
if (!Number.isFinite(oldest) || oldest >= floor)
|
|
70
|
+
break;
|
|
71
|
+
const cutoff = Math.min(oldest + p.sliceMs, floor);
|
|
72
|
+
db.exec('BEGIN');
|
|
73
|
+
try {
|
|
74
|
+
for (const e of oldestQ)
|
|
75
|
+
res.deleted[e.table] = (res.deleted[e.table] ?? 0) + Number(e.del.run(cutoff).changes);
|
|
76
|
+
res.deleted.mints = (res.deleted.mints ?? 0) + Number(delMints.run(cutoff).changes);
|
|
77
|
+
db.exec('COMMIT');
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
db.exec('ROLLBACK');
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
res.slices++;
|
|
84
|
+
res.cutoff = cutoff;
|
|
85
|
+
used = dbSize(db).usedBytes;
|
|
86
|
+
}
|
|
87
|
+
const size1 = dbSize(db);
|
|
88
|
+
res.usedAfter = size1.usedBytes;
|
|
89
|
+
res.fileBytes = size1.fileBytes;
|
|
90
|
+
res.over = size1.usedBytes >= p.highWater * p.maxBytes;
|
|
91
|
+
return res;
|
|
92
|
+
}
|
|
93
|
+
/** Age rules for the autotrade tables (the recorder's prune() calls this next to its own snapshot rules). */
|
|
94
|
+
export function pruneTicksByAge(db, policy, now) {
|
|
95
|
+
const p = { ...DEFAULT_AGE, ...policy };
|
|
96
|
+
const day = 86_400_000;
|
|
97
|
+
const ticks = Number(db.prepare('DELETE FROM autotrade_ticks WHERE t < ?').run(now - p.ticksDays * day).changes);
|
|
98
|
+
const candles = Number(db.prepare('DELETE FROM autotrade_candles_1m WHERE t < ?').run(now - p.candlesDays * day).changes);
|
|
99
|
+
return { ticks, candles };
|
|
100
|
+
}
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
// - `mints` is a per-mint summary the sweep scheduler reads instead of scanning snapshots.
|
|
13
13
|
// - feed_members is keyed on (t, feed, rank): the refresh tick is the run.
|
|
14
14
|
// - chain_events and verdicts are created here (one schema owner) but written by R-02 / L-01.
|
|
15
|
-
|
|
15
|
+
//
|
|
16
|
+
// v2 (task-1504, AUTOTRADE-SPEC §3): `autotrade_ticks` (position-grade samples every 5–10 s for the few coins
|
|
17
|
+
// that are watched, open, a candidate or held; written by the tick lane in ticks.ts) and `autotrade_candles_1m`
|
|
18
|
+
// (1-minute OHLC built from those samples). Both are additive: a v1 file gains the two tables on open.
|
|
19
|
+
export const SCHEMA_VERSION = 2;
|
|
16
20
|
export const HORIZONS_SEC = [3600, 7200, 21600, 86400, 259200];
|
|
17
21
|
/** Every horizon labelled: the bitmask value of `horizons_done` for a finished t0 row. */
|
|
18
22
|
export const ALL_HORIZONS_MASK = (1 << HORIZONS_SEC.length) - 1;
|
|
@@ -143,4 +147,33 @@ CREATE TABLE IF NOT EXISTS creators (
|
|
|
143
147
|
created_at INTEGER -- the coin's creation time when the source gave one
|
|
144
148
|
) WITHOUT ROWID;
|
|
145
149
|
CREATE INDEX IF NOT EXISTS creators_creator ON creators (creator, first_t);
|
|
150
|
+
|
|
151
|
+
-- task-1504 (AUTOTRADE-SPEC §3): position-grade ticks. One row per (mint, poll) for focus coins, written
|
|
152
|
+
-- every sample (no change detection), from DexScreener tokens/v1 batches. price_usd NULL = the poll got no
|
|
153
|
+
-- pair back for the mint (pool gone or not indexed): a held coin's rug shows up here as NULL rows.
|
|
154
|
+
CREATE TABLE IF NOT EXISTS autotrade_ticks (
|
|
155
|
+
mint TEXT NOT NULL,
|
|
156
|
+
t INTEGER NOT NULL, -- unix ms the answer was fetched
|
|
157
|
+
role TEXT NOT NULL, -- why it was polled: 'held' | 'open' | 'candidate' | 'watch'
|
|
158
|
+
price_usd REAL, liquidity_usd REAL, mcap REAL, fdv REAL,
|
|
159
|
+
vol_m5 REAL, vol_h1 REAL,
|
|
160
|
+
buys_m5 INTEGER, sells_m5 INTEGER,
|
|
161
|
+
chg_m5 REAL, chg_h1 REAL,
|
|
162
|
+
pair_address TEXT,
|
|
163
|
+
pair_created_at INTEGER,
|
|
164
|
+
PRIMARY KEY (mint, t)
|
|
165
|
+
) WITHOUT ROWID;
|
|
166
|
+
CREATE INDEX IF NOT EXISTS autotrade_ticks_t ON autotrade_ticks (t);
|
|
167
|
+
|
|
168
|
+
-- 1-minute candles for the same coins, built from the ticks (sampled: high/low are of the 5–10 s samples,
|
|
169
|
+
-- not of every trade). t = the minute's start; n = samples in the minute.
|
|
170
|
+
CREATE TABLE IF NOT EXISTS autotrade_candles_1m (
|
|
171
|
+
mint TEXT NOT NULL,
|
|
172
|
+
t INTEGER NOT NULL,
|
|
173
|
+
o REAL NOT NULL, h REAL NOT NULL, l REAL NOT NULL, c REAL NOT NULL,
|
|
174
|
+
liq_c REAL, mcap_c REAL,
|
|
175
|
+
n INTEGER NOT NULL,
|
|
176
|
+
PRIMARY KEY (mint, t)
|
|
177
|
+
) WITHOUT ROWID;
|
|
178
|
+
CREATE INDEX IF NOT EXISTS autotrade_candles_1m_t ON autotrade_candles_1m (t);
|
|
146
179
|
`;
|