nautechsystems/nautilus_trader · error

historical open interest request requires `period` metadata

Error message

historical open interest request requires `period` metadata

What it means

Historical open interest custom data requests (BinanceFuturesOpenInterestHist) additionally require a `period` metadata string (the Binance OI stats period such as '5m', '1h'). `required_period_metadata` bails when it is missing or blank.

Solutions

  1. Add `period` to the request metadata with a Binance-supported period (e.g. "5m","15m","30m","1h","2h","4h","6h","12h","1d")
  2. Verify the value is serialized as a string, not a number or null
  3. Trim whitespace in configuration
  4. Check Binance futures API docs for currently accepted periods if the request is rejected after adding one

Example fix

// before
IndexMap::from([("instrument_id".to_string(), json!("BTCUSDT-PERP.BINANCE"))])
// after
IndexMap::from([
  ("instrument_id".to_string(), json!("BTCUSDT-PERP.BINANCE")),
  ("period".to_string(), json!("5m")),
])
Defensive patterns

Strategy: validation

Validate before calling

const OI_PERIODS: [&str; 9] = ["5m","15m","30m","1h","2h","4h","6h","12h","1d"];
let period = data_type.metadata().and_then(|m| m.get("period")).and_then(|v| v.as_str());
assert!(period.map_or(false, |p| OI_PERIODS.contains(&p.trim())), "period metadata required");

Type guard

fn has_period(meta: &Option<IndexMap<String, serde_json::Value>>) -> bool {
    meta.as_ref().and_then(|m| m.get("period")).and_then(|v| v.as_str()).map(str::trim).map_or(false, |s| !s.is_empty())
}

Try / catch

match client.request_data(data_type).await {
    Err(e) if e.to_string().contains("requires `period` metadata") => return Err(anyhow::anyhow!("add period metadata e.g. '5m'")),
    other => other,
}

Prevention

When it happens

Trigger: request_data on data type BinanceFuturesOpenInterestHist where metadata lacks a `period` key, has a non-string value, or an empty/whitespace value.

Common situations: Constructing the OpenInterestHist DataCmd without period metadata, or copying a snippet for spot open interest (which has no period) and reusing it for futures hist.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

            .filter(|value| !value.is_empty())
        else {
            anyhow::bail!("custom data request requires `instrument_id` metadata");
        };

        InstrumentId::from_str(raw_instrument_id)
            .with_context(|| format!("invalid instrument_id metadata `{raw_instrument_id}`"))
    }

    fn required_period_metadata(data_type: &DataType) -> anyhow::Result<String> {
        let Some(period) = data_type
            .metadata()
            .as_ref()
            .and_then(|m| m.get("period"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|value| !value.is_empty())
        else {
            anyhow::bail!("historical open interest request requires `period` metadata");
        };

        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 {

View on GitHub (pinned to 18893faf8b)