nautechsystems/nautilus_trader · error · anyhow::Error
Cannot cache futures spread: expected call/put pair call_ins
Error message
Cannot cache futures spread: expected call/put pair call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id} What it means
After confirming both instruments are options, cache_futures_spread checks option_kind(): the first must be OptionKind::Call and the second OptionKind::Put. If either is the wrong kind (put passed as call, two calls, two puts) the method bails, because the put-call parity spread computation requires exactly one call and one put.
Source
Thrown at crates/common/src/greeks.rs:1082
};
let Some(reference_future_instrument) = reference_future_instrument else {
anyhow::bail!(
"Cannot cache futures spread: no reference futures instrument for {futures_instrument_id}"
);
};
if call_instrument.instrument_class() != InstrumentClass::Option
|| put_instrument.instrument_class() != InstrumentClass::Option
{
anyhow::bail!(
"Cannot cache futures spread: non-option instruments provided call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
if call_instrument.option_kind() != Some(OptionKind::Call)
|| put_instrument.option_kind() != Some(OptionKind::Put)
{
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}"View on GitHub (pinned to 18893faf8b)
Solutions
- Check option_kind() on both instruments before calling: first must be Some(OptionKind::Call), second Some(OptionKind::Put).
- If you only have an unordered pair, sort the legs by option_kind and pass call first, put second.
- Verify the adapter populated option_kind correctly for both cached instruments.
Example fix
// before
let price = greeks.cache_futures_spread(leg_a_id, leg_b_id, future_id)?;
// after
let (call_id, put_id) = if cache.instrument(&leg_a_id).unwrap().option_kind() == Some(OptionKind::Call) {
(leg_a_id, leg_b_id)
} else {
(leg_b_id, leg_a_id)
};
let price = greeks.cache_futures_spread(call_id, put_id, future_id)?; Defensive patterns
Strategy: validation
Validate before calling
// rust
fn ordered_pair(cache: &Cache, a: &InstrumentId, b: &InstrumentId) -> Option<(InstrumentId, InstrumentId)> {
let ka = cache.instrument(a)?.option_kind()?;
let kb = cache.instrument(b)?.option_kind()?;
match (ka, kb) {
(OptionKind::Call, OptionKind::Put) => Some((a.clone(), b.clone())),
(OptionKind::Put, OptionKind::Call) => Some((b.clone(), a.clone())),
_ => None,
}
} Type guard
fn call_leg(cache: &Cache, id: &InstrumentId) -> Option<&InstrumentAny> {
cache.instrument(id).filter(|i| i.option_kind() == Some(OptionKind::Call))
} Try / catch
match greeks.cache_futures_spread(call_id, put_id, future_id) {
Ok(p) => use(p),
Err(e) if e.to_string().contains("expected call/put pair") => reorder_legs_and_retry(),
Err(e) => return Err(e),
} Prevention
- Store option legs in structs that name fields explicitly (call_id, put_id) instead of ordered tuples.
- Normalize pair ordering at ingestion time using option_kind.
- Validate adapter-provided option_kind is Some for all loaded options.
When it happens
Trigger: Passing two calls or two puts as the pair; swapping the call and put arguments so option_kind checks fail in both directions; instruments whose option_kind metadata is missing or set incorrectly by the adapter.
Common situations: Constructing the pair from a listing where both legs share a strike/expiry but the code grabs the wrong option_kind; refactored code that dropped an ordering guarantee; adapter loading option_kind as None.
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: option underlyings differ call_
- 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/90166ea0be783f21.
Report an issue: GitHub.