nautechsystems/nautilus_trader · error

expected a COIN-M definition for

Error message

expected a COIN-M definition for {instrument_id}

What it means

For COIN-M historical open interest, the adapter resolves the instrument's definition to get its `pair` and contract type. If the stored definition for the instrument is not the COIN-M variant (e.g. it is a USD-M futures definition), this invariant-style bail fires.

Solutions

  1. Use a COIN-M instrument id ending in the COIN-M venue (e.g. BTCUSD_PERP.BINANCE_COINM-style identifier)
  2. Confirm the instrument was loaded from the COIN-M exchange info endpoints, not USD-M
  3. Check whether the request should target the USD-M open interest handler instead
  4. Rebuild the instrument definitions cache if it was populated with the wrong product type

Example fix

// before
instrument_id = "BTCUSDT-PERP.BINANCE" // USD-M definition
// after
instrument_id = "BTCUSD_PERP.BINANCE_COINM" // COIN-M definition
Defensive patterns

Strategy: type-guard

Validate before calling

let def = http.instrument_metadata(instrument_id).await?;
assert!(matches!(def, BinanceFuturesInstrument::CoinM(_)), "instrument must be COIN-M for OI hist");

Type guard

fn is_coinm(def: &BinanceFuturesInstrument) -> bool {
    matches!(def, BinanceFuturesInstrument::CoinM(_))
}

Try / catch

match client.request_data(data_type).await {
    Err(e) if e.to_string().contains("expected a COIN-M definition") => return Err(anyhow::anyhow!("use a COIN-M instrument id")),
    other => other,
}

Prevention

When it happens

Trigger: request_data for BinanceFuturesOpenInterestHist with a COIN-M-style instrument whose resolved definition from http.instrument_metadata is a BinanceFuturesInstrument::UsdM (or another variant) instead of CoinM.

Common situations: Passing a USD-M instrument id (e.g. BTCUSDT-PERP.BINANCE) where a COIN-M instrument (BTCUSD_PERP.BINANCE_COINM) is expected, or requesting OI hist for the wrong venue suffix.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

        };

        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 definition = http
            .instrument_metadata(*instrument_id)
            .with_context(|| format!("missing COIN-M definition for {instrument_id}"))?;
        let BinanceFuturesInstrument::CoinM(definition) = definition else {
            anyhow::bail!("expected a COIN-M definition for {instrument_id}");
        };

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

    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()),
        );
        DataType::new(
            "BinanceFuturesLiquidation",

View on GitHub (pinned to 18893faf8b)