nautechsystems/nautilus_trader · error
No mark price returned from OKX
Error message
No mark price returned from OKX
What it means
Raised in `request_mark_price` when OKX returns HTTP success but an empty data array — no mark-price record exists for the requested instrument. The adapter requires exactly one record and treats the empty result as an error.
Source
Thrown at crates/adapters/okx/src/http/client.rs:2807
pub async fn request_mark_price(
&self,
instrument_id: InstrumentId,
) -> anyhow::Result<MarkPriceUpdate> {
let inst = self.instrument_from_cache(instrument_id.symbol.inner())?;
let mut params = GetMarkPriceParamsBuilder::default();
params.inst_type(okx_instrument_type(&inst)?);
params.inst_id(instrument_id.symbol.inner());
let params = params.build().map_err(|e| anyhow::anyhow!(e))?;
let resp = self
.inner
.get_mark_price(params)
.await
.map_err(|e| anyhow::anyhow!(e))?;
let raw = resp
.first()
.ok_or_else(|| anyhow::anyhow!("No mark price returned from OKX"))?;
let ts_init = self.generate_ts_init();
let mark_price =
parse_mark_price_update(raw, instrument_id, inst.price_precision(), ts_init)
.map_err(|e| anyhow::anyhow!(e))?;
Ok(mark_price)
}
/// Requests the current price limits for the `instrument_id` from OKX.
///
/// # Errors
///
/// 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();View on GitHub (pinned to 18893faf8b)
Solutions
- Verify the instrument is currently listed and trading on OKX.
- Check the inst_type derived from the cached instrument matches the instrument (SWAP/FUTURES/OPTION vs SPOT).
- Guard the caller: skip or defer mark-price requests for expired/delisted instruments.
- Retry later if OKX is in maintenance; empty responses during downtime are transient.
Example fix
// before
let mark = client.request_mark_price(&expired_future_id).await?;
// after
if !instrument_is_active(&expired_future_id) { return Ok(None); }
let mark = client.request_mark_price(&expired_future_id).await?; Defensive patterns
Strategy: validation
Validate before calling
if instrument.expiry_unix_nanos() <= clock.timestamp_ns() {
// skip mark price for expired instruments
return Ok(None);
} Type guard
fn is_active(now_unix: i64, inst: &InstrumentAny) -> bool {
inst.expiration().map_or(true, |exp| exp.as_unix_nanos() > now_unix)
} Try / catch
match client.request_mark_price(&inst_id).await {
Ok(mp) => mp,
Err(e) if e.to_string().contains("No mark price returned") => {
log::warn!("no OKX mark price for {inst_id}; skipping");
Default::default()
}
Err(e) => return Err(e),
}; Prevention
- Track instrument expiry and stop requesting data once expired.
- Confirm venue/instrument configuration matches live OKX listings at startup.
- Subscribe to delisting notices and remove instruments from polling sets.
- Handle OKX maintenance windows where data can be temporarily empty.
When it happens
Trigger: Calling `request_mark_price` for an instrument_id whose inst_id/inst_type yields no rows on OKX: delisted instrument, expired derivative, spot pair without mark price, or wrong venue/symbol in config.
Common situations: Polling mark price for an expired future or settled option; symbol configured with the wrong venue suffix; querying before the instrument began trading; OKX temporarily returning empty data during maintenance.
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
- No price limit returned from OKX
- OKX only supports L2_MBP order book deltas
- No usable instruments for {instrument_type:?}, cannot initia
- No usable instruments for {instrument_type:?} family {family
- No instruments loaded for configured types {instrument_types
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/8ae5e92e46c1f402.
Report an issue: GitHub.