@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.
- package/dist/catalog/generated/registry.json +2 -607
- package/dist/catalog/generated/schemas.json +9037 -13195
- package/dist/engine-backtest/coverage-notice.d.ts +5 -0
- package/dist/engine-backtest/coverage-notice.js +115 -0
- package/dist/engine-backtest/parse-args.d.ts +1 -1
- package/dist/engine-backtest/parse-args.js +3 -2
- package/dist/engine-backtest/persist.d.ts +3 -1
- package/dist/engine-backtest/persist.js +5 -0
- package/dist/engine-backtest/run-command.js +10 -1
- package/dist/engine-backtest/sweep-command.js +10 -1
- package/dist/engine-backtest/types.d.ts +24 -0
- package/dist/engine-backtest/types.js +3 -0
- package/dist/skills-manifest.json +50 -50
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/e2e-staging.md +6 -4
- package/package.json +1 -1
- package/skills/account/SKILL.md +1 -1
- package/skills/admin/SKILL.md +1 -1
- package/skills/alphafox/SKILL.md +2 -2
- package/skills/alphafox-shared/SKILL.md +1 -1
- package/skills/auth/SKILL.md +1 -1
- package/skills/cache/SKILL.md +1 -1
- package/skills/engine-backtest/SKILL.md +4 -3
- package/skills/exchange/SKILL.md +1 -1
- package/skills/market/SKILL.md +1 -1
- package/skills/notification/SKILL.md +1 -1
- package/skills/strategy/SKILL.md +1 -1
- package/skills/trading/SKILL.md +1 -1
- package/vendor/backtest-runner/README.md +4 -4
- package/vendor/backtest-runner/index.d.ts +13 -0
- package/vendor/backtest-runner/index.mjs +3 -0
- package/vendor/backtest-runner/lib/coverage.mjs +37 -2
- package/vendor/backtest-runner/lib/tape-loader.mjs +6 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TapeCoverageIssue, TapeCoverageNotice } from "./types";
|
|
2
|
+
export declare function summarizeTapeCoverageNotice(issues: readonly TapeCoverageIssue[]): TapeCoverageNotice;
|
|
3
|
+
/** Runtime guard: a missing field must not look like a clean tape. */
|
|
4
|
+
export declare function requireTapeCoverageIssues(value: unknown): readonly TapeCoverageIssue[];
|
|
5
|
+
export declare function snapshotCoverageIssues(issues: readonly TapeCoverageIssue[] | undefined): TapeCoverageIssue[] | undefined;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.summarizeTapeCoverageNotice = summarizeTapeCoverageNotice;
|
|
4
|
+
exports.requireTapeCoverageIssues = requireTapeCoverageIssues;
|
|
5
|
+
exports.snapshotCoverageIssues = snapshotCoverageIssues;
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
function symbolLabel(issue) {
|
|
8
|
+
const timeframe = issue.timeframe && issue.timeframe !== "*" ? ` ${issue.timeframe}` : "";
|
|
9
|
+
return `${issue.symbol}${timeframe}`;
|
|
10
|
+
}
|
|
11
|
+
function uniqueIssues(issues) {
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
const unique = [];
|
|
14
|
+
for (const issue of issues) {
|
|
15
|
+
const key = `${issue.code}\u0000${issue.symbol}\u0000${issue.timeframe}`;
|
|
16
|
+
if (seen.has(key))
|
|
17
|
+
continue;
|
|
18
|
+
seen.add(key);
|
|
19
|
+
unique.push(issue);
|
|
20
|
+
}
|
|
21
|
+
return unique;
|
|
22
|
+
}
|
|
23
|
+
function summarizeTapeCoverageNotice(issues) {
|
|
24
|
+
const prefix = [];
|
|
25
|
+
const internal = [];
|
|
26
|
+
const other = [];
|
|
27
|
+
for (const item of issues) {
|
|
28
|
+
if (item.code === "prefix_gap") {
|
|
29
|
+
prefix.push(item);
|
|
30
|
+
}
|
|
31
|
+
else if (item.code === "internal_gap") {
|
|
32
|
+
internal.push(item);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
other.push(item);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const uniquePrefix = uniqueIssues(prefix);
|
|
39
|
+
const uniqueInternal = uniqueIssues(internal);
|
|
40
|
+
const uniqueOther = uniqueIssues(other);
|
|
41
|
+
const severity = uniqueInternal.length > 0
|
|
42
|
+
? "warning"
|
|
43
|
+
: uniquePrefix.length + uniqueOther.length > 0
|
|
44
|
+
? "notice"
|
|
45
|
+
: "none";
|
|
46
|
+
const messages = [];
|
|
47
|
+
if (uniqueInternal.length > 0) {
|
|
48
|
+
messages.push(`WARNING: missing mid-range candles (more severe): ${uniqueInternal
|
|
49
|
+
.map(symbolLabel)
|
|
50
|
+
.join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
if (uniquePrefix.length > 0) {
|
|
53
|
+
messages.push(`NOTICE: missing start candles (less severe): ${uniquePrefix
|
|
54
|
+
.map(symbolLabel)
|
|
55
|
+
.join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
if (uniqueOther.length > 0) {
|
|
58
|
+
messages.push(`NOTICE: other soft coverage gaps: ${uniqueOther
|
|
59
|
+
.map((issue) => `${symbolLabel(issue)} (${issue.code})`)
|
|
60
|
+
.join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
severity,
|
|
64
|
+
prefix: uniquePrefix,
|
|
65
|
+
internal: uniqueInternal,
|
|
66
|
+
other: uniqueOther,
|
|
67
|
+
messages,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Must match alphafox-web `engineBacktestPersistedSnapshotSchema.coverageIssues`. */
|
|
71
|
+
const PERSISTED_COVERAGE_ISSUE_CODES = new Set([
|
|
72
|
+
"market_missing",
|
|
73
|
+
"ohlcv_missing",
|
|
74
|
+
"invalid_ohlcv",
|
|
75
|
+
"non_monotonic",
|
|
76
|
+
"internal_gap",
|
|
77
|
+
"prefix_gap",
|
|
78
|
+
"suffix_gap",
|
|
79
|
+
"warmup_insufficient",
|
|
80
|
+
"load_failed",
|
|
81
|
+
"coverage_insufficient",
|
|
82
|
+
]);
|
|
83
|
+
/** Runtime guard: a missing field must not look like a clean tape. */
|
|
84
|
+
function requireTapeCoverageIssues(value) {
|
|
85
|
+
if (!Array.isArray(value)) {
|
|
86
|
+
throw new errors_1.EngineBacktestError({
|
|
87
|
+
type: "runtime",
|
|
88
|
+
subtype: "missing_coverage_issues",
|
|
89
|
+
message: "Tape loader omitted coverageIssues; refusing to treat the tape as clean.",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
function snapshotCoverageIssues(issues) {
|
|
95
|
+
if (!issues || issues.length === 0)
|
|
96
|
+
return undefined;
|
|
97
|
+
return issues.map((issue) => {
|
|
98
|
+
if (!PERSISTED_COVERAGE_ISSUE_CODES.has(issue.code)) {
|
|
99
|
+
throw new errors_1.EngineBacktestError({
|
|
100
|
+
type: "runtime",
|
|
101
|
+
subtype: "invalid_coverage_issue",
|
|
102
|
+
message: `Cannot persist unknown tape coverage issue code: ${issue.code}`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
code: issue.code,
|
|
107
|
+
symbol: issue.symbol,
|
|
108
|
+
timeframe: issue.timeframe,
|
|
109
|
+
...(issue.expected === undefined ? {} : { expected: issue.expected }),
|
|
110
|
+
...(issue.actual === undefined ? {} : { actual: issue.actual }),
|
|
111
|
+
...(issue.timestamp === undefined ? {} : { timestamp: issue.timestamp }),
|
|
112
|
+
...(issue.message === undefined ? {} : { message: issue.message }),
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type EngineBacktestRunArgs, type EngineBacktestSweepArgs, type ExecutionModel, type InclusiveUtcDateRange } from "./types";
|
|
2
2
|
export declare const ENGINE_BACKTEST_RUN_USAGE: string[];
|
|
3
3
|
export declare const ENGINE_BACKTEST_SWEEP_USAGE: string[];
|
|
4
4
|
export declare function parseInclusiveUtcDateRange(rangeStart: string, rangeEnd: string): InclusiveUtcDateRange;
|
|
@@ -9,6 +9,7 @@ exports.parseEngineBacktestSweepArgs = parseEngineBacktestSweepArgs;
|
|
|
9
9
|
const errors_1 = require("./errors");
|
|
10
10
|
const replay_timeframe_1 = require("./replay-timeframe");
|
|
11
11
|
const sweep_kernel_1 = require("./sweep-kernel");
|
|
12
|
+
const types_1 = require("./types");
|
|
12
13
|
const DAY_MS = 86_400_000;
|
|
13
14
|
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
14
15
|
const TIERS = new Set(["free", "pro", "pro_max"]);
|
|
@@ -107,7 +108,7 @@ function parseEngineBacktestRunArgs(args) {
|
|
|
107
108
|
let to;
|
|
108
109
|
let initialEquity;
|
|
109
110
|
let tier;
|
|
110
|
-
let dataQualityMode =
|
|
111
|
+
let dataQualityMode = types_1.DEFAULT_DATA_QUALITY_MODE;
|
|
111
112
|
let configSchemaVersion;
|
|
112
113
|
let executionModelOverride;
|
|
113
114
|
let persist = true;
|
|
@@ -343,7 +344,7 @@ function parseEngineBacktestSweepArgs(args) {
|
|
|
343
344
|
return {
|
|
344
345
|
help: true,
|
|
345
346
|
createExperiment: false,
|
|
346
|
-
dataQualityMode:
|
|
347
|
+
dataQualityMode: types_1.DEFAULT_DATA_QUALITY_MODE,
|
|
347
348
|
persist: true,
|
|
348
349
|
replayTimeframe: replay_timeframe_1.ENGINE_BACKTEST_DEFAULT_REPLAY_TIMEFRAME,
|
|
349
350
|
axesRaw,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProfileName } from "../config/profiles";
|
|
2
|
-
import type { CreateRunRequestBody, CreateSweepRequestBody, DataQualityMode, EngineBacktestMetrics, EngineBacktestScenario, EngineBacktestSweepPointRow, ExecutionModel, PersistedSnapshot, SweepMode, SweepSearchMode } from "./types";
|
|
2
|
+
import type { CreateRunRequestBody, CreateSweepRequestBody, DataQualityMode, EngineBacktestMetrics, EngineBacktestScenario, EngineBacktestSweepPointRow, ExecutionModel, PersistedSnapshot, SweepMode, SweepSearchMode, TapeCoverageIssue } from "./types";
|
|
3
3
|
/** Aligned with `@alphafoxai/backtest-runner` DEFAULT_EXECUTION_MODEL. */
|
|
4
4
|
export declare const DEFAULT_EXECUTION_MODEL: ExecutionModel;
|
|
5
5
|
export declare const SNAPSHOT_SCHEMA_VERSION: 1;
|
|
@@ -24,6 +24,7 @@ export declare function buildCreateRunRequest(input: {
|
|
|
24
24
|
readonly dataQualityMode: DataQualityMode;
|
|
25
25
|
readonly engineVersion: string;
|
|
26
26
|
readonly equityCurve?: unknown;
|
|
27
|
+
readonly coverageIssues?: readonly TapeCoverageIssue[];
|
|
27
28
|
}): CreateRunRequestBody;
|
|
28
29
|
/**
|
|
29
30
|
* Experiment detail URL. Locale prefix (`/zh`, `/en`) is optional — web
|
|
@@ -51,6 +52,7 @@ export declare function buildSweepBaseSnapshot(input: {
|
|
|
51
52
|
readonly initialEquity: number;
|
|
52
53
|
readonly subscriptionTier: PersistedSnapshot["subscriptionTier"];
|
|
53
54
|
readonly dataQualityMode: DataQualityMode;
|
|
55
|
+
readonly coverageIssues?: readonly TapeCoverageIssue[];
|
|
54
56
|
readonly symbols: readonly string[];
|
|
55
57
|
readonly timeframes: readonly string[];
|
|
56
58
|
readonly baseTimeframe: string;
|
|
@@ -14,6 +14,7 @@ exports.buildCreateSweepRequest = buildCreateSweepRequest;
|
|
|
14
14
|
exports.assertSweepCreateRequestSize = assertSweepCreateRequestSize;
|
|
15
15
|
const errors_1 = require("./errors");
|
|
16
16
|
const return_curve_1 = require("./return-curve");
|
|
17
|
+
const coverage_notice_1 = require("./coverage-notice");
|
|
17
18
|
/** Aligned with `@alphafoxai/backtest-runner` DEFAULT_EXECUTION_MODEL. */
|
|
18
19
|
exports.DEFAULT_EXECUTION_MODEL = {
|
|
19
20
|
pricePath: "ohlc_path_4",
|
|
@@ -59,6 +60,7 @@ function buildCreateRunRequest(input) {
|
|
|
59
60
|
}
|
|
60
61
|
const timeframes = uniqueSeriesField(scenario.tape.series, "timeframe");
|
|
61
62
|
const engineVersion = input.engineVersion.trim() || "node-wasm";
|
|
63
|
+
const coverageIssues = (0, coverage_notice_1.snapshotCoverageIssues)(input.coverageIssues);
|
|
62
64
|
const snapshot = {
|
|
63
65
|
snapshotSchemaVersion: exports.SNAPSHOT_SCHEMA_VERSION,
|
|
64
66
|
strategyDefinitionId: scenario.trader.strategyDefinitionId,
|
|
@@ -70,6 +72,7 @@ function buildCreateRunRequest(input) {
|
|
|
70
72
|
initialEquity: scenario.exchange.initialEquity,
|
|
71
73
|
subscriptionTier: scenario.trader.subscriptionTier,
|
|
72
74
|
dataQualityMode: input.dataQualityMode,
|
|
75
|
+
...(coverageIssues ? { coverageIssues } : {}),
|
|
73
76
|
symbols,
|
|
74
77
|
timeframes: timeframes.length > 0 ? timeframes : ["1m"],
|
|
75
78
|
baseTimeframe: scenario.tape.baseTimeframe || "1m",
|
|
@@ -141,6 +144,7 @@ function buildSweepBaseSnapshot(input) {
|
|
|
141
144
|
const timeframes = [
|
|
142
145
|
...new Set(input.timeframes.map((item) => item.trim()).filter(Boolean)),
|
|
143
146
|
];
|
|
147
|
+
const coverageIssues = (0, coverage_notice_1.snapshotCoverageIssues)(input.coverageIssues);
|
|
144
148
|
return {
|
|
145
149
|
snapshotSchemaVersion: exports.SNAPSHOT_SCHEMA_VERSION,
|
|
146
150
|
strategyDefinitionId: input.definitionId,
|
|
@@ -152,6 +156,7 @@ function buildSweepBaseSnapshot(input) {
|
|
|
152
156
|
initialEquity: input.initialEquity,
|
|
153
157
|
subscriptionTier: input.subscriptionTier,
|
|
154
158
|
dataQualityMode: input.dataQualityMode,
|
|
159
|
+
...(coverageIssues ? { coverageIssues } : {}),
|
|
155
160
|
symbols,
|
|
156
161
|
timeframes: timeframes.length > 0 ? timeframes : ["1m"],
|
|
157
162
|
baseTimeframe: input.baseTimeframe.trim() || "1m",
|
|
@@ -9,6 +9,7 @@ const profiles_1 = require("../config/profiles");
|
|
|
9
9
|
const envelope_1 = require("../envelope");
|
|
10
10
|
const client_1 = require("../http/client");
|
|
11
11
|
const store_1 = require("../keychain/store");
|
|
12
|
+
const coverage_notice_1 = require("./coverage-notice");
|
|
12
13
|
const errors_1 = require("./errors");
|
|
13
14
|
const load_config_1 = require("./load-config");
|
|
14
15
|
const parse_args_1 = require("./parse-args");
|
|
@@ -336,6 +337,10 @@ async function executeEngineBacktestRun(args, flags, env = process.env, deps = {
|
|
|
336
337
|
details: issues ? { issues } : undefined,
|
|
337
338
|
});
|
|
338
339
|
}
|
|
340
|
+
tapeResult = {
|
|
341
|
+
...tapeResult,
|
|
342
|
+
coverageIssues: (0, coverage_notice_1.requireTapeCoverageIssues)(tapeResult.coverageIssues),
|
|
343
|
+
};
|
|
339
344
|
const runId = mintId();
|
|
340
345
|
const executionModel = {
|
|
341
346
|
...(runner.DEFAULT_EXECUTION_MODEL ?? persist_1.DEFAULT_EXECUTION_MODEL),
|
|
@@ -400,6 +405,7 @@ async function executeEngineBacktestRun(args, flags, env = process.env, deps = {
|
|
|
400
405
|
dataQualityMode: args.dataQualityMode,
|
|
401
406
|
engineVersion,
|
|
402
407
|
equityCurve: result.equityCurve,
|
|
408
|
+
coverageIssues: tapeResult.coverageIssues,
|
|
403
409
|
});
|
|
404
410
|
const res = await postJson(api, profile, env, runsPath(experimentId), body, mintId);
|
|
405
411
|
persistedRunId = extractEntityId(res.json);
|
|
@@ -419,7 +425,9 @@ async function executeEngineBacktestRun(args, flags, env = process.env, deps = {
|
|
|
419
425
|
runId: persistedRunId,
|
|
420
426
|
experimentUrl: (0, persist_1.experimentPageUrl)(profile.name, experimentId),
|
|
421
427
|
persisted: Boolean(persistedRunId),
|
|
422
|
-
coverageWarnings: tapeResult.coverageWarnings
|
|
428
|
+
coverageWarnings: tapeResult.coverageWarnings,
|
|
429
|
+
coverageIssues: tapeResult.coverageIssues,
|
|
430
|
+
coverageNotice: (0, coverage_notice_1.summarizeTapeCoverageNotice)(tapeResult.coverageIssues),
|
|
423
431
|
};
|
|
424
432
|
}
|
|
425
433
|
finally {
|
|
@@ -441,6 +449,7 @@ function engineBacktestHelpData() {
|
|
|
441
449
|
"runs.create is write (not high-risk-write); --yes is not required",
|
|
442
450
|
"Do not pass --token; use alphafox auth login",
|
|
443
451
|
"--replay-timeframe defaults to 1m (min 1m). Indicator series still download their native plan timeframes.",
|
|
452
|
+
"--data-quality defaults to basic: hard tape failures still stop, soft gaps finish the run and appear as coverageNotice (prefix_gap less severe, internal_gap more severe). Use --data-quality strict to fail on any gap.",
|
|
444
453
|
],
|
|
445
454
|
};
|
|
446
455
|
}
|
|
@@ -9,6 +9,7 @@ const profiles_1 = require("../config/profiles");
|
|
|
9
9
|
const client_1 = require("../http/client");
|
|
10
10
|
const paths_1 = require("../cache/paths");
|
|
11
11
|
const store_1 = require("../keychain/store");
|
|
12
|
+
const coverage_notice_1 = require("./coverage-notice");
|
|
12
13
|
const errors_1 = require("./errors");
|
|
13
14
|
const load_config_1 = require("./load-config");
|
|
14
15
|
const parse_axes_1 = require("./parse-axes");
|
|
@@ -175,6 +176,7 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
|
|
|
175
176
|
},
|
|
176
177
|
buffers: {},
|
|
177
178
|
coverageWarnings: [],
|
|
179
|
+
coverageIssues: [],
|
|
178
180
|
};
|
|
179
181
|
if (tapePlans.length > 0) {
|
|
180
182
|
try {
|
|
@@ -209,6 +211,10 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
|
|
|
209
211
|
details: issues ? { issues } : undefined,
|
|
210
212
|
});
|
|
211
213
|
}
|
|
214
|
+
tapeResult = {
|
|
215
|
+
...tapeResult,
|
|
216
|
+
coverageIssues: (0, coverage_notice_1.requireTapeCoverageIssues)(tapeResult.coverageIssues),
|
|
217
|
+
};
|
|
212
218
|
}
|
|
213
219
|
emitProgress(flags, writeLine, "tape", 1);
|
|
214
220
|
while (clients.length < concurrency) {
|
|
@@ -353,6 +359,7 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
|
|
|
353
359
|
initialEquity: args.initialEquity,
|
|
354
360
|
subscriptionTier,
|
|
355
361
|
dataQualityMode: args.dataQualityMode,
|
|
362
|
+
coverageIssues: tapeResult.coverageIssues,
|
|
356
363
|
symbols: tapeSymbols.length > 0 ? tapeSymbols : coverage.symbols,
|
|
357
364
|
timeframes: (0, persist_1.uniqueSeriesField)(tapeResult.tape.series, "timeframe"),
|
|
358
365
|
baseTimeframe: tapeResult.tape.baseTimeframe || args.replayTimeframe,
|
|
@@ -417,7 +424,9 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
|
|
|
417
424
|
experimentUrl: experimentId
|
|
418
425
|
? (0, persist_1.experimentSweepPageUrl)(profile.name, experimentId)
|
|
419
426
|
: undefined,
|
|
420
|
-
coverageWarnings: tapeResult.coverageWarnings
|
|
427
|
+
coverageWarnings: tapeResult.coverageWarnings,
|
|
428
|
+
coverageIssues: tapeResult.coverageIssues,
|
|
429
|
+
coverageNotice: (0, coverage_notice_1.summarizeTapeCoverageNotice)(tapeResult.coverageIssues),
|
|
421
430
|
axes: coarsePlan.axes,
|
|
422
431
|
};
|
|
423
432
|
}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
export type SubscriptionTier = "free" | "pro" | "pro_max";
|
|
2
2
|
export type DataQualityMode = "strict" | "basic";
|
|
3
|
+
/** New runs default to basic; `--data-quality strict` remains an explicit override. */
|
|
4
|
+
export declare const DEFAULT_DATA_QUALITY_MODE: DataQualityMode;
|
|
5
|
+
export interface TapeCoverageIssue {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly symbol: string;
|
|
8
|
+
readonly timeframe: string;
|
|
9
|
+
readonly expected?: number;
|
|
10
|
+
readonly actual?: number;
|
|
11
|
+
readonly timestamp?: number;
|
|
12
|
+
readonly message?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TapeCoverageNotice {
|
|
15
|
+
readonly severity: "none" | "notice" | "warning";
|
|
16
|
+
readonly prefix: readonly TapeCoverageIssue[];
|
|
17
|
+
readonly internal: readonly TapeCoverageIssue[];
|
|
18
|
+
readonly other: readonly TapeCoverageIssue[];
|
|
19
|
+
readonly messages: readonly string[];
|
|
20
|
+
}
|
|
3
21
|
export type PricePath = "ohlc_path_4" | "close_only";
|
|
4
22
|
export interface ExecutionModel {
|
|
5
23
|
readonly pricePath: PricePath;
|
|
@@ -202,6 +220,7 @@ export interface TapeLoadResult {
|
|
|
202
220
|
readonly tape: EngineBacktestTapeInput;
|
|
203
221
|
readonly buffers: Record<string, ArrayBuffer>;
|
|
204
222
|
readonly coverageWarnings: readonly string[];
|
|
223
|
+
readonly coverageIssues: readonly TapeCoverageIssue[];
|
|
205
224
|
}
|
|
206
225
|
export interface BacktestClientLike {
|
|
207
226
|
init(): Promise<string>;
|
|
@@ -265,6 +284,7 @@ export interface PersistedSnapshot {
|
|
|
265
284
|
readonly initialEquity: number;
|
|
266
285
|
readonly subscriptionTier: SubscriptionTier;
|
|
267
286
|
readonly dataQualityMode: DataQualityMode;
|
|
287
|
+
readonly coverageIssues?: readonly TapeCoverageIssue[];
|
|
268
288
|
readonly symbols: string[];
|
|
269
289
|
readonly timeframes: string[];
|
|
270
290
|
readonly baseTimeframe: string;
|
|
@@ -289,6 +309,8 @@ export interface EngineBacktestRunSuccess {
|
|
|
289
309
|
readonly experimentUrl: string;
|
|
290
310
|
readonly persisted: boolean;
|
|
291
311
|
readonly coverageWarnings: readonly string[];
|
|
312
|
+
readonly coverageIssues: readonly TapeCoverageIssue[];
|
|
313
|
+
readonly coverageNotice: TapeCoverageNotice;
|
|
292
314
|
}
|
|
293
315
|
export interface EngineBacktestSweepPointRow {
|
|
294
316
|
readonly coordinate: {
|
|
@@ -372,6 +394,8 @@ export interface EngineBacktestSweepSuccess {
|
|
|
372
394
|
readonly experimentId?: string;
|
|
373
395
|
readonly experimentUrl?: string;
|
|
374
396
|
readonly coverageWarnings: readonly string[];
|
|
397
|
+
readonly coverageIssues: readonly TapeCoverageIssue[];
|
|
398
|
+
readonly coverageNotice: TapeCoverageNotice;
|
|
375
399
|
readonly axes: ReadonlyArray<{
|
|
376
400
|
readonly path: readonly string[];
|
|
377
401
|
readonly current: number;
|
|
@@ -1,153 +1,153 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@alphafox/cli",
|
|
4
|
-
"packageVersion": "0.3.
|
|
4
|
+
"packageVersion": "0.3.11",
|
|
5
5
|
"contractVersion": "2026-08-13",
|
|
6
|
-
"bundleHash": "
|
|
6
|
+
"bundleHash": "10abd3fc0d5bbd4a260f42a02bff35899e829543419cf0f678077546a7bebcc6",
|
|
7
7
|
"skills": [
|
|
8
8
|
{
|
|
9
9
|
"name": "alphafox",
|
|
10
|
-
"version": "0.3.
|
|
10
|
+
"version": "0.3.11",
|
|
11
11
|
"files": [
|
|
12
12
|
{
|
|
13
13
|
"path": "SKILL.md",
|
|
14
|
-
"sha256": "
|
|
15
|
-
"size":
|
|
14
|
+
"sha256": "ebc6138b666514ef543cf5b444d3ff293bd439eae5c39a231fada5b4dae27b11",
|
|
15
|
+
"size": 4408
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
|
-
"hash": "
|
|
18
|
+
"hash": "c4c73d98a0ad828e66fccb406294ab3533530b1d003c71eebd9e569895cbf305"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "alphafox-account",
|
|
22
|
-
"version": "0.3.
|
|
22
|
+
"version": "0.3.11",
|
|
23
23
|
"files": [
|
|
24
24
|
{
|
|
25
25
|
"path": "SKILL.md",
|
|
26
|
-
"sha256": "
|
|
27
|
-
"size":
|
|
26
|
+
"sha256": "714b3f0094a67bc08764ae26f900e3f61463950b2a031f4b46a478a968deea74",
|
|
27
|
+
"size": 784
|
|
28
28
|
}
|
|
29
29
|
],
|
|
30
|
-
"hash": "
|
|
30
|
+
"hash": "94085eb4ec63d15a217df97878282c7f0c1e59c93dfd34e26279113cdcc7b625"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"name": "alphafox-admin",
|
|
34
|
-
"version": "0.3.
|
|
34
|
+
"version": "0.3.11",
|
|
35
35
|
"files": [
|
|
36
36
|
{
|
|
37
37
|
"path": "SKILL.md",
|
|
38
|
-
"sha256": "
|
|
39
|
-
"size":
|
|
38
|
+
"sha256": "dcdd0cdd11e6c4160e314766b7150b70fb0b0b6b52b4ad65723853c0ccefdd93",
|
|
39
|
+
"size": 788
|
|
40
40
|
}
|
|
41
41
|
],
|
|
42
|
-
"hash": "
|
|
42
|
+
"hash": "38f9a8eff99035a55e40e96bd156d407282aa09adcf0c08b7713b18508c112dc"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"name": "alphafox-auth",
|
|
46
|
-
"version": "0.3.
|
|
46
|
+
"version": "0.3.11",
|
|
47
47
|
"files": [
|
|
48
48
|
{
|
|
49
49
|
"path": "SKILL.md",
|
|
50
|
-
"sha256": "
|
|
51
|
-
"size":
|
|
50
|
+
"sha256": "1cc9b91b9f3994d3f0fde453fba4c08c0ff18849692518c1a73182cfc6267066",
|
|
51
|
+
"size": 2371
|
|
52
52
|
}
|
|
53
53
|
],
|
|
54
|
-
"hash": "
|
|
54
|
+
"hash": "f14d148e80c7c25b9306e57039e10b341b04c06fbe8c0602b60d49033436a149"
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"name": "alphafox-cache",
|
|
58
|
-
"version": "0.3.
|
|
58
|
+
"version": "0.3.11",
|
|
59
59
|
"files": [
|
|
60
60
|
{
|
|
61
61
|
"path": "SKILL.md",
|
|
62
|
-
"sha256": "
|
|
63
|
-
"size":
|
|
62
|
+
"sha256": "292e0075bd55f5036a6bcb3afbc6184f5e010e4840b80c1940bbaa51bf8f2a9e",
|
|
63
|
+
"size": 1530
|
|
64
64
|
}
|
|
65
65
|
],
|
|
66
|
-
"hash": "
|
|
66
|
+
"hash": "4baa604211122515fa189bb5830d4b4b0633a92b585fd91b918879828d221740"
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
"name": "alphafox-engine-backtest",
|
|
70
|
-
"version": "0.3.
|
|
70
|
+
"version": "0.3.11",
|
|
71
71
|
"files": [
|
|
72
72
|
{
|
|
73
73
|
"path": "SKILL.md",
|
|
74
|
-
"sha256": "
|
|
75
|
-
"size":
|
|
74
|
+
"sha256": "a0e00cdd48c4c4da1bbbe0d54f2b08dda273e6b6d7160e5a37485e7002218d05",
|
|
75
|
+
"size": 8278
|
|
76
76
|
}
|
|
77
77
|
],
|
|
78
|
-
"hash": "
|
|
78
|
+
"hash": "49f17cdd45fef3c600ee954bb089b239609228dd205f12edad8973c6e2a13fba"
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
"name": "alphafox-exchange",
|
|
82
|
-
"version": "0.3.
|
|
82
|
+
"version": "0.3.11",
|
|
83
83
|
"files": [
|
|
84
84
|
{
|
|
85
85
|
"path": "SKILL.md",
|
|
86
|
-
"sha256": "
|
|
87
|
-
"size":
|
|
86
|
+
"sha256": "e883d53ce237ecec682555c9171519bf16139fee0db968825a47d8cfc4ccb25b",
|
|
87
|
+
"size": 744
|
|
88
88
|
}
|
|
89
89
|
],
|
|
90
|
-
"hash": "
|
|
90
|
+
"hash": "087f26e565876d66709fb303cc6e379648a1b7de6f9f54c4015e973b8398fcb8"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
"name": "alphafox-market",
|
|
94
|
-
"version": "0.3.
|
|
94
|
+
"version": "0.3.11",
|
|
95
95
|
"files": [
|
|
96
96
|
{
|
|
97
97
|
"path": "SKILL.md",
|
|
98
|
-
"sha256": "
|
|
99
|
-
"size":
|
|
98
|
+
"sha256": "d37df5d1498d201abd9206f5536ffbbf35fa20ec7f47aa20da40f07abcd3f847",
|
|
99
|
+
"size": 3079
|
|
100
100
|
}
|
|
101
101
|
],
|
|
102
|
-
"hash": "
|
|
102
|
+
"hash": "dc57e16172f577410f80922436593ab5ac5d1d2ab62402fb2c8ee7d8bac84cbd"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"name": "alphafox-notification",
|
|
106
|
-
"version": "0.3.
|
|
106
|
+
"version": "0.3.11",
|
|
107
107
|
"files": [
|
|
108
108
|
{
|
|
109
109
|
"path": "SKILL.md",
|
|
110
|
-
"sha256": "
|
|
111
|
-
"size":
|
|
110
|
+
"sha256": "0fdcc37a21deaee5195c2576ce563b895dc7aac2af2e269a38b2506860fbb5fc",
|
|
111
|
+
"size": 699
|
|
112
112
|
}
|
|
113
113
|
],
|
|
114
|
-
"hash": "
|
|
114
|
+
"hash": "c4acd5b245e6c1f21f1d35d96a33ffe8c1a4754c8405c92c54180765f14072ea"
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
117
|
"name": "alphafox-shared",
|
|
118
|
-
"version": "0.3.
|
|
118
|
+
"version": "0.3.11",
|
|
119
119
|
"files": [
|
|
120
120
|
{
|
|
121
121
|
"path": "SKILL.md",
|
|
122
|
-
"sha256": "
|
|
123
|
-
"size":
|
|
122
|
+
"sha256": "cfd0716870fb6bcadbb20c7cad1999f32f8f237cfebb1fa143f6a49687256568",
|
|
123
|
+
"size": 5386
|
|
124
124
|
}
|
|
125
125
|
],
|
|
126
|
-
"hash": "
|
|
126
|
+
"hash": "939e048cd4243a13a341d0405a13f40d82dd6553a2db5e42765d90dae909331d"
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
"name": "alphafox-strategy",
|
|
130
|
-
"version": "0.3.
|
|
130
|
+
"version": "0.3.11",
|
|
131
131
|
"files": [
|
|
132
132
|
{
|
|
133
133
|
"path": "SKILL.md",
|
|
134
|
-
"sha256": "
|
|
135
|
-
"size":
|
|
134
|
+
"sha256": "be35901e6d5c3766f73274552e5403e172a9c3ce9d560fbfd78cb68fd1c894c1",
|
|
135
|
+
"size": 1827
|
|
136
136
|
}
|
|
137
137
|
],
|
|
138
|
-
"hash": "
|
|
138
|
+
"hash": "e031e199ee2c1013e2bc39d2fc2a72b9f734ede86b3ca93d2a3ca53ace1c65cf"
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
"name": "alphafox-trading",
|
|
142
|
-
"version": "0.3.
|
|
142
|
+
"version": "0.3.11",
|
|
143
143
|
"files": [
|
|
144
144
|
{
|
|
145
145
|
"path": "SKILL.md",
|
|
146
|
-
"sha256": "
|
|
147
|
-
"size":
|
|
146
|
+
"sha256": "2bac318aae54820edf6454c98e8ce4a16b350fce8e0e29c936ea434a72a0857d",
|
|
147
|
+
"size": 3123
|
|
148
148
|
}
|
|
149
149
|
],
|
|
150
|
-
"hash": "
|
|
150
|
+
"hash": "2da4c6f7a381472f1d82118d5e44e3c52a1a6e7185b2f9e5a16181aa5f1c89bd"
|
|
151
151
|
}
|
|
152
152
|
]
|
|
153
153
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const CLI_NAME = "alphafox";
|
|
2
2
|
export declare const CLI_PACKAGE = "@alphafox/cli";
|
|
3
|
-
export declare const CLI_VERSION = "0.3.
|
|
3
|
+
export declare const CLI_VERSION = "0.3.11";
|
|
4
4
|
export { CATALOG_VERSION as CLI_CONTRACT_VERSION } from "./catalog/operations";
|
package/dist/version.js
CHANGED
|
@@ -3,6 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CLI_CONTRACT_VERSION = exports.CLI_VERSION = exports.CLI_PACKAGE = exports.CLI_NAME = void 0;
|
|
4
4
|
exports.CLI_NAME = "alphafox";
|
|
5
5
|
exports.CLI_PACKAGE = "@alphafox/cli";
|
|
6
|
-
exports.CLI_VERSION = "0.3.
|
|
6
|
+
exports.CLI_VERSION = "0.3.11";
|
|
7
7
|
var operations_1 = require("./catalog/operations");
|
|
8
8
|
Object.defineProperty(exports, "CLI_CONTRACT_VERSION", { enumerable: true, get: function () { return operations_1.CATALOG_VERSION; } });
|
package/docs/e2e-staging.md
CHANGED
|
@@ -10,13 +10,15 @@ Staging CLI issuer: `https://staging.alphafox.app`. Test login (staging-only, ga
|
|
|
10
10
|
2. Device Flow split (`--no-wait` + `--device-code`)
|
|
11
11
|
3. `whoami` / `auth status --verify`
|
|
12
12
|
4. Readonly: strategy definitions, connectors, traders
|
|
13
|
-
5. Write: create chat with idempotency
|
|
14
|
-
6. Backtest create → watch stream → cancel
|
|
13
|
+
5. ~~Write: create chat with idempotency~~ **historical** — chat is not a CLI surface; do not use `chats.create` as the trader-create path
|
|
14
|
+
6. ~~Backtest create → watch stream → cancel~~ **historical** — web `/api/v1/backtests` is not a CLI surface; use `engine-backtest`
|
|
15
15
|
7. High-risk without `--yes` → exit 10
|
|
16
16
|
8. Logout / revoke
|
|
17
17
|
9. Cross-env token rejection (prod token on staging)
|
|
18
18
|
|
|
19
|
-
## Evidence (2026-08-13) — vertical slice pass
|
|
19
|
+
## Evidence (2026-08-13) — vertical slice pass (historical chat / web-backtest slice)
|
|
20
|
+
|
|
21
|
+
The chat and `POST /api/v1/backtests` steps below are a **historical** facade record. They are not the CLI trader-create path. Current create is Engine `trading.traders.create` (`strategyDefinitionId` + `config`). Do not treat this section as a playbook.
|
|
20
22
|
|
|
21
23
|
Anonymous `GET https://staging.alphafox.app/api/v1/meta` → **HTTP 200**, `environment=staging`, `contractVersion=2026-08-13`, `commitSha=65d1f816007adc7acc09db5e86671931737e0379`, `x-request-id=66511bcf-dda9-4974-9378-3aacd8d938ff`. No 302 to `vercel.com/sso-api`. Vercel Authentication is disabled for the whole `alphafox-web` project (Preview `*.vercel.app` is also public; accepted).
|
|
22
24
|
|
|
@@ -66,7 +68,7 @@ Reused staging Device Flow session `userId=019f3073-307f-76e9-adf1-0203af9ab22b`
|
|
|
66
68
|
- **Backtest vertical slice:** cannot `create → stream → cancel` until a chat has an integer `strategyId`, and until the facade body matches contracts (`backtestSettings`) or the catalog is updated to `{chatId, strategyId}`.
|
|
67
69
|
- Parent Feishu task and t101364 (production publish / npm `latest` / production OAuth) stay **todo**. Do not merge web PR 448 to production `main` from this evidence.
|
|
68
70
|
|
|
69
|
-
## Evidence (2026-08-13) — gapfix retest (idempotency + backtests.create)
|
|
71
|
+
## Evidence (2026-08-13) — gapfix retest (historical; idempotency + backtests.create)
|
|
70
72
|
|
|
71
73
|
Anonymous `GET https://staging.alphafox.app/api/v1/meta` → **HTTP 200**, `environment=staging`, `contractVersion=2026-08-13`, `commitSha=fba21ef4b2c3909c51a5a19e2d2a45b30d1d598c`, `x-request-id=8702a21b-a749-4007-82eb-76ca1dd0caaf`. No SSO redirect. Device Flow `test@local.com`: approve `requestId=77293035-ebfd-4b03-b0e1-0779dce81fdd`, token `requestId=9dcc28c1-c1cc-49fc-9435-687dfdb591c2`, `whoami` `userId=019f3073-307f-76e9-adf1-0203af9ab22b` `requestId=1473f107-d742-42eb-99e7-b60c6b583928`. CLI local `92c8610250b30e7881f79a2fde60b00fe50b4628`, catalog `contractsSha=d1f184e3d72581f155497978880d9ab3029ff858`. Staging llm-gateway image `0a10d8e3a1304b04eff2f21c503922a5b7c11491` (workflow `31683345740` failed after the container was healthy: `/opt/alphafox/images/alphafox-llm-gateway.env` permission denied). Do not merge web PR 448 to production `main`.
|
|
72
74
|
|
package/package.json
CHANGED
package/skills/account/SKILL.md
CHANGED