dexterm-cli 0.1.7 → 0.2.1
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/spectator.js +31 -3
- package/dist/packages/agents/src/tiers.js +12 -0
- package/dist/packages/agents/src/tools.js +13 -5
- package/dist/packages/autotrade/assets/limits.schema.json +569 -0
- package/dist/packages/autotrade/examples/executor/README.md +70 -0
- package/dist/packages/autotrade/examples/executor/jsonl-tail-executor.mjs +57 -0
- package/dist/packages/autotrade/examples/executor/kit.mjs +144 -0
- package/dist/packages/autotrade/examples/executor/signal-cmd-executor.mjs +25 -0
- package/dist/packages/autotrade/examples/executor/telegram-bot-recipe.md +47 -0
- package/dist/packages/autotrade/examples/executor/webhook-executor.mjs +47 -0
- package/dist/packages/autotrade/src/backtest.js +7 -2
- package/dist/packages/autotrade/src/chat/config.js +4 -2
- package/dist/packages/autotrade/src/chat/control.js +1 -0
- package/dist/packages/autotrade/src/chat/notify.js +16 -6
- package/dist/packages/autotrade/src/chat/secrets.js +25 -18
- package/dist/packages/autotrade/src/chat/service.js +3 -0
- package/dist/packages/autotrade/src/engine.js +16 -2
- package/dist/packages/autotrade/src/executor-health.js +217 -0
- package/dist/packages/autotrade/src/fleet.js +5 -2
- package/dist/packages/autotrade/src/forward.js +355 -0
- package/dist/packages/autotrade/src/index.js +21 -0
- package/dist/packages/autotrade/src/journey.js +477 -0
- package/dist/packages/autotrade/src/limits.js +682 -0
- package/dist/packages/autotrade/src/outbox.js +394 -0
- package/dist/packages/autotrade/src/paper-limits.js +87 -0
- package/dist/packages/autotrade/src/real-mode.js +688 -0
- package/dist/packages/autotrade/src/receipts.js +330 -0
- package/dist/packages/autotrade/src/report.js +168 -0
- package/dist/packages/autotrade/src/rugproof-probe.js +415 -0
- package/dist/packages/autotrade/src/rugproof.js +231 -0
- package/dist/packages/autotrade/src/runner.js +194 -28
- package/dist/packages/autotrade/src/service-guard.js +125 -0
- package/dist/packages/autotrade/src/service-install.js +215 -0
- package/dist/packages/autotrade/src/service-recover.js +232 -0
- package/dist/packages/autotrade/src/service.js +289 -0
- package/dist/packages/autotrade/src/sessions.js +16 -7
- package/dist/packages/autotrade/src/signal-channels.js +312 -0
- package/dist/packages/autotrade/src/signal.js +196 -0
- package/dist/packages/autotrade/src/strategy-stats.js +344 -0
- package/dist/packages/autotrade/src/strategy.js +15 -2
- package/dist/packages/autotrade/src/trade-ledger.js +2 -2
- package/dist/packages/classifier/src/calibrate.js +138 -0
- package/dist/packages/classifier/src/classify.js +107 -0
- package/dist/packages/classifier/src/config.js +220 -0
- package/dist/packages/classifier/src/dossier.js +95 -0
- package/dist/packages/classifier/src/hardware.js +63 -0
- package/dist/packages/classifier/src/index.js +18 -0
- package/dist/packages/classifier/src/models.js +111 -0
- package/dist/packages/classifier/src/providers/heuristic.js +77 -0
- package/dist/packages/classifier/src/providers/http.js +63 -0
- package/dist/packages/classifier/src/providers/jev-open.js +86 -0
- package/dist/packages/classifier/src/providers/llm.js +89 -0
- package/dist/packages/classifier/src/providers/systemone-http.js +95 -0
- package/dist/packages/classifier/src/questions.js +97 -0
- package/dist/packages/classifier/src/registry.js +42 -0
- package/dist/packages/classifier/src/snapshot.js +31 -0
- package/dist/packages/classifier/src/systemone.js +88 -0
- package/dist/packages/classifier/src/types.js +18 -0
- package/dist/packages/cli/assets/dash/dash.css +139 -0
- package/dist/packages/cli/assets/dash/dash.js +200 -0
- package/dist/packages/cli/assets/dash/icon.svg +1 -0
- package/dist/packages/cli/assets/dash/index.html +36 -0
- package/dist/packages/cli/src/analyst/backend.js +3 -2
- package/dist/packages/cli/src/analyst/codex.js +3 -2
- package/dist/packages/cli/src/app-help-gen.js +26 -2
- package/dist/packages/cli/src/autotrade-cmd/audit.js +19 -2
- package/dist/packages/cli/src/autotrade-cmd/backtest.js +21 -4
- package/dist/packages/cli/src/autotrade-cmd/executor.js +91 -0
- package/dist/packages/cli/src/autotrade-cmd/fleet.js +16 -5
- package/dist/packages/cli/src/autotrade-cmd/forward.js +283 -0
- package/dist/packages/cli/src/autotrade-cmd/journey.js +208 -0
- package/dist/packages/cli/src/autotrade-cmd/limits.js +393 -0
- package/dist/packages/cli/src/autotrade-cmd/receipt.js +63 -0
- package/dist/packages/cli/src/autotrade-cmd/report.js +79 -0
- package/dist/packages/cli/src/autotrade-cmd/run.js +268 -14
- package/dist/packages/cli/src/autotrade-cmd/service.js +873 -0
- package/dist/packages/cli/src/autotrade-cmd/signal.js +824 -0
- package/dist/packages/cli/src/autotrade-cmd.js +50 -4
- package/dist/packages/cli/src/classifier-cmd.js +353 -0
- package/dist/packages/cli/src/classifier-live.js +81 -0
- package/dist/packages/cli/src/dash/data.js +217 -0
- package/dist/packages/cli/src/dash/server.js +92 -0
- package/dist/packages/cli/src/dash-cmd.js +113 -0
- package/dist/packages/cli/src/doctor.js +34 -0
- package/dist/packages/cli/src/ledger-cmd.js +4 -2
- package/dist/packages/cli/src/live/screens/token-live.js +4 -0
- package/dist/packages/cli/src/live/signals.js +8 -0
- package/dist/packages/cli/src/main.js +98 -6
- package/dist/packages/cli/src/mcp-follow.js +21 -1
- package/dist/packages/cli/src/narratives-cmd.js +147 -0
- package/dist/packages/cli/src/narratives.js +114 -0
- package/dist/packages/cli/src/overlay/model.js +147 -0
- package/dist/packages/cli/src/overlay/page.js +146 -0
- package/dist/packages/cli/src/overlay/pnl.js +75 -0
- package/dist/packages/cli/src/overlay/server.js +170 -0
- package/dist/packages/cli/src/overlay-cmd.js +367 -0
- package/dist/packages/cli/src/pnl-card.js +4 -0
- package/dist/packages/cli/src/record-cmd.js +82 -11
- package/dist/packages/cli/src/rules-cmd.js +2 -1
- package/dist/packages/cli/src/rules.js +24 -4
- package/dist/packages/cli/src/screens/autotrade-journey.js +134 -0
- package/dist/packages/cli/src/screens/autotrade-keys.js +13 -0
- package/dist/packages/cli/src/screens/autotrade-screen.js +57 -6
- package/dist/packages/cli/src/screens/autotrade-ui.js +17 -7
- package/dist/packages/cli/src/screens/autotrade-worker.js +4 -2
- package/dist/packages/cli/src/screens/narrative.js +154 -2
- package/dist/packages/cli/src/state.js +1 -0
- package/dist/packages/cli/src/telegram-cmd.js +4 -1
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/custom/src/rules.js +5 -1
- package/dist/packages/data/src/trade/ledger/schema.js +1 -1
- package/dist/packages/mcp/assets/kb/app-help/autotrade-executor-kit.md +44 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-journey.md +46 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-limits.md +48 -0
- package/dist/packages/mcp/assets/kb/app-help/autotrade-real-signal.md +45 -0
- package/dist/packages/mcp/assets/kb/app-help/classification-models.md +41 -0
- package/dist/packages/mcp/assets/kb/app-help/live-trading.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/narratives-tracker.md +45 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +12 -0
- package/dist/packages/mcp/assets/kb/app-help/strategy-dashboard.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/streamer-overlay.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-rug-proof-entries-experimental.md +52 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +2 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-always-on-service.md +59 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-strategy-dashboard-dexterm-dash.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +9 -2
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +3 -1
- 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-copy-and-paste-without-surprises.md +0 -1
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-dexterm-says-command-not-found.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-reopen-dexterm-later.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-terminal-shell-prompt-command-output-and-path.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-expectations.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-1-ask-for-a-strategy.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-2-backtest-it-held-out-split.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-3-forward-test-it-on-live-data.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-4-promote-it-to-always-on-paper.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-5-real-only-if-you-choose.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-ladder.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-report.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-what-the-analyst-knows.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-hosted-jev-with-your-own-key.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-how-they-measured-on-dexterm-s-own-history.md +52 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-set-up-kev-on-your-own-machine.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-settings.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-the-choices.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-where-it-plugs-in.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier-which-model-for-which-machine.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-classifier.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-invocations.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -0
- 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 +1 -0
- 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 +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-supported-platforms.md +2 -11
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-executor-health.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-paper-vs-real.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-digest.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-executor-kit.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-whole-path.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-what-dexterm-does-and-never-does.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real-your-limits.md +77 -0
- package/dist/packages/mcp/assets/kb/docs/docs-going-real.md +24 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +2 -2
- package/dist/packages/mcp/assets/kb/docs/docs-larp-paste-a-coin-on-the-web.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-larp-the-larp-score.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-real-mode-your-strategy-s-trades-to-your.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +0 -2
- package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-narratives-how-coins-are-grouped.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-narratives-limits.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-narratives-what-is-measured.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-narratives-what-testing-showed.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-narratives-where-to-find-it.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-narratives.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-before-you-start.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-limits.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-options.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-security-model.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-set-it-up-in-obs-step-by-step.md +49 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-the-pnl-scene-paper-and-live.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-the-three-scenes.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay-what-is-redacted.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-overlay.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-receipts-what-your-executor-did.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-safety.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-set-it-up.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-channels.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-signal.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-is.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-will-not-do.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode-where-you-see-it.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-real-mode.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-bubble-map.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-streaming-it.md +1 -0
- package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +1 -1
- package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +2 -2
- package/dist/packages/mcp/assets/kb/entries/autotrade.md +4 -2
- package/dist/packages/mcp/assets/kb/entries/narratives-tracker.md +45 -0
- package/dist/packages/mcp/src/autotrade-tools.js +168 -5
- package/dist/packages/mcp/src/engine/restrictions.js +3 -3
- package/dist/packages/mcp/src/index.js +1 -1
- package/dist/packages/mcp/src/ledger-tools.js +155 -7
- package/dist/packages/mcp/src/server.js +125 -10
- package/dist/packages/mcp/src/spectator-sink.js +27 -0
- package/dist/packages/narratives/src/cluster.js +193 -0
- package/dist/packages/narratives/src/embed.js +184 -0
- package/dist/packages/narratives/src/export.js +60 -0
- package/dist/packages/narratives/src/index.js +10 -0
- package/dist/packages/narratives/src/label.js +48 -0
- package/dist/packages/narratives/src/normalize.js +179 -0
- package/dist/packages/narratives/src/recorder.js +93 -0
- package/dist/packages/narratives/src/tracker.js +328 -0
- package/dist/packages/recorder/src/exit-probes.js +215 -0
- package/dist/packages/recorder/src/index.js +1 -0
- package/dist/packages/recorder/src/recorder.js +11 -1
- package/dist/packages/recorder/src/retention.js +4 -0
- package/dist/packages/recorder/src/schema.js +20 -1
- package/dist/packages/tui/src/keymap.js +12 -0
- package/package.json +3 -1
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-powershell-experimental.md +0 -39
|
@@ -14,7 +14,7 @@ import path from 'node:path';
|
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { ACTION_FILE_ENV, ACTION_TOKEN_ENV, ANALYST_ACTION_TYPES, parseActionLine } from '../../../data/src/index.js';
|
|
16
16
|
import { basisLanguage, DEFAULT_PRICE_BASIS, fmtCap, isPriceBasis, knownSupply, NO_SUPPLY_REASON, pickBasis } from '../../../data/src/basis.js';
|
|
17
|
-
import { firewall, userSkillPreamble, PARALLEL_CONTROL_TOOLS, STRATEGY_PROPOSE_TOOLS } from '../../../mcp/src/index.js';
|
|
17
|
+
import { firewall, userSkillPreamble, JOURNEY_CONTROL_TOOLS, JOURNEY_PROPOSE_TOOLS, PARALLEL_CONTROL_TOOLS, STRATEGY_PROPOSE_TOOLS } from '../../../mcp/src/index.js';
|
|
18
18
|
import { skillStore } from "../kb-skill-cmd.js";
|
|
19
19
|
import { applyCodexEvent, buildCodexArgv, codexAllowedTools, codexDeveloperInstructions, codexFailure, expandSkillPrompt, newCodexTurn, strictSchema } from "./codex.js";
|
|
20
20
|
import { runOllamaTurn } from "./ollama.js";
|
|
@@ -37,7 +37,8 @@ export function deniedActionTools(actionTypes) {
|
|
|
37
37
|
return [];
|
|
38
38
|
// task-1507: an observe-only session (AI scan) cannot propose a strategy edit either
|
|
39
39
|
// task-1529: nor start or stop a parallel strategy
|
|
40
|
-
|
|
40
|
+
// task-1647: nor start / stop a forward test or propose a promotion
|
|
41
|
+
const propose = actionTypes.length === 0 ? [...STRATEGY_PROPOSE_TOOLS, ...PARALLEL_CONTROL_TOOLS, ...JOURNEY_CONTROL_TOOLS, ...JOURNEY_PROPOSE_TOOLS] : [];
|
|
41
42
|
return [...ANALYST_ACTION_TYPES.filter((t) => !actionTypes.includes(t)), ...propose].map((t) => `mcp__dexterm__${t}`);
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import fs from 'node:fs';
|
|
18
18
|
import path from 'node:path';
|
|
19
19
|
import { ACTION_FILE_ENV, ACTION_TOKEN_ENV, ANALYST_ACTION_TYPES } from '../../../data/src/index.js';
|
|
20
|
-
import { READ_ONLY_TOOLS, STRATEGY_PROPOSE_TOOLS } from '../../../mcp/src/index.js';
|
|
20
|
+
import { JOURNEY_PROPOSE_TOOLS, READ_ONLY_TOOLS, STRATEGY_PROPOSE_TOOLS } from '../../../mcp/src/index.js';
|
|
21
21
|
import { skillStore } from "../kb-skill-cmd.js";
|
|
22
22
|
/** The analyst engines dexterm can drive: the user's own claude or codex CLI, or (task-1543) a local model on their Ollama. */
|
|
23
23
|
export const ANALYST_ENGINES = ['claude', 'codex', 'ollama'];
|
|
@@ -75,7 +75,8 @@ export function codexAllowedTools(actionTypes) {
|
|
|
75
75
|
const reads = READ_ONLY_TOOLS.filter((n) => !/^paper_/.test(n) && !ANALYST_ACTION_TYPES.includes(n));
|
|
76
76
|
const actions = ANALYST_ACTION_TYPES.filter((t) => !actionTypes || actionTypes.includes(t));
|
|
77
77
|
// task-1507: strategy PROPOSALS (never applied by a tool) for a chat turn; an observe-only scan gets none
|
|
78
|
-
|
|
78
|
+
// task-1647: promotion PROPOSALS too (a file the user applies); forward-test control stays off codex, like parallel control
|
|
79
|
+
const propose = actionTypes && actionTypes.length === 0 ? [] : [...STRATEGY_PROPOSE_TOOLS, ...JOURNEY_PROPOSE_TOOLS];
|
|
79
80
|
return [...reads, ...actions, ...propose];
|
|
80
81
|
}
|
|
81
82
|
const tomlString = (s) => JSON.stringify(s); // a JSON string is a valid TOML basic string
|
|
@@ -191,6 +191,9 @@ const TOPICS = [
|
|
|
191
191
|
{ id: 'use-codex-analyst', title: 'Run the analyst on Codex instead of Claude', screen: 'all', tags: ['codex', 'openai', 'chatgpt', 'engine', 'analyst', 'claude', 'switch', 'backend'],
|
|
192
192
|
flags: ['--analyst-engine'], feature: 'analyst',
|
|
193
193
|
body: 'The analyst can run on your own Codex CLI login instead of Claude (0.1.5). Pick it with `--analyst-engine codex`, `DEXTERM_ANALYST_ENGINE=codex`, `"analyst": {"engine": "codex"}` in `~/.dexterm/config.json`, or type `/engine codex` in the chat box (new conversations; `/engine` alone shows the engine and what is installed). The default `auto` uses claude when it is installed, else codex. dexterm stores no credentials: your `codex login` does the work. The Codex run is locked read-only: only the read-only dexterm tools (plus the UI actions the session allows, none in scan mode), no shell, no file edits, no web search, no other MCP server; a turn that tries anything else is stopped. `dexterm doctor` shows a Codex CLI line (installed, version, logged in). Codex is also supported as an MCP client: `dexterm mcp install codex`.' },
|
|
194
|
+
{ id: 'classification-models', title: 'Pick a classification model (Kev, hosted Jev, or the built-in scores)', screen: 'all', tags: ['classifier', 'classification', 'kev', 'jev', 'model', 'probability', 'rug', 'drain', 'llama', 'calibrated', 'typesafe', 'experimental'],
|
|
195
|
+
commands: ['classifier'], tools: ['classify_coin', 'classify_batch', 'classifier_status'], feature: 'classifier',
|
|
196
|
+
body: 'EXPERIMENTAL. A classification model answers typed questions about one coin with a probability per answer: p(drain within 6 h) and RUG / WIN / DUD over 6 h, or a narrative category. `dexterm classifier list` shows the choices: `heuristic` (the built-in H1 rug score and LARP link score; the default, no download), `kev-local` (an open Kev decision model on your own machine through llama.cpp: `dexterm classifier pull kev-4b-q4` shows the size and source and asks first, verifies the sha256, then `dexterm classifier pick kev-local kev-4b-q4` and `dexterm classifier serve`), `jev-hosted` (TypeSafe\'s hosted Jev with your own API key: `dexterm classifier key set`; per-call cost and a daily call cap), `jev-open` (the open JEV students on a vLLM server), and `claude` / `ollama-llm` zero-shot baselines. `dexterm classifier doctor` reads RAM, CPU and GPU/VRAM and recommends a tier. If the picked model is unreachable the built-in scores answer and say so. Agents use the `classify_coin`, `classify_batch` (triage: flag / pass / escalate) and `classifier_status` MCP tools; a watch rule can use `rugp` (e.g. `when rugp > 0.8 then alert(warn, "...")`). How each model scored on dexterm\'s own labelled history is on the docs page. Every answer is a model estimate with the model id, never advice. Docs: https://dexterm.xyz/docs/classifier/' },
|
|
194
197
|
{ id: 'use-local-model', title: 'Run dexterm\'s AI on a free local model (Ollama)', screen: 'all', tags: ['ollama', 'local', 'model', 'free', 'offline', 'engine', 'llama', 'qwen', 'restrictions', 'privacy'],
|
|
195
198
|
commands: ['ai', 'ask'], flags: ['--analyst-engine'], feature: 'analyst',
|
|
196
199
|
body: 'dexterm\'s AI can run on a free model on your own machine through Ollama: no login, no key, $0 per answer, and it talks only to http://127.0.0.1:11434 by default. Set it up in two minutes: install Ollama (https://ollama.com/download), `ollama pull qwen3:8b`, `dexterm ai engine ollama`, `dexterm ai model qwen3:8b`, then `dexterm doctor` (the Ollama line should pass). In the chat box `/engine ollama` switches new conversations, `/engine claude` switches back; `--analyst-engine ollama` and `DEXTERM_ANALYST_ENGINE=ollama` work too, and `dexterm ai engine ollama --surface autotrade` picks one surface. The local engine uses the same dexterm tools and the same firewalled context as Claude. What it may do is your restriction profile: `observe` (analyse only), `propose` (the default for a new setup: UI actions, a PAPER ticket you confirm, strategy proposals you apply), `paper` (adds paper autotrade sessions and agent paper trading in a mandate you armed: `dexterm ai restrictions paper`), `full` (only after `dexterm ai engine ollama --full` and typing ENABLE FULL, for one model). Caps, the kill switch, mandate tiers, live gating and Telegram buttons are enforced outside the model for every engine. `dexterm ai probe --json` checks tool calling, context and decision formatting; a model without tools answers from the dossier dexterm fetches and says so. Paper autotrade on it: `dexterm autotrade run <strategy> --paper --engine ollama`. Headless: `dexterm ask "question"`, `dexterm ai agent "goal"`. Docs: https://dexterm.xyz/docs/ollama/' },
|
|
@@ -232,7 +235,7 @@ const TOPICS = [
|
|
|
232
235
|
body: '`dexterm autotrade run <id|file> --paper` runs one strategy on paper, headless, one runner per machine: the screener feeds it the same live data as the app (every 10 s, candidate and held coins every 5 s), and each coin that matches gets one session on your own Claude login with the full picture first, then only what changed. Your agent answers with a typed decision (enter, wait, skip, hold, exit, tighten, reprofile); every size is clamped by your strategy and the exits run on their own. At most 2 sessions at once, $0.50 per session and the day caps in ~/.dexterm/autotrade/runner.json (which Claude account, sessions and spend per day, a usage guard). Every step goes to ~/.dexterm/autotrade/runs/<run>.jsonl, hash-chained. `--replay FILE` paces a recorded tape instead of live data; `--stub` uses a scripted agent with no Claude cost. Paper only: a run never signs or touches a wallet, and there is no `--live` (dexterm holds no key and places no live order). Not financial advice. Full docs: https://dexterm.xyz/docs/autotrade/#the-runner-and-candidate-sessions' },
|
|
233
236
|
{ id: 'live-trading', title: 'Live trading: dexterm holds no key; a Telegram trading bot of your choice trades', screen: 'all', tags: ['live', 'trading', 'real', 'money', 'telegram', 'bot', 'key', 'wallet', 'custody', 'signer', 'mainnet'],
|
|
234
237
|
commands: ['telegram', 'wallet', 'live'], feature: 'live-trading',
|
|
235
|
-
body: 'dexterm never holds a key and never signs or sends a transaction: there is no trading wallet, no signer and no live order in dexterm. Live trading happens in a Telegram trading bot of your choice, which holds its own wallet; you act in that bot. dexterm\'s part is the Telegram connector to your OWN bot (`dexterm telegram setup`): alerts, signals and run control for your paper runs. dexterm
|
|
238
|
+
body: 'dexterm never holds a key and never signs or sends a transaction: there is no trading wallet, no signer and no live order in dexterm. Live trading happens in a Telegram trading bot of your choice, which holds its own wallet; you act in that bot. dexterm\'s part is the Telegram connector to your OWN bot (`dexterm telegram setup`): alerts, signals and run control for your paper runs. dexterm does not place orders in a trading bot for you: in real mode (0.2.0, experimental) a strategy you armed on your own terminal sends a signal for each trade to an executor you run, and your executor places the order by its own rules (`dexterm autotrade signal`, https://dexterm.xyz/docs/real-mode/). Paper trading (the ticket, paper accounts, paper autotrade) is local. `dexterm wallet link` keeps a Phantom public key for display only. Not financial advice. Full docs: https://dexterm.xyz/docs/live-trading/' },
|
|
236
239
|
{ id: 'live-yolo', title: 'The full-port yolo strategy on paper: replay and a paper run (HIGH RISK)', screen: 'all', tags: ['yolo', 'full', 'port', 'all-in', 'high', 'risk', 'preset', 'replay', 'paper'],
|
|
237
240
|
commands: ['autotrade'], feature: 'yolo-preset',
|
|
238
241
|
body: 'HIGH RISK, PAPER only; not financial advice. The `yolo` preset enters one young pair at a time on an early 5-minute push after the holder-safety gates (LP burned or locked, no mint or freeze authority, a LARP score of 49 or less checked live before a coin becomes a candidate, liquidity of $25K or more), takes the cost back at about 2x, lets the rest ride on a loose 50% trail, exits at once on a liquidity pull, a dev sell or dead volume, has a -50% stop and a 45-minute time stop, and never buys the same coin twice. On paper it caps an entry at 25% like every strategy. `dexterm autotrade yolo-replay yolo` measures every entry at full port over your recorded history (rugs, the loss spread, how often it zeroed the wallet); `dexterm autotrade run yolo --paper` runs it on paper (`--replay builtin:yolo-rehearsal --stub` for a short fixture run; next to other strategies, or on a shared paper account, through `autotrade.json` and `dexterm autotrade run --all --paper`). Stop: `dexterm autotrade stop`, /kill in Telegram, Ctrl-C. dexterm places no live order. Full docs: https://dexterm.xyz/docs/yolo/' },
|
|
@@ -245,13 +248,28 @@ const TOPICS = [
|
|
|
245
248
|
{ id: 'autotrade-telegram', title: 'Your own Telegram bot for the autotrade runner: notify, control, one-tap approvals (dexterm telegram)', screen: 'all', tags: ['telegram', 'bot', 'phone', 'mobile', 'notify', 'notifications', 'approve', 'approval', 'approvals', 'pair', 'pairing', 'kill', 'pause', 'remote', 'botfather'],
|
|
246
249
|
commands: ['telegram'], feature: 'autotrade-telegram',
|
|
247
250
|
body: 'Create a bot with @BotFather, then run `dexterm telegram setup`: paste the token at the hidden prompt (never as an argument), dexterm checks it and keeps it in your keychain (or the system keyring, or a 0600 file), then shows a 6-digit code; send `/pair <code>` to your bot from your own private chat and confirm the account it shows (@username and id) with `y` on your computer. Pairing is refused while a webhook or another program polls the bot. Only that account in that chat is answered. `dexterm autotrade run <id> --paper` then starts the bot by itself (`--no-telegram` keeps it off): entries, exits, PnL cards, halts and a daily summary reach your phone; `/status`, `/positions`, `/pause`, `/resume` (a pause only), `/kill`, `/flatten` and `/use` (each after a confirm button, `/use` only to an equal-or-tighter strategy; a change it cannot rank counts as looser) and `/approvals on` work from chat, which can make things safer and never less safe. With `entry.decide: "agent+approve"` or approvals on, every entry is a card with [Approve] [Half size] [Skip]: only a button approves, once, for that proposal, your account and your private chat; the price and your limits are checked again at the tap; no answer is a skip. Typed messages are never trades. `dexterm telegram status|test|audit --verify|revoke`. Nothing is hosted; paper only; never send keys or seed phrases in chat. Not financial advice. Full docs: https://dexterm.xyz/docs/telegram/' },
|
|
251
|
+
{ id: 'autotrade-real-signal', title: 'Real mode: send a strategy\'s trades to your own executor as LIVE TRADE FIRE signals (dexterm autotrade signal)', screen: 'all', tags: ['real', 'mode', 'live', 'trade', 'fire', 'signal', 'signals', 'executor', 'webhook', 'hook', 'jsonl', 'receipt', 'receipts', 'arm', 'caps', 'kill', 'outbox', 'nonce'],
|
|
252
|
+
commands: ['autotrade'], tools: ['trade_fire_feed'], feature: 'autotrade-real-signal',
|
|
253
|
+
body: 'Real mode runs a strategy exactly like paper (the same clamp, exits, monitor and run file) and sends every buy and sell it fills, first, as a LIVE TRADE FIRE signal (`dexterm-trade-fire/1`: buy or sell, mint, symbol, size in SOL and % of book, the sell fraction and exit reason, strategy, run, account, an always-growing nonce, an idempotency id and an expiry) to YOUR executor: a Telegram trading bot of your choice or any script. dexterm holds no key and signs nothing. Set channels with `dexterm autotrade signal channels --webhook URL | --signal-cmd \'["/abs/path"]\' | --jsonl on | --mcp on | --telegram on` (they live in connectors.json next to the Telegram setup; a bot cannot read another bot, so the Telegram copy alone is not enough), a webhook HMAC secret with `signal secret generate --out FILE`, hard caps with `signal caps set` (raising needs a typed confirmation), then arm on your own terminal with `dexterm autotrade signal arm <id>` (evidence panel + a typed phrase with a one-time code; any later edit disarms; below the evidence floor, a completed forward test with your `minForwardTrades` closed paper trades and a backtest of these exact rules, or past a missing rung of the ladder, it refuses unless you first type a separate OVERRIDE phrase, which is journaled) and run `dexterm autotrade run <id> --real` (live data only, in the foreground: in 0.2.0 the always-on service runs paper strategies only). Delivery is at least once from a restart-safe outbox, in nonce order, retried until acked or expired; the executor dedupes by nonce, ignores expired signals and acts only on its own account. Every run ends with a `halted` signal that lists the positions still open; the run\'s last lines and the Telegram copy name them too. Open positions are carried to the next real run of the same strategy and account, whose exits keep firing; while no real run is running NO signal goes out, sells included, so manage them in your executor. Positions stay unconfirmed until `dexterm autotrade receipt <nonce> executed|rejected|failed` (data only). `signal kill` stops every signal; `signal status`, `signal tail`, `receipt list` and `autotrade audit` show the outbox, receipts and drift; agents can read the MCP feed with trade_fire_feed. No model, tool or chat tap can arm real mode, raise a cap or lift the kill switch. Experimental; not financial advice.' },
|
|
254
|
+
{ id: 'autotrade-limits', title: 'Set your own real-mode limits: per trade, per day, open positions, loss stops, the breaker (dexterm autotrade limits)', screen: 'all', tags: ['limits', 'limit', 'caps', 'cap', 'size', 'sizing', 'per', 'trade', 'daily', 'loss', 'drawdown', 'breaker', 'streak', 'exposure', 'slippage', 'ttl', 'rate', 'raise', 'lower', 'scope'],
|
|
255
|
+
commands: ['autotrade'], tools: ['trading_limits', 'limits_propose'], feature: 'autotrade-limits',
|
|
256
|
+
body: 'Every limit real mode enforces is yours to set, in one file (`~/.dexterm/autotrade/real/limits.json`, schema `dexterm-limits/1`): per trade as SOL or as % of the book (the smaller wins), SOL and buys per day, open positions, exposure, min and max position, per coin per day, a daily loss stop, a drawdown breaker, a loss-streak pause, max hold time, the evidence floor for arming (`minForwardTrades`: closed paper trades in a completed forward test, placeholder 10), min liquidity and market cap, max slippage and price impact, the signal TTL, a buy rate limit, an optional sell rate (sells are never capped unless you set one) and how long a silent executor may stay silent. `dexterm autotrade limits show [--strategy ID] [--account ID]` lists what is in force and where each number comes from; `limits set perTradeSol=0.5 dailyLossSol=0.3` (or `--scope real|paper|strategy:ID|account:ID`) changes them: a global number replaces the placeholder default, every other scope can only tighten, and the stricter always wins. Paper runs ignore them unless you add a `paper` scope; then a single paper run (not `run --all`) obeys them on its own state (a replay then needs `--sol-usd N`). Daily counters cover the whole home (every strategy and account), so a strategy or account number can only stop buys sooner. Lowering is immediate, even for a running real run; raising shows the old -> new diff and needs the typed phrase on your own terminal, and applies from the next run. `limits diff`, `limits accept` (a hand-edited file), `limits reset`, `limits breaker reset` (typed) and `limits explain` do the rest. The defaults (0.25 SOL a trade, 1 SOL a day, 20 buys, 3 open, stop at -0.5 SOL a day) are placeholders: arming asks for your own numbers first. The analyst can read and explain them (`trading_limits`) and propose a change as a diff card (`limits_propose`); it can never apply one.' },
|
|
257
|
+
{ id: 'autotrade-executor-kit', title: 'Wire up a test executor, check it is alive, and get the digest (dexterm autotrade executor, signal test, report)', screen: 'all', tags: ['executor', 'kit', 'example', 'heartbeat', 'alive', 'silent', 'health', 'test', 'webhook', 'receiver', 'dry', 'run', 'digest', 'report', 'daily', 'weekly', 'divergence', 'slippage', 'latency'],
|
|
258
|
+
commands: ['autotrade'], feature: 'autotrade-executor-kit',
|
|
259
|
+
body: '`dexterm autotrade executor kit --out ~/my-executor` copies the reference executors: a webhook receiver, a `--signal-cmd` hook and a JSONL tail. Each verifies the HMAC, dedupes by nonce, honours the TTL, writes a receipt and a heartbeat, and is DRY RUN: it only logs what it would do, with no wallet code in it. Start one, point a channel at it (`dexterm autotrade signal channels --webhook http://127.0.0.1:8787/signal`), then `dexterm autotrade signal test` sends a test signal (`test: true`, no coin, nothing to trade) and shows, per channel, whether it was delivered, how long it took and the executor\'s receipt. `dexterm autotrade executor status` shows the last heartbeat, the last receipt and the lag; a real run pauses new buy signals (exits still go out) when nothing is heard for longer than your `executorSilenceMin` limit, and tells you. With receipts that carry fills, `signal status`, `autotrade audit` and `dexterm autotrade report` show paper vs real: slippage against dexterm\'s own paper fill, latency, partial fills and rejections. `report [--period day|week] [--telegram]` is the digest per strategy and mode with the win rate\'s interval and a small-sample note; `report --schedule daily|weekly` makes a run with your bot on send it.' },
|
|
248
260
|
{ id: 'autotrade-chat', title: 'Change an autotrade strategy by chat (paper)', screen: 'autotrade', tags: ['autotrade', 'strategy', 'chat', 'analyst', 'proposal', 'tune', 'change', 'diff', 'looser', 'less', 'safe'],
|
|
249
261
|
actions: [{ action: 'analyst.toggle' }, { action: 'autotrade.proposal', screen: 'autotrade' }],
|
|
250
262
|
tools: ['strategy_list', 'strategy_get', 'strategy_validate', 'strategy_propose_edit', 'strategy_propose_new', 'strategy_backtest', 'autotrade_status'], feature: 'autotrade-chat',
|
|
251
263
|
body: 'Ask the analyst to change one of your strategies ("make my swing exit faster"). It can only PROPOSE: the change arrives as a card on the AUTOTRADE screen (`Q`) with the field lines that change, the validation result and TIGHTER, NEUTRAL or LOOSER. `o` opens the card, `y` applies it as a new version (journaled with who proposed it), `n` dismisses it. When any risk number gets less safe (bigger size or exposure, more loss, a stop or safety gate removed) the edit is LOOSER and needs a second `y` on the less-safe popup. The mode, the book, the daily session budget and the exit-liquidity floor are yours only: the analyst cannot change them. It can backtest a proposal next to the current version on your recorded history (up to 14 days, read-only). Paper only.' },
|
|
264
|
+
{ id: 'autotrade-journey', title: 'Build a strategy by chat, back-test it, forward-test it, then promote it (paper, then real)', screen: 'all', tags: ['autotrade', 'strategy', 'build', 'chat', 'journey', 'ladder', 'backtest', 'held-out', 'forward', 'test', 'promote', 'promotion', 'always-on', 'real', 'evidence'],
|
|
265
|
+
commands: ['autotrade'], tools: ['strategy_journey', 'strategy_forward_start', 'strategy_forward_stop', 'strategy_forward_report', 'strategy_promote_propose'], feature: 'autotrade-journey',
|
|
266
|
+
body: 'A strategy climbs one ladder, one rung at a time: draft → backtested → forward-tested → always-on (paper) → real. Each rung needs evidence measured on that strategy\'s exact rules; any rule change starts it again (a rename or arming keeps it). Ask the analyst to build one ("build me a strategy that buys young pairs on a 5-minute push and takes profit at 2x"): it proposes a copy of a preset with changes (strategy_propose_new), you apply it on the Q screen. BACKTESTED: `dexterm autotrade backtest <id>` or strategy_backtest runs it on recorded history and on each half of the held-out split (the newest 30% of coins); verdict rule 6 allows gain wording only with at least 20 trades in both halves and a held-out net above 0. FORWARD-TESTED: `dexterm autotrade forward start <id> --hours 24` (or `--trades N`, or strategy_forward_start from the chat) runs the same version on the LIVE tape, on paper, with its own bankroll and run file, in the background next to the always-on service; `forward status`, `forward stop` and `forward report` (the forward numbers next to the backtest: trades, win rate with its interval, net, drawdown, rug rate, fill cost drift, and a plain line when a sample is too small). Only a test that ran its whole window on live data counts. ALWAYS-ON: the analyst can propose it (strategy_promote_propose), you apply it with `dexterm autotrade promote apply <pp-id>` (y on your own terminal) or on the Q screen journey view (`J`, then `y`); it joins autotrade.json and the always-on service runs it on paper. REAL: you set your own limits (`dexterm autotrade limits set`; `signal caps set` sets four of them), then `dexterm autotrade promote <id> --to real` hands over to `signal arm` (evidence panel, typed phrase with a one-time code). No tool or model can apply a promotion, skip a rung, set a cap, pick a bankroll or arm real mode. `dexterm autotrade journey <id>` shows where a strategy stands. Paper by default; not financial advice.' },
|
|
252
267
|
{ id: 'trade-ledger', title: 'See every trade in one ledger: win rate, profiles, exit reasons, strategy versions (dexterm ledger)', screen: 'all', tags: ['ledger', 'journal', 'trades', 'history', 'results', 'stats', 'win', 'rate', 'drawdown', 'profile', 'exit', 'reason', 'compare', 'export', 'csv', 'equity', 'mfe', 'mae'],
|
|
253
268
|
commands: ['ledger'], tools: ['ledger_trades', 'ledger_positions', 'ledger_summary', 'ledger_equity', 'ledger_compare', 'candidate_dossier', 'outcomes_lookup', 'trading_status', 'telegram_status', 'backtest_requests'], feature: 'trade-ledger',
|
|
254
|
-
body: '`dexterm ledger trades` lists every round trip dexterm booked, from the paper ticket, the agent paper desk and autotrade runs, newest first: strategy version, the exit profile and entry reason it was opened with (marked * when not declared with the fill), the exit reason, size and % of book, entry and exit market cap, P/L, hold and MFE/MAE. `positions` shows the open ones. `summary` gives the win rate with its 95% interval, P/L, profit factor, drawdown and rug rate by profile, exit reason, strategy, source and mode; `equity` the realized equity curve; `compare` strategy versions side by side (for strategies run in parallel); `export csv|json|parquet-lite [--out FILE]`; `card <tradeId>` a PnL card from a row. Filters: --source, --mode paper|devnet|live, --strategy id[@version], --profile, --exit, --mint, --run, --days, --status. Reads never write; `dexterm ledger sync` appends to ~/.dexterm/ledger/trades.jsonl (sha256-chained) and `dexterm ledger verify` names the first edited line. Agents read the same rows with the read-only ledger_* tools; `trading_status` and `telegram_status` show settings and state, never a key, address, exact balance, bot token or chat id. A backtest an agent asks for (backtest_request) only runs when you run it: `dexterm autotrade requests`. PAPER rows are simulated; filter by --mode before comparing. Not financial advice. Full docs: https://dexterm.xyz/docs/paper-trading/#the-trade-ledger' },
|
|
269
|
+
body: '`dexterm ledger trades` lists every round trip dexterm booked, from the paper ticket, the agent paper desk and autotrade runs, newest first: strategy version, the exit profile and entry reason it was opened with (marked * when not declared with the fill), the exit reason, size and % of book, entry and exit market cap, P/L, hold and MFE/MAE. `positions` shows the open ones. `summary` gives the win rate with its 95% interval, P/L, profit factor, drawdown and rug rate by profile, exit reason, strategy, source and mode; `equity` the realized equity curve; `compare` strategy versions side by side (for strategies run in parallel); `export csv|json|parquet-lite [--out FILE]`; `card <tradeId>` a PnL card from a row. Filters: --source, --mode paper|devnet|live|real (real = a real-mode run: dexterm\'s model of the trades it signalled), --strategy id[@version], --profile, --exit, --mint, --run, --days, --status. Reads never write; `dexterm ledger sync` appends to ~/.dexterm/ledger/trades.jsonl (sha256-chained) and `dexterm ledger verify` names the first edited line. Agents read the same rows with the read-only ledger_* tools; `trading_status` and `telegram_status` show settings and state, never a key, address, exact balance, bot token or chat id. A backtest an agent asks for (backtest_request) only runs when you run it: `dexterm autotrade requests`. PAPER rows are simulated; filter by --mode before comparing. Not financial advice. Full docs: https://dexterm.xyz/docs/paper-trading/#the-trade-ledger' },
|
|
270
|
+
{ id: 'strategy-dashboard', title: 'Track how your autotrade strategies perform in a browser, on this machine only (dexterm dash)', screen: 'all', tags: ['dash', 'dashboard', 'performance', 'strategies', 'track', 'equity', 'drawdown', 'compare', 'baseline', 'rugs', 'stuck', 'browser', 'web', 'phone'],
|
|
271
|
+
commands: ['dash'],
|
|
272
|
+
body: '`dexterm dash` reads your own autotrade run ledgers ($DEXTERM_HOME/autotrade/runs) and serves a dashboard on http://127.0.0.1:4317/ (loopback only; no account, no cloud, nothing leaves the machine; read-only). Views: Overview (every strategy side by side with an equity sparkline), Strategy (equity curve, drawdown, exit reasons, open positions with the engine mark and the realizable value from the last cached pool read, every trade), Rugs & stuck (closed at -80% or worse and stuck positions written off at what they actually sold), Compare (each strategy minus a --baseline with the 95% interval of the difference) and Live status; it refreshes every 30 s. Every number carries its sample size; under 20 closed trades a strategy is exploratory. Flags: --port, --home (repeat for several homes), --baseline ID, --books FILE (your own script\'s books), --allow-host NAME (a private proxy in front), --json, --export DIR [--redact-mints] (a static read-only snapshot). PAPER results are simulated. Not financial advice.' },
|
|
255
273
|
{ id: 'coin-images', title: 'Turn coin images on or off', screen: 'all', tags: ['images', 'image', 'pictures', 'icons', 'logos', 'off'],
|
|
256
274
|
flags: ['--no-images'], feature: 'coin-images',
|
|
257
275
|
body: 'Coin pictures render as real pixels in image-capable terminals (kitty/iTerm2/sixel) and as block art everywhere else. Turn them off with `--no-images` (or `DEXTERM_IMAGES=0`, or `enabled:false` in `~/.dexterm/images.json`). `dexterm doctor` has a Coin images line that says which path you get.' },
|
|
@@ -271,6 +289,9 @@ const TOPICS = [
|
|
|
271
289
|
{ id: 'spectate-an-agent', title: 'Watch an agent drive dexterm (dexterm spectate)', screen: 'all', tags: ['spectate', 'spectator', 'watch', 'agent', 'cook', 'observe', 'replay', 'stream', 'record', 'mcp', 'follow'],
|
|
272
290
|
commands: ['spectate'], flags: ['--replay', '--spectate-mcp', '--spectate-thoughts'], feature: 'spectator',
|
|
273
291
|
body: 'Watch an agent work. For an agent on the loopback bus, `dexterm agent serve --spectator-popup` opens the AGENT ACTIVITY window, `--spectate` starts the read-only, token-gated, loopback stream, and `--record FILE` saves a redacted session. For an agent connected over MCP (`dexterm mcp install`), run the app with `dexterm --spectate-mcp`: the running terminal follows the agent and an agent dock appears under the screen — a phase rail (SCANNING > VETTING > SIZING > EXECUTING > HOLDING > EXITING) with a heartbeat, the call timeline with result chips, the rows it scans glow and a cursor marks the coin it opens, a vetting card fills its checks and LARP evidence, and a PAPER trade shows its ticket, the fill, a live position with the mark PnL and a result card. `--spectate-thoughts FILE` streams the agent\'s own words into a thought ribbon from a JSONL file it writes (for example `claude -p --output-format stream-json`); display only. PAPER is always on screen; ctrl+p pauses, ctrl+k kills (a hard stop) and any key you press takes control; calm mode drops the motion. While the agent is active, the token screen\'s analyst panel is its BRAIN VIEW: the reasoning stream with phase tags (or dexterm\'s narration when there is no thoughts feed), the vetting checklist with the app\'s numbers and pass/warn/fail/pending, a data strip, the PAPER plan and position, and the session\'s decision log with a simulated scoreboard. While it holds a PAPER position, a PnL chip shows coin, PnL, entry to mark, peak, drawdown, time held and the take-profit it said, on every terminal screen (in the dock, or on the band under the header on terminals under 24 rows) and on the chart popup; display only. `dexterm spectate --replay FILE` replays a saved session step by step and `dexterm spectate --info` shows the live stream endpoint. Every shared copy is redacted — a tool name, refusal, agent line or navigation can never leak a wallet or key — and a spectator can only watch, never act. Full docs: https://dexterm.xyz/docs/spectator/' },
|
|
292
|
+
{ id: 'streamer-overlay', title: 'Streamer overlay for OBS (dexterm overlay)', screen: 'all', tags: ['overlay', 'obs', 'stream', 'streamer', 'browser source', 'scene', 'twitch', 'spectator', 'pnl', 'coin card', 'activity'],
|
|
293
|
+
commands: ['overlay'], feature: 'streamer-overlay',
|
|
294
|
+
body: 'Put the agent session on a live stream. `dexterm overlay` serves three transparent scenes for an OBS Browser source on 127.0.0.1: the agent activity feed, the coin card the agent is on (name, LARP evidence and the deterministic verdict, a top-10 holder bar, the live holder/rug signals) and the PnL card (the same renderer as the H key; stamped PAPER for the agent\'s simulated fills, or PAPER / LIVE by wallet mode with `--ledger <tradeId>`). `--scene all` puts all three in one source, `--theme cyberspace|ice|neon` picks a preset. It watches the read-only spectator stream a running `dexterm --spectate-mcp` (or `dexterm agent serve --spectate`) opens; `--replay FILE` plays a saved session and every scene then says SAMPLE DATA. Each printed URL works once (a single-use ticket, 10 minutes to paste it; press n for fresh ones). Addresses are always shortened, every scene shows the age of its data, and the overlay can only watch: it sends nothing back and cannot pause, trade, arm or sign. Full docs: https://dexterm.xyz/docs/overlay/' },
|
|
274
295
|
{ id: 'recorder-history', title: 'Work with the history recorder (dexterm recorder)', screen: 'all', tags: ['recorder', 'record', 'history', 'stats', 'label', 'prune', 'outcomes', 'database', 'session', 'capture', 'data'],
|
|
275
296
|
commands: ['recorder'], flags: ['--record', '--no-recorder'], feature: 'recorder',
|
|
276
297
|
body: '`dexterm recorder stats|label|prune|export` works with the local history recorder\'s tables (the outcome-labelled data the rug model and backtests learn from). No network. Recording is on by default; `--no-recorder` disables it for a run and `--record FILE` captures a data session to a file for replay.' },
|
|
@@ -280,6 +301,9 @@ const TOPICS = [
|
|
|
280
301
|
{ id: 'narrative-board', title: 'See the narrative board (dexterm narrative)', screen: 'trending', tags: ['narrative', 'narratives', 'board', 'trending', 'movers', 'window', 'summary'],
|
|
281
302
|
commands: ['narrative'], feature: 'trending-board',
|
|
282
303
|
body: '`dexterm narrative [--window 1h|24h] [--summary]` prints the narrative board — what is trending across the recorded feed — with no network and no model. In-app it is the trending screen.' },
|
|
304
|
+
{ id: 'narratives-tracker', title: 'Track memes and narratives (dexterm narratives, experimental)', screen: 'narrative', tags: ['narratives', 'narrative', 'memes', 'meme', 'copycat', 'lifecycle', 'ignition', 'heat', 'saturated', 'experimental'],
|
|
305
|
+
actions: [{ action: 'narrative.view', screen: 'narrative' }, { action: 'narrative.drill', screen: 'narrative' }], commands: ['narratives'], tools: ['narratives_top', 'narrative_detail', 'narrative_of'], feature: 'narratives',
|
|
306
|
+
body: 'EXPERIMENTAL. The narratives tracker groups coins by the theme their name and ticker carry (ticker puns, animals, celebrities, AI agents, seasonal events, copy-cats of a runner) and measures each theme per window: coins, new-coin rate, volume share, unique-trader growth, top runner and drawdown, a lifecycle state (spark / ignition / peak / fading / dead, with the rule that set it) and a copy-cat saturation flag. On the N screen, n flips the board into the NARRATIVES table (the first switch reads the last 26 h of recorded history and takes tens of seconds), enter to see a narrative\'s coins in spawn order, esc to go back. `dexterm narratives [top|detail ID|of MINT|export]` prints the same from the recorder; the analyst uses narratives_top, narrative_detail and narrative_of. Rules can use narrative_rank, narrative_score and the tags narrative:ignition / narrative:saturated. Keyless and local; measured heat, not advice.' },
|
|
283
307
|
{ id: 'kol-lists', title: 'Manage KOL wallet lists (dexterm kol)', screen: 'kol', tags: ['kol', 'kols', 'influencer', 'leader', 'list', 'lists', 'wallet', 'mute', 'import'],
|
|
284
308
|
commands: ['kol'], feature: 'kol-lists',
|
|
285
309
|
body: '`dexterm kol list|create|add|remove|mute|unmute|enable|disable|import` manages key-opinion-leader wallet lists on the shared DB (official / authorized sources only). In-app the KOL screen shows the lists and their activity.' },
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// hash covers the previous hash and its own content) and checks it against the run's summary, so an edited, added,
|
|
5
5
|
// dropped, reordered or cut-off line is named with its event number. Exit 1 when any checked run is broken.
|
|
6
6
|
import { dextermHome } from '../../../custom/src/index.js';
|
|
7
|
-
import {
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { Outbox, allOutboxSignals, auditRun, divergenceLines, divergenceReport, driftReport, listRunIds, outboxPath, readReceipts } from '../../../autotrade/src/index.js';
|
|
8
9
|
import { parseArgs } from "./strategies.js";
|
|
9
10
|
export const AUDIT_USAGE = 'dexterm autotrade audit [<run-id>|--last] [--verify] [--json]';
|
|
10
11
|
function line(a, verify) {
|
|
@@ -38,8 +39,19 @@ export async function auditCmd(argv, io) {
|
|
|
38
39
|
const verify = !!opts.verify;
|
|
39
40
|
const audits = ids.map((id) => auditRun(home, id));
|
|
40
41
|
const broken = verify ? audits.filter((a) => !a.ok) : [];
|
|
42
|
+
// task-1645: real mode: the drift between the trade-fire signals and the executor's receipts.
|
|
43
|
+
let real = null;
|
|
44
|
+
// task-1654: paper vs real: each receipted fill against dexterm's own paper fill (every outbox file, archives too).
|
|
45
|
+
let divergence = null;
|
|
46
|
+
if (existsSync(outboxPath(home))) {
|
|
47
|
+
const receipts = readReceipts(home).receipts;
|
|
48
|
+
const { rows: _rows, ...rest } = driftReport(Outbox.open(home, { channels: [], noLock: true }).signals(), receipts, Date.now());
|
|
49
|
+
real = rest;
|
|
50
|
+
const { rows: _r2, ...dv } = divergenceReport(allOutboxSignals(home), receipts);
|
|
51
|
+
divergence = dv;
|
|
52
|
+
}
|
|
41
53
|
if (opts.json) {
|
|
42
|
-
io.stdout.write(`${JSON.stringify({ verified: verify, runs: audits, broken: broken.length }, null, 2)}\n`);
|
|
54
|
+
io.stdout.write(`${JSON.stringify({ verified: verify, runs: audits, broken: broken.length, ...(real ? { realSignals: real } : {}), ...(divergence ? { paperVsReal: divergence } : {}) }, null, 2)}\n`);
|
|
43
55
|
return broken.length ? 1 : 0;
|
|
44
56
|
}
|
|
45
57
|
if (!audits.length) {
|
|
@@ -53,5 +65,10 @@ export async function auditCmd(argv, io) {
|
|
|
53
65
|
io.stdout.write(broken.length ? `${broken.length} broken ledger${broken.length === 1 ? '' : 's'}: the lines from that event on cannot be trusted\n` : 'every chain verifies\n');
|
|
54
66
|
else
|
|
55
67
|
io.stdout.write('add --verify to recompute the hash chains\n');
|
|
68
|
+
if (real)
|
|
69
|
+
io.stdout.write(`real-mode signals vs receipts · executed ${real.counts.executed} · rejected ${real.counts.rejected} · failed ${real.counts.failed} · unconfirmed ${real.unconfirmed}${real.sizeDrift ? ` · size drift on ${real.sizeDrift}` : ''}${real.orphanReceipts ? ` · ${real.orphanReceipts} orphan receipt(s)` : ''} (dexterm autotrade receipt list)\n`);
|
|
70
|
+
if (divergence?.receipted)
|
|
71
|
+
for (const l of divergenceLines(divergence))
|
|
72
|
+
io.stdout.write(`${l}\n`);
|
|
56
73
|
return broken.length ? 1 : 0;
|
|
57
74
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// `dexterm autotrade backtest|sweep|extract` (task-1503 AT-1, AUTOTRADE-SPEC §3/§3a). PAPER, recorded data only:
|
|
2
2
|
// the recorder DB is opened read-only, fills are modelled, no network, no model, no keys. Mechanical entries
|
|
3
3
|
// (`auto`): the agent's judgement is what the runner card measures, so a backtest never spawns a session.
|
|
4
|
+
import { dextermHome } from '../../../custom/src/index.js';
|
|
4
5
|
import { defaultDbPath } from '../../../data/src/index.js';
|
|
5
6
|
import { openHistoryReadOnly } from '../../../recorder/src/index.js';
|
|
6
|
-
import { backtest, everyCandidate, fmtIssue, loadTape, renderRecall, renderReport, renderSweep, renderYoloReplay, builtinTapePath, runnerRecall, saveTape, sweep, tapeFromDb, tapeFromDoc, yoloReplay, } from '../../../autotrade/src/index.js';
|
|
7
|
+
import { backtest, backtestEvidence, everyCandidate, recordBacktestEvidence, renderHeldOut, fmtIssue, loadTape, renderRecall, renderReport, renderSweep, renderYoloReplay, builtinTapePath, runnerRecall, saveTape, sweep, tapeFromDb, tapeFromDoc, yoloReplay, } from '../../../autotrade/src/index.js';
|
|
7
8
|
import { parseArgs, resolveStrategyArg, storeFor } from "./strategies.js";
|
|
8
|
-
const BOOL = ['json', 'every-candidate'];
|
|
9
|
+
const BOOL = ['json', 'every-candidate', 'no-held-out'];
|
|
9
10
|
function strategyFrom(pos, opts, io, usage, anyMode = false) {
|
|
10
11
|
const arg = typeof opts.strategy === 'string' ? opts.strategy : pos[0];
|
|
11
12
|
const r = resolveStrategyArg(arg, io);
|
|
@@ -99,7 +100,8 @@ export function tapeFrom(opts, io) {
|
|
|
99
100
|
}
|
|
100
101
|
export async function backtestCmd(argv, io) {
|
|
101
102
|
const { pos, opts } = parseArgs(argv, BOOL);
|
|
102
|
-
const usage = 'dexterm autotrade backtest <id|file> [--tape FILE | --db PATH] [--days N | --since T] [--until T] [--every-candidate [--unit-usd 20]] [--recall 5,10] [--json]';
|
|
103
|
+
const usage = 'dexterm autotrade backtest <id|file> [--tape FILE | --db PATH] [--days N | --since T] [--until T] [--every-candidate [--unit-usd 20]] [--recall 5,10] [--no-held-out] [--json]';
|
|
104
|
+
const arg = typeof opts.strategy === 'string' ? opts.strategy : pos[0];
|
|
103
105
|
const s0 = strategyFrom(pos, opts, io, usage);
|
|
104
106
|
if (!s0)
|
|
105
107
|
return 2;
|
|
@@ -116,11 +118,26 @@ export async function backtestCmd(argv, io) {
|
|
|
116
118
|
}
|
|
117
119
|
catch { /* the line is a convenience for `list`; never fail a backtest on it */ }
|
|
118
120
|
}
|
|
121
|
+
// task-1647: a stored strategy's backtest is journey evidence: the same rules on each half of the held-out split.
|
|
122
|
+
let evidence = null;
|
|
123
|
+
const stored = !!arg && !opts['every-candidate'] && !opts['no-held-out'] && storeFor(io).get(arg)?.ok === true && arg === s.id;
|
|
124
|
+
if (stored) {
|
|
125
|
+
const days = numOpt(opts, 'days', 0);
|
|
126
|
+
evidence = (await backtestEvidence(s, tape, { by: 'user', days: days > 0 ? days : null, full: { report, run } })).evidence;
|
|
127
|
+
try {
|
|
128
|
+
recordBacktestEvidence(dextermHome(io.env), evidence);
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
io.stderr.write(`warning: the journey record was not written (${e.message})\n`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
119
134
|
if (opts.json) {
|
|
120
|
-
io.stdout.write(JSON.stringify(recall ? { ...
|
|
135
|
+
io.stdout.write(JSON.stringify({ ...report, ...(recall ? { recall } : {}), ...(evidence ? { heldOut: evidence.heldOut, evidence: { verdict: evidence.verdict, claim: evidence.claim, rulesHash: evidence.rulesHash } } : {}) }, null, 2) + '\n');
|
|
121
136
|
return 0;
|
|
122
137
|
}
|
|
123
138
|
io.stdout.write(renderReport(report) + '\n');
|
|
139
|
+
if (evidence)
|
|
140
|
+
io.stdout.write(renderHeldOut(evidence).join('\n') + `\n journey: recorded as the backtest of ${s.id} v${s.version} (dexterm autotrade journey ${s.id})\n`);
|
|
124
141
|
if (opts['every-candidate'])
|
|
125
142
|
io.stdout.write(` every-candidate copy of ${s0.id}: each candidate entered at $${numOpt(opts, 'unit-usd', 20)} with the book caps lifted (an evaluation, not a strategy to run)\n`);
|
|
126
143
|
if (recall)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// `dexterm autotrade executor …` (task-1654, 0.2.0 card E): is YOUR executor alive?
|
|
2
|
+
//
|
|
3
|
+
// status the last heartbeat, the last receipt, the receipt lag, and whether a real run would pause new buys now
|
|
4
|
+
// heartbeat append one heartbeat (for executors that prefer to shell out; the kit appends or POSTs its own)
|
|
5
|
+
// kit copy the executor reference kit (webhook / signal-cmd / JSONL tail, all dry run) to a folder of yours
|
|
6
|
+
//
|
|
7
|
+
// A heartbeat is data: it can only keep new buy signals from pausing. It cannot raise a limit, lift the kill switch,
|
|
8
|
+
// reset the breaker or confirm a position (receipts do that, and only as data).
|
|
9
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
|
|
10
|
+
import { join, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { dextermHome } from '../../../custom/src/index.js';
|
|
13
|
+
import { Outbox, appendHeartbeat, effectiveLimits, executorHealth, healthLine, heartbeatPath, loadLimits, readReceipts, ACCOUNT_ID_RE } from '../../../autotrade/src/index.js';
|
|
14
|
+
import { parseArgs } from "./strategies.js";
|
|
15
|
+
/** The kit inside the app (shipped in the npm package too: build-dist copies examples/). */
|
|
16
|
+
export const KIT_DIR = fileURLToPath(new URL('../../../autotrade/examples/executor/', import.meta.url));
|
|
17
|
+
const out = (io, s) => { io.stdout.write(`${s}\n`); };
|
|
18
|
+
const err = (io, s) => { io.stderr.write(`dexterm autotrade executor: ${s}\n`); };
|
|
19
|
+
export async function executorCmd(argv, io, deps = {}) {
|
|
20
|
+
const home = dextermHome(io.env);
|
|
21
|
+
const now = deps.now ?? Date.now;
|
|
22
|
+
const { pos, opts } = parseArgs(argv.slice(1), ['json', 'dry-run']);
|
|
23
|
+
switch (argv[0]) {
|
|
24
|
+
case undefined:
|
|
25
|
+
case 'help':
|
|
26
|
+
out(io, 'dexterm autotrade executor status [--json] | heartbeat [--name N] [--account ID] [--dry-run] [--nonce N] [--note TEXT] | kit --out DIR');
|
|
27
|
+
return 0;
|
|
28
|
+
case 'kit': {
|
|
29
|
+
if (typeof opts.out !== 'string') {
|
|
30
|
+
err(io, 'kit --out DIR (a new or empty folder)');
|
|
31
|
+
return 2;
|
|
32
|
+
}
|
|
33
|
+
const dir = resolve(opts.out);
|
|
34
|
+
if (existsSync(dir) && readdirSync(dir).length) {
|
|
35
|
+
err(io, `${dir} is not empty`);
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
mkdirSync(dir, { recursive: true });
|
|
39
|
+
const files = readdirSync(KIT_DIR).filter((f) => /\.(mjs|md)$/.test(f));
|
|
40
|
+
for (const f of files)
|
|
41
|
+
copyFileSync(join(KIT_DIR, f), join(dir, f));
|
|
42
|
+
out(io, `executor reference kit (DRY RUN: it logs what it would do; no wallet code) copied to ${dir}: ${files.join(', ')}`);
|
|
43
|
+
out(io, ` start one: node ${join(dir, 'webhook-executor.mjs')} --account main`);
|
|
44
|
+
out(io, ' wire it: dexterm autotrade signal channels --webhook http://127.0.0.1:8787/signal');
|
|
45
|
+
out(io, ' prove it: dexterm autotrade signal test (then dexterm autotrade executor status)');
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
case 'status': {
|
|
49
|
+
const l = loadLimits(home);
|
|
50
|
+
const e = effectiveLimits(l.ok ? l.doc : null, { strategyId: '-', accountId: '-', mode: 'real' });
|
|
51
|
+
const ob = Outbox.open(home, { channels: [], noLock: true });
|
|
52
|
+
const h = executorHealth(home, { now: now(), windowMin: e.values.executorSilenceMin ?? null, signal: (n) => ob.get(n), receipts: readReceipts(home).receipts });
|
|
53
|
+
if (opts.json) {
|
|
54
|
+
out(io, JSON.stringify(h, null, 2));
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
out(io, `executor ${healthLine(h)}`);
|
|
58
|
+
out(io, ` heartbeats: ${heartbeatPath(home)} (append, POST to the run's listener, or dexterm autotrade executor heartbeat)`);
|
|
59
|
+
if (h.alive === false)
|
|
60
|
+
out(io, ' a real run would PAUSE new buy signals now (exits still go out); start your executor, then dexterm autotrade signal test');
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
case 'heartbeat': {
|
|
64
|
+
if (pos.length) {
|
|
65
|
+
err(io, `unexpected ${pos.join(' ')}`);
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
68
|
+
const accountId = typeof opts.account === 'string' ? opts.account : null;
|
|
69
|
+
if (accountId && !ACCOUNT_ID_RE.test(accountId)) {
|
|
70
|
+
err(io, '--account: lowercase letters, digits, - and _');
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
73
|
+
const nonce = opts.nonce === undefined ? null : Number(opts.nonce);
|
|
74
|
+
try {
|
|
75
|
+
const hb = appendHeartbeat(home, { at: now(), executor: typeof opts.name === 'string' ? opts.name : 'cli', accountId, dryRun: opts['dry-run'] === true ? true : null, lastNonce: nonce, note: typeof opts.note === 'string' ? opts.note : null });
|
|
76
|
+
if (opts.json)
|
|
77
|
+
out(io, JSON.stringify(hb));
|
|
78
|
+
else
|
|
79
|
+
out(io, `heartbeat from ${hb.executor} recorded`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
err(io, e.message);
|
|
84
|
+
return 2;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
default:
|
|
88
|
+
err(io, `unknown subcommand ${JSON.stringify(argv[0])}`);
|
|
89
|
+
return 2;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -25,6 +25,7 @@ import { appendFileSync } from 'node:fs';
|
|
|
25
25
|
import { join } from 'node:path';
|
|
26
26
|
import { dextermHome } from '../../../custom/src/index.js';
|
|
27
27
|
import { KillSwitch, createDataLayer, defaultDbPath, resolveRpcUrl } from '../../../data/src/index.js';
|
|
28
|
+
import { DrainPoller, RugProbe, chainVets, defaultFetchJson, rugproofVet } from '../../../autotrade/src/index.js';
|
|
28
29
|
import { FleetGuard, HEARTBEAT_MS, accountTable, LiveSource, RUNNER_RENDEZVOUS_SCHEMA, RUN_SUMMARY_SCHEMA, RunnerSafety, acquireRunLock, bankrollUsd, claudeSessionFactory, clearControl, clearMemberControls, clearStaleControl, compareRows, compareTable, controlPath, fileLedger, fleetStatusPath, fleetTotals, loadFleetConfig, loadRunnerConfig, loadTape, memberStrategy, newRunId, prepareAccount, releaseRunLock, renderDossierRecord, runFleet, runFiles, runnerRendezvousPath, runnerSystemPrompt, sharedDossier, stubSession, writeJsonAtomic, writeRendezvous, } from '../../../autotrade/src/index.js';
|
|
29
30
|
import { parseArgs, resolveStrategyArg } from "./strategies.js";
|
|
30
31
|
import { larpVet } from "./run.js";
|
|
@@ -161,7 +162,7 @@ export async function fleetRunCmd(argv, io, over = {}) {
|
|
|
161
162
|
continue;
|
|
162
163
|
}
|
|
163
164
|
if (r.strategy.mode !== 'paper') {
|
|
164
|
-
problems.push(`${m.id}: ${r.strategy.id} is mode "${r.strategy.mode}"; the runner refuses anything but paper`);
|
|
165
|
+
problems.push(`${m.id}: ${r.strategy.id} is mode "${r.strategy.mode}"; the runner refuses anything but paper${r.strategy.mode === 'real' ? ` in a parallel run (real mode runs one strategy on its own: dexterm autotrade run ${r.strategy.id} --real)` : ''}`);
|
|
165
166
|
continue;
|
|
166
167
|
}
|
|
167
168
|
const acct = m.account ? accountOf.get(m.account) : undefined;
|
|
@@ -174,7 +175,8 @@ export async function fleetRunCmd(argv, io, over = {}) {
|
|
|
174
175
|
}
|
|
175
176
|
const { strategy, basedOn } = memberStrategy(r.strategy, m, b.usd);
|
|
176
177
|
const sol = acct ? (acct.sol !== undefined ? { sol: acct.sol, solUsd: acct.solUsd } : {}) : m.bankroll && 'sol' in m.bankroll ? { sol: m.bankroll.sol, solUsd: solUsd } : {};
|
|
177
|
-
|
|
178
|
+
const adopt = over.adopt?.(m.id);
|
|
179
|
+
members.push({ config: m, strategy, basedOn, bankroll: { usd: strategy.bankroll.startUsd, ...sol }, ...(adopt?.length ? { adopt } : {}) });
|
|
178
180
|
}
|
|
179
181
|
if (problems.length) {
|
|
180
182
|
io.stderr.write(`dexterm autotrade run --all: nothing started:\n${problems.map((p) => ` ${p}`).join('\n')}\n`);
|
|
@@ -199,6 +201,7 @@ export async function fleetRunCmd(argv, io, over = {}) {
|
|
|
199
201
|
clearMemberControls(home, fleetId);
|
|
200
202
|
const { ledger: fleetLedger, files: fleetFiles } = fileLedger(home, fleetId);
|
|
201
203
|
const startedAt = Date.now();
|
|
204
|
+
over.onStarted?.(fleetId, fleetLedger);
|
|
202
205
|
const guard = new FleetGuard(fleetCfg.global, runnerCfg, { loadavg: over.loadavg });
|
|
203
206
|
const rawPath = opts.raw ? join(home, 'autotrade', 'runs', `${fleetId}.stream.jsonl`) : null;
|
|
204
207
|
const sessions = over.sessions ?? (opts.stub
|
|
@@ -216,6 +219,7 @@ export async function fleetRunCmd(argv, io, over = {}) {
|
|
|
216
219
|
let source;
|
|
217
220
|
let dossier;
|
|
218
221
|
let vet;
|
|
222
|
+
let drain;
|
|
219
223
|
if (data) {
|
|
220
224
|
live = new LiveSource({ store: data.store, dexscreener: data.dexscreener }, { onLog: (l) => fleetLedger.add(Date.now(), 'source_error', { error: l.slice(0, 300) }) });
|
|
221
225
|
const ls = live;
|
|
@@ -233,7 +237,12 @@ export async function fleetRunCmd(argv, io, over = {}) {
|
|
|
233
237
|
});
|
|
234
238
|
dossier = () => async (k, h, matched, b) => renderDossierRecord(await shared.get(k.mint), { mint: k.mint, symbol: k.symbol, matched }, k, h, b);
|
|
235
239
|
// A member's LARP gate (entry.safety.larp, the yolo preset's) on live data, as `autotrade run` has it.
|
|
236
|
-
|
|
240
|
+
const larpVets = fleetLarpVet((mint) => mcp.call('larp_check', { mint }));
|
|
241
|
+
// task-1748: a member's rug-proof gates (entry.safety.rugproof) run first, then its LARP gate; one probe (and its
|
|
242
|
+
// caches) for the whole fleet. Read-only: DexScreener, the user's RPC (or the public one), Jupiter quotes.
|
|
243
|
+
const probe = new RugProbe({ fetchJson: defaultFetchJson(), rpcUrl: resolveRpcUrl({ env: io.env }).url });
|
|
244
|
+
vet = (mm) => chainVets(mm.strategy.entry.safety.rugproof ? rugproofVet(mm.strategy.entry.safety.rugproof, { probe }) : undefined, larpVets(mm));
|
|
245
|
+
drain = (mm) => { const g = mm.strategy.entry.safety.rugproof?.drainGuard; return g ? new DrainPoller(g, probe, (rec) => fleetLedger.add(Date.now(), 'drain_probe', { member: mm.strategy.id, ...rec })) : undefined; };
|
|
237
246
|
}
|
|
238
247
|
// Heartbeat: the aggregate rendezvous, the fleet status, every summary.
|
|
239
248
|
const states = new Map();
|
|
@@ -300,11 +309,13 @@ export async function fleetRunCmd(argv, io, over = {}) {
|
|
|
300
309
|
const res = await runFleet({
|
|
301
310
|
fleetId, home, members, tape, source, runner: runnerCfg, guard, fleetLedger, fleetLedgerPath: fleetFiles.ledger, ...(accounts.length ? { accounts } : {}),
|
|
302
311
|
pacing: replay ? { mode: 'paced', msPerTick: num(opts['ms-per-tick'], 2000) } : { mode: 'live' },
|
|
303
|
-
sessions, dossier, vet, newRunId: (id) => newRunId(id), verbose: true, onLine: say,
|
|
312
|
+
sessions, dossier, vet: over.vetWrap ? (m) => over.vetWrap(m, vet?.(m)) : vet, drain, newRunId: (id) => newRunId(id), verbose: true, onLine: say,
|
|
304
313
|
globalSafety: () => {
|
|
305
314
|
if (Date.now() >= stopAt)
|
|
306
315
|
stop('max-minutes');
|
|
307
|
-
const
|
|
316
|
+
const s0 = safety.step();
|
|
317
|
+
const extra = over.extraSafety?.().pauseEntries ?? null;
|
|
318
|
+
const v = extra && !s0.pauseEntries ? { ...s0, pauseEntries: extra } : s0;
|
|
308
319
|
return stopping && !v.halt ? { ...v, halt: 'runner-stopped', detail: 'a signal or --max-minutes', endWhenFlat: true } : v;
|
|
309
320
|
},
|
|
310
321
|
hooks: {
|