nautechsystems/nautilus_trader · error · anyhow::Error
Cannot cache futures spread: missing option price for
Error message
Cannot cache futures spread: missing option price for {put_instrument_id} What it means
Same flow as the call-side error: `cache_futures_spread` fetches the PUT option's price via `get_price`, and bails with this error when the cache holds no price for the put instrument. Both legs (call and put) are required to derive the synthetic futures spread.
Solutions
- Subscribe to quotes/trades for the put instrument and wait for a price before calling.
- Verify the put instrument ID encodes the correct strike/expiry/venue.
- Skip strikes lacking put quotes or fall back to a model price; only cache spreads for fully quoted pairs.
- Check contract activity status/expiry before calling.
Example fix
// before
engine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying)?;
// after
match engine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying) {
Ok(()) => {}
Err(e) if e.to_string().contains(&put_id.to_string()) => warn!("no put price for {put_id}; skipping"),
Err(e) => return Err(e),
} Defensive patterns
Strategy: fallback
Validate before calling
// Rust: check put leg before computing the spread
if cache.price(&put_instrument_id).is_none() {
return Ok(()); // skip strikes without put quotes
} Type guard
fn price_or_skip(cache: &Cache, id: &InstrumentId) -> Option<f64> { cache.price(id) } Try / catch
match engine.cache_futures_spread(&fut_id, &call_id, &put_id, &u) {
Err(e) if e.to_string().contains(&put_id.to_string()) => debug!("no put price {put_id}"),
other => other?,
} Prevention
- Build put IDs programmatically from the chain definition to avoid symbol mistakes.
- Skip illiquid strikes or substitute put spreads from nearby strikes.
- Verify both legs exist and are active before scheduling spread caching.
- Log the full failing instrument ID from the error to spot systematic symbol bugs.
When it happens
Trigger: cache_futures_spread called when the put option instrument has no cached price — no subscription for the put, no quotes at that strike, wrong/swap-corrected put ID, or the put contract has expired.
Common situations: Sparse put quotes on far strikes; subscribing only calls for a synthetic-short strategy; instrument-ID construction mistakes (wrong expiry/strike encoded in the put symbol); pre-market hours with empty option books.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Cannot cache futures spread: missing option price for
- Cannot cache futures spread: no reference futures price for
- Account not found after cache update
- cached client order has no association with requested venue…
- cached client order is associated with venue order , not…
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/131634716bca3d0e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/common/src/greeks.rs:1127
if call_instrument.expiration_ns() != put_instrument.expiration_ns() {
anyhow::bail!(
"Cannot cache futures spread: expiration dates differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
let reference_future_price = self.get_price_object(&futures_instrument_id).ok_or_else(|| {
anyhow::anyhow!(
"Cannot cache futures spread: no reference futures price for {futures_instrument_id}"
)
})?;
let call_price = self.get_price(&call_instrument_id).ok_or_else(|| {
anyhow::anyhow!(
"Cannot cache futures spread: missing option price for {call_instrument_id}"
)
})?;
let put_price = self.get_price(&put_instrument_id).ok_or_else(|| {
anyhow::anyhow!(
"Cannot cache futures spread: missing option price for {put_instrument_id}"
)
})?;
let underlying_instrument_id =
InstrumentId::from(format!("{call_underlying}.{}", call_instrument_id.venue));
// Reject if the underlying is present in cache but is not a future
{
let cache = self.cache.borrow();
if let Some(underlying) = cache.instrument(&underlying_instrument_id)
&& underlying.instrument_class() != InstrumentClass::Future
{
anyhow::bail!(
"Cannot cache futures spread: underlying {underlying_instrument_id} is not a futures contract"
);
}
}View on GitHub (pinned to 18893faf8b)