nautechsystems/nautilus_trader · error · anyhow::Error
Cannot cache futures spread: option underlyings differ call_
Error message
Cannot cache futures spread: option underlyings differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id} What it means
cache_futures_spread requires the call and put legs to be a matched pair on the same underlying: the parity-based implied future price is only meaningful when both options reference the same underlying. If call_underlying != put_underlying the method bails with this error listing both instruments.
Source
Thrown at crates/common/src/greeks.rs:1099
{
anyhow::bail!(
"Cannot cache futures spread: expected call/put pair call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
let Some(call_underlying) = call_instrument.underlying() else {
anyhow::bail!(
"Cannot cache futures spread: missing call underlying for {call_instrument_id}"
);
};
let Some(put_underlying) = put_instrument.underlying() else {
anyhow::bail!(
"Cannot cache futures spread: missing put underlying for {put_instrument_id}"
);
};
if call_underlying != put_underlying {
anyhow::bail!(
"Cannot cache futures spread: option underlyings differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
if call_instrument.strike_price() != put_instrument.strike_price() {
anyhow::bail!(
"Cannot cache futures spread: strike prices differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
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!(View on GitHub (pinned to 18893faf8b)
Solutions
- Group option instruments by underlying and only pass call/put pairs sharing the same underlying.
- Compare underlying() on both legs before calling and skip/log mismatched pairs.
- Correct the ID source if the pairing was built from a mis-sorted or mis-keyed collection.
Example fix
// before
let price = greeks.cache_futures_spread(esm6_call_id, esz6_put_id, esm6_future_id)?;
// after
if call.underlying() != put.underlying() {
tracing::warn!("skipping mismatched pair {call_id} / {put_id}");
return Ok(None);
}
let price = greeks.cache_futures_spread(esm6_call_id, esm6_put_id, esm6_future_id)?; Defensive patterns
Strategy: validation
Validate before calling
// rust
fn same_underlying(cache: &Cache, call_id: &InstrumentId, put_id: &InstrumentId) -> bool {
cache.instrument(call_id).and_then(|i| i.underlying()) == cache.instrument(put_id).and_then(|i| i.underlying())
} Type guard
fn pair_key(cache: &Cache, call_id: &InstrumentId, put_id: &InstrumentId) -> Option<Ustr> {
let u = cache.instrument(call_id)?.underlying()?;
(cache.instrument(put_id)?.underlying()? == u).then_some(u)
} Try / catch
match greeks.cache_futures_spread(call_id, put_id, future_id) {
Ok(p) => use(p),
Err(e) if e.to_string().contains("underlyings differ") => {
tracing::warn!("dropping cross-underlying pair {call_id}/{put_id}");
Ok(None)
}
Err(e) => Err(e),
} Prevention
- Group options by underlying before forming call/put pairs.
- Never pair options across contract months or symbols for parity computations.
- Include underlying in the pairing key when indexing option chains.
When it happens
Trigger: Passing options from different underlyings (e.g. ESM6 options vs ESZ6 options) as the call/put pair; a copy-paste mistake pairing options across contract months or across symbols entirely.
Common situations: Loop code that pairs consecutive option listings without grouping by underlying; building calendars/diagonal-style structures and feeding them to a routine that expects same-strike/same-expiry parity pairs.
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: non-option instruments provided
- Cannot cache futures spread: expected call/put pair call_ins
- Cannot cache futures spread: strike prices differ call_instr
- Cannot cache futures spread: expiration dates differ call_in
- option_summary_family_subs mutex poisoned
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4c50c5bdaf2658ef.
Report an issue: GitHub.