nautechsystems/nautilus_trader · error · anyhow::Error

No RPI order book returned from OKX

Error message

No RPI order book returned from OKX

What it means

request_rpi_order_book_snapshot calls OKX's RPI (retail price improvement) order book endpoint and expects one snapshot; an empty data array triggers this error, mirroring the regular order book path but for the RPI book variant.

Source

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

        &self,
        instrument_id: InstrumentId,
        depth: Option<u32>,
    ) -> anyhow::Result<OrderBook> {
        let inst = self.instrument_from_cache_by_id(instrument_id)?;
        let price_precision = inst.price_precision();
        let size_precision = inst.size_precision();
        let params = GetRpiOrderBookParams {
            inst_id: instrument_id.symbol.to_string(),
            sz: depth,
        };
        let resp = self
            .inner
            .get_rpi_order_book(params)
            .await
            .map_err(|e| anyhow::anyhow!(e))?;
        let snapshot = resp
            .first()
            .ok_or_else(|| anyhow::anyhow!("No RPI order book returned from OKX"))?;
        let ts_event = UnixNanos::from(snapshot.ts * NANOSECONDS_IN_MILLISECOND);
        let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);

        for (i, level) in snapshot.bids.iter().enumerate() {
            let price = Price::from_decimal_dp(level.0, price_precision)?;
            let size = Quantity::from_decimal_dp(level.1, size_precision)?;
            let order = BookOrder::new(OrderSide::Buy, price, size, i as u64);
            book.add(order, 0, i as u64, ts_event);
        }

        let bids_len = snapshot.bids.len();
        for (i, level) in snapshot.asks.iter().enumerate() {
            let price = Price::from_decimal_dp(level.0, price_precision)?;
            let size = Quantity::from_decimal_dp(level.1, size_precision)?;
            let index = (bids_len + i) as u64;
            let order = BookOrder::new(OrderSide::Sell, price, size, index);
            book.add(order, 0, index, ts_event);
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the instrument supports OKX's RPI order book before requesting it.
  2. Fall back to the standard order book endpoint when the RPI book is unavailable.
  3. Verify the inst_id is valid and currently trading.
  4. Handle the empty case gracefully (skip or retry) instead of surfacing it as fatal.

Example fix

// before
let snapshot = resp.first().ok_or_else(|| anyhow::anyhow!("No RPI order book returned from OKX"))?;
// after
let Some(snapshot) = resp.first() else {
    tracing::debug!("no RPI book for {instrument_id}, falling back to standard book");
    return self.request_book_snapshot(instrument_id, depth, ts_init).await;
};
Defensive patterns

Strategy: fallback

Validate before calling

if !supports_rpi(inst_type) {
    return self.request_book_snapshot(instrument_id, depth, ts_init).await;
}

Try / catch

let Some(snapshot) = resp.first() else {
    tracing::debug!("no RPI book; falling back to standard order book");
    return self.request_book_snapshot(instrument_id, depth, ts_init).await;
};

Prevention

When it happens

Trigger: Calling the RPI order book request for an instrument that has no RPI book data (unsupported instrument type, empty response from get_rpi_order_book), or an invalid instId.

Common situations: Requesting RPI books for instruments that don't support the RPI program, using an adapter/endpoint combination where OKX returns data: [] for the RPI depth endpoint, or polling a suspended instrument.

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/217d34f48c091ca5. Report an issue: GitHub.