@alphafox/cli 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/catalog/omit.d.ts +3 -1
- package/dist/catalog/omit.js +9 -2
- package/dist/commands/run.js +10 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/dist/resolve-symbols/catalog.d.ts +6 -0
- package/dist/resolve-symbols/catalog.js +43 -0
- package/dist/resolve-symbols/exchanges.js +7 -4
- package/dist/resolve-symbols/match.d.ts +4 -3
- package/dist/resolve-symbols/match.js +50 -48
- package/dist/resolve-symbols/parse-args.d.ts +1 -0
- package/dist/resolve-symbols/parse-args.js +20 -3
- package/dist/resolve-symbols/run-command.js +19 -6
- package/dist/resolve-symbols/types.d.ts +14 -0
- package/dist/skills-manifest.json +41 -41
- package/dist/update/notify.d.ts +24 -0
- package/dist/update/notify.js +109 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/account/SKILL.md +1 -1
- package/skills/admin/SKILL.md +1 -1
- package/skills/alphafox/SKILL.md +32 -8
- package/skills/alphafox-shared/SKILL.md +12 -4
- package/skills/auth/SKILL.md +1 -1
- package/skills/engine-backtest/SKILL.md +5 -5
- package/skills/exchange/SKILL.md +1 -1
- package/skills/market/SKILL.md +32 -12
- package/skills/notification/SKILL.md +1 -1
- package/skills/strategy/SKILL.md +17 -15
- package/skills/trading/SKILL.md +37 -7
package/README.md
CHANGED
|
@@ -45,6 +45,9 @@ alphafox update
|
|
|
45
45
|
alphafox skills status
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
Ordinary CLI commands check npm at most once every 24 hours and only print a
|
|
49
|
+
stderr notice when a newer version exists. They never auto-upgrade.
|
|
50
|
+
|
|
48
51
|
`alphafox update` upgrades the npm package and then syncs the exact Skills
|
|
49
52
|
bundle shipped inside it. It never updates Skills independently from GitHub.
|
|
50
53
|
Locally modified Skills are reported and preserved; use
|
package/dist/catalog/omit.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Not a CLI surface:
|
|
3
|
+
* - Web `/api/v1/backtests` jobs (`backtests` / `backtests.*`)
|
|
4
|
+
* - Chat product (`chats` / `chats.*`, `chat_summaries` / `chat_summaries.*`)
|
|
3
5
|
* Local Engine WASM (`engine-backtest run`) and `engine_backtest.*` stay.
|
|
4
6
|
* Match `backtests` / `backtests.*` only — never `engine_backtest.*`.
|
|
5
7
|
*/
|
package/dist/catalog/omit.js
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isOmittedCatalogOperation = isOmittedCatalogOperation;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Not a CLI surface:
|
|
6
|
+
* - Web `/api/v1/backtests` jobs (`backtests` / `backtests.*`)
|
|
7
|
+
* - Chat product (`chats` / `chats.*`, `chat_summaries` / `chat_summaries.*`)
|
|
6
8
|
* Local Engine WASM (`engine-backtest run`) and `engine_backtest.*` stay.
|
|
7
9
|
* Match `backtests` / `backtests.*` only — never `engine_backtest.*`.
|
|
8
10
|
*/
|
|
9
11
|
function isOmittedCatalogOperation(operationId) {
|
|
10
|
-
return operationId === "backtests" ||
|
|
12
|
+
return (operationId === "backtests" ||
|
|
13
|
+
operationId.startsWith("backtests.") ||
|
|
14
|
+
operationId === "chats" ||
|
|
15
|
+
operationId.startsWith("chats.") ||
|
|
16
|
+
operationId === "chat_summaries" ||
|
|
17
|
+
operationId.startsWith("chat_summaries."));
|
|
11
18
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -16,6 +16,7 @@ const version_1 = require("../version");
|
|
|
16
16
|
const run_command_1 = require("../engine-backtest/run-command");
|
|
17
17
|
const run_command_2 = require("../resolve-symbols/run-command");
|
|
18
18
|
const run_command_3 = require("../skills/run-command");
|
|
19
|
+
const notify_1 = require("../update/notify");
|
|
19
20
|
const run_command_4 = require("../update/run-command");
|
|
20
21
|
const validate_body_1 = require("../catalog/validate-body");
|
|
21
22
|
const types_1 = require("../install/types");
|
|
@@ -100,13 +101,21 @@ async function runCli(argv, env = process.env) {
|
|
|
100
101
|
"alphafox api METHOD PATH [--body JSON|--config @file]",
|
|
101
102
|
"alphafox engine-backtest run --experiment <uuid> --definition <id> --config @file --exchange <id> --range FROM..TO --initial-equity N",
|
|
102
103
|
"alphafox engine-backtest sweep --experiment <uuid> --definition <id> --config @file --axes @file --exchange <id> --range FROM..TO --initial-equity N --no-persist",
|
|
103
|
-
"alphafox resolve-symbols <query...> [--exchange binance]",
|
|
104
|
+
"alphafox resolve-symbols <query...> [--exchange binance] [--asset-class equity_perp]",
|
|
104
105
|
"alphafox <domain> <resource> <action> [flags]",
|
|
105
106
|
],
|
|
106
107
|
}, { format: flags.format, jq: flags.jq });
|
|
107
108
|
return 0;
|
|
108
109
|
}
|
|
109
110
|
try {
|
|
111
|
+
if (!(0, notify_1.shouldSkipUpdateCheck)(cmd, env)) {
|
|
112
|
+
try {
|
|
113
|
+
await (0, notify_1.maybeNotifyCliUpdate)({ env, currentVersion: version_1.CLI_VERSION });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Update notices must never fail a command.
|
|
117
|
+
}
|
|
118
|
+
}
|
|
110
119
|
if (cmd !== "version" &&
|
|
111
120
|
cmd !== "install" &&
|
|
112
121
|
cmd !== "update" &&
|
package/dist/index.d.ts
CHANGED
|
@@ -20,3 +20,4 @@ export { AGENT_INSTALL_GUIDE_BLOB_URL, AGENT_INSTALL_GUIDE_URL, SKILLS_GITHUB_SO
|
|
|
20
20
|
export { buildSkillsManifest, inspectSkills, loadAndVerifySkillsManifest, loadSkillsState, syncSkills, writeSkillsManifest, } from "./skills/manager";
|
|
21
21
|
export { inspectCurrentSkills, installedSkillsRoot, skillsStatePath, syncCurrentSkills, } from "./skills/run-command";
|
|
22
22
|
export { executeCliUpdate, parseUpdateArgs, } from "./update/run-command";
|
|
23
|
+
export { formatUpdateNotice, maybeNotifyCliUpdate, shouldSkipUpdateCheck, } from "./update/notify";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateCatalogWriteBody = exports.CLI_CONTRACT_VERSION = exports.CLI_PACKAGE = exports.CLI_VERSION = exports.checkGeneratedCatalogCompatibility = exports.buildCapabilityManifest = exports.getOperationSchemaDocument = exports.findCatalogOperationByRoute = exports.findCatalogOperation = exports.COMPATIBILITY_RANGE = exports.CATALOG_VERSION = exports.CATALOG_SOURCE = exports.CATALOG_OPERATIONS = exports.startLoopbackCallbackServer = exports.runBrowserPkceLogin = exports.accessTokenNeedsRefresh = exports.refreshStoredTokensOrNull = exports.refreshStoredTokens = exports.requiresHighRiskConfirmation = exports.inferRawApiRisk = exports.assertHighRiskConfirmation = exports.resolveTypedCommand = exports.checkCliCompatibility = exports.pathTemplateMatches = exports.normalizeApiPath = exports.isInternalDisallowedPath = exports.isFacadeAllowlistedPath = exports.WINDOWS_CRED_MAX_BYTES = exports.windowsCredentialTarget = exports.linuxSecretToolBin = exports.linuxSecretServiceArgs = exports.keychainPlatform = exports.probeOsKeychain = exports.getLastTokenSaveResult = exports.tokenFingerprint = exports.deleteTokens = exports.loadTokens = exports.saveTokens = exports.assertNoTokenFields = exports.saveConfigFile = exports.loadConfigFile = exports.resolveProfile = exports.applyJqFilter = exports.writeError = exports.writeSuccess = exports.parseJsonEnvelope = exports.errorEnvelope = exports.successEnvelope = exports.parseGlobalFlags = exports.runCli = void 0;
|
|
4
|
-
exports.parseUpdateArgs = exports.executeCliUpdate = exports.syncCurrentSkills = exports.skillsStatePath = exports.installedSkillsRoot = exports.inspectCurrentSkills = exports.writeSkillsManifest = exports.syncSkills = exports.loadSkillsState = exports.loadAndVerifySkillsManifest = exports.inspectSkills = exports.buildSkillsManifest = exports.SKILLS_GITHUB_SOURCE = exports.AGENT_INSTALL_GUIDE_URL = exports.AGENT_INSTALL_GUIDE_BLOB_URL = exports.skillsListHasAlphafox = exports.semverLessThan = exports.runInstallWizard = exports.parseInstallArgs = exports.loadJsonArg = exports.parseRequestBodyFlags = void 0;
|
|
4
|
+
exports.shouldSkipUpdateCheck = exports.maybeNotifyCliUpdate = exports.formatUpdateNotice = exports.parseUpdateArgs = exports.executeCliUpdate = exports.syncCurrentSkills = exports.skillsStatePath = exports.installedSkillsRoot = exports.inspectCurrentSkills = exports.writeSkillsManifest = exports.syncSkills = exports.loadSkillsState = exports.loadAndVerifySkillsManifest = exports.inspectSkills = exports.buildSkillsManifest = exports.SKILLS_GITHUB_SOURCE = exports.AGENT_INSTALL_GUIDE_URL = exports.AGENT_INSTALL_GUIDE_BLOB_URL = exports.skillsListHasAlphafox = exports.semverLessThan = exports.runInstallWizard = exports.parseInstallArgs = exports.loadJsonArg = exports.parseRequestBodyFlags = void 0;
|
|
5
5
|
var run_1 = require("./commands/run");
|
|
6
6
|
Object.defineProperty(exports, "runCli", { enumerable: true, get: function () { return run_1.runCli; } });
|
|
7
7
|
Object.defineProperty(exports, "parseGlobalFlags", { enumerable: true, get: function () { return run_1.parseGlobalFlags; } });
|
|
@@ -95,3 +95,7 @@ Object.defineProperty(exports, "syncCurrentSkills", { enumerable: true, get: fun
|
|
|
95
95
|
var run_command_2 = require("./update/run-command");
|
|
96
96
|
Object.defineProperty(exports, "executeCliUpdate", { enumerable: true, get: function () { return run_command_2.executeCliUpdate; } });
|
|
97
97
|
Object.defineProperty(exports, "parseUpdateArgs", { enumerable: true, get: function () { return run_command_2.parseUpdateArgs; } });
|
|
98
|
+
var notify_1 = require("./update/notify");
|
|
99
|
+
Object.defineProperty(exports, "formatUpdateNotice", { enumerable: true, get: function () { return notify_1.formatUpdateNotice; } });
|
|
100
|
+
Object.defineProperty(exports, "maybeNotifyCliUpdate", { enumerable: true, get: function () { return notify_1.maybeNotifyCliUpdate; } });
|
|
101
|
+
Object.defineProperty(exports, "shouldSkipUpdateCheck", { enumerable: true, get: function () { return notify_1.shouldSkipUpdateCheck; } });
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import type { SymbolMetadata } from "./types";
|
|
1
2
|
export declare const MARKET_SYMBOLS_PATH = "/api/v1/market/symbols";
|
|
2
3
|
export declare function marketSymbolsPath(exchangeId: string): string;
|
|
4
|
+
export interface MarketCatalogPayload {
|
|
5
|
+
readonly symbols: string[];
|
|
6
|
+
readonly symbolMetadata: Readonly<Record<string, SymbolMetadata>>;
|
|
7
|
+
}
|
|
3
8
|
export declare function extractCatalogSymbols(json: unknown): string[];
|
|
9
|
+
export declare function extractMarketCatalog(json: unknown): MarketCatalogPayload;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MARKET_SYMBOLS_PATH = void 0;
|
|
4
4
|
exports.marketSymbolsPath = marketSymbolsPath;
|
|
5
5
|
exports.extractCatalogSymbols = extractCatalogSymbols;
|
|
6
|
+
exports.extractMarketCatalog = extractMarketCatalog;
|
|
6
7
|
const errors_1 = require("./errors");
|
|
7
8
|
exports.MARKET_SYMBOLS_PATH = "/api/v1/market/symbols";
|
|
8
9
|
function marketSymbolsPath(exchangeId) {
|
|
@@ -10,6 +11,9 @@ function marketSymbolsPath(exchangeId) {
|
|
|
10
11
|
return `${exports.MARKET_SYMBOLS_PATH}?${query}`;
|
|
11
12
|
}
|
|
12
13
|
function extractCatalogSymbols(json) {
|
|
14
|
+
return extractMarketCatalog(json).symbols;
|
|
15
|
+
}
|
|
16
|
+
function extractMarketCatalog(json) {
|
|
13
17
|
const root = asRecord(json);
|
|
14
18
|
const payload = asRecord(root?.data) ?? root;
|
|
15
19
|
const symbols = payload?.symbols;
|
|
@@ -40,8 +44,47 @@ function extractCatalogSymbols(json) {
|
|
|
40
44
|
details: json,
|
|
41
45
|
});
|
|
42
46
|
}
|
|
47
|
+
return {
|
|
48
|
+
symbols: out,
|
|
49
|
+
symbolMetadata: extractSymbolMetadata(payload?.symbolMetadata),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function extractSymbolMetadata(value) {
|
|
53
|
+
const rec = asRecord(value);
|
|
54
|
+
if (!rec)
|
|
55
|
+
return {};
|
|
56
|
+
const out = {};
|
|
57
|
+
for (const [rawKey, rawMeta] of Object.entries(rec)) {
|
|
58
|
+
const key = rawKey.trim();
|
|
59
|
+
const meta = parseSymbolMetadata(rawMeta);
|
|
60
|
+
if (!key || !meta)
|
|
61
|
+
continue;
|
|
62
|
+
out[key] = meta;
|
|
63
|
+
}
|
|
43
64
|
return out;
|
|
44
65
|
}
|
|
66
|
+
function parseSymbolMetadata(value) {
|
|
67
|
+
const rec = asRecord(value);
|
|
68
|
+
if (!rec)
|
|
69
|
+
return undefined;
|
|
70
|
+
const meta = {};
|
|
71
|
+
if (typeof rec.isTradFiRwa === "boolean") {
|
|
72
|
+
meta.isTradFiRwa = rec.isTradFiRwa;
|
|
73
|
+
}
|
|
74
|
+
if (typeof rec.assetClass === "string" && rec.assetClass.trim()) {
|
|
75
|
+
meta.assetClass = rec.assetClass.trim();
|
|
76
|
+
}
|
|
77
|
+
if (isFiniteNumber(rec.minAmount))
|
|
78
|
+
meta.minAmount = rec.minAmount;
|
|
79
|
+
if (isFiniteNumber(rec.minCost))
|
|
80
|
+
meta.minCost = rec.minCost;
|
|
81
|
+
if (isFiniteNumber(rec.contractSize))
|
|
82
|
+
meta.contractSize = rec.contractSize;
|
|
83
|
+
return Object.keys(meta).length > 0 ? meta : undefined;
|
|
84
|
+
}
|
|
85
|
+
function isFiniteNumber(value) {
|
|
86
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
87
|
+
}
|
|
45
88
|
function asRecord(value) {
|
|
46
89
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
47
90
|
return undefined;
|
|
@@ -42,12 +42,15 @@ for (const exchange of exports.RESOLVE_SYMBOLS_EXCHANGES) {
|
|
|
42
42
|
EXCHANGE_BY_ALIAS.set(alias.toLowerCase(), exchange);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
const PASSTHROUGH_EXCHANGE_ID = /^[a-z][a-z0-9_]{1,63}$/;
|
|
45
46
|
function resolveSymbolsExchangeId(raw) {
|
|
46
47
|
const key = raw.trim().toLowerCase();
|
|
47
48
|
const exchange = EXCHANGE_BY_ALIAS.get(key);
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
if (exchange)
|
|
50
|
+
return exchange;
|
|
51
|
+
if (PASSTHROUGH_EXCHANGE_ID.test(key)) {
|
|
52
|
+
return { id: key, label: raw.trim(), aliases: [key] };
|
|
51
53
|
}
|
|
52
|
-
|
|
54
|
+
const allowed = exports.RESOLVE_SYMBOLS_EXCHANGES.map((item) => item.aliases[0]).join("|");
|
|
55
|
+
throw new Error(`--exchange must be ${allowed} or a market.symbols.list catalog id (got ${raw.trim() || "<empty>"})`);
|
|
53
56
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { CatalogSymbol, ResolveSymbolsQueryResult } from "./types";
|
|
1
|
+
import type { CatalogSymbol, ResolveAssetClassFilter, ResolveSymbolsQueryResult, SymbolMetadata } from "./types";
|
|
2
2
|
export declare function normalizeSymbolSearchKey(value: string): string;
|
|
3
3
|
export declare function parseCatalogSymbol(symbol: string): CatalogSymbol | null;
|
|
4
|
-
export declare function indexCatalogSymbols(symbols: readonly string[]): readonly CatalogSymbol[];
|
|
5
|
-
export declare function
|
|
4
|
+
export declare function indexCatalogSymbols(symbols: readonly string[], metadata?: Readonly<Record<string, SymbolMetadata>>): readonly CatalogSymbol[];
|
|
5
|
+
export declare function catalogMatchesAssetClass(item: CatalogSymbol, assetClass: ResolveAssetClassFilter): boolean;
|
|
6
|
+
export declare function resolveQueryAgainstCatalog(query: string, catalog: readonly CatalogSymbol[], limit?: number, assetClass?: ResolveAssetClassFilter): ResolveSymbolsQueryResult;
|
|
6
7
|
export declare function levenshtein(left: string, right: string): number;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.normalizeSymbolSearchKey = normalizeSymbolSearchKey;
|
|
4
4
|
exports.parseCatalogSymbol = parseCatalogSymbol;
|
|
5
5
|
exports.indexCatalogSymbols = indexCatalogSymbols;
|
|
6
|
+
exports.catalogMatchesAssetClass = catalogMatchesAssetClass;
|
|
6
7
|
exports.resolveQueryAgainstCatalog = resolveQueryAgainstCatalog;
|
|
7
8
|
exports.levenshtein = levenshtein;
|
|
8
9
|
const LINEAR_PERP_PATTERN = /^([\p{L}\p{N}]+(?:-[\p{L}\p{N}]+)*)\/(USDT|USDC|USD):\2$/u;
|
|
@@ -69,7 +70,7 @@ function parseCatalogSymbol(symbol) {
|
|
|
69
70
|
compactKey: normalizeSymbolSearchKey(upper),
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
|
-
function indexCatalogSymbols(symbols) {
|
|
73
|
+
function indexCatalogSymbols(symbols, metadata = {}) {
|
|
73
74
|
const seen = new Set();
|
|
74
75
|
const indexed = [];
|
|
75
76
|
for (const raw of symbols) {
|
|
@@ -79,64 +80,59 @@ function indexCatalogSymbols(symbols) {
|
|
|
79
80
|
if (seen.has(parsed.symbol))
|
|
80
81
|
continue;
|
|
81
82
|
seen.add(parsed.symbol);
|
|
82
|
-
indexed.push(
|
|
83
|
+
indexed.push({
|
|
84
|
+
...parsed,
|
|
85
|
+
metadata: metadata[raw] ?? metadata[parsed.symbol],
|
|
86
|
+
});
|
|
83
87
|
}
|
|
84
88
|
return indexed;
|
|
85
89
|
}
|
|
86
|
-
function
|
|
90
|
+
function catalogMatchesAssetClass(item, assetClass) {
|
|
91
|
+
if (assetClass === "all")
|
|
92
|
+
return true;
|
|
93
|
+
const cls = item.metadata?.assetClass;
|
|
94
|
+
const tradfi = item.metadata?.isTradFiRwa === true;
|
|
95
|
+
if (assetClass === "equity_perp")
|
|
96
|
+
return cls === "equity_perp";
|
|
97
|
+
if (assetClass === "rwa_perp")
|
|
98
|
+
return cls === "rwa_perp";
|
|
99
|
+
return !tradfi && cls !== "equity_perp" && cls !== "rwa_perp";
|
|
100
|
+
}
|
|
101
|
+
function resolveQueryAgainstCatalog(query, catalog, limit = 8, assetClass = "all") {
|
|
87
102
|
const trimmed = query.trim();
|
|
88
|
-
const
|
|
103
|
+
const scoped = catalog.filter((item) => catalogMatchesAssetClass(item, assetClass));
|
|
104
|
+
const matches = rankMatches(trimmed, scoped);
|
|
89
105
|
const exact = matches.filter((item) => EXACT_REASONS.has(item.reason));
|
|
90
106
|
const close = matches.filter((item) => !EXACT_REASONS.has(item.reason));
|
|
91
107
|
const capped = (items) => items.slice(0, Math.max(1, limit));
|
|
92
108
|
if (exact.length === 1) {
|
|
93
|
-
return
|
|
94
|
-
query: trimmed,
|
|
95
|
-
status: "exact",
|
|
96
|
-
resolved: exact[0].symbol,
|
|
97
|
-
needsConfirmation: false,
|
|
98
|
-
matches: exact,
|
|
99
|
-
matchCount: exact.length,
|
|
100
|
-
};
|
|
109
|
+
return result("exact", exact[0].symbol, false, exact, exact.length);
|
|
101
110
|
}
|
|
102
111
|
if (exact.length > 1) {
|
|
103
|
-
return
|
|
104
|
-
query: trimmed,
|
|
105
|
-
status: "ambiguous",
|
|
106
|
-
resolved: null,
|
|
107
|
-
needsConfirmation: true,
|
|
108
|
-
matches: capped(exact),
|
|
109
|
-
matchCount: exact.length,
|
|
110
|
-
};
|
|
112
|
+
return result("ambiguous", null, true, capped(exact), exact.length);
|
|
111
113
|
}
|
|
112
114
|
if (close.length === 1) {
|
|
113
|
-
return
|
|
114
|
-
query: trimmed,
|
|
115
|
-
status: "close",
|
|
116
|
-
resolved: close[0].symbol,
|
|
117
|
-
needsConfirmation: true,
|
|
118
|
-
matches: close,
|
|
119
|
-
matchCount: 1,
|
|
120
|
-
};
|
|
115
|
+
return result("close", close[0].symbol, true, close, 1);
|
|
121
116
|
}
|
|
122
117
|
if (close.length > 1) {
|
|
118
|
+
return result("ambiguous", null, true, capped(close), close.length);
|
|
119
|
+
}
|
|
120
|
+
return result("none", null, false, [], 0);
|
|
121
|
+
function result(status, resolved, needsConfirmation, matches, matchCount) {
|
|
122
|
+
const chosen = resolved
|
|
123
|
+
? matches.find((item) => item.symbol === resolved)
|
|
124
|
+
: undefined;
|
|
123
125
|
return {
|
|
124
126
|
query: trimmed,
|
|
125
|
-
status
|
|
126
|
-
resolved
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
status,
|
|
128
|
+
resolved,
|
|
129
|
+
assetClass: chosen?.assetClass ?? null,
|
|
130
|
+
isTradFiRwa: chosen?.isTradFiRwa ?? false,
|
|
131
|
+
needsConfirmation,
|
|
132
|
+
matches,
|
|
133
|
+
matchCount,
|
|
130
134
|
};
|
|
131
135
|
}
|
|
132
|
-
return {
|
|
133
|
-
query: trimmed,
|
|
134
|
-
status: "none",
|
|
135
|
-
resolved: null,
|
|
136
|
-
needsConfirmation: false,
|
|
137
|
-
matches: [],
|
|
138
|
-
matchCount: 0,
|
|
139
|
-
};
|
|
140
136
|
}
|
|
141
137
|
function rankMatches(query, catalog) {
|
|
142
138
|
const canonicalQuery = query.trim().toUpperCase();
|
|
@@ -161,21 +157,21 @@ function rankMatches(query, catalog) {
|
|
|
161
157
|
}
|
|
162
158
|
function rankCatalogSymbol(item, canonicalQuery, queryKey, queryBaseKey) {
|
|
163
159
|
if (item.symbol === canonicalQuery) {
|
|
164
|
-
return match(item
|
|
160
|
+
return match(item, "exact_canonical", 100);
|
|
165
161
|
}
|
|
166
162
|
if (item.pair === canonicalQuery) {
|
|
167
|
-
return match(item
|
|
163
|
+
return match(item, "exact_pair", 96);
|
|
168
164
|
}
|
|
169
165
|
if (item.compactKey === queryKey || item.searchKey === queryKey) {
|
|
170
|
-
return match(item
|
|
166
|
+
return match(item, "exact_compact", 98);
|
|
171
167
|
}
|
|
172
168
|
if (item.baseKey === queryKey || item.baseKey === queryBaseKey) {
|
|
173
|
-
return match(item
|
|
169
|
+
return match(item, "exact_base", 95);
|
|
174
170
|
}
|
|
175
171
|
let best = null;
|
|
176
172
|
const consider = (reason, score) => {
|
|
177
173
|
if (!best || score > best.score) {
|
|
178
|
-
best = match(item
|
|
174
|
+
best = match(item, reason, score);
|
|
179
175
|
}
|
|
180
176
|
};
|
|
181
177
|
if (queryBaseKey.length >= 2 && item.baseKey.startsWith(queryBaseKey)) {
|
|
@@ -219,8 +215,14 @@ function isCloseDistance(left, right, distance) {
|
|
|
219
215
|
return distance === 1;
|
|
220
216
|
return distance <= 2;
|
|
221
217
|
}
|
|
222
|
-
function match(
|
|
223
|
-
return {
|
|
218
|
+
function match(item, reason, score) {
|
|
219
|
+
return {
|
|
220
|
+
symbol: item.symbol,
|
|
221
|
+
reason,
|
|
222
|
+
score,
|
|
223
|
+
assetClass: item.metadata?.assetClass ?? null,
|
|
224
|
+
isTradFiRwa: item.metadata?.isTradFiRwa === true,
|
|
225
|
+
};
|
|
224
226
|
}
|
|
225
227
|
function clampScore(score) {
|
|
226
228
|
return Math.max(1, Math.min(94, score));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ResolveSymbolsRunArgs } from "./types";
|
|
2
2
|
export declare const RESOLVE_SYMBOLS_DEFAULT_LIMIT = 8;
|
|
3
3
|
export declare const RESOLVE_SYMBOLS_MAX_LIMIT = 25;
|
|
4
|
+
export declare const RESOLVE_SYMBOLS_ASSET_CLASSES: readonly ["all", "equity_perp", "rwa_perp", "crypto"];
|
|
4
5
|
export declare const RESOLVE_SYMBOLS_USAGE: string[];
|
|
5
6
|
export declare function parseResolveSymbolsArgs(args: readonly string[]): ResolveSymbolsRunArgs;
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RESOLVE_SYMBOLS_USAGE = exports.RESOLVE_SYMBOLS_MAX_LIMIT = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = void 0;
|
|
3
|
+
exports.RESOLVE_SYMBOLS_USAGE = exports.RESOLVE_SYMBOLS_ASSET_CLASSES = exports.RESOLVE_SYMBOLS_MAX_LIMIT = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = void 0;
|
|
4
4
|
exports.parseResolveSymbolsArgs = parseResolveSymbolsArgs;
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
const exchanges_1 = require("./exchanges");
|
|
7
7
|
exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = 8;
|
|
8
8
|
exports.RESOLVE_SYMBOLS_MAX_LIMIT = 25;
|
|
9
|
+
exports.RESOLVE_SYMBOLS_ASSET_CLASSES = [
|
|
10
|
+
"all",
|
|
11
|
+
"equity_perp",
|
|
12
|
+
"rwa_perp",
|
|
13
|
+
"crypto",
|
|
14
|
+
];
|
|
9
15
|
exports.RESOLVE_SYMBOLS_USAGE = [
|
|
10
|
-
"alphafox resolve-symbols <query...> [--exchange binance] [--limit 8]",
|
|
16
|
+
"alphafox resolve-symbols <query...> [--exchange binance] [--asset-class all] [--limit 8]",
|
|
11
17
|
"alphafox resolve-symbols --query BTC --query ETH --exchange okx",
|
|
18
|
+
"alphafox resolve-symbols NVDA AAPL TSLA --exchange binance --asset-class equity_perp",
|
|
12
19
|
];
|
|
13
20
|
function usage(message, subtype = "invalid_args") {
|
|
14
21
|
throw new errors_1.ResolveSymbolsError({
|
|
@@ -30,6 +37,7 @@ function parseResolveSymbolsArgs(args) {
|
|
|
30
37
|
const queries = [];
|
|
31
38
|
let exchange = exchanges_1.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE;
|
|
32
39
|
let limit = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT;
|
|
40
|
+
let assetClass = "all";
|
|
33
41
|
let help = false;
|
|
34
42
|
for (let i = 0; i < args.length; i += 1) {
|
|
35
43
|
const a = args[i];
|
|
@@ -59,6 +67,14 @@ function parseResolveSymbolsArgs(args) {
|
|
|
59
67
|
case "--exchange":
|
|
60
68
|
exchange = read();
|
|
61
69
|
break;
|
|
70
|
+
case "--asset-class": {
|
|
71
|
+
const value = read().trim().toLowerCase();
|
|
72
|
+
if (!exports.RESOLVE_SYMBOLS_ASSET_CLASSES.includes(value)) {
|
|
73
|
+
usage(`--asset-class must be ${exports.RESOLVE_SYMBOLS_ASSET_CLASSES.join("|")}`, "invalid_asset_class");
|
|
74
|
+
}
|
|
75
|
+
assetClass = value;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
62
78
|
case "--limit": {
|
|
63
79
|
const n = Number(read());
|
|
64
80
|
if (!Number.isInteger(n) || n <= 0 || n > exports.RESOLVE_SYMBOLS_MAX_LIMIT) {
|
|
@@ -72,7 +88,7 @@ function parseResolveSymbolsArgs(args) {
|
|
|
72
88
|
}
|
|
73
89
|
}
|
|
74
90
|
if (help) {
|
|
75
|
-
return { help: true, queries: [], exchange, limit };
|
|
91
|
+
return { help: true, queries: [], exchange, limit, assetClass };
|
|
76
92
|
}
|
|
77
93
|
const normalizedQueries = queries.map((item) => item.trim()).filter(Boolean);
|
|
78
94
|
if (normalizedQueries.length === 0) {
|
|
@@ -90,6 +106,7 @@ function parseResolveSymbolsArgs(args) {
|
|
|
90
106
|
queries: uniqueQueries(normalizedQueries),
|
|
91
107
|
exchange: resolvedExchange,
|
|
92
108
|
limit,
|
|
109
|
+
assetClass,
|
|
93
110
|
};
|
|
94
111
|
}
|
|
95
112
|
function uniqueQueries(queries) {
|
|
@@ -41,10 +41,12 @@ function resolveSymbolsHelpData() {
|
|
|
41
41
|
name: "resolve-symbols",
|
|
42
42
|
usage: parse_args_1.RESOLVE_SYMBOLS_USAGE,
|
|
43
43
|
notes: [
|
|
44
|
-
"Resolves user-mentioned tickers against
|
|
45
|
-
"Default --exchange is binance (binance_perp_usdt).
|
|
46
|
-
"
|
|
47
|
-
"
|
|
44
|
+
"Resolves user-mentioned tickers against market.symbols.list for the chosen catalog",
|
|
45
|
+
"Default --exchange is binance (binance_perp_usdt). Built-in aliases: binance|okx|bybit|bitget|hyperliquid|aster",
|
|
46
|
+
"US stock perps live in the same catalog (NVDA/USDT:USDT) and are tagged symbolMetadata.assetClass=equity_perp / isTradFiRwa",
|
|
47
|
+
"For 美股 use --asset-class equity_perp. For gold/silver RWAs use rwa_perp. Crypto perps are untagged or --asset-class crypto",
|
|
48
|
+
"Each match includes assetClass and isTradFiRwa. Do not treat an equity_perp as a crypto coin",
|
|
49
|
+
"Catalog dump remains alphafox market symbols list --exchange <id>",
|
|
48
50
|
],
|
|
49
51
|
};
|
|
50
52
|
}
|
|
@@ -77,12 +79,22 @@ async function executeResolveSymbols(args, flags, env = process.env, deps = {})
|
|
|
77
79
|
details: res.json,
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
|
-
const
|
|
82
|
+
const payload = (0, catalog_1.extractMarketCatalog)(res.json);
|
|
83
|
+
const catalog = (0, match_1.indexCatalogSymbols)(payload.symbols, payload.symbolMetadata);
|
|
84
|
+
if (args.assetClass !== "all" &&
|
|
85
|
+
!catalog.some((item) => item.metadata?.assetClass)) {
|
|
86
|
+
throw new errors_1.ResolveSymbolsError({
|
|
87
|
+
type: "runtime",
|
|
88
|
+
subtype: "metadata_missing",
|
|
89
|
+
message: "market.symbols.list did not include symbolMetadata; cannot apply --asset-class",
|
|
90
|
+
hint: "Retry without --asset-class, or use a catalog that returns symbolMetadata.assetClass.",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
81
93
|
return {
|
|
82
94
|
exchange: exchange.id,
|
|
83
95
|
exchangeLabel: exchange.label,
|
|
84
96
|
catalogSize: catalog.length,
|
|
85
|
-
queries: args.queries.map((query) => (0, match_1.resolveQueryAgainstCatalog)(query, catalog, args.limit)),
|
|
97
|
+
queries: args.queries.map((query) => (0, match_1.resolveQueryAgainstCatalog)(query, catalog, args.limit, args.assetClass)),
|
|
86
98
|
};
|
|
87
99
|
}
|
|
88
100
|
async function cmdResolveSymbols(args, flags, env = process.env, deps = {}) {
|
|
@@ -119,6 +131,7 @@ async function cmdResolveSymbols(args, flags, env = process.env, deps = {}) {
|
|
|
119
131
|
path: (0, catalog_1.marketSymbolsPath)(exchange.id),
|
|
120
132
|
exchange: exchange.id,
|
|
121
133
|
exchangeLabel: exchange.label,
|
|
134
|
+
assetClass: parsed.assetClass,
|
|
122
135
|
queries: parsed.queries,
|
|
123
136
|
}, { format: flags.format, jq: flags.jq });
|
|
124
137
|
return 0;
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
export type ResolveSymbolsStatus = "exact" | "close" | "ambiguous" | "none";
|
|
2
|
+
export type ResolveAssetClassFilter = "all" | "equity_perp" | "rwa_perp" | "crypto";
|
|
3
|
+
export interface SymbolMetadata {
|
|
4
|
+
readonly isTradFiRwa?: boolean;
|
|
5
|
+
readonly assetClass?: string;
|
|
6
|
+
readonly minAmount?: number;
|
|
7
|
+
readonly minCost?: number;
|
|
8
|
+
readonly contractSize?: number;
|
|
9
|
+
}
|
|
2
10
|
export type ResolveSymbolsMatchReason = "exact_canonical" | "exact_compact" | "exact_pair" | "exact_base" | "prefix" | "contains" | "close";
|
|
3
11
|
export interface ResolveSymbolsMatch {
|
|
4
12
|
readonly symbol: string;
|
|
5
13
|
readonly reason: ResolveSymbolsMatchReason;
|
|
6
14
|
readonly score: number;
|
|
15
|
+
readonly assetClass: string | null;
|
|
16
|
+
readonly isTradFiRwa: boolean;
|
|
7
17
|
}
|
|
8
18
|
export interface ResolveSymbolsQueryResult {
|
|
9
19
|
readonly query: string;
|
|
10
20
|
readonly status: ResolveSymbolsStatus;
|
|
11
21
|
readonly resolved: string | null;
|
|
22
|
+
readonly assetClass: string | null;
|
|
23
|
+
readonly isTradFiRwa: boolean;
|
|
12
24
|
readonly needsConfirmation: boolean;
|
|
13
25
|
readonly matches: readonly ResolveSymbolsMatch[];
|
|
14
26
|
readonly matchCount: number;
|
|
@@ -24,6 +36,7 @@ export interface ResolveSymbolsRunArgs {
|
|
|
24
36
|
readonly queries: readonly string[];
|
|
25
37
|
readonly exchange: string;
|
|
26
38
|
readonly limit: number;
|
|
39
|
+
readonly assetClass: ResolveAssetClassFilter;
|
|
27
40
|
}
|
|
28
41
|
export interface CatalogSymbol {
|
|
29
42
|
readonly symbol: string;
|
|
@@ -34,4 +47,5 @@ export interface CatalogSymbol {
|
|
|
34
47
|
readonly searchKey: string;
|
|
35
48
|
readonly baseKey: string;
|
|
36
49
|
readonly compactKey: string;
|
|
50
|
+
readonly metadata?: SymbolMetadata;
|
|
37
51
|
}
|