{"record":{"id":"223174dd480609ec","repo":"nautechsystems/nautilus_trader","slug":"cannot-cache-futures-spread-no-reference-futures-223174","errorCode":null,"errorMessage":"Cannot cache futures spread: no reference futures price for {futures_instrument_id}","messagePattern":"Cannot cache futures spread: no reference futures price for (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/common/src/greeks.rs","lineNumber":1117,"sourceCode":"            anyhow::bail!(\n                \"Cannot cache futures spread: option underlyings differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}\"\n            );\n        }\n\n        if call_instrument.strike_price() != put_instrument.strike_price() {\n            anyhow::bail!(\n                \"Cannot cache futures spread: strike prices differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}\"\n            );\n        }\n\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","sourceCodeStart":1099,"sourceCodeEnd":1135,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/greeks.rs#L1099-L1135","documentation":"`cache_futures_spread` computes a synthetic futures spread price from call/put option prices against a reference futures price. Before anything else it fetches the reference futures price via `get_price_object`; if the cache holds no price for that futures instrument it bails with this error, because the spread cannot be anchored without it.","triggerScenarios":"Calling cache_futures_spread(futures_instrument_id, call_instrument_id, put_instrument_id, ...) where the reference futures instrument has no cached price (no subscription, no trade/quote yet, or wrong instrument ID).","commonSituations":"Subscribing only to options but not the underlying future; referencing an expired or delisted futures contract; using the options-chain synthetic underlying ID instead of the tradable futures ID; calling before first market data arrives on a cold start.","solutions":["Subscribe to quote/trade data for futures_instrument_id before calling cache_futures_spread.","Check the futures instrument ID is the actual contract (e.g. ESZ5.GLBX), not the synthetic underlying.","Verify the contract is still active/not expired and present in the cache.","Seed the reference price in the cache if the venue does not stream it, or fall back to an earlier cached price."],"exampleFix":"// before\nengine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying)?;\n// after\nif engine.cache.price(&fut_id).is_none() {\n    engine.subscribe_quotes(fut_id)?; // ensure reference future has data\n}\nengine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying)?;","handlingStrategy":"validation","validationCode":"// Rust: verify reference future price exists first\nif cache.price(&futures_instrument_id).is_none() {\n    anyhow::bail!(\"reference future {futures_instrument_id} not priced; subscribe before caching spread\");\n}","typeGuard":"fn has_price(cache: &Cache, id: &InstrumentId) -> bool { cache.price(id).is_some() }","tryCatchPattern":"if let Err(e) = engine.cache_futures_spread(&fut_id, &call_id, &put_id, &underlying) {\n    warn!(\"spread cache skipped: {e}\");\n}","preventionTips":["Subscribe to the underlying futures contract, not just options.","Confirm contract expiry/rollover so IDs never point at delisted contracts.","Run spread caching only after market-data warm-up completes.","Keep a lookup of active contracts per venue instead of hard-coded symbols."],"tags":["rust","futures","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-14T05:17:10.506Z"}