{"record":{"id":"3ae416021d7415dc","repo":"nautechsystems/nautilus_trader","slug":"instrument-instrument-id-has-no-underlying-ident","errorCode":null,"errorMessage":"Instrument {instrument_id} has no underlying identifier","messagePattern":"Instrument (.+?) has no underlying identifier","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/common/src/greeks.rs","lineNumber":452,"sourceCode":"                vega_time_weight_base,\n                vol_index_instrument_id,\n                vol_beta_weights,\n            )?;\n        }\n\n        if let Some(pos) = position {\n            greeks_data.pnl = greeks_data.price - pos.avg_px_open;\n        }\n\n        Ok(greeks_data)\n    }\n\n    fn resolve_underlying_instrument_id(\n        instrument: &InstrumentAny,\n        instrument_id: InstrumentId,\n    ) -> anyhow::Result<InstrumentId> {\n        let Some(underlying) = instrument.underlying() else {\n            anyhow::bail!(\"Instrument {instrument_id} has no underlying identifier\");\n        };\n\n        Ok(InstrumentId::from(format!(\n            \"{}.{}\",\n            underlying, instrument_id.venue\n        )))\n    }\n\n    #[expect(clippy::too_many_arguments)]\n    fn calculate_non_option_greeks(\n        &self,\n        instrument: &InstrumentAny,\n        instrument_id: InstrumentId,\n        spot_shock: f64,\n        ts_event: UnixNanos,\n        position: Option<Position>,\n        percent_greeks: bool,\n        index_instrument_id: Option<InstrumentId>,","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/greeks.rs#L434-L470","documentation":"The greeks calculator needs the underlying instrument of an option to resolve its underlying instrument ID, but the instrument registered in the cache does not expose an underlying identifier. This happens when the instrument is not an option-like derivative (or was built without its underlying field populated), so `instrument.underlying()` returns None and the resolver bails.","triggerScenarios":"Calling `resolve_underlying_instrument_id` (indirectly via greeks calculation paths) with an `InstrumentAny` whose variant has no `underlying()` — e.g. a plain equity/futures instrument, or an option loaded from a provider that did not populate the underlying field.","commonSituations":"Pointing greeks calculation at a non-option instrument ID; instrument definitions loaded from CSV/adapters missing the underlying symbol column; venue-specific options whose underlying symbol isn't in the cache record.","solutions":["Verify the instrument ID passed to the greeks calculation is an option and that its definition includes the underlying symbol","Re-load or re-register the instrument with its underlying field populated (check the adapter/provider data source)","Add an explicit check that the instrument is an option before invoking greeks calculation"],"exampleFix":"// before\nlet greeks = calculator.calculate_option_greeks(&instrument_id, ...)?;\n// after\nlet instrument = cache.instrument(&instrument_id).expect(\"instrument cached\");\nassert!(instrument.underlying().is_some(), \"option {instrument_id} missing underlying\");\nlet greeks = calculator.calculate_option_greeks(&instrument_id, ...)?;","handlingStrategy":"validation","validationCode":"fn has_underlying(instrument: &InstrumentAny) -> bool {\n    instrument.underlying().is_some()\n}","typeGuard":"fn is_option_with_underlying(instrument: &InstrumentAny) -> bool {\n    matches!(instrument, InstrumentAny::Option(_)) && instrument.underlying().is_some()\n}","tryCatchPattern":"match resolve_or_calc(...) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"has no underlying identifier\") => {\n        tracing::warn!(\"instrument lacks underlying; skipping greeks\");\n        Default::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Verify instrument definitions include the underlying field when loading from adapters/CSV","Check that the ID passed to greeks calculation is an option, not a spot/futures instrument","Log instrument metadata at load time to catch missing underlyings early"],"tags":["rust","greeks","options","missing-data"],"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-14T11:17:12.474Z"}