@alphafox/cli 0.3.3 → 0.3.4

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 (67) hide show
  1. package/README.md +4 -3
  2. package/dist/catalog/allowlist.d.ts +1 -1
  3. package/dist/catalog/allowlist.js +1 -1
  4. package/dist/catalog/generated/registry.json +338 -49
  5. package/dist/catalog/generated/schemas.json +1880 -779
  6. package/dist/catalog/omit.d.ts +6 -0
  7. package/dist/catalog/omit.js +11 -0
  8. package/dist/catalog/operations.d.ts +1 -1
  9. package/dist/catalog/operations.js +5 -2
  10. package/dist/commands/run.js +8 -1
  11. package/dist/engine-backtest/load-config.d.ts +4 -0
  12. package/dist/engine-backtest/load-config.js +44 -0
  13. package/dist/engine-backtest/parse-args.d.ts +3 -1
  14. package/dist/engine-backtest/parse-args.js +87 -3
  15. package/dist/engine-backtest/parse-axes.d.ts +3 -0
  16. package/dist/engine-backtest/parse-axes.js +167 -0
  17. package/dist/engine-backtest/persist.d.ts +60 -1
  18. package/dist/engine-backtest/persist.js +196 -1
  19. package/dist/engine-backtest/return-curve.d.ts +27 -0
  20. package/dist/engine-backtest/return-curve.js +80 -0
  21. package/dist/engine-backtest/run-command.d.ts +1 -4
  22. package/dist/engine-backtest/run-command.js +61 -45
  23. package/dist/engine-backtest/sweep-command.d.ts +13 -0
  24. package/dist/engine-backtest/sweep-command.js +749 -0
  25. package/dist/engine-backtest/sweep-kernel/caps.d.ts +26 -0
  26. package/dist/engine-backtest/sweep-kernel/caps.js +48 -0
  27. package/dist/engine-backtest/sweep-kernel/index.d.ts +13 -0
  28. package/dist/engine-backtest/sweep-kernel/index.js +34 -0
  29. package/dist/engine-backtest/sweep-kernel/plan.d.ts +22 -0
  30. package/dist/engine-backtest/sweep-kernel/plan.js +320 -0
  31. package/dist/engine-backtest/sweep-kernel/results.d.ts +72 -0
  32. package/dist/engine-backtest/sweep-kernel/results.js +150 -0
  33. package/dist/engine-backtest/sweep-kernel/session.d.ts +55 -0
  34. package/dist/engine-backtest/sweep-kernel/session.js +56 -0
  35. package/dist/engine-backtest/sweep-kernel/types.d.ts +36 -0
  36. package/dist/engine-backtest/sweep-kernel/types.js +2 -0
  37. package/dist/engine-backtest/types.d.ts +130 -0
  38. package/dist/install/wizard.js +1 -2
  39. package/dist/resolve-symbols/catalog.d.ts +3 -0
  40. package/dist/resolve-symbols/catalog.js +50 -0
  41. package/dist/resolve-symbols/errors.d.ts +18 -0
  42. package/dist/resolve-symbols/errors.js +26 -0
  43. package/dist/resolve-symbols/exchanges.d.ts +8 -0
  44. package/dist/resolve-symbols/exchanges.js +53 -0
  45. package/dist/resolve-symbols/match.d.ts +6 -0
  46. package/dist/resolve-symbols/match.js +250 -0
  47. package/dist/resolve-symbols/parse-args.d.ts +5 -0
  48. package/dist/resolve-symbols/parse-args.js +106 -0
  49. package/dist/resolve-symbols/run-command.d.ts +21 -0
  50. package/dist/resolve-symbols/run-command.js +149 -0
  51. package/dist/resolve-symbols/types.d.ts +37 -0
  52. package/dist/resolve-symbols/types.js +2 -0
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/docs/alphafox-cli-installation-guide.md +2 -2
  56. package/package.json +1 -1
  57. package/skills/account/SKILL.md +1 -1
  58. package/skills/admin/SKILL.md +1 -1
  59. package/skills/alphafox/SKILL.md +38 -0
  60. package/skills/alphafox-shared/SKILL.md +5 -1
  61. package/skills/auth/SKILL.md +1 -1
  62. package/skills/engine-backtest/SKILL.md +48 -6
  63. package/skills/exchange/SKILL.md +1 -1
  64. package/skills/market/SKILL.md +27 -4
  65. package/skills/notification/SKILL.md +1 -1
  66. package/skills/strategy/SKILL.md +7 -20
  67. package/skills/trading/SKILL.md +3 -1
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractSweepMetrics = extractSweepMetrics;
4
+ exports.selectBestNonLiquidatedPoint = selectBestNonLiquidatedPoint;
5
+ exports.resolveIslandRiskLevel = resolveIslandRiskLevel;
6
+ exports.analyzeSweepIsland = analyzeSweepIsland;
7
+ function extractSweepMetrics(source) {
8
+ return {
9
+ returnPct: source.returnPct,
10
+ maxDrawdownPct: source.maxDrawdownPct,
11
+ sharpeRatio: source.sharpeRatio,
12
+ winRatePct: source.winRatePct,
13
+ ...(source.maxLeverage === undefined
14
+ ? {}
15
+ : { maxLeverage: source.maxLeverage }),
16
+ liquidationCount: typeof source.liquidationCount === "number" &&
17
+ Number.isFinite(source.liquidationCount)
18
+ ? source.liquidationCount
19
+ : source.liquidated
20
+ ? 1
21
+ : 0,
22
+ netPnl: source.netPnl,
23
+ finalEquity: source.finalEquity,
24
+ tradeCount: source.tradeCount,
25
+ };
26
+ }
27
+ function selectBestNonLiquidatedPoint(points) {
28
+ const candidates = points.filter(isOkNonLiquidatedPoint);
29
+ if (candidates.length === 0) {
30
+ return null;
31
+ }
32
+ const best = candidates.reduce((leader, point) => point.metrics.returnPct > leader.metrics.returnPct ? point : leader);
33
+ return {
34
+ coordinate: best.coordinate,
35
+ returnPct: best.metrics.returnPct,
36
+ };
37
+ }
38
+ function resolveIslandRiskLevel(score) {
39
+ if (score >= 80) {
40
+ return "high";
41
+ }
42
+ if (score >= 50) {
43
+ return "medium";
44
+ }
45
+ if (score >= 30) {
46
+ return "low_medium";
47
+ }
48
+ return "low";
49
+ }
50
+ /**
51
+ * Score each swept point by how sharply it deviates from its neighbors, then
52
+ * flag the point closest to `centerValue` when it sits on a fragile island.
53
+ */
54
+ function analyzeSweepIsland(points, centerValue) {
55
+ const rows = [...points].sort((left, right) => left.value - right.value);
56
+ if (rows.length === 0) {
57
+ return {
58
+ points: [],
59
+ warning: null,
60
+ overallScore: 0,
61
+ overallRiskLevel: "low",
62
+ };
63
+ }
64
+ const returnStd = populationStd(rows.map((row) => row.returnPct));
65
+ const drawdownStd = populationStd(rows.map((row) => row.maxDrawdownPct));
66
+ const centerIndex = rows.reduce((bestIndex, row, index) => Math.abs(row.value - centerValue) <
67
+ Math.abs(rows[bestIndex].value - centerValue)
68
+ ? index
69
+ : bestIndex, 0);
70
+ const scored = rows.map((row, index) => {
71
+ const neighbors = [index - 1, index + 1]
72
+ .filter((i) => i >= 0 && i < rows.length)
73
+ .map((i) => rows[i]);
74
+ const score = scoreNeighborhood(row, neighbors, returnStd, drawdownStd);
75
+ return {
76
+ ...row,
77
+ isCenter: index === centerIndex,
78
+ islandScore: Number(score.toFixed(2)),
79
+ riskLevel: resolveIslandRiskLevel(score),
80
+ };
81
+ });
82
+ const center = scored[centerIndex];
83
+ const centerNeighbors = [centerIndex - 1, centerIndex + 1]
84
+ .filter((i) => i >= 0 && i < scored.length)
85
+ .map((i) => scored[i]);
86
+ const hasLiquidationJump = center.liquidationCount === 0 &&
87
+ centerNeighbors.some((row) => row.liquidationCount > 0);
88
+ const warning = hasLiquidationJump || center.islandScore >= 50
89
+ ? {
90
+ centerValue: center.value,
91
+ islandScore: center.islandScore,
92
+ riskLevel: center.riskLevel,
93
+ counterexamples: centerNeighbors
94
+ .filter((row) => row.liquidationCount > center.liquidationCount)
95
+ .map((row) => ({
96
+ counterValue: row.value,
97
+ centerReturnPct: center.returnPct,
98
+ counterReturnPct: row.returnPct,
99
+ centerLiquidationCount: center.liquidationCount,
100
+ counterLiquidationCount: row.liquidationCount,
101
+ })),
102
+ }
103
+ : null;
104
+ const overallScore = scored.reduce((highest, row) => Math.max(highest, row.islandScore), 0);
105
+ return {
106
+ points: scored,
107
+ warning,
108
+ overallScore,
109
+ overallRiskLevel: resolveIslandRiskLevel(overallScore),
110
+ };
111
+ }
112
+ function isOkNonLiquidatedPoint(point) {
113
+ return (point.status === "ok" &&
114
+ point.metrics !== undefined &&
115
+ Number.isFinite(point.metrics.returnPct) &&
116
+ Number.isFinite(point.metrics.liquidationCount) &&
117
+ point.metrics.liquidationCount === 0);
118
+ }
119
+ function scoreNeighborhood(row, neighbors, returnStd, drawdownStd) {
120
+ if (neighbors.length === 0) {
121
+ return 0;
122
+ }
123
+ const neighborReturn = average(neighbors.map((n) => n.returnPct));
124
+ const neighborDrawdown = average(neighbors.map((n) => n.maxDrawdownPct));
125
+ const neighborLiquidations = average(neighbors.map((n) => n.liquidationCount));
126
+ const returnAdvantage = (Math.min(Math.max(row.returnPct - neighborReturn, 0) / Math.max(returnStd, 0.5), 2) /
127
+ 2) *
128
+ 100;
129
+ const drawdownAdvantage = (Math.min(Math.max(neighborDrawdown - row.maxDrawdownPct, 0) /
130
+ Math.max(drawdownStd, 0.1), 2) /
131
+ 2) *
132
+ 100;
133
+ const liquidationJump = (Math.min(Math.max(neighborLiquidations - row.liquidationCount, 0), 3) /
134
+ 3) *
135
+ 100;
136
+ return (0.35 * returnAdvantage + 0.3 * drawdownAdvantage + 0.25 * liquidationJump);
137
+ }
138
+ function average(values) {
139
+ if (values.length === 0) {
140
+ return 0;
141
+ }
142
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
143
+ }
144
+ function populationStd(values) {
145
+ if (values.length < 2) {
146
+ return 0;
147
+ }
148
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
149
+ return Math.sqrt(values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / values.length);
150
+ }
@@ -0,0 +1,55 @@
1
+ export type SweepExecutionState = {
2
+ readonly status: "idle";
3
+ } | {
4
+ readonly status: "running";
5
+ readonly total: number;
6
+ } | {
7
+ readonly status: "succeeded";
8
+ readonly resultId: string;
9
+ } | {
10
+ readonly status: "failed";
11
+ readonly message: string;
12
+ } | {
13
+ readonly status: "cancelled";
14
+ };
15
+ export type SweepCloudSaveState = {
16
+ readonly status: "idle";
17
+ } | {
18
+ readonly status: "saving";
19
+ } | {
20
+ readonly status: "saved";
21
+ readonly sweepId: string;
22
+ } | {
23
+ readonly status: "failed";
24
+ readonly message: string;
25
+ };
26
+ export interface SweepSessionState {
27
+ readonly execution: SweepExecutionState;
28
+ readonly cloudSave: SweepCloudSaveState;
29
+ }
30
+ export type SweepSessionEvent = {
31
+ readonly type: "execution-started";
32
+ readonly total: number;
33
+ } | {
34
+ readonly type: "execution-succeeded";
35
+ readonly resultId: string;
36
+ } | {
37
+ readonly type: "execution-failed";
38
+ readonly message: string;
39
+ } | {
40
+ readonly type: "execution-cancelled";
41
+ } | {
42
+ readonly type: "execution-reset";
43
+ } | {
44
+ readonly type: "cloud-save-started";
45
+ } | {
46
+ readonly type: "cloud-save-succeeded";
47
+ readonly sweepId: string;
48
+ } | {
49
+ readonly type: "cloud-save-failed";
50
+ readonly message: string;
51
+ } | {
52
+ readonly type: "cloud-save-retry";
53
+ };
54
+ export declare function createSweepSessionState(): SweepSessionState;
55
+ export declare function reduceSweepSession(state: SweepSessionState, event: SweepSessionEvent): SweepSessionState;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSweepSessionState = createSweepSessionState;
4
+ exports.reduceSweepSession = reduceSweepSession;
5
+ const IDLE_CLOUD_SAVE = { status: "idle" };
6
+ function createSweepSessionState() {
7
+ return {
8
+ execution: { status: "idle" },
9
+ cloudSave: IDLE_CLOUD_SAVE,
10
+ };
11
+ }
12
+ function reduceSweepSession(state, event) {
13
+ switch (event.type) {
14
+ case "execution-started":
15
+ return {
16
+ execution: { status: "running", total: event.total },
17
+ cloudSave: IDLE_CLOUD_SAVE,
18
+ };
19
+ case "execution-succeeded":
20
+ return {
21
+ execution: { status: "succeeded", resultId: event.resultId },
22
+ cloudSave: IDLE_CLOUD_SAVE,
23
+ };
24
+ case "execution-failed":
25
+ return {
26
+ execution: { status: "failed", message: event.message },
27
+ cloudSave: IDLE_CLOUD_SAVE,
28
+ };
29
+ case "execution-cancelled":
30
+ return {
31
+ execution: { status: "cancelled" },
32
+ cloudSave: IDLE_CLOUD_SAVE,
33
+ };
34
+ case "execution-reset":
35
+ return createSweepSessionState();
36
+ case "cloud-save-started":
37
+ case "cloud-save-retry":
38
+ return applyCloudSave(state, { status: "saving" });
39
+ case "cloud-save-succeeded":
40
+ return applyCloudSave(state, {
41
+ status: "saved",
42
+ sweepId: event.sweepId,
43
+ });
44
+ case "cloud-save-failed":
45
+ return applyCloudSave(state, {
46
+ status: "failed",
47
+ message: event.message,
48
+ });
49
+ }
50
+ }
51
+ function applyCloudSave(state, cloudSave) {
52
+ if (state.execution.status !== "succeeded") {
53
+ return state;
54
+ }
55
+ return { ...state, cloudSave };
56
+ }
@@ -0,0 +1,36 @@
1
+ export type SweepSubscriptionTier = "free" | "pro" | "pro_max";
2
+ export type SweepSearchMode = "standard" | "fast";
3
+ export interface SweepCoordinate {
4
+ readonly values: readonly number[];
5
+ }
6
+ export interface SweepAxisWindow {
7
+ readonly min: number;
8
+ readonly max: number;
9
+ readonly step: number;
10
+ }
11
+ /** Explicit values, or a min/max/step window around the current value. */
12
+ export interface SweepAxisInput {
13
+ readonly path: readonly string[];
14
+ readonly current: number;
15
+ readonly isInteger: boolean;
16
+ readonly min?: number;
17
+ readonly max?: number;
18
+ readonly minExclusive?: boolean;
19
+ readonly maxExclusive?: boolean;
20
+ readonly values?: readonly number[];
21
+ readonly window?: SweepAxisWindow;
22
+ }
23
+ export interface SweepPlanAxis {
24
+ readonly path: readonly string[];
25
+ readonly current: number;
26
+ readonly values: readonly number[];
27
+ }
28
+ export interface SweepPlan {
29
+ readonly axes: readonly SweepPlanAxis[];
30
+ readonly coordinates: readonly SweepCoordinate[];
31
+ readonly requestedCombinationCount: number;
32
+ readonly sampled: boolean;
33
+ }
34
+ export type SweepConfigRecord = {
35
+ readonly [key: string]: unknown;
36
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -34,6 +34,14 @@ export interface EngineBacktestRunArgs {
34
34
  /** Replay/download bar. Defaults to 1m; never finer. */
35
35
  readonly replayTimeframe: string;
36
36
  }
37
+ export type SweepMode = "neighborhood" | "range";
38
+ export type SweepSearchMode = "standard" | "fast";
39
+ export interface EngineBacktestSweepArgs extends EngineBacktestRunArgs {
40
+ readonly axesRaw?: string;
41
+ readonly mode: SweepMode;
42
+ readonly searchMode: SweepSearchMode;
43
+ readonly concurrency: number;
44
+ }
37
45
  export interface EngineBacktestSeriesRequirement {
38
46
  readonly symbol: string;
39
47
  readonly timeframe: string;
@@ -105,6 +113,37 @@ export interface EngineBacktestResult {
105
113
  }>;
106
114
  readonly warnings?: string[];
107
115
  }
116
+ export interface EngineBacktestBatchVariant {
117
+ readonly runId: string;
118
+ readonly config: unknown;
119
+ }
120
+ export interface EngineBacktestBatchRequest {
121
+ readonly version: 1;
122
+ readonly batchId: string;
123
+ readonly baseScenario: Omit<EngineBacktestScenario, "tape">;
124
+ readonly variants: readonly EngineBacktestBatchVariant[];
125
+ readonly tape: EngineBacktestTapeInput;
126
+ }
127
+ export interface EngineBacktestBatchPointResult {
128
+ readonly runId: string;
129
+ readonly status: "completed" | "failed";
130
+ readonly metrics: EngineBacktestMetrics;
131
+ readonly errors?: Array<{
132
+ code: string;
133
+ message: string;
134
+ path?: string;
135
+ }>;
136
+ }
137
+ export interface EngineBacktestBatchResult {
138
+ readonly batchId: string;
139
+ readonly status: "completed" | "failed";
140
+ readonly results: readonly EngineBacktestBatchPointResult[];
141
+ readonly errors?: Array<{
142
+ code: string;
143
+ message: string;
144
+ path?: string;
145
+ }>;
146
+ }
108
147
  export interface EngineBacktestSupportReason {
109
148
  readonly code: string;
110
149
  readonly message: string;
@@ -180,6 +219,7 @@ export interface BacktestClientLike {
180
219
  readonly config: unknown;
181
220
  }): Promise<EngineBacktestPlan | EngineBacktestFailure>;
182
221
  runBacktest(scenario: EngineBacktestScenario, buffers: Record<string, ArrayBuffer>, onProgress?: (fraction: number) => void): Promise<EngineBacktestResult>;
222
+ runBacktestBatch(batch: EngineBacktestBatchRequest, buffers: Record<string, ArrayBuffer>, onProgress?: (fraction: number) => void): Promise<EngineBacktestBatchResult>;
183
223
  terminate(): void;
184
224
  }
185
225
  export interface BacktestWasmModule {
@@ -237,6 +277,8 @@ export interface CreateRunRequestBody {
237
277
  readonly metrics: EngineBacktestMetrics;
238
278
  readonly engineVersion: string;
239
279
  readonly configSchemaVersion: number;
280
+ /** Compact [[unix_ms, cumulative_return]]; omitted when the local run has no series. */
281
+ readonly returnCurve?: ReadonlyArray<readonly [number, number]>;
240
282
  }
241
283
  export interface EngineBacktestRunSuccess {
242
284
  readonly metrics: EngineBacktestMetrics;
@@ -247,3 +289,91 @@ export interface EngineBacktestRunSuccess {
247
289
  readonly persisted: boolean;
248
290
  readonly coverageWarnings: readonly string[];
249
291
  }
292
+ export interface EngineBacktestSweepPointRow {
293
+ readonly coordinate: {
294
+ readonly values: readonly number[];
295
+ };
296
+ readonly status: "ok" | "failed";
297
+ readonly metrics?: {
298
+ readonly returnPct: number;
299
+ readonly maxDrawdownPct: number;
300
+ readonly sharpeRatio: number;
301
+ readonly winRatePct: number;
302
+ readonly maxLeverage?: number;
303
+ readonly liquidationCount: number;
304
+ readonly netPnl: number;
305
+ readonly finalEquity: number;
306
+ readonly tradeCount: number;
307
+ };
308
+ readonly error?: string;
309
+ }
310
+ export interface CreateSweepPointSummary {
311
+ readonly coordinate: {
312
+ readonly values: readonly number[];
313
+ };
314
+ readonly status: "ok" | "failed";
315
+ readonly error?: string;
316
+ readonly metrics?: EngineBacktestSweepPointRow["metrics"];
317
+ }
318
+ export interface CreateSweepRequestBody {
319
+ readonly clientSweepId: string;
320
+ readonly baseInputSnapshot: PersistedSnapshot;
321
+ readonly axes: ReadonlyArray<{
322
+ readonly path: readonly string[];
323
+ readonly current: number;
324
+ readonly values: readonly number[];
325
+ }>;
326
+ readonly searchMetadata: {
327
+ readonly mode: SweepMode;
328
+ readonly searchMode: SweepSearchMode;
329
+ readonly concurrency: number;
330
+ readonly requestedCombinationCount: number;
331
+ readonly sampled: boolean;
332
+ };
333
+ readonly aggregateSummary: {
334
+ readonly successfulCount: number;
335
+ readonly failedCount: number;
336
+ readonly liquidatedCount: number;
337
+ readonly elapsedMs: number;
338
+ readonly best: {
339
+ readonly coordinate: {
340
+ readonly values: readonly number[];
341
+ };
342
+ readonly returnPct: number;
343
+ } | null;
344
+ };
345
+ readonly pointSummaries: readonly CreateSweepPointSummary[];
346
+ readonly engineVersion: string;
347
+ readonly configSchemaVersion: number;
348
+ }
349
+ export interface EngineBacktestSweepSuccess {
350
+ readonly persisted: boolean;
351
+ readonly sweepId?: string;
352
+ readonly clientSweepId?: string;
353
+ readonly mode: SweepMode;
354
+ readonly searchMode: SweepSearchMode;
355
+ readonly requestedCombinationCount: number;
356
+ readonly sampled: boolean;
357
+ readonly combinationCount: number;
358
+ readonly successfulCount: number;
359
+ readonly failedCount: number;
360
+ readonly liquidatedCount: number;
361
+ readonly elapsedMs: number;
362
+ readonly best: {
363
+ readonly coordinate: {
364
+ readonly values: readonly number[];
365
+ };
366
+ readonly returnPct: number;
367
+ readonly config: unknown;
368
+ } | null;
369
+ readonly points: readonly EngineBacktestSweepPointRow[];
370
+ readonly engineVersion: string;
371
+ readonly experimentId?: string;
372
+ readonly experimentUrl?: string;
373
+ readonly coverageWarnings: readonly string[];
374
+ readonly axes: ReadonlyArray<{
375
+ readonly path: readonly string[];
376
+ readonly current: number;
377
+ readonly values: readonly number[];
378
+ }>;
379
+ }
@@ -49,8 +49,7 @@ function parseNpmListVersion(output) {
49
49
  return match?.[1] ?? null;
50
50
  }
51
51
  function skillsListHasAlphafox(output) {
52
- const prefix = types_1.SKILLS_NAME_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
53
- return new RegExp(`(^|\\s)${prefix}[\\w-]+`, "m").test(output);
52
+ return /(^|\s)alphafox(?:-[\w-]+)?(?=\s|$)/m.test(output);
54
53
  }
55
54
  function nextSteps(input) {
56
55
  const steps = ["请重启 AI 工具,以便加载刚安装的 Skills。"];
@@ -0,0 +1,3 @@
1
+ export declare const MARKET_SYMBOLS_PATH = "/api/v1/market/symbols";
2
+ export declare function marketSymbolsPath(exchangeId: string): string;
3
+ export declare function extractCatalogSymbols(json: unknown): string[];
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MARKET_SYMBOLS_PATH = void 0;
4
+ exports.marketSymbolsPath = marketSymbolsPath;
5
+ exports.extractCatalogSymbols = extractCatalogSymbols;
6
+ const errors_1 = require("./errors");
7
+ exports.MARKET_SYMBOLS_PATH = "/api/v1/market/symbols";
8
+ function marketSymbolsPath(exchangeId) {
9
+ const query = new URLSearchParams({ exchange: exchangeId }).toString();
10
+ return `${exports.MARKET_SYMBOLS_PATH}?${query}`;
11
+ }
12
+ function extractCatalogSymbols(json) {
13
+ const root = asRecord(json);
14
+ const payload = asRecord(root?.data) ?? root;
15
+ const symbols = payload?.symbols;
16
+ if (!Array.isArray(symbols)) {
17
+ throw new errors_1.ResolveSymbolsError({
18
+ type: "runtime",
19
+ subtype: "catalog_invalid",
20
+ message: "market.symbols.list response did not include a symbols array",
21
+ details: json,
22
+ });
23
+ }
24
+ const out = [];
25
+ const seen = new Set();
26
+ for (const item of symbols) {
27
+ if (typeof item !== "string")
28
+ continue;
29
+ const trimmed = item.trim();
30
+ if (!trimmed || seen.has(trimmed))
31
+ continue;
32
+ seen.add(trimmed);
33
+ out.push(trimmed);
34
+ }
35
+ if (out.length === 0) {
36
+ throw new errors_1.ResolveSymbolsError({
37
+ type: "runtime",
38
+ subtype: "catalog_empty",
39
+ message: "market.symbols.list returned no contract symbols",
40
+ details: json,
41
+ });
42
+ }
43
+ return out;
44
+ }
45
+ function asRecord(value) {
46
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
47
+ return undefined;
48
+ }
49
+ return value;
50
+ }
@@ -0,0 +1,18 @@
1
+ export declare class ResolveSymbolsError extends Error {
2
+ readonly type: string;
3
+ readonly subtype?: string;
4
+ readonly status?: number;
5
+ readonly code?: string | number;
6
+ readonly hint?: string;
7
+ readonly details?: unknown;
8
+ constructor(input: {
9
+ readonly message: string;
10
+ readonly type?: string;
11
+ readonly subtype?: string;
12
+ readonly status?: number;
13
+ readonly code?: string | number;
14
+ readonly hint?: string;
15
+ readonly details?: unknown;
16
+ });
17
+ }
18
+ export declare function isResolveSymbolsError(value: unknown): value is ResolveSymbolsError;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResolveSymbolsError = void 0;
4
+ exports.isResolveSymbolsError = isResolveSymbolsError;
5
+ class ResolveSymbolsError extends Error {
6
+ type;
7
+ subtype;
8
+ status;
9
+ code;
10
+ hint;
11
+ details;
12
+ constructor(input) {
13
+ super(input.message);
14
+ this.name = "ResolveSymbolsError";
15
+ this.type = input.type ?? "runtime";
16
+ this.subtype = input.subtype;
17
+ this.status = input.status;
18
+ this.code = input.code;
19
+ this.hint = input.hint;
20
+ this.details = input.details;
21
+ }
22
+ }
23
+ exports.ResolveSymbolsError = ResolveSymbolsError;
24
+ function isResolveSymbolsError(value) {
25
+ return value instanceof ResolveSymbolsError;
26
+ }
@@ -0,0 +1,8 @@
1
+ export declare const RESOLVE_SYMBOLS_DEFAULT_EXCHANGE = "binance_perp_usdt";
2
+ export interface ResolveSymbolsExchange {
3
+ readonly id: string;
4
+ readonly label: string;
5
+ readonly aliases: readonly string[];
6
+ }
7
+ export declare const RESOLVE_SYMBOLS_EXCHANGES: readonly ResolveSymbolsExchange[];
8
+ export declare function resolveSymbolsExchangeId(raw: string): ResolveSymbolsExchange;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESOLVE_SYMBOLS_EXCHANGES = exports.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE = void 0;
4
+ exports.resolveSymbolsExchangeId = resolveSymbolsExchangeId;
5
+ exports.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE = "binance_perp_usdt";
6
+ exports.RESOLVE_SYMBOLS_EXCHANGES = [
7
+ {
8
+ id: "binance_perp_usdt",
9
+ label: "Binance",
10
+ aliases: ["binance", "binanceusdm", "binance_perp_usdt"],
11
+ },
12
+ {
13
+ id: "okx_perp_usdt",
14
+ label: "OKX",
15
+ aliases: ["okx", "okx_perp_usdt"],
16
+ },
17
+ {
18
+ id: "bybit_perp_usdt",
19
+ label: "Bybit",
20
+ aliases: ["bybit", "bybit_perp_usdt"],
21
+ },
22
+ {
23
+ id: "bitget_perp_usdt",
24
+ label: "Bitget",
25
+ aliases: ["bitget", "bitget_perp_usdt"],
26
+ },
27
+ {
28
+ id: "hyperliquid_perp_usdc",
29
+ label: "HyperLiquid",
30
+ aliases: ["hyperliquid", "hyperliquid_perp_usdc"],
31
+ },
32
+ {
33
+ id: "aster_perp_usdt",
34
+ label: "Aster",
35
+ aliases: ["aster", "aster_perp_usdt"],
36
+ },
37
+ ];
38
+ const EXCHANGE_BY_ALIAS = new Map();
39
+ for (const exchange of exports.RESOLVE_SYMBOLS_EXCHANGES) {
40
+ EXCHANGE_BY_ALIAS.set(exchange.id, exchange);
41
+ for (const alias of exchange.aliases) {
42
+ EXCHANGE_BY_ALIAS.set(alias.toLowerCase(), exchange);
43
+ }
44
+ }
45
+ function resolveSymbolsExchangeId(raw) {
46
+ const key = raw.trim().toLowerCase();
47
+ const exchange = EXCHANGE_BY_ALIAS.get(key);
48
+ if (!exchange) {
49
+ const allowed = exports.RESOLVE_SYMBOLS_EXCHANGES.map((item) => item.aliases[0]).join("|");
50
+ throw new Error(`--exchange must be ${allowed} (got ${raw.trim() || "<empty>"})`);
51
+ }
52
+ return exchange;
53
+ }
@@ -0,0 +1,6 @@
1
+ import type { CatalogSymbol, ResolveSymbolsQueryResult } from "./types";
2
+ export declare function normalizeSymbolSearchKey(value: string): string;
3
+ export declare function parseCatalogSymbol(symbol: string): CatalogSymbol | null;
4
+ export declare function indexCatalogSymbols(symbols: readonly string[]): readonly CatalogSymbol[];
5
+ export declare function resolveQueryAgainstCatalog(query: string, catalog: readonly CatalogSymbol[], limit?: number): ResolveSymbolsQueryResult;
6
+ export declare function levenshtein(left: string, right: string): number;