Packages

Elixir client library for cryptocurrency exchanges — generated from CCXT specs via compile-time macros.

Current section

Files

Jump to
ccxt_client priv specs json output exchange_v3.json
Raw

priv/specs/json/output/exchange_v3.json

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "exchange_v3.json",
"title": "CCXT Exchange Extraction Output",
"description": "Per-exchange extraction output from ccxt_extract. Contains everything CCXT knows about a single exchange, organized into two layers: runtime values (QuickBEAM) and structural AST (OXC). Schema 3.0.0 (Task 117, breaking) replaces runtime.markets with the compact runtime.symbols_index derivation and drops structure.parse_methods + structure.ws_methods (extractors retained; discovery files still written for internal Phase 12 / Phase 15 consumers). See SCHEMA.md migration notes.",
"type": "object",
"required": ["schema_version", "extracted_at", "ccxt_version", "exchange", "runtime", "structure", "_provenance"],
"additionalProperties": false,
"properties": {
"schema_version": {
"const": "3.0.0",
"description": "Schema version. Consumers should check this before parsing."
},
"extracted_at": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp of extraction."
},
"ccxt_version": {
"type": "string",
"description": "CCXT npm package version used for extraction."
},
"exchange": {
"$ref": "#/$defs/ExchangeMeta"
},
"runtime": {
"$ref": "#/$defs/RuntimeData"
},
"structure": {
"$ref": "#/$defs/StructureData"
},
"_provenance": {
"$ref": "#/$defs/ProvenanceMap",
"description": "Required per-path provenance map (raw/derived/override). Required and non-null since schema 2.0.0 (Task 61c)."
}
},
"$defs": {
"ProvenanceMap": {
"type": "object",
"description": "Flat map keyed by RFC 6901 JSON Pointer strings into the payload. Values tag the source tier: raw (discovery passthrough), derived (assembly-time computation), override (priv/overrides/<id>.json).",
"propertyNames": {
"type": "string",
"pattern": "^/"
},
"additionalProperties": {
"type": "string",
"enum": ["raw", "derived", "override"]
}
},
"ExchangeMeta": {
"type": "object",
"description": "Exchange identity and metadata from CCXT runtime.",
"required": ["id", "name", "alias"],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "CCXT exchange identifier (e.g., 'binance', 'kraken')."
},
"name": {
"type": "string",
"description": "Human-readable exchange name."
},
"certified": {
"type": "boolean",
"description": "Whether the exchange is CCXT-certified."
},
"pro": {
"type": "boolean",
"description": "Whether the exchange has WebSocket (pro) support."
},
"version": {
"type": ["string", "null"],
"description": "Exchange API version string."
},
"country": {
"type": "array",
"items": { "type": "string" },
"description": "ISO country codes where the exchange is based."
},
"alias": {
"type": "boolean",
"description": "True if this is a re-brand alias (e.g., huobi -> htx)."
},
"referral": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/Referral" }
],
"description": "Referral URL and discount, if any."
},
"tier": {
"type": "string",
"enum": ["tier1", "tier2", "tier3", "dex", "unclassified"],
"description": "Priority tier for this project — hand-curated in priv/priority_tiers.json, not extracted from CCXT. tier1/tier2/dex are the buckets we commit to deriving recipes for; tier3 and unclassified still get full raw extraction."
}
}
},
"Referral": {
"type": "object",
"required": ["url"],
"additionalProperties": false,
"properties": {
"url": { "type": "string" },
"discount": { "type": ["number", "null"] }
}
},
"RuntimeData": {
"type": "object",
"description": "Runtime values extracted via QuickBEAM JavaScript execution. Layer 1: what an exchange IS (configuration, capabilities, markets).",
"required": ["describe", "symbols_index", "symbol_patterns", "url_templates", "testnet_urls"],
"additionalProperties": false,
"properties": {
"describe": {
"oneOf": [
{ "type": "null" },
{ "type": "object", "additionalProperties": true }
],
"description": "Complete describe() output from CCXT runtime. Contains all configuration: api endpoints, has capabilities, fees, limits, urls, exceptions, features, timeframes, requiredCredentials, etc. May contain sentinel strings: '__undefined' (JS undefined values), '__function:<ClassName>' (JS function references with resolved CCXT error class names, e.g. '__function:ExchangeError'). Null for alias exchanges or when no usable describe data was assembled; check pipeline integrity stats to distinguish source gaps from expected nulls."
},
"symbols_index": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/SymbolsIndex" }
],
"description": "Derived per-symbol spot/swap classification from loadMarkets(). Introduced at schema 3.0.0 (Task 117) as the compact replacement for the full runtime.markets snapshot. Consumers needing per-market price, precision, fees, limits, or exchange-native ids must call loadMarkets() at runtime — that was the only drift-safe path anyway. Null for alias exchanges, exchanges without cached market data, or when no usable markets data was assembled."
},
"symbol_patterns": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/SymbolPatterns" }
],
"description": "Derived symbol formatting patterns per market type. Consumers use these rules for symbol conversion (unified <-> exchange-native). Null when no market data is available."
},
"url_templates": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/UrlTemplates" }
],
"description": "URL templates per API section, showing base URL, sample path, and fully resolved URL from sign(). Consumers derive path prefixes injected by sign() (e.g., OKX's /api/v5/, Gate's /spot/). Null for alias exchanges or when sign() fails for all sections."
},
"testnet_urls": {
"$ref": "#/$defs/TestnetUrls",
"description": "Derived testnet / sandbox URL catalog with {hostname} placeholders pre-resolved. See $defs/TestnetUrls for pattern classification."
}
}
},
"TestnetUrls": {
"type": "object",
"description": "Testnet / sandbox URL catalog derived from describe.urls.test + describe.options.sandboxMode. Promotes the raw testnet signals out of the opaque runtime.describe blob so consumers read one canonical shape. Introduced at schema 2.4.0 (Task 100).",
"required": ["pattern", "urls", "sandbox_flag_field", "unresolved_reason"],
"additionalProperties": false,
"properties": {
"pattern": {
"type": "string",
"enum": ["separate_host", "sandbox_flag", "none"],
"description": "Classification: 'separate_host' when describe.urls.test is non-empty; 'sandbox_flag' when only describe.options.sandboxMode is present; 'none' when neither is present."
},
"urls": {
"oneOf": [
{ "type": "null" },
{ "type": "object", "additionalProperties": true }
],
"description": "Testnet URL map from describe.urls.test with {hostname} substitutions resolved. Shape preserved from CCXT (flat section map OR nested host → section map). Non-null only when pattern == 'separate_host'."
},
"sandbox_flag_field": {
"oneOf": [
{ "type": "null" },
{ "type": "string" }
],
"description": "Name of the describe.options flag used to switch to sandbox mode (currently always 'sandboxMode' when present). Populated independently of pattern — may coexist with 'separate_host' (okx uses both a same-host URL AND a flag)."
},
"unresolved_reason": {
"oneOf": [
{ "type": "null" },
{ "type": "string", "enum": ["no_testnet_data"] }
],
"description": "Non-null only when pattern == 'none'. Explains why no testnet data was derived."
}
}
},
"SymbolsIndex": {
"type": "object",
"description": "Compact per-symbol spot/swap classification derived from loadMarkets(). Keys are CCXT unified symbols (e.g. 'BTC/USDT', 'BTC/USDT:USDT'). Introduced at schema 3.0.0 (Task 117) as the drift-safe replacement for the full runtime.markets snapshot. Consumers derive market_count via map size; for price/precision/fees/limits/info call loadMarkets() at runtime.",
"additionalProperties": {
"type": "object",
"required": ["spot", "swap"],
"additionalProperties": false,
"properties": {
"spot": {
"type": "boolean",
"description": "True when the source market carries spot == true or type == 'spot'."
},
"swap": {
"type": "boolean",
"description": "True when the source market carries swap == true or type == 'swap'."
}
}
}
},
"SymbolPatterns": {
"type": "object",
"description": "Symbol formatting patterns derived from market data. Keys are market types ('spot', 'swap', 'future', 'option') mapping to per-type pattern analysis, plus 'currency_aliases' from describe().commonCurrencies.",
"required": ["currency_aliases"],
"properties": {
"spot": { "$ref": "#/$defs/SymbolPatternEntry" },
"swap": { "$ref": "#/$defs/SymbolPatternEntry" },
"future": { "$ref": "#/$defs/SymbolPatternEntry" },
"option": { "$ref": "#/$defs/SymbolPatternEntry" },
"currency_aliases": {
"type": "object",
"additionalProperties": { "type": "string" },
"description": "Exchange-native currency code -> CCXT unified code mapping from describe().commonCurrencies (e.g., 'XXBT' -> 'BTC')."
}
},
"additionalProperties": false
},
"SymbolPatternEntry": {
"type": "object",
"description": "Symbol formatting pattern analysis for a single market type.",
"required": ["id_structure", "separator", "case", "suffix", "sample_count", "anomaly_count", "anomalies", "examples"],
"additionalProperties": false,
"properties": {
"id_structure": {
"type": ["string", "null"],
"description": "How the exchange ID is composed from currency codes: 'baseId_quoteId' (most common), 'quoteId_baseId', 'baseId_only', 'numeric', or 'opaque'. Null if no dominant pattern (below 80% threshold)."
},
"separator": {
"type": ["string", "null"],
"description": "Character between currency codes in the ID: '' (concatenated), '-' (dash), '_' (underscore). Null if no dominant pattern."
},
"case": {
"type": ["string", "null"],
"enum": ["upper", "lower", "mixed", null],
"description": "Case convention of the exchange ID. Null if no dominant pattern."
},
"suffix": {
"type": ["string", "null"],
"description": "Constant suffix appended after the currency pair (e.g., '-SWAP', '-PERPETUAL', 'M'). Null if no suffix detected."
},
"sample_count": {
"type": "integer",
"description": "Number of markets analyzed for this type."
},
"anomaly_count": {
"type": "integer",
"description": "Number of markets that don't match the dominant pattern."
},
"anomalies": {
"type": "array",
"items": { "type": "string" },
"description": "Exchange IDs that don't match the dominant pattern (max 50). Consumers should use direct symbol->id lookup for these."
},
"examples": {
"type": "array",
"items": {
"type": "object",
"required": ["symbol", "id", "baseId", "quoteId"],
"additionalProperties": false,
"properties": {
"symbol": { "type": "string" },
"id": { "type": "string" },
"baseId": { "type": "string" },
"quoteId": { "type": "string" }
}
},
"description": "Representative sample markets (max 3) showing the symbol-to-id mapping."
}
}
},
"UrlTemplates": {
"type": "object",
"description": "URL templates per API section. Keys are dot-joined section paths (e.g., 'public', 'private', 'public.spot', 'private.delivery'). Each entry records the raw sign() probe: inputs (api_param, http_method, sample_path) and output (resolved_url), plus a derived url_prefix when provably correct.",
"additionalProperties": { "$ref": "#/$defs/UrlTemplateEntry" }
},
"UrlTemplateEntry": {
"type": "object",
"description": "Raw sign() probe for a single API section. Records what was passed to sign() and what it returned. url_prefix is derived (resolved_url minus trailing sample_path) only when provably correct — null for suffix-mutation exchanges (bit2c, lbank, zonda) and sign() failures.",
"required": ["api_param", "http_method", "sample_path", "resolved_url", "url_prefix"],
"additionalProperties": false,
"properties": {
"api_param": {
"oneOf": [
{ "type": "string" },
{ "type": "array", "items": { "type": "string" } }
],
"description": "Section identifier passed to sign(). String for flat sections ('public'), array for multi-level (['public', 'spot'])."
},
"http_method": {
"type": "string",
"description": "HTTP method of the sampled endpoint passed to sign() (GET, POST, PUT, DELETE, PATCH)."
},
"sample_path": {
"type": "string",
"description": "First endpoint path from this API section (used as sign() input)."
},
"resolved_url": {
"type": ["string", "null"],
"description": "Full URL returned by sign(). Null if sign() threw (e.g., private sections without credentials, or sections missing from urls.api)."
},
"url_prefix": {
"type": ["string", "null"],
"description": "Derived: resolved_url with sample_path stripped from the end. Null when resolved_url doesn't cleanly end with sample_path (suffix-mutation exchanges like bit2c, lbank, zonda) or when sign() failed. Consumers use this as the full URL prefix for the section."
}
}
},
"StructureData": {
"type": "object",
"description": "Structural data extracted via OXC TypeScript AST parsing. Layer 2: what an exchange DOES (code structure, method bodies).",
"required": ["class_info", "methods", "sign_method", "authenticated_sections", "sign_recipe", "handle_errors", "interface_signatures", "pagination", "overrides", "unified_endpoints", "request_defaults"],
"additionalProperties": false,
"properties": {
"class_info": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/ClassInfo" }
],
"description": "Class hierarchy information. Null for alias exchanges without class entries or when no usable class info was assembled; check pipeline integrity stats to distinguish source gaps from expected nulls."
},
"methods": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/MethodInventory" }
],
"description": "Method signature inventory (no AST bodies). Null for alias exchanges or when no usable method inventory was assembled; check pipeline integrity stats to distinguish source gaps from expected nulls."
},
"sign_method": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/MethodAST" }
],
"description": "The sign() method AST for request authentication. Null if the exchange has no sign() method or is an alias."
},
"authenticated_sections": {
"oneOf": [
{ "type": "null" },
{ "type": "array", "items": { "type": "string" } }
],
"description": "API sections proven to require authentication via checkRequiredCredentials() gates in sign() AST. Handles api === 'X' and api[N] === 'X' patterns. Sorted. Null when sign() absent; empty list when sign() exists but no checkRequiredCredentials() gates found. Some exchanges authenticate without checkRequiredCredentials() — use sign_method AST for broader auth detection."
},
"sign_recipe": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/SignRecipeRecord" },
"description": "Map of section name -> SignRecipeRecord. Keys mirror structure.authenticated_sections (contract-test invariant sign_recipe_keys_match_auth_sections enforces parity). Empty map when sign_method is null or no authenticated sections were detected. Scaffolded in schema 2.2.0; Task 64 emits all-null records with unresolved_reason = \"not_yet_derived\". Tasks 65–69 populate derivation fields incrementally. Standalone copy at priv/schema/sign_recipe_v1.json (must stay in sync)."
},
"handle_errors": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/HandleErrorsData" }
],
"description": "The handleErrors() method AST plus exception mappings. Null if the exchange has no handleErrors(), is an alias, or no usable handleErrors data was assembled; check pipeline integrity stats to distinguish source gaps from expected nulls."
},
"interface_signatures": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"additionalProperties": { "$ref": "#/$defs/InterfaceSignature" }
}
],
"description": "Map of API method name -> InterfaceSignature from abstract/*.ts interface declarations. These are the generated per-exchange typed endpoint methods (e.g., publicGetTicker, privatePostOrder). Null for alias exchanges, exchanges without abstract files, or when no usable data was assembled; check pipeline integrity stats to distinguish source gaps from expected nulls."
},
"pagination": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"properties": {
"_unresolved": {
"type": "array",
"items": { "$ref": "#/$defs/PaginationEntry" },
"description": "Pagination calls where the target method name is a runtime variable (not a string literal). Each entry has target_method: null and containing_method set to the method body where the call was found."
}
},
"additionalProperties": {
"type": "array",
"items": { "$ref": "#/$defs/PaginationEntry" }
}
}
],
"description": "Map of target method name -> [PaginationEntry] showing which methods use pagination and what strategy. Values are always arrays (multiple entries when branch-dependent variants exist, e.g. coinbase fetchAccounts V2/V3). Optional _unresolved key holds entries with variable method names. Null for exchanges without pagination calls or when no usable pagination data was assembled."
},
"overrides": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/OverridesData" }
],
"description": "Method override analysis for derived exchanges. Null for root exchanges (those extending Exchange directly) or when no usable override data was assembled; check pipeline integrity stats to distinguish source gaps from expected nulls."
},
"unified_endpoints": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"additionalProperties": {
"type": "array",
"items": { "type": "string" }
}
}
],
"description": "Map of unified method name -> list of interface method names called within that method body (e.g., 'fetchTicker' -> ['publicGetV5MarketTickers']). Multiple entries when the method branches by market type or API version. Alias/derived exchanges inherit parent mappings when they have no own unified endpoints. Null when no unified endpoint mappings were found (own or inherited)."
},
"request_defaults": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/RequestDefaults" }
],
"description": "Map of method name -> per-key default request body entries extracted from literal ObjectExpressions that flow into this.<httpVerb>() calls. Per the Honesty Rule: resolvable primitives emit kind='literal' with value set; non-literal expressions emit kind='unresolved' with a closed-vocabulary reason tag (the value is null). Methods with no HTTP call, pure delegation, empty literal body, or divergent multiple-call-site bodies are silently absent. Null when no methods produced extractable defaults."
}
}
},
"RequestDefaults": {
"type": "object",
"description": "Map of method name -> (map of property key -> RequestDefaultsEntry). Keys are method names as they appear in the TypeScript class (e.g., 'fetchTime'). Per-property keys preserve the exchange's own string-literal keys verbatim; computed keys surface as the synthetic key '_computed'.",
"additionalProperties": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/RequestDefaultsEntry" }
}
},
"RequestDefaultsEntry": {
"type": "object",
"required": ["value", "kind", "reason"],
"additionalProperties": false,
"properties": {
"value": {
"description": "The resolved literal value (string, number, boolean, null, nested literal object, array of literals) when kind='literal'; always null when kind='unresolved'."
},
"kind": {
"type": "string",
"enum": ["literal", "unresolved"],
"description": "'literal' means the value is a fully provable primitive or nested literal; 'unresolved' means the AST expression is non-literal and the value is null."
},
"reason": {
"oneOf": [
{ "type": "null" },
{
"type": "string",
"enum": ["conditional_value", "identifier_reference", "dynamic_construction", "computed_key", "spread_elaboration"]
}
],
"description": "Null when kind='literal'. Otherwise a closed-vocabulary tag: 'conditional_value' (ternary / logical expression), 'identifier_reference' (variable or member access), 'dynamic_construction' (call, binary, template literal, partially-literal object/array), 'computed_key' (key was a computed expression), 'spread_elaboration' (reserved for future spread tracking)."
}
}
},
"SignRecipeRecord": {
"type": "object",
"additionalProperties": false,
"required": ["crypto_op", "canonical_string", "signature_placement", "auth_headers", "nonce", "pre_sign_transforms", "unresolved_reason", "patch_count"],
"description": "Declarative per-section signing recipe. Authoritative copy of priv/schema/sign_recipe_v1.json (kept in lockstep). See CcxtExtract.SignRecipe moduledoc and SCHEMA.md § Signing Recipe. All derivation fields are nullable: Task 64 ships every record with null derivation fields + unresolved_reason = \"not_yet_derived\". Tasks 65–69 populate fields incrementally.",
"properties": {
"crypto_op": {
"oneOf": [{ "type": "null" }, { "$ref": "#/$defs/SignRecipeCryptoOp" }],
"description": "Hash / signature algorithm. Populated by Task 65."
},
"canonical_string": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"minProperties": 1,
"additionalProperties": false,
"patternProperties": {
"^(GET|POST|PUT|DELETE|PATCH|\\*)$": { "$ref": "#/$defs/SignRecipeCanonicalString" }
}
}
],
"description": "Per-verb map of canonical-string records. Keys are HTTP verb names (GET|POST|PUT|DELETE|PATCH) when sign() branches on method, or the sentinel `*` when every verb shares the same canonical string. A single section may mix families (e.g. GET = hmac_simple, POST = hmac_with_body). Populated by Tasks 66a (HMAC-simple entries) / 66b (HMAC-with-body entries)."
},
"signature_placement": {
"oneOf": [{ "type": "null" }, { "$ref": "#/$defs/SignRecipeSignaturePlacement" }],
"description": "Where the final signature is attached to the request. Populated by Task 65."
},
"auth_headers": {
"oneOf": [
{ "type": "null" },
{ "type": "array", "items": { "$ref": "#/$defs/SignRecipeAuthHeader" } }
],
"description": "Ordered list of auth-related headers (excluding the signature header itself — see signature_placement). Populated by Task 67."
},
"nonce": {
"oneOf": [{ "type": "null" }, { "$ref": "#/$defs/SignRecipeNonce" }],
"description": "Timestamp / nonce source and wire format. Populated by Task 67."
},
"pre_sign_transforms": {
"oneOf": [
{ "type": "null" },
{ "type": "array", "items": { "$ref": "#/$defs/SignRecipePreSignTransform" } }
],
"description": "Ordered transforms applied to the canonical string, body, or resulting signature before going on the wire. Populated by Task 68."
},
"unresolved_reason": {
"oneOf": [
{ "type": "null" },
{ "type": "string", "enum": ["not_yet_derived", "custom_signing_family", "ambiguous_ast", "no_sign_method"] }
],
"description": "Closed-vocabulary reason when derivation fields are null. \"not_yet_derived\" is the scaffold default. Must be null only when every derivation field above is non-null (enforced by Task 69)."
},
"patch_count": {
"type": "integer",
"minimum": 0,
"description": "Three-Strikes counter (see CLAUDE.md). At 3, migrate the recipe to priv/overrides/<id>.json rather than continuing to stretch derivation."
}
}
},
"SignRecipeCryptoOp": {
"type": "object",
"additionalProperties": false,
"required": ["algo"],
"properties": {
"algo": {
"type": "string",
"enum": ["hmac_sha256", "hmac_sha512", "hmac_sha384", "ed25519", "rsa", "custom"]
},
"reason": {
"type": "string",
"minLength": 1,
"description": "Optional rationale; required only when algo = \"custom\"."
}
}
},
"SignRecipeCanonicalString": {
"type": "object",
"additionalProperties": false,
"required": ["family", "components", "encoding"],
"properties": {
"family": {
"type": "string",
"enum": ["hmac_simple", "hmac_with_body", "jwt", "custom"]
},
"components": {
"type": "array",
"items": { "$ref": "#/$defs/SignRecipeCanonicalComponent" }
},
"encoding": {
"type": "string",
"enum": ["url_encoded", "json", "raw"]
}
}
},
"SignRecipeCanonicalComponent": {
"type": "object",
"additionalProperties": false,
"required": ["source"],
"properties": {
"source": {
"type": "string",
"enum": ["timestamp", "api_key", "recv_window", "method", "path", "query", "body", "literal"]
},
"value": {
"type": "string",
"description": "Required iff source = \"literal\"; absent otherwise."
}
}
},
"SignRecipeSignaturePlacement": {
"type": "object",
"additionalProperties": false,
"required": ["location", "key"],
"properties": {
"location": {
"type": "string",
"enum": ["header", "query", "body"]
},
"key": {
"type": "string",
"minLength": 1
}
}
},
"SignRecipeAuthHeader": {
"type": "object",
"additionalProperties": false,
"required": ["name", "source"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"source": {
"type": "string",
"enum": ["api_key", "passphrase", "timestamp", "signature", "recv_window", "literal"]
},
"value": {
"type": "string",
"description": "Required iff source = \"literal\"; absent otherwise."
}
}
},
"SignRecipeNonce": {
"type": "object",
"additionalProperties": false,
"required": ["source", "format"],
"properties": {
"source": {
"type": "string",
"enum": ["timestamp_ms", "timestamp_sec", "timestamp_us", "timestamp_ns", "monotonic", "exchange_supplied"]
},
"format": {
"type": "string",
"enum": ["integer", "iso8601", "hex", "string"]
}
}
},
"SignRecipePreSignTransform": {
"type": "object",
"additionalProperties": false,
"required": ["op", "target"],
"properties": {
"op": {
"type": "string",
"enum": ["hex_encode", "base64_encode", "lowercase", "url_encode", "json_encode"]
},
"target": {
"type": "string",
"enum": ["signature", "body", "canonical_string"]
}
}
},
"ClassInfo": {
"type": "object",
"required": ["rest"],
"additionalProperties": false,
"properties": {
"rest": {
"$ref": "#/$defs/ClassEntry",
"description": "REST class information."
},
"ws": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/ClassEntry" }
],
"description": "WS class information. Null if no WebSocket implementation."
}
}
},
"ClassEntry": {
"type": "object",
"required": ["node_key", "class_name", "extends_resolved", "parent_key", "file", "method_count"],
"additionalProperties": false,
"properties": {
"node_key": {
"type": "string",
"description": "Unique identity: 'rest:<id>' or 'ws:<id>'."
},
"class_name": {
"type": "string",
"description": "TypeScript class name."
},
"id": {
"type": "string",
"description": "Exchange identifier (same as class_name for most exchanges)."
},
"type": {
"type": "string",
"enum": ["rest", "ws"],
"description": "Class type: 'rest' for REST exchange, 'ws' for WebSocket exchange."
},
"extends_raw": {
"type": "string",
"description": "Raw extends value from AST (may be an import alias, e.g., 'binanceRest')."
},
"extends_resolved": {
"type": "string",
"description": "Resolved parent class name (e.g., 'binance' after alias resolution)."
},
"parent_key": {
"type": "string",
"description": "Parent node_key (e.g., 'Exchange', 'rest:binance')."
},
"file": {
"type": "string",
"description": "Source filename (e.g., 'binance.ts')."
},
"method_count": {
"type": "integer",
"description": "Number of methods defined in this class."
},
"methods": {
"type": "array",
"items": { "type": "string" },
"description": "List of method names defined in this class."
},
"method_details": {
"type": "array",
"items": { "$ref": "#/$defs/MethodDetail" },
"description": "Per-method metadata (name, async, param count, statement count)."
}
}
},
"MethodDetail": {
"type": "object",
"required": ["name", "async", "params", "statements"],
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
"async": { "type": "boolean" },
"params": { "type": "integer", "description": "Parameter count." },
"statements": { "type": "integer", "description": "Statement count in method body." }
}
},
"MethodInventory": {
"type": "object",
"required": ["rest"],
"additionalProperties": false,
"properties": {
"rest": {
"type": "array",
"items": { "$ref": "#/$defs/MethodSignature" },
"description": "REST method signatures."
},
"ws": {
"oneOf": [
{ "type": "null" },
{
"type": "array",
"items": { "$ref": "#/$defs/MethodSignature" }
}
],
"description": "WS method signatures. Null if no WebSocket implementation."
}
}
},
"MethodSignature": {
"type": "object",
"description": "Method signature without AST body.",
"required": ["name", "async", "params", "return_type", "statements"],
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
"async": { "type": "boolean" },
"params": {
"type": "array",
"items": { "$ref": "#/$defs/MethodParam" }
},
"return_type": {
"type": ["string", "null"],
"description": "TypeScript return type annotation, if present."
},
"statements": {
"type": "integer",
"description": "Statement count in method body."
}
}
},
"MethodParam": {
"type": "object",
"required": ["name"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Parameter name."
},
"type": {
"type": ["string", "null"],
"description": "TypeScript type annotation, if present."
}
}
},
"InterfaceSignature": {
"type": "object",
"description": "Typed API method signature from abstract interface declaration. Has name, params, and return type but no method body (unlike MethodAST).",
"required": ["name", "params", "return_type"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Method name (e.g., 'publicGetTicker', 'privatePostOrder')."
},
"params": {
"type": "array",
"items": { "$ref": "#/$defs/MethodParam" },
"description": "Parameter list with names and TypeScript type annotations."
},
"return_type": {
"type": ["string", "null"],
"description": "TypeScript return type annotation (typically 'Promise<implicitReturnType>')."
}
}
},
"MethodAST": {
"type": "object",
"description": "Complete method with full ESTree AST body. Used for sign(), handleErrors(), parse*(), watch*(), handle*(), and override methods.",
"required": ["async", "params", "return_type", "statements", "body"],
"additionalProperties": false,
"properties": {
"async": { "type": "boolean" },
"params": {
"type": "array",
"items": { "$ref": "#/$defs/MethodParam" }
},
"return_type": {
"type": ["string", "null"]
},
"statements": {
"type": "integer"
},
"body": {
"$ref": "#/$defs/ASTNode",
"description": "Complete ESTree BlockStatement AST. Preserves all node types, position info (start/end byte offsets), and nested structure."
}
}
},
"ASTNode": {
"type": "object",
"description": "ESTree AST node. Shape varies by node type. Always has 'type' field. Position info via 'start'/'end' byte offsets. Permissive schema — ESTree nodes are too varied to enumerate exhaustively.",
"required": ["type"],
"additionalProperties": true,
"properties": {
"type": {
"type": "string",
"description": "ESTree node type (e.g., 'BlockStatement', 'VariableDeclaration', 'CallExpression')."
},
"start": {
"type": "integer",
"description": "Start byte offset in source file."
},
"end": {
"type": "integer",
"description": "End byte offset in source file."
}
}
},
"HandleErrorsData": {
"type": "object",
"description": "handleErrors() method AST plus exception mappings from describe().",
"required": ["method", "exceptions", "http_exceptions", "error_code_fields", "throw_dispatches"],
"additionalProperties": false,
"properties": {
"method": {
"$ref": "#/$defs/MethodAST",
"description": "The handleErrors() method AST."
},
"exceptions": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"additionalProperties": true,
"description": "Exception mappings from describe().exceptions. Keys include 'broad' and 'exact' plus market-type-specific keys (e.g., 'spot', 'inverse', 'linear', 'option'). Values are objects mapping error strings to error class names."
}
],
"description": "Exception mappings from describe().exceptions. Null if not available."
},
"http_exceptions": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"additionalProperties": { "type": "string" },
"description": "HTTP status code -> error class name mapping."
}
],
"description": "HTTP exception mappings from describe().httpExceptions. Null if not available."
},
"error_code_fields": {
"type": "array",
"description": "Derived from handleErrors() AST: all this.safeString/safeString2/safeValue calls that extract error codes and messages from API responses. Each entry records the object, field name, safe* method used, and alternate field (for safeString2).",
"items": {
"$ref": "#/$defs/ErrorCodeFieldEntry"
}
},
"throw_dispatches": {
"type": "array",
"description": "Derived from handleErrors() AST: one entry per this.throwExactlyMatchedException / throwBroadlyMatchedException call. Each entry records which helper was called, the normalized exceptions-map source (exceptions/exceptions.exact/exceptions.broad/by_url.exact/by_url.broad/other), a raw string rendering of the source expression (anti-rot hatch), the resolved safe* binding for the lookup variable (arg[1]), and the unique resolved safe* binding referenced anywhere in the message expression (arg[2]) when one can be proven.",
"items": {
"$ref": "#/$defs/ThrowDispatchEntry"
}
}
}
},
"ThrowDispatchEntry": {
"type": "object",
"description": "A single this.throw{Exactly,Broadly}MatchedException dispatch in handleErrors(). Pairs the exceptions-map source with the safe* binding that feeds the lookup argument, and with the unique safe* binding referenced by the message expression when derivable, so consumers can reconstruct code/message pairings without re-parsing the AST.",
"required": ["helper", "exceptions_source", "exceptions_source_raw", "lookup", "message_lookup"],
"additionalProperties": false,
"properties": {
"helper": {
"type": "string",
"enum": ["throwExactlyMatchedException", "throwBroadlyMatchedException"]
},
"exceptions_source": {
"type": "string",
"enum": ["exceptions", "exceptions.exact", "exceptions.broad", "by_url.exact", "by_url.broad", "other"],
"description": "Normalized tag for arg[0]. 'exceptions' covers bare this.exceptions. 'exceptions.exact'/'exceptions.broad' cover both this.exceptions['exact'] and this.exceptions.exact shapes. 'by_url.*' covers this.getExceptionsByUrl(url, 'exact'|'broad'). 'other' is the anti-rot fallback — use exceptions_source_raw to disambiguate."
},
"exceptions_source_raw": {
"type": "string",
"description": "Compact string rendering of the arg[0] AST (always present). Anti-rot hatch so consumers can still inspect the original expression when exceptions_source is 'other'."
},
"lookup": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"required": ["object", "object_path", "field", "field2", "method"],
"additionalProperties": false,
"properties": {
"object": { "oneOf": [{ "type": "null" }, { "type": "string" }] },
"object_path": {
"oneOf": [
{ "type": "null" },
{ "type": "array", "items": { "type": "string" } }
]
},
"field": { "oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "integer" }] },
"field2": { "oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "integer" }] },
"method": {
"oneOf": [
{ "type": "null" },
{ "type": "string", "enum": ["safeString", "safeString2", "safeValue"] }
]
}
}
}
],
"description": "Resolved safe* binding for arg[1], or null when arg[1] is not a bound Identifier. Shape mirrors ErrorCodeFieldEntry minus roles/sentinel_values, plus method to save a cross-reference."
},
"message_lookup": {
"oneOf": [
{ "type": "null" },
{
"type": "object",
"required": ["object", "object_path", "field", "field2", "method"],
"additionalProperties": false,
"properties": {
"object": { "oneOf": [{ "type": "null" }, { "type": "string" }] },
"object_path": {
"oneOf": [
{ "type": "null" },
{ "type": "array", "items": { "type": "string" } }
]
},
"field": { "oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "integer" }] },
"field2": { "oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "integer" }] },
"method": {
"oneOf": [
{ "type": "null" },
{ "type": "string", "enum": ["safeString", "safeString2", "safeValue"] }
]
}
}
}
],
"description": "Resolved safe* binding for the unique lookup value referenced anywhere in arg[2], or null when the message expression does not point at a single bound lookup value."
}
}
},
"ErrorCodeFieldEntry": {
"type": "object",
"description": "A single this.safe*(object, field) call found in handleErrors() AST, with role classification based on how the extracted value is used. Roles are derived structurally from the AST: throwExactlyMatchedException usage → error_code (value used as exact-map lookup key, typically a short identifier like Bybit's retCode or OKX's sCode — not necessarily numeric). throwBroadlyMatchedException usage → error_message (value is the message string scanned for substring matches in the broad-map). === / !== comparisons → status_sentinel with operator preserved for polarity detection. A field that flows through both throw helpers in the same handleErrors() accumulates both roles.",
"required": ["object", "object_path", "field", "method", "field2", "roles", "sentinel_values"],
"additionalProperties": false,
"properties": {
"object": {
"oneOf": [{ "type": "null" }, { "type": "string" }],
"description": "The identifier name of the first argument (e.g., 'response', 'error', 'data'). Null if the first argument is not an Identifier node."
},
"object_path": {
"oneOf": [
{ "type": "null" },
{ "type": "array", "items": { "type": "string" } }
],
"description": "Derivation path tracing the object back to the response parameter. E.g., ['response', 'data', 'failure', '0'] means the object was derived via response['data']['failure'][0]. Null when the object is 'response' directly (trivial path) or when the derivation chain cannot be resolved."
},
"field": {
"oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "integer" }],
"description": "The literal value of the second argument — the field name being accessed (e.g., 'code', 'msg'). Integer for array index access. Null if not a Literal node."
},
"method": {
"type": "string",
"enum": ["safeString", "safeString2", "safeValue"],
"description": "Which safe* method was called."
},
"field2": {
"oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "integer" }],
"description": "For safeString2: the alternate field name (third argument). Null for safeString/safeValue or when not provided."
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["error_code", "error_message", "status_sentinel"]
},
"description": "How this field's value is used in handleErrors(). error_code: passed to throwExactlyMatchedException as the exact-map lookup key (dispatches to an exception class via `exceptions.exact[value]`). error_message: passed to throwBroadlyMatchedException as the string to scan for substrings of `exceptions.broad` keys. status_sentinel: compared against literal values via === or !== (see sentinel_values for captured literals and polarity). A field can have multiple roles when its value flows through more than one of these helpers. Empty array when the variable's usage is not structurally resolvable."
},
"sentinel_values": {
"oneOf": [
{ "type": "null" },
{
"type": "array",
"items": {
"type": "object",
"required": ["value", "operator"],
"additionalProperties": false,
"properties": {
"value": { "type": "string", "description": "The literal value being compared against, stringified." },
"operator": { "type": "string", "enum": ["===", "!=="], "description": "The comparison operator used. Consumers can determine polarity: === with early-return means success value, !== with throw means success value (negated), === with throw means error value." }
}
}
}
],
"description": "Literal values compared against this field via === or !== operators, sorted by value. Each entry includes the operator for polarity detection. Null when the field has no status_sentinel role."
}
}
},
"PaginationEntry": {
"type": "object",
"description": "Pagination strategy and parameters for a single call site. Each entry includes provenance (containing_method, target_method). Strategy-specific fields (cursor_received, cursor_sent, cursor_increment, page_key) are present only for relevant strategies.",
"required": ["strategy", "containing_method", "target_method"],
"additionalProperties": true,
"properties": {
"strategy": {
"type": "string",
"enum": ["dynamic", "deterministic", "cursor", "incremental"],
"description": "Pagination strategy type."
},
"containing_method": {
"type": "string",
"description": "Name of the method body where this pagination call was found (e.g. 'fetchAccountsV2')."
},
"target_method": {
"type": ["string", "null"],
"description": "Target method name passed to fetchPaginatedCall* (first argument). Null when the argument is a runtime variable rather than a string literal."
},
"max_entries_per_request": {
"type": ["integer", "null"],
"description": "Maximum entries per paginated API call. Null when not statically resolvable from source."
},
"cursor_received": {
"type": ["string", "null"],
"description": "Field name in response to extract next cursor (cursor strategy only)."
},
"cursor_sent": {
"type": ["string", "null"],
"description": "Field name to send cursor in request (cursor strategy only)."
},
"cursor_increment": {
"type": ["integer", "null"],
"description": "Numeric increment for cursor values (cursor strategy only)."
},
"page_key": {
"type": ["string", "null"],
"description": "Parameter name for page number (incremental strategy only)."
}
}
},
"OverridesData": {
"type": "object",
"description": "Method override analysis for exchanges that extend another exchange. Grouped by REST/WS class.",
"required": ["extends", "rest", "ws"],
"additionalProperties": false,
"properties": {
"extends": {
"type": "string",
"description": "Parent exchange id (e.g., 'binance' for binanceus)."
},
"rest": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/OverrideEntry" }
],
"description": "REST class override details. Null if no REST-specific override."
},
"ws": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/OverrideEntry" }
],
"description": "WS class override details. Null if no WS-specific override."
}
}
},
"OverrideEntry": {
"type": "object",
"description": "Override details for a single class (REST or WS).",
"required": ["parent_key", "overridden", "new_methods", "inherited"],
"additionalProperties": false,
"properties": {
"parent_key": {
"type": "string",
"description": "Parent node_key (e.g., 'rest:binance')."
},
"overridden": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/MethodAST" },
"description": "Map of overridden method name -> MethodAST. These methods exist on the parent and are redefined."
},
"new_methods": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/MethodAST" },
"description": "Map of new method name -> MethodAST. These methods don't exist on the parent."
},
"inherited": {
"type": "array",
"items": { "type": "string" },
"description": "List of method names inherited from the parent (names only, no AST)."
}
}
}
}
}