jcc_hyper_tool 0.1.4 → 0.1.6
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/README.md +159 -21
- package/dist/advisor/actionRunner.d.ts +35 -0
- package/dist/advisor/actionRunner.d.ts.map +1 -0
- package/dist/advisor/actionRunner.js +194 -0
- package/dist/advisor/actionRunner.js.map +1 -0
- package/dist/advisor/actionSchema.d.ts +1551 -0
- package/dist/advisor/actionSchema.d.ts.map +1 -0
- package/dist/advisor/actionSchema.js +202 -0
- package/dist/advisor/actionSchema.js.map +1 -0
- package/dist/advisor/adviseDisplay.d.ts +26 -0
- package/dist/advisor/adviseDisplay.d.ts.map +1 -0
- package/dist/advisor/adviseDisplay.js +174 -0
- package/dist/advisor/adviseDisplay.js.map +1 -0
- package/dist/advisor/adviseEngine.d.ts +53 -0
- package/dist/advisor/adviseEngine.d.ts.map +1 -0
- package/dist/advisor/adviseEngine.js +172 -0
- package/dist/advisor/adviseEngine.js.map +1 -0
- package/dist/advisor/ambushPolicy.d.ts +47 -0
- package/dist/advisor/ambushPolicy.d.ts.map +1 -0
- package/dist/advisor/ambushPolicy.js +161 -0
- package/dist/advisor/ambushPolicy.js.map +1 -0
- package/dist/advisor/contextBuilder.d.ts +51 -0
- package/dist/advisor/contextBuilder.d.ts.map +1 -0
- package/dist/advisor/contextBuilder.js +153 -0
- package/dist/advisor/contextBuilder.js.map +1 -0
- package/dist/advisor/deepseekClient.d.ts +26 -0
- package/dist/advisor/deepseekClient.d.ts.map +1 -0
- package/dist/advisor/deepseekClient.js +96 -0
- package/dist/advisor/deepseekClient.js.map +1 -0
- package/dist/advisor/executionPolicy.d.ts +92 -0
- package/dist/advisor/executionPolicy.d.ts.map +1 -0
- package/dist/advisor/executionPolicy.js +273 -0
- package/dist/advisor/executionPolicy.js.map +1 -0
- package/dist/advisor/guards.d.ts +31 -0
- package/dist/advisor/guards.d.ts.map +1 -0
- package/dist/advisor/guards.js +198 -0
- package/dist/advisor/guards.js.map +1 -0
- package/dist/advisor/indicatorCompute.d.ts +45 -0
- package/dist/advisor/indicatorCompute.d.ts.map +1 -0
- package/dist/advisor/indicatorCompute.js +259 -0
- package/dist/advisor/indicatorCompute.js.map +1 -0
- package/dist/advisor/positionPlan.d.ts +33 -0
- package/dist/advisor/positionPlan.d.ts.map +1 -0
- package/dist/advisor/positionPlan.js +118 -0
- package/dist/advisor/positionPlan.js.map +1 -0
- package/dist/advisor/promptTemplate.d.ts +11 -0
- package/dist/advisor/promptTemplate.d.ts.map +1 -0
- package/dist/advisor/promptTemplate.js +230 -0
- package/dist/advisor/promptTemplate.js.map +1 -0
- package/dist/advisor/sessionLog.d.ts +41 -0
- package/dist/advisor/sessionLog.d.ts.map +1 -0
- package/dist/advisor/sessionLog.js +7 -0
- package/dist/advisor/sessionLog.js.map +1 -0
- package/dist/advisor/touchProbability.d.ts +54 -0
- package/dist/advisor/touchProbability.d.ts.map +1 -0
- package/dist/advisor/touchProbability.js +215 -0
- package/dist/advisor/touchProbability.js.map +1 -0
- package/dist/advisor/tpslPolicy.d.ts +22 -0
- package/dist/advisor/tpslPolicy.d.ts.map +1 -0
- package/dist/advisor/tpslPolicy.js +120 -0
- package/dist/advisor/tpslPolicy.js.map +1 -0
- package/dist/cli/advisorCli.d.ts +3 -0
- package/dist/cli/advisorCli.d.ts.map +1 -0
- package/dist/cli/advisorCli.js +706 -0
- package/dist/cli/advisorCli.js.map +1 -0
- package/dist/cli/index.js +6 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/riskCli.d.ts +3 -0
- package/dist/cli/riskCli.d.ts.map +1 -0
- package/dist/cli/riskCli.js +120 -0
- package/dist/cli/riskCli.js.map +1 -0
- package/dist/cli/strategyCli.d.ts.map +1 -1
- package/dist/cli/strategyCli.js +4 -2
- package/dist/cli/strategyCli.js.map +1 -1
- package/dist/cli/tradingCli.d.ts.map +1 -1
- package/dist/cli/tradingCli.js +34 -2
- package/dist/cli/tradingCli.js.map +1 -1
- package/dist/core/cliBin.d.ts +8 -0
- package/dist/core/cliBin.d.ts.map +1 -0
- package/dist/core/cliBin.js +15 -0
- package/dist/core/cliBin.js.map +1 -0
- package/dist/core/errorFormat.d.ts +7 -0
- package/dist/core/errorFormat.d.ts.map +1 -0
- package/dist/core/errorFormat.js +59 -0
- package/dist/core/errorFormat.js.map +1 -0
- package/dist/core/errors.d.ts +2 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +3 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/hyperliquid/asset.d.ts +2 -0
- package/dist/core/hyperliquid/asset.d.ts.map +1 -1
- package/dist/core/hyperliquid/asset.js +25 -0
- package/dist/core/hyperliquid/asset.js.map +1 -1
- package/dist/core/hyperliquid/client.d.ts +3 -0
- package/dist/core/hyperliquid/client.d.ts.map +1 -1
- package/dist/core/hyperliquid/client.js +31 -21
- package/dist/core/hyperliquid/client.js.map +1 -1
- package/dist/core/hyperliquid/exchange.d.ts +3 -0
- package/dist/core/hyperliquid/exchange.d.ts.map +1 -1
- package/dist/core/hyperliquid/exchange.js +31 -21
- package/dist/core/hyperliquid/exchange.js.map +1 -1
- package/dist/core/hyperliquid/exchangeParse.d.ts +3 -0
- package/dist/core/hyperliquid/exchangeParse.d.ts.map +1 -1
- package/dist/core/hyperliquid/exchangeParse.js +62 -0
- package/dist/core/hyperliquid/exchangeParse.js.map +1 -1
- package/dist/core/hyperliquid/httpRequest.d.ts +36 -0
- package/dist/core/hyperliquid/httpRequest.d.ts.map +1 -0
- package/dist/core/hyperliquid/httpRequest.js +171 -0
- package/dist/core/hyperliquid/httpRequest.js.map +1 -0
- package/dist/core/hyperliquid/orders.d.ts +5 -0
- package/dist/core/hyperliquid/orders.d.ts.map +1 -1
- package/dist/core/hyperliquid/orders.js +15 -5
- package/dist/core/hyperliquid/orders.js.map +1 -1
- package/dist/core/hyperliquid/rounding.d.ts +23 -0
- package/dist/core/hyperliquid/rounding.d.ts.map +1 -0
- package/dist/core/hyperliquid/rounding.js +53 -0
- package/dist/core/hyperliquid/rounding.js.map +1 -0
- package/dist/core/risk/isolatedTopUp.d.ts +112 -0
- package/dist/core/risk/isolatedTopUp.d.ts.map +1 -0
- package/dist/core/risk/isolatedTopUp.js +209 -0
- package/dist/core/risk/isolatedTopUp.js.map +1 -0
- package/dist/core/risk/isolatedTopUpHistory.d.ts +7 -0
- package/dist/core/risk/isolatedTopUpHistory.d.ts.map +1 -0
- package/dist/core/risk/isolatedTopUpHistory.js +26 -0
- package/dist/core/risk/isolatedTopUpHistory.js.map +1 -0
- package/dist/core/risk/isolatedTopUpParse.d.ts +16 -0
- package/dist/core/risk/isolatedTopUpParse.d.ts.map +1 -0
- package/dist/core/risk/isolatedTopUpParse.js +78 -0
- package/dist/core/risk/isolatedTopUpParse.js.map +1 -0
- package/dist/core/risk/runIsolatedTopUpReconcile.d.ts +28 -0
- package/dist/core/risk/runIsolatedTopUpReconcile.d.ts.map +1 -0
- package/dist/core/risk/runIsolatedTopUpReconcile.js +184 -0
- package/dist/core/risk/runIsolatedTopUpReconcile.js.map +1 -0
- package/dist/core/state/gcTradeState.js +1 -1
- package/dist/core/state/gcTradeState.js.map +1 -1
- package/dist/core/state/gridReconcile.d.ts +2 -0
- package/dist/core/state/gridReconcile.d.ts.map +1 -1
- package/dist/core/state/gridReconcile.js +42 -2
- package/dist/core/state/gridReconcile.js.map +1 -1
- package/dist/core/state/intentFactory.d.ts +1 -0
- package/dist/core/state/intentFactory.d.ts.map +1 -1
- package/dist/core/state/intentFactory.js +1 -0
- package/dist/core/state/intentFactory.js.map +1 -1
- package/dist/core/state/profileStore.d.ts +6 -0
- package/dist/core/state/profileStore.d.ts.map +1 -0
- package/dist/core/state/profileStore.js +26 -0
- package/dist/core/state/profileStore.js.map +1 -0
- package/dist/core/state/runReconcile.d.ts.map +1 -1
- package/dist/core/state/runReconcile.js +92 -30
- package/dist/core/state/runReconcile.js.map +1 -1
- package/dist/core/state/schema.d.ts +565 -124
- package/dist/core/state/schema.d.ts.map +1 -1
- package/dist/core/state/schema.js +62 -0
- package/dist/core/state/schema.js.map +1 -1
- package/dist/core/state/snapshot.d.ts +13 -0
- package/dist/core/state/snapshot.d.ts.map +1 -1
- package/dist/core/state/snapshot.js +36 -0
- package/dist/core/state/snapshot.js.map +1 -1
- package/dist/core/state/statePaths.d.ts +1 -0
- package/dist/core/state/statePaths.d.ts.map +1 -1
- package/dist/core/state/statePaths.js +4 -0
- package/dist/core/state/statePaths.js.map +1 -1
- package/dist/core/state/store.d.ts +21 -3
- package/dist/core/state/store.d.ts.map +1 -1
- package/dist/core/state/store.js +123 -14
- package/dist/core/state/store.js.map +1 -1
- package/dist/core/state/types.d.ts +13 -0
- package/dist/core/state/types.d.ts.map +1 -1
- package/dist/daemon/index.js +50 -5
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/validateChild.d.ts +13 -0
- package/dist/daemon/validateChild.d.ts.map +1 -0
- package/dist/daemon/validateChild.js +77 -0
- package/dist/daemon/validateChild.js.map +1 -0
- package/dist/strategies/grid.d.ts +1 -0
- package/dist/strategies/grid.d.ts.map +1 -1
- package/dist/strategies/grid.js +1 -0
- package/dist/strategies/grid.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import * as readline from "node:readline/promises";
|
|
5
|
+
import { DEFAULT_DEEPSEEK_MODEL } from "../advisor/deepseekClient.js";
|
|
6
|
+
import { executeAdvisorAction, runAdvisorModelTurnWithRepair } from "../advisor/adviseEngine.js";
|
|
7
|
+
import { advisorActionToCliArgs, buildGlobalPrefix, formatCliInvocation, } from "../advisor/actionRunner.js";
|
|
8
|
+
import { buildAdvisorMarketSnapshot } from "../advisor/contextBuilder.js";
|
|
9
|
+
import { formatAdvisorExecutedDisplay, formatAdvisorShowHeader, formatAdvisorTurnDisplay, UNLIMITED_REASONING_PREVIEW, } from "../advisor/adviseDisplay.js";
|
|
10
|
+
import { resolveAdvisorExecutionPolicy, resolveAdvisorSzCap, prepareAdvisorActionForExecution } from "../advisor/executionPolicy.js";
|
|
11
|
+
import { buildAdvisorSystemPrompt, formatFirstUserPayload } from "../advisor/promptTemplate.js";
|
|
12
|
+
import { listPendingAmbushIntents } from "../advisor/ambushPolicy.js";
|
|
13
|
+
import { buildActivePositionPlan } from "../advisor/positionPlan.js";
|
|
14
|
+
import { writeAdvisorSessionLog } from "../advisor/sessionLog.js";
|
|
15
|
+
import { loadEffectiveConfig, requireUserContext } from "../core/config.js";
|
|
16
|
+
import { ConfigError } from "../core/errors.js";
|
|
17
|
+
import { HyperliquidInfoClient } from "../core/hyperliquid/client.js";
|
|
18
|
+
import { clearUserProfile, mergeUserProfileDefaults, upsertUserProfileFields, } from "../core/state/profileStore.js";
|
|
19
|
+
import { UserProfileSchema } from "../core/state/schema.js";
|
|
20
|
+
import { defaultAdvisorLogDir } from "../core/state/statePaths.js";
|
|
21
|
+
import { readOrCreateTradeStateFile } from "../core/state/store.js";
|
|
22
|
+
import { requireStdinYes } from "./confirm.js";
|
|
23
|
+
import { printJson, readGlobalOptions, requireStateCliEnabled, resolveTradeStatePath, } from "./util.js";
|
|
24
|
+
function requireApiKey() {
|
|
25
|
+
const k = process.env.DEEPSEEK_API_KEY?.trim();
|
|
26
|
+
if (!k) {
|
|
27
|
+
throw new ConfigError("DEEPSEEK_API_KEY is required for `advise`.");
|
|
28
|
+
}
|
|
29
|
+
return k;
|
|
30
|
+
}
|
|
31
|
+
function parseMaxSz(raw) {
|
|
32
|
+
const n = Number(raw);
|
|
33
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
34
|
+
throw new ConfigError("--max-sz must be a positive finite number.");
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
function parseOptionalConfidence(raw, flag) {
|
|
39
|
+
if (raw === undefined || raw.trim() === "") {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const n = Number(raw);
|
|
43
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
44
|
+
throw new ConfigError(`${flag} must be between 0 and 1.`);
|
|
45
|
+
}
|
|
46
|
+
return n;
|
|
47
|
+
}
|
|
48
|
+
function parseOptionalNonNegative(raw, flag) {
|
|
49
|
+
if (raw === undefined || raw.trim() === "") {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const n = Number(raw);
|
|
53
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
54
|
+
throw new ConfigError(`${flag} must be a non-negative finite number.`);
|
|
55
|
+
}
|
|
56
|
+
return n;
|
|
57
|
+
}
|
|
58
|
+
function parseOptionalUnitInterval(raw, flag) {
|
|
59
|
+
if (raw === undefined || raw.trim() === "") {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const n = Number(raw);
|
|
63
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
64
|
+
throw new ConfigError(`${flag} must be between 0 and 1.`);
|
|
65
|
+
}
|
|
66
|
+
return n;
|
|
67
|
+
}
|
|
68
|
+
async function persistAndPrintSession(logPath, session) {
|
|
69
|
+
await writeAdvisorSessionLog(logPath, session);
|
|
70
|
+
}
|
|
71
|
+
function globalArgs(globals) {
|
|
72
|
+
return {
|
|
73
|
+
network: globals.network,
|
|
74
|
+
apiUrl: globals.apiUrl,
|
|
75
|
+
stateFile: globals.stateFile,
|
|
76
|
+
noState: globals.noState,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function readLatestAdvisorMid(logDir, coin) {
|
|
80
|
+
try {
|
|
81
|
+
const entries = await readdir(logDir).catch(() => []);
|
|
82
|
+
const jsonFiles = entries
|
|
83
|
+
.filter((e) => e.endsWith(".json"))
|
|
84
|
+
.map((e) => path.join(logDir, e))
|
|
85
|
+
.sort()
|
|
86
|
+
.reverse();
|
|
87
|
+
for (const file of jsonFiles.slice(0, 20)) {
|
|
88
|
+
const raw = await readFile(file, "utf-8");
|
|
89
|
+
const session = JSON.parse(raw);
|
|
90
|
+
if (session.coin !== coin)
|
|
91
|
+
continue;
|
|
92
|
+
const snap = session.marketSnapshot;
|
|
93
|
+
if (!snap)
|
|
94
|
+
continue;
|
|
95
|
+
const mid = snap.mid;
|
|
96
|
+
if (typeof mid === "number" && Number.isFinite(mid) && mid > 0)
|
|
97
|
+
return mid;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// ignore — no previous sessions
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function collectPair(value, prev) {
|
|
106
|
+
return [...prev, value];
|
|
107
|
+
}
|
|
108
|
+
function requireRecordedAdvisorStatePath(cmd, explicit) {
|
|
109
|
+
requireStateCliEnabled(cmd, "`advisor profile` / `advise profile`");
|
|
110
|
+
const p = resolveTradeStatePath(cmd, explicit);
|
|
111
|
+
if (!p) {
|
|
112
|
+
throw new ConfigError("Unexpected: advisor profile needs a trade-state path.");
|
|
113
|
+
}
|
|
114
|
+
return p;
|
|
115
|
+
}
|
|
116
|
+
/** `profile show|set|clear` — mounted under both `advisor` and `advise`. */
|
|
117
|
+
function attachAdvisorProfileCommands(parent) {
|
|
118
|
+
const profile = parent
|
|
119
|
+
.command("profile")
|
|
120
|
+
.description("Risk profile (merged into `advise` system prompt)");
|
|
121
|
+
profile
|
|
122
|
+
.command("show")
|
|
123
|
+
.description("Print merged userProfile JSON")
|
|
124
|
+
.option("--state-file <path>", "trade-state path")
|
|
125
|
+
.action(async (o, cmd) => {
|
|
126
|
+
readGlobalOptions(cmd);
|
|
127
|
+
const statePath = requireRecordedAdvisorStatePath(cmd, o.stateFile);
|
|
128
|
+
const f = await readOrCreateTradeStateFile(statePath, Date.now());
|
|
129
|
+
printJson({ userProfile: mergeUserProfileDefaults(f.userProfile) });
|
|
130
|
+
});
|
|
131
|
+
profile
|
|
132
|
+
.command("set")
|
|
133
|
+
.description("Update userProfile (atomic write; does not touch intents)")
|
|
134
|
+
.option("--state-file <path>", "trade-state path")
|
|
135
|
+
.option("--risk <level>", "conservative | balanced | aggressive")
|
|
136
|
+
.option("--max-sz <n>", "defaultMaxSz")
|
|
137
|
+
.option("--min-sz <n>", "defaultMinSz (minimum bracket sz floor)")
|
|
138
|
+
.option("--require-tpsl", "Force requireTpsl true on profile when passed", false)
|
|
139
|
+
.option("--no-require-tpsl", "Force requireTpsl false on profile when passed", false)
|
|
140
|
+
.option("--price-dev-pct <n>", "priceDeviationPctMax decimal fraction (e.g. 0.03)")
|
|
141
|
+
.option("--notes <text>", "tradingStyleNotes")
|
|
142
|
+
.option("--min-confidence <n>", "minConfidenceToExecute (0–1, 0=off)")
|
|
143
|
+
.option("--limit-jitter-pct <n>", "limitPriceJitterPct (e.g. 0.002)")
|
|
144
|
+
.option("--sl-entry-shift <n>", "bracketSlEntryShift (0–1, 1=entry at model SL)")
|
|
145
|
+
.option("--execute-cooldown-sec <n>", "executeCooldownSec between same coin+action (+ side for bracket)")
|
|
146
|
+
.option("--anti-rush-close-min-sec <n>", "antiRushCloseMinSec (block early close_position)")
|
|
147
|
+
.option("--protection-grace-sec <n>", "protectionGraceSec (wait for reconcile TP/SL after fill)")
|
|
148
|
+
.option("--min-close-pnl-pct <n>", "minClosePnlPct (block close on tiny PnL unless tp_unreachable)")
|
|
149
|
+
.option("--ambush-replace-dev-pct <n>", "ambushReplaceDeviationPct (default: priceDeviationPctMax)")
|
|
150
|
+
.option("--default-ambush-expires-min <n>", "defaultAmbushExpiresMin reference for model")
|
|
151
|
+
.option("--no-forbid-cancel-all-ambush", "Allow cancel_all while TTL ambush pending", false)
|
|
152
|
+
.option("--coin-max <pair>", "COIN=size max override (repeatable)", collectPair, [])
|
|
153
|
+
.option("--coin-min <pair>", "COIN=size min override (repeatable)", collectPair, [])
|
|
154
|
+
.action(async (opts, cmd) => {
|
|
155
|
+
readGlobalOptions(cmd);
|
|
156
|
+
const statePath = requireRecordedAdvisorStatePath(cmd, opts.stateFile);
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
if (opts.noRequireTpsl && opts.requireTpsl) {
|
|
159
|
+
throw new ConfigError("Use either --require-tpsl or --no-require-tpsl, not both.");
|
|
160
|
+
}
|
|
161
|
+
const patch = { updatedAtMs: now };
|
|
162
|
+
const risk = opts.risk?.trim();
|
|
163
|
+
if (risk) {
|
|
164
|
+
if (risk !== "conservative" && risk !== "balanced" && risk !== "aggressive") {
|
|
165
|
+
throw new ConfigError("--risk must be conservative | balanced | aggressive");
|
|
166
|
+
}
|
|
167
|
+
patch.risk = risk;
|
|
168
|
+
}
|
|
169
|
+
if (opts.maxSz !== undefined) {
|
|
170
|
+
const n = Number(opts.maxSz);
|
|
171
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
172
|
+
throw new ConfigError("--max-sz must be positive");
|
|
173
|
+
}
|
|
174
|
+
patch.defaultMaxSz = n;
|
|
175
|
+
}
|
|
176
|
+
if (opts.minSz !== undefined) {
|
|
177
|
+
const n = Number(opts.minSz);
|
|
178
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
179
|
+
throw new ConfigError("--min-sz must be positive");
|
|
180
|
+
}
|
|
181
|
+
patch.defaultMinSz = n;
|
|
182
|
+
}
|
|
183
|
+
if (opts.noRequireTpsl) {
|
|
184
|
+
patch.requireTpsl = false;
|
|
185
|
+
}
|
|
186
|
+
else if (opts.requireTpsl) {
|
|
187
|
+
patch.requireTpsl = true;
|
|
188
|
+
}
|
|
189
|
+
if (opts.priceDevPct !== undefined) {
|
|
190
|
+
const n = Number(opts.priceDevPct);
|
|
191
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
192
|
+
throw new ConfigError("--price-dev-pct must be positive");
|
|
193
|
+
}
|
|
194
|
+
patch.priceDeviationPctMax = n;
|
|
195
|
+
}
|
|
196
|
+
if (opts.notes !== undefined) {
|
|
197
|
+
patch.tradingStyleNotes = opts.notes;
|
|
198
|
+
}
|
|
199
|
+
if (opts.minConfidence !== undefined) {
|
|
200
|
+
patch.minConfidenceToExecute = parseOptionalConfidence(opts.minConfidence, "--min-confidence");
|
|
201
|
+
}
|
|
202
|
+
if (opts.limitJitterPct !== undefined) {
|
|
203
|
+
const n = Number(opts.limitJitterPct);
|
|
204
|
+
if (!Number.isFinite(n) || n < 0 || n > 0.05) {
|
|
205
|
+
throw new ConfigError("--limit-jitter-pct must be between 0 and 0.05");
|
|
206
|
+
}
|
|
207
|
+
patch.limitPriceJitterPct = n;
|
|
208
|
+
}
|
|
209
|
+
if (opts.slEntryShift !== undefined) {
|
|
210
|
+
patch.bracketSlEntryShift = parseOptionalUnitInterval(opts.slEntryShift, "--sl-entry-shift");
|
|
211
|
+
}
|
|
212
|
+
if (opts.executeCooldownSec !== undefined) {
|
|
213
|
+
patch.executeCooldownSec = parseOptionalNonNegative(opts.executeCooldownSec, "--execute-cooldown-sec");
|
|
214
|
+
}
|
|
215
|
+
if (opts.antiRushCloseMinSec !== undefined) {
|
|
216
|
+
patch.antiRushCloseMinSec = parseOptionalNonNegative(opts.antiRushCloseMinSec, "--anti-rush-close-min-sec");
|
|
217
|
+
}
|
|
218
|
+
if (opts.protectionGraceSec !== undefined) {
|
|
219
|
+
patch.protectionGraceSec = parseOptionalNonNegative(opts.protectionGraceSec, "--protection-grace-sec");
|
|
220
|
+
}
|
|
221
|
+
if (opts.minClosePnlPct !== undefined) {
|
|
222
|
+
const n = Number(opts.minClosePnlPct);
|
|
223
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
224
|
+
throw new ConfigError("--min-close-pnl-pct must be a non-negative number");
|
|
225
|
+
}
|
|
226
|
+
patch.minClosePnlPct = n;
|
|
227
|
+
}
|
|
228
|
+
if (opts.ambushReplaceDevPct !== undefined) {
|
|
229
|
+
const n = Number(opts.ambushReplaceDevPct);
|
|
230
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
231
|
+
throw new ConfigError("--ambush-replace-dev-pct must be positive");
|
|
232
|
+
}
|
|
233
|
+
patch.ambushReplaceDeviationPct = n;
|
|
234
|
+
}
|
|
235
|
+
if (opts.defaultAmbushExpiresMin !== undefined) {
|
|
236
|
+
const n = Number(opts.defaultAmbushExpiresMin);
|
|
237
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
238
|
+
throw new ConfigError("--default-ambush-expires-min must be positive");
|
|
239
|
+
}
|
|
240
|
+
patch.defaultAmbushExpiresMin = n;
|
|
241
|
+
}
|
|
242
|
+
if (opts.noForbidCancelAllAmbush) {
|
|
243
|
+
patch.forbidCancelAllWithActiveAmbush = false;
|
|
244
|
+
}
|
|
245
|
+
const pairs = opts.coinMax ?? [];
|
|
246
|
+
if (pairs.length > 0) {
|
|
247
|
+
const map = {
|
|
248
|
+
...(mergeUserProfileDefaults((await readOrCreateTradeStateFile(statePath, now)).userProfile).maxSzByCoin ?? {}),
|
|
249
|
+
};
|
|
250
|
+
for (const p of pairs) {
|
|
251
|
+
const [c, s] = p.split("=");
|
|
252
|
+
const coin = c?.trim();
|
|
253
|
+
const sz = Number(s?.trim());
|
|
254
|
+
if (!coin || !Number.isFinite(sz) || sz <= 0) {
|
|
255
|
+
throw new ConfigError(`Invalid --coin-max '${p}' (expect COIN=size)`);
|
|
256
|
+
}
|
|
257
|
+
map[coin] = sz;
|
|
258
|
+
}
|
|
259
|
+
patch.maxSzByCoin = map;
|
|
260
|
+
}
|
|
261
|
+
const minPairs = opts.coinMin ?? [];
|
|
262
|
+
if (minPairs.length > 0) {
|
|
263
|
+
const map = {
|
|
264
|
+
...(mergeUserProfileDefaults((await readOrCreateTradeStateFile(statePath, now)).userProfile).minSzByCoin ?? {}),
|
|
265
|
+
};
|
|
266
|
+
for (const p of minPairs) {
|
|
267
|
+
const [c, s] = p.split("=");
|
|
268
|
+
const coin = c?.trim();
|
|
269
|
+
const sz = Number(s?.trim());
|
|
270
|
+
if (!coin || !Number.isFinite(sz) || sz <= 0) {
|
|
271
|
+
throw new ConfigError(`Invalid --coin-min '${p}' (expect COIN=size)`);
|
|
272
|
+
}
|
|
273
|
+
map[coin] = sz;
|
|
274
|
+
}
|
|
275
|
+
patch.minSzByCoin = map;
|
|
276
|
+
}
|
|
277
|
+
const patchKeys = Object.keys(patch).filter((k) => k !== "updatedAtMs");
|
|
278
|
+
if (patchKeys.length === 0) {
|
|
279
|
+
throw new ConfigError("profile set: pass at least one of --risk, --max-sz, --min-sz, --notes, --price-dev-pct, --min-confidence, --limit-jitter-pct, --sl-entry-shift, --execute-cooldown-sec, --anti-rush-close-min-sec, --protection-grace-sec, --min-close-pnl-pct, --ambush-replace-dev-pct, --default-ambush-expires-min, --no-forbid-cancel-all-ambush, --coin-max, --coin-min, --require-tpsl / --no-require-tpsl");
|
|
280
|
+
}
|
|
281
|
+
const out = await upsertUserProfileFields(statePath, patch, now);
|
|
282
|
+
printJson({ ok: true, userProfile: out.userProfile });
|
|
283
|
+
});
|
|
284
|
+
profile
|
|
285
|
+
.command("clear")
|
|
286
|
+
.description("Remove userProfile from trade-state.json")
|
|
287
|
+
.option("--state-file <path>", "trade-state path")
|
|
288
|
+
.action(async (o, cmd) => {
|
|
289
|
+
readGlobalOptions(cmd);
|
|
290
|
+
const statePath = requireRecordedAdvisorStatePath(cmd, o.stateFile);
|
|
291
|
+
const cur = await readOrCreateTradeStateFile(statePath, Date.now());
|
|
292
|
+
const hadProfile = Boolean(cur.userProfile);
|
|
293
|
+
await clearUserProfile(statePath, Date.now());
|
|
294
|
+
printJson({ ok: true, hadProfile });
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
export function attachAdvisorCli(program) {
|
|
298
|
+
const advise = program
|
|
299
|
+
.command("advise")
|
|
300
|
+
.description("DeepSeek advisor and profile preferences");
|
|
301
|
+
const adviseRun = advise
|
|
302
|
+
.command("run", { isDefault: true })
|
|
303
|
+
.description("K-lines + positions → structured action (dry-run default; `--live` for real orders via spawned CLI)")
|
|
304
|
+
.requiredOption("--coin <sym>", "Perp symbol (e.g. BTC, xyz:CL)")
|
|
305
|
+
.requiredOption("--max-sz <n>", "Hard cap on size for bracket / close positions")
|
|
306
|
+
.option("--note <text>", "Note injected into the first user message")
|
|
307
|
+
.option("--once", "Single model turn then exit", false)
|
|
308
|
+
.option("--auto", "After the model turn, run the execute step immediately", false)
|
|
309
|
+
.option("--require-tpsl", "Enforce TP/SL on bracket actions (default on)", true)
|
|
310
|
+
.option("--no-require-tpsl", "Disable TP/SL enforcement for brackets")
|
|
311
|
+
.option("--bars5m <n>", "5m bars", "48")
|
|
312
|
+
.option("--bars1h <n>", "1h bars", "48")
|
|
313
|
+
.option("--bars1d <n>", "1d bars", "30")
|
|
314
|
+
.option("--context-version <v>", "Prompt context: raw_kline (v1, default) | precomputed_indicators (v2)", "raw_kline")
|
|
315
|
+
.option("--live", "Forwarded to spawned order/state commands", false)
|
|
316
|
+
.option("--assume-yes", "Forwarded: skip YES on spawned --live", false)
|
|
317
|
+
.option("--dump-kline <path>", "Dump raw K-line snapshot and account data to a JSON file for debugging")
|
|
318
|
+
.option("--dump-prompt <dir>", "Save system and user prompts to files in <dir> (system.md, user.json) for external model testing")
|
|
319
|
+
.option("--skip-if-stable <pct>", "Skip LLM call if mid changed < pct since last session (e.g. 0.002 = 0.2%)")
|
|
320
|
+
.option("--log-dir <path>", "Session log directory", "")
|
|
321
|
+
.option("--json", "Print raw JSON to stdout (default: human-readable summary)", false)
|
|
322
|
+
.option("--min-confidence <n>", "Block execute when model confidence is below this (0–1)")
|
|
323
|
+
.option("--limit-jitter-pct <n>", "Random limit-price offset fraction before execute (e.g. 0.002 = up to ±0.2%)")
|
|
324
|
+
.option("--execute-cooldown-sec <n>", "Min seconds between same coin+action executions (stored in trade-state)")
|
|
325
|
+
.option("--sl-entry-shift <n>", "Shift bracket entry toward model SL (0=off, 1=entry at SL; TP/SL move equally)")
|
|
326
|
+
.option("--anti-rush-close-min-sec <n>", "Block close_position until position age exceeds this (0=off; profile antiRushCloseMinSec)")
|
|
327
|
+
.option("--ambush-replace-dev-pct <n>", "Replace pending TTL ambush when new limit deviates more than this fraction")
|
|
328
|
+
.action(async (opts, command) => {
|
|
329
|
+
const coinRaw = String(opts.coin ?? "");
|
|
330
|
+
const globals = readGlobalOptions(command);
|
|
331
|
+
const cfg = loadEffectiveConfig({ network: globals.network, apiUrl: globals.apiUrl });
|
|
332
|
+
const user = requireUserContext(cfg);
|
|
333
|
+
const apiKey = requireApiKey();
|
|
334
|
+
const maxSz = parseMaxSz(opts.maxSz !== undefined ? String(opts.maxSz) : undefined);
|
|
335
|
+
const noState = Boolean(globals.noState);
|
|
336
|
+
const statePath = resolveTradeStatePath(command, undefined);
|
|
337
|
+
const requireTpsl = opts.noRequireTpsl === true ? false : opts.requireTpsl !== false;
|
|
338
|
+
const jsonOutput = opts.json === true;
|
|
339
|
+
const auto = opts.auto === true;
|
|
340
|
+
const minConfidence = parseOptionalConfidence(opts.minConfidence !== undefined ? String(opts.minConfidence) : undefined, "--min-confidence");
|
|
341
|
+
const limitJitterPct = parseOptionalNonNegative(opts.limitJitterPct !== undefined ? String(opts.limitJitterPct) : undefined, "--limit-jitter-pct");
|
|
342
|
+
const executeCooldownSec = parseOptionalNonNegative(opts.executeCooldownSec !== undefined ? String(opts.executeCooldownSec) : undefined, "--execute-cooldown-sec");
|
|
343
|
+
const slEntryShift = parseOptionalUnitInterval(opts.slEntryShift !== undefined ? String(opts.slEntryShift) : undefined, "--sl-entry-shift");
|
|
344
|
+
const antiRushCloseMinSec = parseOptionalNonNegative(opts.antiRushCloseMinSec !== undefined ? String(opts.antiRushCloseMinSec) : undefined, "--anti-rush-close-min-sec");
|
|
345
|
+
const ambushReplaceDevPct = parseOptionalConfidence(opts.ambushReplaceDevPct !== undefined ? String(opts.ambushReplaceDevPct) : undefined, "--ambush-replace-dev-pct");
|
|
346
|
+
const k5 = Number(opts.bars5m ?? "48");
|
|
347
|
+
const k1h = Number(opts.bars1h ?? "48");
|
|
348
|
+
const k1d = Number(opts.bars1d ?? "30");
|
|
349
|
+
for (const [label, v] of [
|
|
350
|
+
["--bars5m", k5],
|
|
351
|
+
["--bars1h", k1h],
|
|
352
|
+
["--bars1d", k1d],
|
|
353
|
+
]) {
|
|
354
|
+
if (!Number.isInteger(v) || v < 1) {
|
|
355
|
+
throw new ConfigError(`${label} must be a positive integer.`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const contextVersion = opts.contextVersion === "precomputed_indicators"
|
|
359
|
+
? "precomputed_indicators"
|
|
360
|
+
: "raw_kline";
|
|
361
|
+
let profile;
|
|
362
|
+
if (!noState && statePath) {
|
|
363
|
+
const f = await readOrCreateTradeStateFile(statePath, Date.now());
|
|
364
|
+
profile = f.userProfile ? UserProfileSchema.parse(f.userProfile) : undefined;
|
|
365
|
+
}
|
|
366
|
+
const profileSnapshot = mergeUserProfileDefaults(profile);
|
|
367
|
+
const info = new HyperliquidInfoClient({ infoOrigin: user.infoOrigin });
|
|
368
|
+
const nowMs = Date.now();
|
|
369
|
+
const snap = await buildAdvisorMarketSnapshot({
|
|
370
|
+
info,
|
|
371
|
+
coin: coinRaw,
|
|
372
|
+
network: cfg.network,
|
|
373
|
+
userAddress: user.userAddress,
|
|
374
|
+
userNote: opts.note ? String(opts.note) : undefined,
|
|
375
|
+
nowMs,
|
|
376
|
+
kline5mBars: k5,
|
|
377
|
+
kline1hBars: k1h,
|
|
378
|
+
kline1dBars: k1d,
|
|
379
|
+
contextVersion,
|
|
380
|
+
});
|
|
381
|
+
if (typeof opts.dumpKline === "string" && opts.dumpKline.trim()) {
|
|
382
|
+
const dumpPath = opts.dumpKline.trim();
|
|
383
|
+
const dump = {
|
|
384
|
+
coin: snap.coin,
|
|
385
|
+
network: snap.network,
|
|
386
|
+
now_ms: snap.now_ms,
|
|
387
|
+
mid: snap.mid,
|
|
388
|
+
positions: snap.positions,
|
|
389
|
+
kline_5m: snap.kline_5m,
|
|
390
|
+
kline_1h: snap.kline_1h,
|
|
391
|
+
kline_1d: snap.kline_1d,
|
|
392
|
+
};
|
|
393
|
+
await writeFile(dumpPath, JSON.stringify(dump, null, 2), "utf-8");
|
|
394
|
+
console.error(`[advisor] K-line dump written to ${dumpPath}`);
|
|
395
|
+
}
|
|
396
|
+
let pendingAmbush = [];
|
|
397
|
+
let activePositionPlan;
|
|
398
|
+
if (!noState && statePath) {
|
|
399
|
+
const f = await readOrCreateTradeStateFile(statePath, nowMs);
|
|
400
|
+
pendingAmbush = listPendingAmbushIntents(f, snap.coin, nowMs);
|
|
401
|
+
const posRow = snap.positions[snap.coin];
|
|
402
|
+
if (posRow?.signed_sz) {
|
|
403
|
+
activePositionPlan = buildActivePositionPlan({
|
|
404
|
+
state: f,
|
|
405
|
+
coin: snap.coin,
|
|
406
|
+
nowMs,
|
|
407
|
+
mid: snap.mid,
|
|
408
|
+
positionSignedSz: posRow.signed_sz,
|
|
409
|
+
positionEntryPx: posRow.entry_px,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const sessionId = randomUUID();
|
|
414
|
+
const logDir = typeof opts.logDir === "string" && opts.logDir.trim()
|
|
415
|
+
? opts.logDir.trim()
|
|
416
|
+
: defaultAdvisorLogDir();
|
|
417
|
+
const logPath = path.join(logDir, `${sessionId}.json`);
|
|
418
|
+
const modelLabel = process.env.DEEPSEEK_MODEL?.trim() || DEFAULT_DEEPSEEK_MODEL;
|
|
419
|
+
const session = {
|
|
420
|
+
sessionId,
|
|
421
|
+
coin: snap.coin,
|
|
422
|
+
createdAtMs: nowMs,
|
|
423
|
+
model: modelLabel,
|
|
424
|
+
userProfileSnapshot: profileSnapshot,
|
|
425
|
+
marketSnapshot: {
|
|
426
|
+
coin: snap.coin,
|
|
427
|
+
network: snap.network,
|
|
428
|
+
now_ms: snap.now_ms,
|
|
429
|
+
mid: snap.mid,
|
|
430
|
+
positions: snap.positions,
|
|
431
|
+
kline_5m: snap.kline_5m,
|
|
432
|
+
kline_1h: snap.kline_1h,
|
|
433
|
+
kline_1d: snap.kline_1d,
|
|
434
|
+
},
|
|
435
|
+
turns: [],
|
|
436
|
+
};
|
|
437
|
+
const system = buildAdvisorSystemPrompt(profile, contextVersion);
|
|
438
|
+
const firstUser = formatFirstUserPayload(snap, {
|
|
439
|
+
pendingAmbushIntents: pendingAmbush,
|
|
440
|
+
activePositionPlan: activePositionPlan ?? null,
|
|
441
|
+
});
|
|
442
|
+
session.turns.push({ role: "system", content: system });
|
|
443
|
+
session.turns.push({ role: "user", content: firstUser });
|
|
444
|
+
if (typeof opts.dumpPrompt === "string" && opts.dumpPrompt.trim()) {
|
|
445
|
+
const dir = opts.dumpPrompt.trim();
|
|
446
|
+
await import("node:fs/promises").then((fs) => fs.mkdir(dir, { recursive: true }));
|
|
447
|
+
await writeFile(path.join(dir, "system.md"), system, "utf-8");
|
|
448
|
+
await writeFile(path.join(dir, "user.json"), firstUser, "utf-8");
|
|
449
|
+
console.error(`[advisor] prompts saved to ${dir}/system.md and ${dir}/user.json`);
|
|
450
|
+
}
|
|
451
|
+
const execBase = {
|
|
452
|
+
globals: globalArgs(globals),
|
|
453
|
+
maxSz,
|
|
454
|
+
requireTpsl,
|
|
455
|
+
live: Boolean(opts.live),
|
|
456
|
+
assumeYes: Boolean(opts.assumeYes),
|
|
457
|
+
statePath,
|
|
458
|
+
noState,
|
|
459
|
+
profile,
|
|
460
|
+
auto,
|
|
461
|
+
minConfidence,
|
|
462
|
+
limitJitterPct,
|
|
463
|
+
executeCooldownSec,
|
|
464
|
+
slEntryShift,
|
|
465
|
+
antiRushCloseMinSec,
|
|
466
|
+
ambushReplaceDeviationPct: ambushReplaceDevPct,
|
|
467
|
+
};
|
|
468
|
+
// Skip LLM if mid barely moved since last session
|
|
469
|
+
const skipPct = opts.skipIfStable !== undefined ? Number(opts.skipIfStable) : NaN;
|
|
470
|
+
if (Number.isFinite(skipPct) && skipPct > 0 && snap.mid !== null) {
|
|
471
|
+
const lastMid = await readLatestAdvisorMid(logDir, snap.coin);
|
|
472
|
+
if (lastMid !== null && lastMid > 0) {
|
|
473
|
+
const change = Math.abs(snap.mid - lastMid) / lastMid;
|
|
474
|
+
if (change <= skipPct) {
|
|
475
|
+
process.stderr.write(`advise: mid ${snap.mid} vs last ${lastMid} changed ${(change * 100).toFixed(3)}% ≤ ${(skipPct * 100).toFixed(1)}%, skipping.\n`);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
process.stderr.write("正在请求模型(可能需要 10–60 秒)…\n");
|
|
481
|
+
const { messages, response, actions, lastAssistant } = await runAdvisorModelTurnWithRepair({
|
|
482
|
+
apiKey,
|
|
483
|
+
messages: [
|
|
484
|
+
{ role: "system", content: system },
|
|
485
|
+
{ role: "user", content: firstUser },
|
|
486
|
+
],
|
|
487
|
+
});
|
|
488
|
+
session.turns.push({
|
|
489
|
+
role: "assistant",
|
|
490
|
+
content: lastAssistant.content,
|
|
491
|
+
reasoning_content: lastAssistant.reasoningContent,
|
|
492
|
+
action: actions[0]?.action,
|
|
493
|
+
});
|
|
494
|
+
await persistAndPrintSession(logPath, session);
|
|
495
|
+
const printTurn = (act, strategy, reasoning, analysis) => {
|
|
496
|
+
if (jsonOutput) {
|
|
497
|
+
printJson({
|
|
498
|
+
strategy,
|
|
499
|
+
action: act,
|
|
500
|
+
analysis,
|
|
501
|
+
logPath,
|
|
502
|
+
reasoning_content: reasoning ?? null,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
process.stdout.write(formatAdvisorTurnDisplay({
|
|
507
|
+
action: act,
|
|
508
|
+
coin: `${snap.coin} [${strategy}]`,
|
|
509
|
+
mid: snap.mid,
|
|
510
|
+
logPath,
|
|
511
|
+
reasoningContent: reasoning,
|
|
512
|
+
reasoningPreviewMax: UNLIMITED_REASONING_PREVIEW,
|
|
513
|
+
analysis,
|
|
514
|
+
}));
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
const ana = response.analysis;
|
|
518
|
+
for (const { strategy, action } of actions) {
|
|
519
|
+
printTurn(action, strategy, lastAssistant.reasoningContent, ana);
|
|
520
|
+
}
|
|
521
|
+
if (actions.length === 0) {
|
|
522
|
+
// Both strategies wait — show analysis + each strategy's reason
|
|
523
|
+
const scalpReason = response.scalp?.reason ?? "";
|
|
524
|
+
const ambushReason = response.ambush?.reason ?? "";
|
|
525
|
+
printTurn({ action: "wait", reason: `scalp: ${scalpReason || "—"}\n ambush: ${ambushReason || "—"}`, confidence: 0 }, "—", lastAssistant.reasoningContent, ana);
|
|
526
|
+
}
|
|
527
|
+
const runExecute = async (act) => {
|
|
528
|
+
const r = await executeAdvisorAction({
|
|
529
|
+
action: act,
|
|
530
|
+
coin: snap.coin,
|
|
531
|
+
snapshot: snap,
|
|
532
|
+
profile,
|
|
533
|
+
maxSz,
|
|
534
|
+
requireTpsl,
|
|
535
|
+
globals: execBase.globals,
|
|
536
|
+
live: execBase.live,
|
|
537
|
+
assumeYes: execBase.assumeYes,
|
|
538
|
+
statePath,
|
|
539
|
+
noState,
|
|
540
|
+
executionPolicyCli: {
|
|
541
|
+
auto: execBase.auto,
|
|
542
|
+
minConfidence: execBase.minConfidence,
|
|
543
|
+
limitJitterPct: execBase.limitJitterPct,
|
|
544
|
+
executeCooldownSec: execBase.executeCooldownSec,
|
|
545
|
+
slEntryShift: execBase.slEntryShift,
|
|
546
|
+
antiRushCloseMinSec: execBase.antiRushCloseMinSec,
|
|
547
|
+
ambushReplaceDeviationPct: execBase.ambushReplaceDeviationPct,
|
|
548
|
+
},
|
|
549
|
+
confirmLive: (p) => requireStdinYes(p + "输入 YES 确认执行:\n"),
|
|
550
|
+
});
|
|
551
|
+
process.stdout.write(r.stdout);
|
|
552
|
+
if (r.stderr) {
|
|
553
|
+
process.stderr.write(r.stderr);
|
|
554
|
+
}
|
|
555
|
+
session.executed = {
|
|
556
|
+
command: r.command,
|
|
557
|
+
actionSpec: act,
|
|
558
|
+
exchangeResponse: r.response,
|
|
559
|
+
exitCode: r.exitCode,
|
|
560
|
+
};
|
|
561
|
+
await persistAndPrintSession(logPath, session);
|
|
562
|
+
if (jsonOutput) {
|
|
563
|
+
printJson({ executed: session.executed });
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
process.stdout.write(formatAdvisorExecutedDisplay({
|
|
567
|
+
command: r.command,
|
|
568
|
+
action: act,
|
|
569
|
+
exitCode: r.exitCode,
|
|
570
|
+
logPath,
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
if (opts.auto === true) {
|
|
575
|
+
for (const { strategy, action } of actions) {
|
|
576
|
+
process.stderr.write(`\n[${strategy}] 执行中…\n`);
|
|
577
|
+
try {
|
|
578
|
+
await runExecute(action);
|
|
579
|
+
}
|
|
580
|
+
catch (e) {
|
|
581
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
582
|
+
process.stderr.write(`advise [${strategy}]: 执行未提交(护栏拦截):\n ${msg}\n`);
|
|
583
|
+
session.executed = {
|
|
584
|
+
command: "(blocked before exchange)",
|
|
585
|
+
actionSpec: action,
|
|
586
|
+
exchangeResponse: { error: msg },
|
|
587
|
+
exitCode: 1,
|
|
588
|
+
};
|
|
589
|
+
await persistAndPrintSession(logPath, session);
|
|
590
|
+
if (jsonOutput) {
|
|
591
|
+
printJson({ executed: session.executed, guardError: msg });
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
process.stdout.write(formatAdvisorExecutedDisplay({
|
|
595
|
+
command: "(blocked before exchange)",
|
|
596
|
+
action,
|
|
597
|
+
exitCode: 1,
|
|
598
|
+
logPath,
|
|
599
|
+
}));
|
|
600
|
+
process.stdout.write(`\n拦截原因: ${msg}\n\n`);
|
|
601
|
+
}
|
|
602
|
+
process.exitCode = 1;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (opts.once === true) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
611
|
+
// In REPL mode, use the first action for backward compat
|
|
612
|
+
const primary = actions[0]?.action ?? { action: "wait", reason: "", confidence: 0 };
|
|
613
|
+
let latest = primary;
|
|
614
|
+
let msgs = messages;
|
|
615
|
+
try {
|
|
616
|
+
while (true) {
|
|
617
|
+
const line = (await rl.question("advise> (自然语言 | show 预览 | execute 执行 | quit 退出)\n")).trim();
|
|
618
|
+
if (line === "" || line === "quit" || line === "exit") {
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
if (line === "show") {
|
|
622
|
+
if (!jsonOutput) {
|
|
623
|
+
process.stderr.write(formatAdvisorShowHeader(latest, snap.coin));
|
|
624
|
+
}
|
|
625
|
+
const policy = resolveAdvisorExecutionPolicy(profile, {
|
|
626
|
+
auto: execBase.auto,
|
|
627
|
+
minConfidence: execBase.minConfidence,
|
|
628
|
+
limitJitterPct: execBase.limitJitterPct,
|
|
629
|
+
executeCooldownSec: execBase.executeCooldownSec,
|
|
630
|
+
slEntryShift: execBase.slEntryShift,
|
|
631
|
+
});
|
|
632
|
+
const szCap = resolveAdvisorSzCap({
|
|
633
|
+
coin: snap.coin,
|
|
634
|
+
maxSz,
|
|
635
|
+
profile,
|
|
636
|
+
mid: snap.mid,
|
|
637
|
+
positionSignedSz: snap.positions[snap.coin]?.signed_sz,
|
|
638
|
+
});
|
|
639
|
+
const prepared = prepareAdvisorActionForExecution(latest, {
|
|
640
|
+
slEntryShift: policy.bracketSlEntryShift,
|
|
641
|
+
limitJitterPct: policy.limitJitterPct,
|
|
642
|
+
mid: snap.mid,
|
|
643
|
+
maxDevPct: profileSnapshot.priceDeviationPctMax,
|
|
644
|
+
szDecimals: snap.sz_decimals,
|
|
645
|
+
kline1h: snap.kline_1h,
|
|
646
|
+
profile,
|
|
647
|
+
});
|
|
648
|
+
const cmd = formatCliInvocation(advisorActionToCliArgs(prepared.action, {
|
|
649
|
+
coin: snap.coin,
|
|
650
|
+
mid: snap.mid,
|
|
651
|
+
positionSignedSz: snap.positions[snap.coin]?.signed_sz,
|
|
652
|
+
globals: buildGlobalPrefix(execBase.globals),
|
|
653
|
+
live: execBase.live,
|
|
654
|
+
assumeYes: execBase.assumeYes,
|
|
655
|
+
szCap,
|
|
656
|
+
limitJitterPct: policy.limitJitterPct,
|
|
657
|
+
maxDevPct: profileSnapshot.priceDeviationPctMax,
|
|
658
|
+
szDecimals: snap.sz_decimals,
|
|
659
|
+
}));
|
|
660
|
+
process.stderr.write(`${cmd}\n`);
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (line === "execute" || line === "e") {
|
|
664
|
+
await runExecute(latest);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (line === "cancel") {
|
|
668
|
+
process.stderr.write("已清除待执行动作,可继续对话。\n");
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
msgs = [...msgs, { role: "user", content: line }];
|
|
672
|
+
session.turns.push({ role: "user", content: line });
|
|
673
|
+
process.stderr.write("正在请求模型…\n");
|
|
674
|
+
const turn = await runAdvisorModelTurnWithRepair({ apiKey, messages: msgs });
|
|
675
|
+
msgs = turn.messages;
|
|
676
|
+
const replPrimary = turn.actions[0];
|
|
677
|
+
latest = replPrimary?.action ?? { action: "wait", reason: "", confidence: 0 };
|
|
678
|
+
session.turns.push({
|
|
679
|
+
role: "assistant",
|
|
680
|
+
content: turn.lastAssistant.content,
|
|
681
|
+
reasoning_content: turn.lastAssistant.reasoningContent,
|
|
682
|
+
action: turn.actions[0]?.action,
|
|
683
|
+
});
|
|
684
|
+
await persistAndPrintSession(logPath, session);
|
|
685
|
+
const replAna = turn.response.analysis;
|
|
686
|
+
for (const { strategy, action } of turn.actions) {
|
|
687
|
+
printTurn(action, strategy, turn.lastAssistant.reasoningContent, replAna);
|
|
688
|
+
}
|
|
689
|
+
if (turn.actions.length === 0) {
|
|
690
|
+
const sr = turn.response.scalp?.reason ?? "";
|
|
691
|
+
const ar = turn.response.ambush?.reason ?? "";
|
|
692
|
+
printTurn({ action: "wait", reason: `scalp: ${sr || "—"}\n ambush: ${ar || "—"}`, confidence: 0 }, "—", turn.lastAssistant.reasoningContent, replAna);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
finally {
|
|
697
|
+
rl.close();
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
attachAdvisorProfileCommands(advise);
|
|
701
|
+
const advisor = program
|
|
702
|
+
.command("advisor")
|
|
703
|
+
.description("Advisor preferences stored as `userProfile` in trade-state.json");
|
|
704
|
+
attachAdvisorProfileCommands(advisor);
|
|
705
|
+
}
|
|
706
|
+
//# sourceMappingURL=advisorCli.js.map
|