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.
Files changed (138) hide show
  1. package/README.md +160 -17
  2. package/dist/advisor/actionRunner.d.ts +6 -0
  3. package/dist/advisor/actionRunner.d.ts.map +1 -1
  4. package/dist/advisor/actionRunner.js +38 -7
  5. package/dist/advisor/actionRunner.js.map +1 -1
  6. package/dist/advisor/actionSchema.d.ts +1209 -75
  7. package/dist/advisor/actionSchema.d.ts.map +1 -1
  8. package/dist/advisor/actionSchema.js +117 -5
  9. package/dist/advisor/actionSchema.js.map +1 -1
  10. package/dist/advisor/adviseDisplay.d.ts +26 -0
  11. package/dist/advisor/adviseDisplay.d.ts.map +1 -0
  12. package/dist/advisor/adviseDisplay.js +174 -0
  13. package/dist/advisor/adviseDisplay.js.map +1 -0
  14. package/dist/advisor/adviseEngine.d.ts +12 -2
  15. package/dist/advisor/adviseEngine.d.ts.map +1 -1
  16. package/dist/advisor/adviseEngine.js +88 -7
  17. package/dist/advisor/adviseEngine.js.map +1 -1
  18. package/dist/advisor/ambushPolicy.d.ts +47 -0
  19. package/dist/advisor/ambushPolicy.d.ts.map +1 -0
  20. package/dist/advisor/ambushPolicy.js +161 -0
  21. package/dist/advisor/ambushPolicy.js.map +1 -0
  22. package/dist/advisor/contextBuilder.d.ts +17 -0
  23. package/dist/advisor/contextBuilder.d.ts.map +1 -1
  24. package/dist/advisor/contextBuilder.js +25 -6
  25. package/dist/advisor/contextBuilder.js.map +1 -1
  26. package/dist/advisor/executionPolicy.d.ts +92 -0
  27. package/dist/advisor/executionPolicy.d.ts.map +1 -0
  28. package/dist/advisor/executionPolicy.js +273 -0
  29. package/dist/advisor/executionPolicy.js.map +1 -0
  30. package/dist/advisor/guards.d.ts +14 -0
  31. package/dist/advisor/guards.d.ts.map +1 -1
  32. package/dist/advisor/guards.js +143 -16
  33. package/dist/advisor/guards.js.map +1 -1
  34. package/dist/advisor/indicatorCompute.d.ts +45 -0
  35. package/dist/advisor/indicatorCompute.d.ts.map +1 -0
  36. package/dist/advisor/indicatorCompute.js +259 -0
  37. package/dist/advisor/indicatorCompute.js.map +1 -0
  38. package/dist/advisor/positionPlan.d.ts +33 -0
  39. package/dist/advisor/positionPlan.d.ts.map +1 -0
  40. package/dist/advisor/positionPlan.js +118 -0
  41. package/dist/advisor/positionPlan.js.map +1 -0
  42. package/dist/advisor/promptTemplate.d.ts +8 -2
  43. package/dist/advisor/promptTemplate.d.ts.map +1 -1
  44. package/dist/advisor/promptTemplate.js +175 -23
  45. package/dist/advisor/promptTemplate.js.map +1 -1
  46. package/dist/advisor/touchProbability.d.ts +54 -0
  47. package/dist/advisor/touchProbability.d.ts.map +1 -0
  48. package/dist/advisor/touchProbability.js +215 -0
  49. package/dist/advisor/touchProbability.js.map +1 -0
  50. package/dist/advisor/tpslPolicy.d.ts +22 -0
  51. package/dist/advisor/tpslPolicy.d.ts.map +1 -0
  52. package/dist/advisor/tpslPolicy.js +120 -0
  53. package/dist/advisor/tpslPolicy.js.map +1 -0
  54. package/dist/cli/advisorCli.d.ts.map +1 -1
  55. package/dist/cli/advisorCli.js +478 -126
  56. package/dist/cli/advisorCli.js.map +1 -1
  57. package/dist/cli/index.js +2 -0
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/cli/riskCli.d.ts +3 -0
  60. package/dist/cli/riskCli.d.ts.map +1 -0
  61. package/dist/cli/riskCli.js +120 -0
  62. package/dist/cli/riskCli.js.map +1 -0
  63. package/dist/cli/strategyCli.d.ts.map +1 -1
  64. package/dist/cli/strategyCli.js +3 -1
  65. package/dist/cli/strategyCli.js.map +1 -1
  66. package/dist/cli/tradingCli.d.ts.map +1 -1
  67. package/dist/cli/tradingCli.js +31 -1
  68. package/dist/cli/tradingCli.js.map +1 -1
  69. package/dist/core/hyperliquid/asset.d.ts +2 -0
  70. package/dist/core/hyperliquid/asset.d.ts.map +1 -1
  71. package/dist/core/hyperliquid/asset.js +25 -0
  72. package/dist/core/hyperliquid/asset.js.map +1 -1
  73. package/dist/core/hyperliquid/orders.d.ts +5 -0
  74. package/dist/core/hyperliquid/orders.d.ts.map +1 -1
  75. package/dist/core/hyperliquid/orders.js +15 -5
  76. package/dist/core/hyperliquid/orders.js.map +1 -1
  77. package/dist/core/hyperliquid/rounding.d.ts +23 -0
  78. package/dist/core/hyperliquid/rounding.d.ts.map +1 -0
  79. package/dist/core/hyperliquid/rounding.js +53 -0
  80. package/dist/core/hyperliquid/rounding.js.map +1 -0
  81. package/dist/core/risk/isolatedTopUp.d.ts +112 -0
  82. package/dist/core/risk/isolatedTopUp.d.ts.map +1 -0
  83. package/dist/core/risk/isolatedTopUp.js +209 -0
  84. package/dist/core/risk/isolatedTopUp.js.map +1 -0
  85. package/dist/core/risk/isolatedTopUpHistory.d.ts +7 -0
  86. package/dist/core/risk/isolatedTopUpHistory.d.ts.map +1 -0
  87. package/dist/core/risk/isolatedTopUpHistory.js +26 -0
  88. package/dist/core/risk/isolatedTopUpHistory.js.map +1 -0
  89. package/dist/core/risk/isolatedTopUpParse.d.ts +16 -0
  90. package/dist/core/risk/isolatedTopUpParse.d.ts.map +1 -0
  91. package/dist/core/risk/isolatedTopUpParse.js +78 -0
  92. package/dist/core/risk/isolatedTopUpParse.js.map +1 -0
  93. package/dist/core/risk/runIsolatedTopUpReconcile.d.ts +28 -0
  94. package/dist/core/risk/runIsolatedTopUpReconcile.d.ts.map +1 -0
  95. package/dist/core/risk/runIsolatedTopUpReconcile.js +184 -0
  96. package/dist/core/risk/runIsolatedTopUpReconcile.js.map +1 -0
  97. package/dist/core/state/bracketReconcile.d.ts.map +1 -1
  98. package/dist/core/state/bracketReconcile.js +6 -2
  99. package/dist/core/state/bracketReconcile.js.map +1 -1
  100. package/dist/core/state/gridReconcile.d.ts +2 -0
  101. package/dist/core/state/gridReconcile.d.ts.map +1 -1
  102. package/dist/core/state/gridReconcile.js +42 -2
  103. package/dist/core/state/gridReconcile.js.map +1 -1
  104. package/dist/core/state/intentFactory.d.ts +1 -0
  105. package/dist/core/state/intentFactory.d.ts.map +1 -1
  106. package/dist/core/state/intentFactory.js +1 -0
  107. package/dist/core/state/intentFactory.js.map +1 -1
  108. package/dist/core/state/profileStore.d.ts.map +1 -1
  109. package/dist/core/state/profileStore.js +12 -16
  110. package/dist/core/state/profileStore.js.map +1 -1
  111. package/dist/core/state/runReconcile.d.ts.map +1 -1
  112. package/dist/core/state/runReconcile.js +53 -18
  113. package/dist/core/state/runReconcile.js.map +1 -1
  114. package/dist/core/state/schema.d.ts +439 -68
  115. package/dist/core/state/schema.d.ts.map +1 -1
  116. package/dist/core/state/schema.js +51 -0
  117. package/dist/core/state/schema.js.map +1 -1
  118. package/dist/core/state/snapshot.d.ts +13 -0
  119. package/dist/core/state/snapshot.d.ts.map +1 -1
  120. package/dist/core/state/snapshot.js +36 -0
  121. package/dist/core/state/snapshot.js.map +1 -1
  122. package/dist/core/state/store.d.ts +21 -3
  123. package/dist/core/state/store.d.ts.map +1 -1
  124. package/dist/core/state/store.js +122 -13
  125. package/dist/core/state/store.js.map +1 -1
  126. package/dist/core/state/types.d.ts +13 -0
  127. package/dist/core/state/types.d.ts.map +1 -1
  128. package/dist/daemon/index.js +47 -2
  129. package/dist/daemon/index.js.map +1 -1
  130. package/dist/daemon/validateChild.d.ts +13 -0
  131. package/dist/daemon/validateChild.d.ts.map +1 -0
  132. package/dist/daemon/validateChild.js +77 -0
  133. package/dist/daemon/validateChild.js.map +1 -0
  134. package/dist/strategies/grid.d.ts +1 -0
  135. package/dist/strategies/grid.d.ts.map +1 -1
  136. package/dist/strategies/grid.js +1 -0
  137. package/dist/strategies/grid.js.map +1 -1
  138. package/package.json +3 -1
@@ -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: K-lines + positions → structured action (dry-run default; `--live` for real orders via spawned CLI)")
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, action, lastAssistant } = await runAdvisorModelTurnWithRepair({
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
- printJson({ action, logPath, reasoning_content: lastAssistant.reasoningContent ?? null });
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
- printJson({ executed: session.executed });
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
- await runExecute(action);
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
- let latest = action;
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
- process.stderr.write(`${formatCliInvocation(advisorActionToCliArgs(latest, {
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
- }))}\n`);
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
- latest = turn.action;
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
- printJson({
233
- action: turn.action,
234
- reasoning_content: turn.lastAssistant.reasoningContent ?? null,
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
- const profile = advisor
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