jcc_hyper_tool 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -17
- package/dist/advisor/actionRunner.d.ts +6 -0
- package/dist/advisor/actionRunner.d.ts.map +1 -1
- package/dist/advisor/actionRunner.js +38 -7
- package/dist/advisor/actionRunner.js.map +1 -1
- package/dist/advisor/actionSchema.d.ts +1209 -75
- package/dist/advisor/actionSchema.d.ts.map +1 -1
- package/dist/advisor/actionSchema.js +117 -5
- package/dist/advisor/actionSchema.js.map +1 -1
- 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 +12 -2
- package/dist/advisor/adviseEngine.d.ts.map +1 -1
- package/dist/advisor/adviseEngine.js +88 -7
- package/dist/advisor/adviseEngine.js.map +1 -1
- 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 +17 -0
- package/dist/advisor/contextBuilder.d.ts.map +1 -1
- package/dist/advisor/contextBuilder.js +25 -6
- package/dist/advisor/contextBuilder.js.map +1 -1
- 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 +14 -0
- package/dist/advisor/guards.d.ts.map +1 -1
- package/dist/advisor/guards.js +143 -16
- package/dist/advisor/guards.js.map +1 -1
- 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 +8 -2
- package/dist/advisor/promptTemplate.d.ts.map +1 -1
- package/dist/advisor/promptTemplate.js +175 -23
- package/dist/advisor/promptTemplate.js.map +1 -1
- 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.map +1 -1
- package/dist/cli/advisorCli.js +478 -126
- package/dist/cli/advisorCli.js.map +1 -1
- package/dist/cli/index.js +2 -0
- 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 +3 -1
- package/dist/cli/strategyCli.js.map +1 -1
- package/dist/cli/tradingCli.d.ts.map +1 -1
- package/dist/cli/tradingCli.js +31 -1
- package/dist/cli/tradingCli.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/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/bracketReconcile.d.ts.map +1 -1
- package/dist/core/state/bracketReconcile.js +6 -2
- package/dist/core/state/bracketReconcile.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.map +1 -1
- package/dist/core/state/profileStore.js +12 -16
- package/dist/core/state/profileStore.js.map +1 -1
- package/dist/core/state/runReconcile.d.ts.map +1 -1
- package/dist/core/state/runReconcile.js +53 -18
- package/dist/core/state/runReconcile.js.map +1 -1
- package/dist/core/state/schema.d.ts +439 -68
- package/dist/core/state/schema.d.ts.map +1 -1
- package/dist/core/state/schema.js +51 -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/store.d.ts +21 -3
- package/dist/core/state/store.d.ts.map +1 -1
- package/dist/core/state/store.js +122 -13
- 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 +47 -2
- 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
package/dist/cli/advisorCli.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import * as readline from "node:readline/promises";
|
|
4
5
|
import { DEFAULT_DEEPSEEK_MODEL } from "../advisor/deepseekClient.js";
|
|
5
6
|
import { executeAdvisorAction, runAdvisorModelTurnWithRepair } from "../advisor/adviseEngine.js";
|
|
6
7
|
import { advisorActionToCliArgs, buildGlobalPrefix, formatCliInvocation, } from "../advisor/actionRunner.js";
|
|
7
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";
|
|
8
11
|
import { buildAdvisorSystemPrompt, formatFirstUserPayload } from "../advisor/promptTemplate.js";
|
|
12
|
+
import { listPendingAmbushIntents } from "../advisor/ambushPolicy.js";
|
|
13
|
+
import { buildActivePositionPlan } from "../advisor/positionPlan.js";
|
|
9
14
|
import { writeAdvisorSessionLog } from "../advisor/sessionLog.js";
|
|
10
15
|
import { loadEffectiveConfig, requireUserContext } from "../core/config.js";
|
|
11
16
|
import { ConfigError } from "../core/errors.js";
|
|
@@ -30,6 +35,36 @@ function parseMaxSz(raw) {
|
|
|
30
35
|
}
|
|
31
36
|
return n;
|
|
32
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
|
+
}
|
|
33
68
|
async function persistAndPrintSession(logPath, session) {
|
|
34
69
|
await writeAdvisorSessionLog(logPath, session);
|
|
35
70
|
}
|
|
@@ -41,10 +76,231 @@ function globalArgs(globals) {
|
|
|
41
76
|
noState: globals.noState,
|
|
42
77
|
};
|
|
43
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
|
+
}
|
|
44
297
|
export function attachAdvisorCli(program) {
|
|
45
|
-
program
|
|
298
|
+
const advise = program
|
|
46
299
|
.command("advise")
|
|
47
|
-
.description("DeepSeek advisor
|
|
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)")
|
|
48
304
|
.requiredOption("--coin <sym>", "Perp symbol (e.g. BTC, xyz:CL)")
|
|
49
305
|
.requiredOption("--max-sz <n>", "Hard cap on size for bracket / close positions")
|
|
50
306
|
.option("--note <text>", "Note injected into the first user message")
|
|
@@ -55,9 +311,20 @@ export function attachAdvisorCli(program) {
|
|
|
55
311
|
.option("--bars5m <n>", "5m bars", "48")
|
|
56
312
|
.option("--bars1h <n>", "1h bars", "48")
|
|
57
313
|
.option("--bars1d <n>", "1d bars", "30")
|
|
314
|
+
.option("--context-version <v>", "Prompt context: raw_kline (v1, default) | precomputed_indicators (v2)", "raw_kline")
|
|
58
315
|
.option("--live", "Forwarded to spawned order/state commands", false)
|
|
59
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%)")
|
|
60
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")
|
|
61
328
|
.action(async (opts, command) => {
|
|
62
329
|
const coinRaw = String(opts.coin ?? "");
|
|
63
330
|
const globals = readGlobalOptions(command);
|
|
@@ -68,6 +335,14 @@ export function attachAdvisorCli(program) {
|
|
|
68
335
|
const noState = Boolean(globals.noState);
|
|
69
336
|
const statePath = resolveTradeStatePath(command, undefined);
|
|
70
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");
|
|
71
346
|
const k5 = Number(opts.bars5m ?? "48");
|
|
72
347
|
const k1h = Number(opts.bars1h ?? "48");
|
|
73
348
|
const k1d = Number(opts.bars1d ?? "30");
|
|
@@ -80,6 +355,9 @@ export function attachAdvisorCli(program) {
|
|
|
80
355
|
throw new ConfigError(`${label} must be a positive integer.`);
|
|
81
356
|
}
|
|
82
357
|
}
|
|
358
|
+
const contextVersion = opts.contextVersion === "precomputed_indicators"
|
|
359
|
+
? "precomputed_indicators"
|
|
360
|
+
: "raw_kline";
|
|
83
361
|
let profile;
|
|
84
362
|
if (!noState && statePath) {
|
|
85
363
|
const f = await readOrCreateTradeStateFile(statePath, Date.now());
|
|
@@ -98,7 +376,40 @@ export function attachAdvisorCli(program) {
|
|
|
98
376
|
kline5mBars: k5,
|
|
99
377
|
kline1hBars: k1h,
|
|
100
378
|
kline1dBars: k1d,
|
|
379
|
+
contextVersion,
|
|
101
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
|
+
}
|
|
102
413
|
const sessionId = randomUUID();
|
|
103
414
|
const logDir = typeof opts.logDir === "string" && opts.logDir.trim()
|
|
104
415
|
? opts.logDir.trim()
|
|
@@ -123,10 +434,20 @@ export function attachAdvisorCli(program) {
|
|
|
123
434
|
},
|
|
124
435
|
turns: [],
|
|
125
436
|
};
|
|
126
|
-
const system = buildAdvisorSystemPrompt(profile);
|
|
127
|
-
const firstUser = formatFirstUserPayload(snap
|
|
437
|
+
const system = buildAdvisorSystemPrompt(profile, contextVersion);
|
|
438
|
+
const firstUser = formatFirstUserPayload(snap, {
|
|
439
|
+
pendingAmbushIntents: pendingAmbush,
|
|
440
|
+
activePositionPlan: activePositionPlan ?? null,
|
|
441
|
+
});
|
|
128
442
|
session.turns.push({ role: "system", content: system });
|
|
129
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
|
+
}
|
|
130
451
|
const execBase = {
|
|
131
452
|
globals: globalArgs(globals),
|
|
132
453
|
maxSz,
|
|
@@ -136,9 +457,28 @@ export function attachAdvisorCli(program) {
|
|
|
136
457
|
statePath,
|
|
137
458
|
noState,
|
|
138
459
|
profile,
|
|
460
|
+
auto,
|
|
461
|
+
minConfidence,
|
|
462
|
+
limitJitterPct,
|
|
463
|
+
executeCooldownSec,
|
|
464
|
+
slEntryShift,
|
|
465
|
+
antiRushCloseMinSec,
|
|
466
|
+
ambushReplaceDeviationPct: ambushReplaceDevPct,
|
|
139
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
|
+
}
|
|
140
480
|
process.stderr.write("正在请求模型(可能需要 10–60 秒)…\n");
|
|
141
|
-
const { messages,
|
|
481
|
+
const { messages, response, actions, lastAssistant } = await runAdvisorModelTurnWithRepair({
|
|
142
482
|
apiKey,
|
|
143
483
|
messages: [
|
|
144
484
|
{ role: "system", content: system },
|
|
@@ -149,10 +489,41 @@ export function attachAdvisorCli(program) {
|
|
|
149
489
|
role: "assistant",
|
|
150
490
|
content: lastAssistant.content,
|
|
151
491
|
reasoning_content: lastAssistant.reasoningContent,
|
|
152
|
-
action,
|
|
492
|
+
action: actions[0]?.action,
|
|
153
493
|
});
|
|
154
494
|
await persistAndPrintSession(logPath, session);
|
|
155
|
-
|
|
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
|
+
}
|
|
156
527
|
const runExecute = async (act) => {
|
|
157
528
|
const r = await executeAdvisorAction({
|
|
158
529
|
action: act,
|
|
@@ -166,6 +537,15 @@ export function attachAdvisorCli(program) {
|
|
|
166
537
|
assumeYes: execBase.assumeYes,
|
|
167
538
|
statePath,
|
|
168
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
|
+
},
|
|
169
549
|
confirmLive: (p) => requireStdinYes(p + "输入 YES 确认执行:\n"),
|
|
170
550
|
});
|
|
171
551
|
process.stdout.write(r.stdout);
|
|
@@ -179,17 +559,58 @@ export function attachAdvisorCli(program) {
|
|
|
179
559
|
exitCode: r.exitCode,
|
|
180
560
|
};
|
|
181
561
|
await persistAndPrintSession(logPath, session);
|
|
182
|
-
|
|
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
|
+
}
|
|
183
573
|
};
|
|
184
574
|
if (opts.auto === true) {
|
|
185
|
-
|
|
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
|
+
}
|
|
186
605
|
return;
|
|
187
606
|
}
|
|
188
607
|
if (opts.once === true) {
|
|
189
608
|
return;
|
|
190
609
|
}
|
|
191
610
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
192
|
-
|
|
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;
|
|
193
614
|
let msgs = messages;
|
|
194
615
|
try {
|
|
195
616
|
while (true) {
|
|
@@ -198,14 +619,45 @@ export function attachAdvisorCli(program) {
|
|
|
198
619
|
break;
|
|
199
620
|
}
|
|
200
621
|
if (line === "show") {
|
|
201
|
-
|
|
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, {
|
|
202
649
|
coin: snap.coin,
|
|
203
650
|
mid: snap.mid,
|
|
204
651
|
positionSignedSz: snap.positions[snap.coin]?.signed_sz,
|
|
205
652
|
globals: buildGlobalPrefix(execBase.globals),
|
|
206
653
|
live: execBase.live,
|
|
207
654
|
assumeYes: execBase.assumeYes,
|
|
208
|
-
|
|
655
|
+
szCap,
|
|
656
|
+
limitJitterPct: policy.limitJitterPct,
|
|
657
|
+
maxDevPct: profileSnapshot.priceDeviationPctMax,
|
|
658
|
+
szDecimals: snap.sz_decimals,
|
|
659
|
+
}));
|
|
660
|
+
process.stderr.write(`${cmd}\n`);
|
|
209
661
|
continue;
|
|
210
662
|
}
|
|
211
663
|
if (line === "execute" || line === "e") {
|
|
@@ -221,134 +673,34 @@ export function attachAdvisorCli(program) {
|
|
|
221
673
|
process.stderr.write("正在请求模型…\n");
|
|
222
674
|
const turn = await runAdvisorModelTurnWithRepair({ apiKey, messages: msgs });
|
|
223
675
|
msgs = turn.messages;
|
|
224
|
-
|
|
676
|
+
const replPrimary = turn.actions[0];
|
|
677
|
+
latest = replPrimary?.action ?? { action: "wait", reason: "", confidence: 0 };
|
|
225
678
|
session.turns.push({
|
|
226
679
|
role: "assistant",
|
|
227
680
|
content: turn.lastAssistant.content,
|
|
228
681
|
reasoning_content: turn.lastAssistant.reasoningContent,
|
|
229
|
-
action: turn.action,
|
|
682
|
+
action: turn.actions[0]?.action,
|
|
230
683
|
});
|
|
231
684
|
await persistAndPrintSession(logPath, session);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
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
|
+
}
|
|
236
694
|
}
|
|
237
695
|
}
|
|
238
696
|
finally {
|
|
239
697
|
rl.close();
|
|
240
698
|
}
|
|
241
699
|
});
|
|
700
|
+
attachAdvisorProfileCommands(advise);
|
|
242
701
|
const advisor = program
|
|
243
702
|
.command("advisor")
|
|
244
703
|
.description("Advisor preferences stored as `userProfile` in trade-state.json");
|
|
245
|
-
|
|
246
|
-
.command("profile")
|
|
247
|
-
.description("Risk profile (merged into `advise` system prompt)");
|
|
248
|
-
profile
|
|
249
|
-
.command("show")
|
|
250
|
-
.description("Print merged userProfile JSON")
|
|
251
|
-
.option("--state-file <path>", "trade-state path")
|
|
252
|
-
.action(async (o, cmd) => {
|
|
253
|
-
readGlobalOptions(cmd);
|
|
254
|
-
const statePath = requireRecordedAdvisorStatePath(cmd, o.stateFile);
|
|
255
|
-
const f = await readOrCreateTradeStateFile(statePath, Date.now());
|
|
256
|
-
printJson({ userProfile: mergeUserProfileDefaults(f.userProfile) });
|
|
257
|
-
});
|
|
258
|
-
profile
|
|
259
|
-
.command("set")
|
|
260
|
-
.description("Update userProfile (atomic write; does not touch intents)")
|
|
261
|
-
.option("--state-file <path>", "trade-state path")
|
|
262
|
-
.option("--risk <level>", "conservative | balanced | aggressive")
|
|
263
|
-
.option("--max-sz <n>", "defaultMaxSz")
|
|
264
|
-
.option("--require-tpsl", "Force requireTpsl true on profile when passed", false)
|
|
265
|
-
.option("--no-require-tpsl", "Force requireTpsl false on profile when passed", false)
|
|
266
|
-
.option("--price-dev-pct <n>", "priceDeviationPctMax decimal fraction (e.g. 0.03)")
|
|
267
|
-
.option("--notes <text>", "tradingStyleNotes")
|
|
268
|
-
.option("--coin-max <pair>", "COIN=size max override (repeatable)", collectPair, [])
|
|
269
|
-
.action(async (opts, cmd) => {
|
|
270
|
-
readGlobalOptions(cmd);
|
|
271
|
-
const statePath = requireRecordedAdvisorStatePath(cmd, opts.stateFile);
|
|
272
|
-
const now = Date.now();
|
|
273
|
-
if (opts.noRequireTpsl && opts.requireTpsl) {
|
|
274
|
-
throw new ConfigError("Use either --require-tpsl or --no-require-tpsl, not both.");
|
|
275
|
-
}
|
|
276
|
-
const patch = { updatedAtMs: now };
|
|
277
|
-
const risk = opts.risk?.trim();
|
|
278
|
-
if (risk) {
|
|
279
|
-
if (risk !== "conservative" && risk !== "balanced" && risk !== "aggressive") {
|
|
280
|
-
throw new ConfigError("--risk must be conservative | balanced | aggressive");
|
|
281
|
-
}
|
|
282
|
-
patch.risk = risk;
|
|
283
|
-
}
|
|
284
|
-
if (opts.maxSz !== undefined) {
|
|
285
|
-
const n = Number(opts.maxSz);
|
|
286
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
287
|
-
throw new ConfigError("--max-sz must be positive");
|
|
288
|
-
}
|
|
289
|
-
patch.defaultMaxSz = n;
|
|
290
|
-
}
|
|
291
|
-
if (opts.noRequireTpsl) {
|
|
292
|
-
patch.requireTpsl = false;
|
|
293
|
-
}
|
|
294
|
-
else if (opts.requireTpsl) {
|
|
295
|
-
patch.requireTpsl = true;
|
|
296
|
-
}
|
|
297
|
-
if (opts.priceDevPct !== undefined) {
|
|
298
|
-
const n = Number(opts.priceDevPct);
|
|
299
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
300
|
-
throw new ConfigError("--price-dev-pct must be positive");
|
|
301
|
-
}
|
|
302
|
-
patch.priceDeviationPctMax = n;
|
|
303
|
-
}
|
|
304
|
-
if (opts.notes !== undefined) {
|
|
305
|
-
patch.tradingStyleNotes = opts.notes;
|
|
306
|
-
}
|
|
307
|
-
const pairs = opts.coinMax ?? [];
|
|
308
|
-
if (pairs.length > 0) {
|
|
309
|
-
const map = {
|
|
310
|
-
...(mergeUserProfileDefaults((await readOrCreateTradeStateFile(statePath, now)).userProfile).maxSzByCoin ?? {}),
|
|
311
|
-
};
|
|
312
|
-
for (const p of pairs) {
|
|
313
|
-
const [c, s] = p.split("=");
|
|
314
|
-
const coin = c?.trim();
|
|
315
|
-
const sz = Number(s?.trim());
|
|
316
|
-
if (!coin || !Number.isFinite(sz) || sz <= 0) {
|
|
317
|
-
throw new ConfigError(`Invalid --coin-max '${p}' (expect COIN=size)`);
|
|
318
|
-
}
|
|
319
|
-
map[coin] = sz;
|
|
320
|
-
}
|
|
321
|
-
patch.maxSzByCoin = map;
|
|
322
|
-
}
|
|
323
|
-
const patchKeys = Object.keys(patch).filter((k) => k !== "updatedAtMs");
|
|
324
|
-
if (patchKeys.length === 0) {
|
|
325
|
-
throw new ConfigError("profile set: pass at least one of --risk, --max-sz, --notes, --price-dev-pct, --coin-max, --require-tpsl / --no-require-tpsl");
|
|
326
|
-
}
|
|
327
|
-
const out = await upsertUserProfileFields(statePath, patch, now);
|
|
328
|
-
printJson({ ok: true, userProfile: out.userProfile });
|
|
329
|
-
});
|
|
330
|
-
profile
|
|
331
|
-
.command("clear")
|
|
332
|
-
.description("Remove userProfile from trade-state.json")
|
|
333
|
-
.option("--state-file <path>", "trade-state path")
|
|
334
|
-
.action(async (o, cmd) => {
|
|
335
|
-
readGlobalOptions(cmd);
|
|
336
|
-
const statePath = requireRecordedAdvisorStatePath(cmd, o.stateFile);
|
|
337
|
-
const cur = await readOrCreateTradeStateFile(statePath, Date.now());
|
|
338
|
-
const hadProfile = Boolean(cur.userProfile);
|
|
339
|
-
await clearUserProfile(statePath, Date.now());
|
|
340
|
-
printJson({ ok: true, hadProfile });
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
function collectPair(value, prev) {
|
|
344
|
-
return [...prev, value];
|
|
345
|
-
}
|
|
346
|
-
function requireRecordedAdvisorStatePath(cmd, explicit) {
|
|
347
|
-
requireStateCliEnabled(cmd, "`advisor profile`");
|
|
348
|
-
const p = resolveTradeStatePath(cmd, explicit);
|
|
349
|
-
if (!p) {
|
|
350
|
-
throw new ConfigError("Unexpected: advisor profile needs a trade-state path.");
|
|
351
|
-
}
|
|
352
|
-
return p;
|
|
704
|
+
attachAdvisorProfileCommands(advisor);
|
|
353
705
|
}
|
|
354
706
|
//# sourceMappingURL=advisorCli.js.map
|