nautechsystems/nautilus_trader · error

expected a COIN-M definition for {instrument_id}

Error message

expected a COIN-M definition for {instrument_id}

What it means

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.)

Source

Thrown at crates/adapters/binance/src/futures/data.rs:468

        Ok(period.to_string())
    }

    fn coinm_open_interest_hist_params(
        http: &BinanceFuturesHttpClient,
        instrument_id: &InstrumentId,
    ) -> anyhow::Result<(String, String)> {
        let symbol = format_binance_symbol(instrument_id);
        if let Some(pair) = symbol.strip_suffix("_PERP") {
            return Ok((pair.to_string(), "PERPETUAL".to_string()));
        }

        let cache = http.instruments_cache();
        let definition = cache
            .get(&Ustr::from(symbol.as_str()))
            .with_context(|| format!("missing COIN-M definition for {instrument_id}"))?;
        let BinanceFuturesInstrument::CoinM(definition) = definition.value() else {
            anyhow::bail!("expected a COIN-M definition for {instrument_id}");
        };

        Ok((
            definition.pair.to_string(),
            definition.contract_type.clone(),
        ))
    }

    fn parse_open_interest_decimal(field: &str, value: &str) -> anyhow::Result<Decimal> {
        Decimal::from_str_exact(value)
            .with_context(|| format!("invalid Binance open interest `{field}` value `{value}`"))
    }

    fn liquidation_data_type(instrument_id: InstrumentId) -> DataType {
        let mut metadata = Params::new();
        metadata.insert(
            "instrument_id".to_string(),
            serde_json::Value::String(instrument_id.to_string()),

View on GitHub (pinned to a4b06ed870)

Solutions

  1. 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`
  2. 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
  3. Verify the instrument definitions were loaded before the request (instruments are fetched via HTTP on connect); request after connect completes

Example fix

# before: USD-M symbol hits the COIN-M definition lookup and fails
meta = {'instrument_id': 'BTCUSDT-PERP.BINANCE', 'period': '1h'}

# after: COIN-M PERP symbol short-circuits to (BTCUSD, PERPETUAL)
meta = {'instrument_id': 'BTCUSD_PERP.BINANCE', 'period': '1h'}
Defensive patterns

Strategy: validation

Validate before calling

def is_coinm_instrument_id(instrument_id) -> bool:
    sym = instrument_id.symbol.value  # e.g. BTCUSD_PERP, BTCUSD_250627, BTCUSDT-PERP
    if sym.endswith('_PERP') and not sym.endswith(('USDT_PERP', 'USDC_PERP')):
        return True  # COIN-M perpetual short-circuits without cache lookup
    # dated COIN-M futures look like <BASE>USD_YYMMDD and never quote USDT/USDC
    return sym.endswith('USD') and not sym.endswith(('USDT', 'USDC', 'BUSD'))

if not is_coinm_instrument_id(instrument_id):
    raise ValueError(f'{instrument_id} is not a COIN-M instrument; use e.g. BTCUSD_PERP.BINANCE')

Type guard

def is_coinm_symbol(symbol: str) -> bool:
    return symbol.endswith('_PERP') or (symbol.endswith('USD') and not symbol.endswith(('USDT', 'USDC', 'BUSD')))

Try / catch

try:
    actor.request_custom_data(data_type, ...)
except Exception as e:
    msg = str(e)
    if 'expected a COIN-M definition' in msg:
        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
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/859cab73d2edc104. Report an issue: GitHub.