nautechsystems/nautilus_trader · error

Spread instrument {symbol} not found

Error message

Spread instrument {symbol} not found

What it means

Thrown by `request_spread_instrument` when OKX's spreads endpoint responds successfully but returns an empty list for the requested `sprd_id`. The adapter treats this as 'the spread instrument does not exist on OKX' and fails instrument loading for that symbol.

Source

Thrown at crates/adapters/okx/src/http/client.rs:2701

        self.cache_instrument(instrument.clone());

        Ok(instrument)
    }

    async fn request_spread_instrument(&self, symbol: &str) -> anyhow::Result<InstrumentAny> {
        let resp = self
            .inner
            .get_spreads(GetSpreadsParams {
                sprd_id: Some(symbol.to_string()),
                ..Default::default()
            })
            .await
            .map_err(|e| anyhow::anyhow!(e))?;

        let raw_spread = resp
            .first()
            .ok_or_else(|| anyhow::anyhow!("Spread instrument {symbol} not found"))?;
        let ts_init = self.generate_ts_init();

        parse_spread_instrument(raw_spread, None, None, None, None, ts_init)
            .map_err(|e| OKXInstrumentDefinitionError::new(symbol, e).into())
    }

    /// Requests event contract series metadata from OKX.
    ///
    /// # Errors
    ///
    /// Returns an error if the HTTP request fails or the response cannot be deserialized.
    pub async fn request_event_contract_series(
        &self,
        params: GetEventContractSeriesParams,
    ) -> Result<Vec<OKXEventContractSeries>, OKXHttpError> {
        self.inner.get_event_contract_series(params).await
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the symbol matches an existing OKX sprd_id exactly (check OKX spread instruments endpoint or web UI).
  2. Remove or correct the spread instrument in your adapter/venue configuration.
  3. Check whether the spread was delisted and pick an active equivalent.
  4. If the symbol is user-defined (custom spread), load it as a locally defined instrument instead of fetching from OKX.

Example fix

// before
let instrument_id = InstrumentId::from_str("OKX-SPREAD/BTC-USDT_BTC-USDT-TYPO")?;
// after
let instrument_id = InstrumentId::from_str("OKX-SPREAD/BTC-USDT_BTC-USDT-240329")?;
Defensive patterns

Strategy: validation

Validate before calling

// Before loading, verify the spread exists on OKX
let exists = http.get_spreads(GetSpreadsParams { sprd_id: Some(symbol.into()), ..Default::default() }).await?.len() > 0;
anyhow::ensure!(exists, "unknown OKX spread: {symbol}");

Try / catch

match client.load_instrument(&spread_id).await {
    Ok(inst) => Ok(Some(inst)),
    Err(e) if e.to_string().contains("not found") => { log::warn!("skipping unknown spread {spread_id}"); Ok(None) }
    Err(e) => Err(e),
};

Prevention

When it happens

Trigger: Requesting an instrument for a spread symbol that OKX does not list (typo in symbol, delisted spread, or a synthetic/invented spread ID not registered on OKX).

Common situations: Config referencing a spread instrument ID that was delisted; symbol formatted incorrectly (must match OKX's sprd_id exactly, e.g. BTC-USDT_BTC-USDT-240329); querying before the spread's listing date.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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