nautechsystems/nautilus_trader · error · anyhow::Error
Cannot cache futures spread: underlying {underlying_instrume
Error message
Cannot cache futures spread: underlying {underlying_instrument_id} is not a futures contract What it means
After computing the underlying instrument ID from the options' underlying symbol and venue, cache_futures_spread checks the Cache: if an instrument exists under that ID but its class is not InstrumentClass::Future, the method bails. This guards against caching a futures spread against an underlying that was registered as something else (e.g. an index or spot instrument), which would make the spread meaningless.
Source
Thrown at crates/common/src/greeks.rs:1141
"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"
);
}
}
let implied_future_price =
self.calculate_implied_future_price(&call_instrument, call_price, put_price);
let spread = implied_future_price - reference_future_price.as_f64();
let spread_price = reference_future_instrument.make_price(spread);
self.cached_futures_spreads.borrow_mut().insert(
underlying_instrument_id,
(futures_instrument_id, spread_price),
);
Ok(reference_future_price + spread_price)
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the underlying instrument is added to the Cache with InstrumentClass::Future, or remove the non-future instrument occupying that ID.
- Check how underlying_instrument_id is composed (underlying symbol + option venue) and confirm it points at the intended futures contract.
- Adjust the adapter's instrument classification so the underlying definition loads as a Future.
Example fix
// before cache.add_instrument(index_def)?; // 'SPX.NYSE' registered as Index // after cache.add_instrument(future_def)?; // ensure the underlying ID maps to a Future instrument
Defensive patterns
Strategy: validation
Validate before calling
// rust
let underlying_id = InstrumentId::from(format!("{underlying}.{}", call_id.venue));
let ok = match cache.instrument(&underlying_id) {
None => true, // unknown is allowed; only wrong class is rejected
Some(i) => i.instrument_class() == InstrumentClass::Future,
};
if !ok { tracing::error!("{underlying_id} registered as non-future"); } Type guard
fn is_cached_future(cache: &Cache, id: &InstrumentId) -> bool {
cache.instrument(id).is_none_or(|i| i.instrument_class() == InstrumentClass::Future)
} Try / catch
let res = greeks.cache_futures_spread(call_id, put_id, future_id);
if let Err(e) = res {
if e.to_string().contains("is not a futures contract") {
fix_underlying_classification(&underlying_id);
}
} Prevention
- Verify instrument_class at ingestion: the underlying of futures options must load as InstrumentClass::Future.
- Avoid registering index/spot definitions under IDs that collide with the futures underlying symbol.
- Assert the composed underlying ID (symbol + venue) matches the intended futures contract in integration tests.
When it happens
Trigger: The underlying symbol+venue resolves to a cached instrument registered as Index/Spot/CurrencyPair instead of Future — e.g. options on an index where the underlying 'ES' was loaded as an index definition, or the underlying ID collides with a non-futures instrument on that venue.
Common situations: Loading both an index and a futures product under similar IDs; adapters that classify the underlying as Spot; venue suffix construction (format!("{underlying}.{venue}")) accidentally matching a different listing.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot cache futures spread: no reference futures instrument
- Cannot cache futures spread: no reference futures price for
- Instrument not found in cache: {symbol}
- `close_position` cannot be combined with `reduce_only` on Bi
- DataActor {} must be registered before calling `cache()` - t
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2797a0e01a408f27.
Report an issue: GitHub.