nautechsystems/nautilus_trader · error

No price limit returned from OKX

Error message

No price limit returned from OKX

What it means

Raised in `request_price_limit` when OKX returns success but an empty data array, meaning no price-limit record exists for the requested instrument. The adapter requires at least one row and converts the empty result into an error.

Source

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

    ///
    /// Returns an error if the HTTP request fails or no price limit is returned.
    pub async fn request_price_limit(
        &self,
        instrument_id: InstrumentId,
    ) -> anyhow::Result<OKXPriceLimit> {
        let mut params = GetPriceLimitParamsBuilder::default();
        params.inst_id(instrument_id.symbol.inner());
        let params = params.build().map_err(|e| anyhow::anyhow!(e))?;

        let resp = self
            .inner
            .get_price_limit(params)
            .await
            .map_err(|e| anyhow::anyhow!(e))?;

        resp.first()
            .cloned()
            .ok_or_else(|| anyhow::anyhow!("No price limit returned from OKX"))
    }

    fn option_summary_exp_time(symbol: &str) -> anyhow::Result<Option<String>> {
        let parts: Vec<&str> = symbol.split('-').collect();
        anyhow::ensure!(
            parts.len() >= 5,
            "Expected OKX option symbol with expiry, received {symbol}"
        );
        Ok(Some(parts[2].to_string()))
    }

    /// Requests the latest index price for the `instrument_id` from OKX.
    ///
    /// # Errors
    ///
    /// Returns an error if the HTTP request fails or no index price is returned.
    pub async fn request_index_price(
        &self,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the instrument is actively listed on OKX and its ID is spelled correctly.
  2. Treat the missing price limit gracefully in the caller (e.g. skip limit checks or use risk-engine defaults).
  3. Retry after brief backoff if OKX is under maintenance.
  4. Reload instruments from OKX so the cache only holds live instruments.

Example fix

// before
let limit = client.request_price_limit(&inst_id).await?;
// after
let limit = match client.request_price_limit(&inst_id).await {
    Ok(l) => l,
    Err(e) if e.to_string().contains("No price limit returned") => return Ok(None),
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: fallback

Validate before calling

if !instrument_is_listed_on_okx(&instrument_id) {
    log::warn!("price limit unavailable for delisted {instrument_id}; using risk-engine defaults");
    return Ok(None);
}

Try / catch

let limit = match client.request_price_limit(&inst_id).await {
    Ok(l) => Some(l),
    Err(e) if e.to_string().contains("No price limit returned") => None,
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling `request_price_limit` for an instrument that OKX has no price-limit entry for: delisted/expired instrument, spot pair without limit bands, or wrong symbol in configuration.

Common situations: Checking price limits for expired options before order placement; misconfigured instrument IDs; querying during OKX maintenance windows when data is temporarily unavailable.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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