{"record":{"id":"131634716bca3d0e","repo":"nautechsystems/nautilus_trader","slug":"cannot-cache-futures-spread-missing-option-price-131634","errorCode":null,"errorMessage":"Cannot cache futures spread: missing option price for {put_instrument_id}","messagePattern":"Cannot cache futures spread: missing option price for (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/common/src/greeks.rs","lineNumber":1127,"sourceCode":"\n        if call_instrument.expiration_ns() != put_instrument.expiration_ns() {\n            anyhow::bail!(\n                \"Cannot cache futures spread: expiration dates differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}\"\n            );\n        }\n\n        let reference_future_price = self.get_price_object(&futures_instrument_id).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Cannot cache futures spread: no reference futures price for {futures_instrument_id}\"\n            )\n        })?;\n        let call_price = self.get_price(&call_instrument_id).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Cannot cache futures spread: missing option price for {call_instrument_id}\"\n            )\n        })?;\n        let put_price = self.get_price(&put_instrument_id).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Cannot cache futures spread: missing option price for {put_instrument_id}\"\n            )\n        })?;\n\n        let underlying_instrument_id =\n            InstrumentId::from(format!(\"{call_underlying}.{}\", call_instrument_id.venue));\n\n        // Reject if the underlying is present in cache but is not a future\n        {\n            let cache = self.cache.borrow();\n            if let Some(underlying) = cache.instrument(&underlying_instrument_id)\n                && underlying.instrument_class() != InstrumentClass::Future\n            {\n                anyhow::bail!(\n                    \"Cannot cache futures spread: underlying {underlying_instrument_id} is not a futures contract\"\n                );\n            }\n        }","sourceCodeStart":1109,"sourceCodeEnd":1145,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/greeks.rs#L1109-L1145","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nengine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying)?;\n// after\nmatch engine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(&put_id.to_string()) => warn!(\"no put price for {put_id}; skipping\"),\n    Err(e) => return Err(e),\n}","handlingStrategy":"fallback","validationCode":"// Rust: check put leg before computing the spread\nif cache.price(&put_instrument_id).is_none() {\n    return Ok(()); // skip strikes without put quotes\n}","typeGuard":"fn price_or_skip(cache: &Cache, id: &InstrumentId) -> Option<f64> { cache.price(id) }","tryCatchPattern":"match engine.cache_futures_spread(&fut_id, &call_id, &put_id, &u) {\n    Err(e) if e.to_string().contains(&put_id.to_string()) => debug!(\"no put price {put_id}\"),\n    other => other?,\n}","preventionTips":["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."],"tags":["rust","options","missing-price","cache"],"backgroundTag":"resource-not-found","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}