{"record":{"id":"859cab73d2edc104","repo":"nautechsystems/nautilus_trader","slug":"expected-a-coin-m-definition-for-instrument-id","errorCode":null,"errorMessage":"expected a COIN-M definition for {instrument_id}","messagePattern":"expected a COIN-M definition for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/binance/src/futures/data.rs","lineNumber":468,"sourceCode":"\n        Ok(period.to_string())\n    }\n\n    fn coinm_open_interest_hist_params(\n        http: &BinanceFuturesHttpClient,\n        instrument_id: &InstrumentId,\n    ) -> anyhow::Result<(String, String)> {\n        let symbol = format_binance_symbol(instrument_id);\n        if let Some(pair) = symbol.strip_suffix(\"_PERP\") {\n            return Ok((pair.to_string(), \"PERPETUAL\".to_string()));\n        }\n\n        let cache = http.instruments_cache();\n        let definition = cache\n            .get(&Ustr::from(symbol.as_str()))\n            .with_context(|| format!(\"missing COIN-M definition for {instrument_id}\"))?;\n        let BinanceFuturesInstrument::CoinM(definition) = definition.value() else {\n            anyhow::bail!(\"expected a COIN-M definition for {instrument_id}\");\n        };\n\n        Ok((\n            definition.pair.to_string(),\n            definition.contract_type.clone(),\n        ))\n    }\n\n    fn parse_open_interest_decimal(field: &str, value: &str) -> anyhow::Result<Decimal> {\n        Decimal::from_str_exact(value)\n            .with_context(|| format!(\"invalid Binance open interest `{field}` value `{value}`\"))\n    }\n\n    fn liquidation_data_type(instrument_id: InstrumentId) -> DataType {\n        let mut metadata = Params::new();\n        metadata.insert(\n            \"instrument_id\".to_string(),\n            serde_json::Value::String(instrument_id.to_string()),","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/binance/src/futures/data.rs#L450-L486","documentation":"When resolving COIN-M open-interest-history request parameters, symbols ending in `_PERP` short-circuit to the pair/`PERPETUAL` tuple; every other symbol is looked up in the client's instruments cache and must yield a `BinanceFuturesInstrument::CoinM` definition. This error means a definition WAS found in the cache but it is a different variant (USD-M), so `pair` and `contract_type` cannot be extracted for the COIN-M endpoint. (A missing cache entry produces the separate 'missing COIN-M definition' context error.)","triggerScenarios":"Requesting `BinanceFuturesOpenInterestHist` on the COIN-M request path for a non-PERP symbol whose formatted symbol resolves to a USD-M instrument definition in the cache — e.g. a USD-M style symbol such as `BTCUSDT...` routed through `coinm_open_interest_hist_params`. Also possible when the instrument provider loaded USD-M definitions under a symbol the COIN-M path then looks up.","commonSituations":"Using a USD-M instrument id (BTCUSDT-PERP.BINANCE) with a client/market configured for COIN-M; dated COIN-M futures (e.g. BTCUSD_250627) whose definitions were never loaded or were loaded from the wrong market's exchange info; switching `market` in config while the instruments cache still holds definitions from the other market.","solutions":["Use a genuine COIN-M instrument id — `BTCUSD_PERP.BINANCE` (short-circuits, no cache lookup) or a dated COIN-M contract like `BTCUSD_250627.BINANCE`","Make sure the data client's market/instrument_provider configuration matches the instruments you request (COIN-M provider config for COIN-M requests) so the cache holds CoinM definitions","Verify the instrument definitions were loaded before the request (instruments are fetched via HTTP on connect); request after connect completes"],"exampleFix":"# before: USD-M symbol hits the COIN-M definition lookup and fails\nmeta = {'instrument_id': 'BTCUSDT-PERP.BINANCE', 'period': '1h'}\n\n# after: COIN-M PERP symbol short-circuits to (BTCUSD, PERPETUAL)\nmeta = {'instrument_id': 'BTCUSD_PERP.BINANCE', 'period': '1h'}","handlingStrategy":"validation","validationCode":"def is_coinm_instrument_id(instrument_id) -> bool:\n    sym = instrument_id.symbol.value  # e.g. BTCUSD_PERP, BTCUSD_250627, BTCUSDT-PERP\n    if sym.endswith('_PERP') and not sym.endswith(('USDT_PERP', 'USDC_PERP')):\n        return True  # COIN-M perpetual short-circuits without cache lookup\n    # dated COIN-M futures look like <BASE>USD_YYMMDD and never quote USDT/USDC\n    return sym.endswith('USD') and not sym.endswith(('USDT', 'USDC', 'BUSD'))\n\nif not is_coinm_instrument_id(instrument_id):\n    raise ValueError(f'{instrument_id} is not a COIN-M instrument; use e.g. BTCUSD_PERP.BINANCE')","typeGuard":"def is_coinm_symbol(symbol: str) -> bool:\n    return symbol.endswith('_PERP') or (symbol.endswith('USD') and not symbol.endswith(('USDT', 'USDC', 'BUSD')))","tryCatchPattern":"try:\n    actor.request_custom_data(data_type, ...)\nexcept Exception as e:\n    msg = str(e)\n    if 'expected a COIN-M definition' in msg:\n        raise ValueError(f'{instrument_id} resolved to a USD-M definition on the COIN-M path; use a COIN-M symbol like BTCUSD_PERP') from e\n    raise","preventionTips":["Keep USD-M (…USDT/USDC symbols) and COIN-M (…USD symbols) instruments in separate, clearly named config lists","Ensure the client's market/instrument_provider settings match the instruments you request so the cache holds the right variant","For COIN-M PERPs prefer symbols ending in _PERP — they bypass the cache lookup entirely"],"tags":["binance","coin-m","futures","open-interest","instrument-definition"],"backgroundTag":"instrument-definition-mismatch","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}