@alphafox/cli 0.3.9 → 0.3.11

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 (34) hide show
  1. package/dist/catalog/generated/registry.json +2 -607
  2. package/dist/catalog/generated/schemas.json +9037 -13195
  3. package/dist/engine-backtest/coverage-notice.d.ts +5 -0
  4. package/dist/engine-backtest/coverage-notice.js +115 -0
  5. package/dist/engine-backtest/parse-args.d.ts +1 -1
  6. package/dist/engine-backtest/parse-args.js +3 -2
  7. package/dist/engine-backtest/persist.d.ts +3 -1
  8. package/dist/engine-backtest/persist.js +5 -0
  9. package/dist/engine-backtest/run-command.js +10 -1
  10. package/dist/engine-backtest/sweep-command.js +10 -1
  11. package/dist/engine-backtest/types.d.ts +24 -0
  12. package/dist/engine-backtest/types.js +3 -0
  13. package/dist/skills-manifest.json +50 -50
  14. package/dist/version.d.ts +1 -1
  15. package/dist/version.js +1 -1
  16. package/docs/e2e-staging.md +6 -4
  17. package/package.json +1 -1
  18. package/skills/account/SKILL.md +1 -1
  19. package/skills/admin/SKILL.md +1 -1
  20. package/skills/alphafox/SKILL.md +2 -2
  21. package/skills/alphafox-shared/SKILL.md +1 -1
  22. package/skills/auth/SKILL.md +1 -1
  23. package/skills/cache/SKILL.md +1 -1
  24. package/skills/engine-backtest/SKILL.md +4 -3
  25. package/skills/exchange/SKILL.md +1 -1
  26. package/skills/market/SKILL.md +1 -1
  27. package/skills/notification/SKILL.md +1 -1
  28. package/skills/strategy/SKILL.md +1 -1
  29. package/skills/trading/SKILL.md +1 -1
  30. package/vendor/backtest-runner/README.md +4 -4
  31. package/vendor/backtest-runner/index.d.ts +13 -0
  32. package/vendor/backtest-runner/index.mjs +3 -0
  33. package/vendor/backtest-runner/lib/coverage.mjs +37 -2
  34. package/vendor/backtest-runner/lib/tape-loader.mjs +6 -1
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox
3
3
  description: AlphaFox CLI entry router. Use for any AlphaFox request — install, update, login, whoami, 回测, engine backtest, 清理回测缓存 / 历史数据, strategy definitions, create/list/start/stop a running strategy (trader), ticker/标的 resolve (美股 or crypto), market data, exchange connectors, wallet, subscriptions, notifications, or admin. If a CLI command prints `[alphafox] update available`, ask the user「检测到新的版本,是否需要我帮你升级?」and only then run `alphafox update --format json --no-input`. After a large backtest, if tape cache is large, ask「回测下载的历史数据比较大,要不要我帮你清理本地缓存?」then open `alphafox-cache`. Start here, then open the routed domain skill. Do not guess alphafox-engine-backtest vs alphafox-strategy vs alphafox-trading from memory.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # AlphaFox
@@ -37,7 +37,7 @@ If several rows apply, load **all** of them (typical: `alphafox-shared` + `alpha
37
37
  The CLI may print this on **stderr** at most once every 24 hours:
38
38
 
39
39
  ```text
40
- [alphafox] update available: 0.3.8 -> 0.3.9. After the user confirms, run: alphafox update --format json --no-input,
40
+ [alphafox] update available: 0.3.10 -> 0.3.11. After the user confirms, run: alphafox update --format json --no-input,
41
41
  ```
42
42
 
43
43
  If you see that notice (or `updateAvailable: true` from `alphafox update --check`):
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-shared
3
3
  description: Shared AlphaFox CLI rules for Agents — auth, profiles, envelopes, risk gates, and public operationIds only.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # AlphaFox shared Agent contract
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-auth
3
3
  description: Login, status, logout, whoami, and environment isolation for AlphaFox CLI.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Auth Skill
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-cache
3
3
  description: Inspect and clean local Engine backtest caches (downloaded OHLCV tape and wasm runtime). Use when the user asks to 清理缓存, free disk, or after a large historical backtest.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Cache
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-engine-backtest
3
3
  description: Local Engine WASM backtest (alphafox engine-backtest run|sweep) vs catalog experiment CRUD.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Engine Backtest
@@ -52,7 +52,7 @@ alphafox engine-backtest run \
52
52
  --format jsonl --no-input
53
53
  ```
54
54
 
55
- Also valid: `--from` / `--to` instead of `--range`. `--create-experiment --name "..."` when there is no `--experiment` (needs `strategyDefinitionId` + `strategyDefinitionDisplay` `{zh,en}`; pass `--definition-label-zh` / `--definition-label-en` or the CLI falls back to the definition id). Persisted runs use the account tier from `subscriptions.me.get`; if `--tier` is supplied, it must match. With `--no-persist`, `runs.create` is skipped and `--tier` may simulate `free|pro|pro_max` (default `pro`). `--data-quality` defaults to `strict`. `--replay-timeframe` defaults to `1m` (allowed `1m|3m|5m|15m|30m|1h|4h`); this is the replay/download bar and is merged with plan indicator timeframes so a 4h RSI grid still replays on 1m. `runs.create` is `write`, not `high-risk-write` — do not add `--yes`. Do not update or delete experiments through this command.
55
+ Also valid: `--from` / `--to` instead of `--range`. `--create-experiment --name "..."` when there is no `--experiment` (needs `strategyDefinitionId` + `strategyDefinitionDisplay` `{zh,en}`; pass `--definition-label-zh` / `--definition-label-en` or the CLI falls back to the definition id). Persisted runs use the account tier from `subscriptions.me.get`; if `--tier` is supplied, it must match. With `--no-persist`, `runs.create` is skipped and `--tier` may simulate `free|pro|pro_max` (default `pro`). `--data-quality` defaults to `basic` (soft gaps finish the run and appear as `coverageNotice`; `prefix_gap` is less severe than `internal_gap`). `--data-quality strict` still fails on any gap. `--replay-timeframe` defaults to `1m` (allowed `1m|3m|5m|15m|30m|1h|4h`); this is the replay/download bar and is merged with plan indicator timeframes so a 4h RSI grid still replays on 1m. `runs.create` is `write`, not `high-risk-write` — do not add `--yes`. Do not update or delete experiments through this command.
56
56
 
57
57
  `--format jsonl` writes one JSON object per progress line (`{event:"progress",stage,fraction}`), then a final `{ok:true,data:{...}}` envelope.
58
58
 
@@ -92,13 +92,14 @@ Owner isolation and 7-day expiry are enforced by the server. Applying a coordina
92
92
 
93
93
  1. Edit config JSON.
94
94
  2. `engine-backtest run` (reuse `--experiment` after the first create).
95
- 3. Read `data.metrics` / `data.engineVersion` / `data.runId` / `data.experimentUrl`.
95
+ 3. Read `data.metrics` / `data.engineVersion` / `data.runId` / `data.experimentUrl`. After the run, also read `data.coverageNotice` (`warning` = mid-range candle gaps; `notice` = start / other soft gaps).
96
96
  4. Adjust parameters and run again. Do not invent a token flag if persist returns 401 — `alphafox auth login`.
97
97
  5. After a long-range or 1m run, follow `alphafox-cache`: `alphafox cache status`. If `data.tape.large` is true, ask **回测下载的历史数据比较大,要不要我帮你清理本地缓存?** and wait for yes.
98
98
 
99
99
  ## Safety
100
100
 
101
101
  - Unauthenticated persist/create fails like `whoami` (HTTP 401, exit 77).
102
+ - Default `basic` data quality: missing market / empty / corrupted tape **stops**; soft gaps complete the run and surface as `coverageNotice`. Do not silently switch to `strict` or hide those notices.
102
103
  - `strict` data quality: missing/gapped tape **stops**. Do not retry as `basic` unless the operator asks.
103
104
  - `planBacktest` unsupported → fail with the plan reason. Do not degrade to a guessed universe.
104
105
  - Catalog `engine_backtest.experiments.byId.update` / `.delete` / `.sweeps.byId.delete` are high-risk-write and are **not** this command's run/sweep path.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-exchange
3
3
  description: Exchange connectors list and connection management via Public API.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Exchange connectors
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-market
3
3
  description: Market data and ticker resolution for US equity perps, RWAs, and crypto on the same perp catalog. Use when the user names 美股, NVDA, AAPL, BTC, or any 标的. Keep the operator's asset class via symbolMetadata — do not rewrite NVDA into a crypto coin.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Market
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-notification
3
3
  description: Notification channels and subscriptions.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Notification
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-strategy
3
3
  description: Strategy definitions — list types (grid, dca, copy, …) and validate config. Creating a running strategy is creating a trader; use alphafox-trading for that.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Strategy definitions
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-trading
3
3
  description: Running strategies (traders) — create, list, start, and stop. A trader is a live or paper strategy instance (grid, dca, copy, …), not a person.
4
- version: 0.3.9
4
+ version: 0.3.11
5
5
  ---
6
6
 
7
7
  # Running strategies (traders)
@@ -33,13 +33,13 @@ import {
33
33
  assembleScenario,
34
34
  } from "@alphafoxai/backtest-runner";
35
35
 
36
- const { tape, buffers, coverageWarnings } = await loadTape({
36
+ const { tape, buffers, coverageWarnings, coverageIssues } = await loadTape({
37
37
  exchangeId: "binance_perp_usdt",
38
38
  symbols: ["BTC/USDT:USDT"],
39
39
  timeframes: ["1m", "1h"],
40
40
  fromMs,
41
41
  toMs,
42
- dataQualityMode: "strict",
42
+ dataQualityMode: "basic",
43
43
  });
44
44
 
45
45
  const scenario = assembleScenario({
@@ -82,8 +82,8 @@ const scenario = assembleScenario({
82
82
 
83
83
  ## 数据质量
84
84
 
85
- - `strict`(默认):任何缺口 / 缺数 / 非法 K 线都抛 `TapeDataUnavailableError`。
86
- - `basic`:硬失败(缺市场、空序列、非法 K 线、拉数失败)仍抛错;软缺口进入 `coverageWarnings`。
85
+ - `basic`(默认):硬失败(缺市场、空序列、非法 K 线、拉数失败)仍抛错;软缺口进入 `coverageIssues` `coverageWarnings`。起始缺口(`prefix_gap`)较轻,中间缺口(`internal_gap`)较重。
86
+ - `strict`:任何缺口 / 缺数 / 非法 K 线都抛 `TapeDataUnavailableError`。
87
87
  - 禁止 mock 成功或静默降级。
88
88
 
89
89
  ## 已知限制(相对 alphafox-web)
@@ -109,6 +109,7 @@ export function createFileTapeCache(
109
109
  export function createDisabledTapeCache(): FileTapeCache;
110
110
 
111
111
  export type TapeDataQualityMode = "strict" | "basic";
112
+ export const DEFAULT_TAPE_DATA_QUALITY_MODE: TapeDataQualityMode;
112
113
  export type TapeDataIssueCode =
113
114
  | "market_missing"
114
115
  | "ohlcv_missing"
@@ -140,6 +141,17 @@ export function isTapeDataUnavailableError(
140
141
  value: unknown
141
142
  ): value is TapeDataUnavailableError;
142
143
 
144
+ export function summarizeTapeCoverageIssues(issues: readonly TapeDataIssue[]): {
145
+ readonly prefix: TapeDataIssue[];
146
+ readonly internal: TapeDataIssue[];
147
+ readonly other: TapeDataIssue[];
148
+ };
149
+
150
+ export function formatCoverageSoftWarning(
151
+ issues: readonly TapeDataIssue[],
152
+ coverageRatio: number
153
+ ): string;
154
+
143
155
  export type EngineBacktestPrecisionMode = "DECIMAL_PLACES" | "TICK_SIZE";
144
156
  export type EngineBacktestSubscriptionTier = "free" | "pro" | "pro_max";
145
157
  export type EngineBacktestPricePath = "ohlc_path_4" | "close_only";
@@ -334,6 +346,7 @@ export interface TapeLoadResult {
334
346
  readonly tape: EngineBacktestTapeInput;
335
347
  readonly buffers: Record<string, ArrayBuffer>;
336
348
  readonly coverageWarnings: readonly string[];
349
+ readonly coverageIssues: readonly TapeDataIssue[];
337
350
  readonly chartData?: {
338
351
  readonly exchangeId: string;
339
352
  readonly series: readonly TapeChartSeries[];
@@ -27,8 +27,11 @@ export {
27
27
  export {
28
28
  TapeDataUnavailableError,
29
29
  isTapeDataUnavailableError,
30
+ DEFAULT_TAPE_DATA_QUALITY_MODE,
30
31
  evaluateOhlcvCoverage,
31
32
  analyzeOhlcvCoverage,
33
+ formatCoverageSoftWarning,
34
+ summarizeTapeCoverageIssues,
32
35
  } from "./lib/coverage.mjs";
33
36
  export {
34
37
  TIMEFRAME_MS,
@@ -178,6 +178,8 @@ export function analyzeOhlcvCoverage(input) {
178
178
  };
179
179
  }
180
180
 
181
+ export const DEFAULT_TAPE_DATA_QUALITY_MODE = "basic";
182
+
181
183
  export function evaluateOhlcvCoverage(input) {
182
184
  const report = analyzeOhlcvCoverage(input);
183
185
 
@@ -210,12 +212,45 @@ export function evaluateOhlcvCoverage(input) {
210
212
  };
211
213
  }
212
214
 
215
+ export function summarizeTapeCoverageIssues(issues) {
216
+ const prefix = [];
217
+ const internal = [];
218
+ const other = [];
219
+ for (const item of issues) {
220
+ if (item.code === "prefix_gap") {
221
+ prefix.push(item);
222
+ } else if (item.code === "internal_gap") {
223
+ internal.push(item);
224
+ } else {
225
+ other.push(item);
226
+ }
227
+ }
228
+ return { prefix, internal, other };
229
+ }
230
+
213
231
  export function formatCoverageSoftWarning(issues, coverageRatio) {
214
- const codes = [...new Set(issues.map((item) => item.code))].join(", ");
215
232
  const sample = issues[0];
216
233
  const symbol = sample?.symbol ?? "unknown";
217
234
  const timeframe = sample?.timeframe ?? "*";
218
- return `${symbol} ${timeframe}: accepted soft data issues (${codes}) with ${(coverageRatio * 100).toFixed(1)}% replay coverage`;
235
+ const codes = [...new Set(issues.map((item) => item.code))];
236
+ const summary = summarizeTapeCoverageIssues(issues);
237
+ const parts = [];
238
+ if (summary.prefix.length > 0) {
239
+ parts.push("missing start candles (less severe)");
240
+ }
241
+ if (summary.internal.length > 0) {
242
+ parts.push("missing mid-range candles (more severe)");
243
+ }
244
+ if (summary.other.length > 0) {
245
+ parts.push(
246
+ `other soft gaps (${summary.other.map((item) => item.code).join(", ")})`
247
+ );
248
+ }
249
+ const detail =
250
+ parts.length > 0
251
+ ? parts.join("; ")
252
+ : `accepted soft data issues (${codes.join(", ")})`;
253
+ return `${symbol} ${timeframe}: ${detail} with ${(coverageRatio * 100).toFixed(1)}% replay coverage`;
219
254
  }
220
255
 
221
256
  function issue(input, code, detail = {}) {
@@ -5,6 +5,7 @@ import {
5
5
  DEFAULT_TAPE_CACHE_DIR,
6
6
  } from "./cache.mjs";
7
7
  import {
8
+ DEFAULT_TAPE_DATA_QUALITY_MODE,
8
9
  formatCoverageSoftWarning,
9
10
  isTapeDataUnavailableError,
10
11
  TapeDataUnavailableError,
@@ -187,7 +188,8 @@ export async function loadTape(request, options = {}) {
187
188
  const seriesConcurrency = resolveTapeSeriesConcurrency(
188
189
  options.seriesConcurrency ?? request.seriesConcurrency
189
190
  );
190
- const dataQualityMode = request.dataQualityMode ?? "strict";
191
+ const dataQualityMode =
192
+ request.dataQualityMode ?? DEFAULT_TAPE_DATA_QUALITY_MODE;
191
193
  const baseTimeframe = resolvePlanBaseTimeframe({
192
194
  baseTimeframe: request.baseTimeframe,
193
195
  timeframes: [
@@ -284,6 +286,7 @@ export async function loadTape(request, options = {}) {
284
286
  const totalSeries = seriesJobs.length;
285
287
  const dataIssues = [];
286
288
  const coverageWarnings = [];
289
+ const coverageIssues = [];
287
290
  const seriesFractions = new Array(totalSeries).fill(0);
288
291
  let lastOhlcvDetail = "";
289
292
  const reportOhlcv = (index, fraction, detail) => {
@@ -343,6 +346,7 @@ export async function loadTape(request, options = {}) {
343
346
  continue;
344
347
  }
345
348
  if (result.loaded.softIssues.length > 0) {
349
+ coverageIssues.push(...result.loaded.softIssues);
346
350
  coverageWarnings.push(
347
351
  formatCoverageSoftWarning(
348
352
  result.loaded.softIssues,
@@ -409,6 +413,7 @@ export async function loadTape(request, options = {}) {
409
413
  },
410
414
  buffers,
411
415
  coverageWarnings,
416
+ coverageIssues,
412
417
  chartData: {
413
418
  exchangeId: exchangeDefinition.id,
414
419
  series: chartSeries,