nautechsystems/nautilus_trader · error · anyhow::Error
Instrument {instrument_id} has no underlying identifier
Error message
Instrument {instrument_id} has no underlying identifier What it means
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.
Source
Thrown at crates/common/src/greeks.rs:452
vega_time_weight_base,
vol_index_instrument_id,
vol_beta_weights,
)?;
}
if let Some(pos) = position {
greeks_data.pnl = greeks_data.price - pos.avg_px_open;
}
Ok(greeks_data)
}
fn resolve_underlying_instrument_id(
instrument: &InstrumentAny,
instrument_id: InstrumentId,
) -> anyhow::Result<InstrumentId> {
let Some(underlying) = instrument.underlying() else {
anyhow::bail!("Instrument {instrument_id} has no underlying identifier");
};
Ok(InstrumentId::from(format!(
"{}.{}",
underlying, instrument_id.venue
)))
}
#[expect(clippy::too_many_arguments)]
fn calculate_non_option_greeks(
&self,
instrument: &InstrumentAny,
instrument_id: InstrumentId,
spot_shock: f64,
ts_event: UnixNanos,
position: Option<Position>,
percent_greeks: bool,
index_instrument_id: Option<InstrumentId>,View on GitHub (pinned to 18893faf8b)
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
Example fix
// before
let greeks = calculator.calculate_option_greeks(&instrument_id, ...)?;
// after
let instrument = cache.instrument(&instrument_id).expect("instrument cached");
assert!(instrument.underlying().is_some(), "option {instrument_id} missing underlying");
let greeks = calculator.calculate_option_greeks(&instrument_id, ...)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_underlying(instrument: &InstrumentAny) -> bool {
instrument.underlying().is_some()
} Type guard
fn is_option_with_underlying(instrument: &InstrumentAny) -> bool {
matches!(instrument, InstrumentAny::Option(_)) && instrument.underlying().is_some()
} Try / catch
match resolve_or_calc(...) {
Ok(v) => v,
Err(e) if e.to_string().contains("has no underlying identifier") => {
tracing::warn!("instrument lacks underlying; skipping greeks");
Default::default()
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- No price available for {instrument_id}
- option_summary_family_subs mutex poisoned
- Missing data in SetFeeProtocol event log
- Invalid `OptionKind`, was '{invalid}'
- Unknown IB option right: {value}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/3ae416021d7415dc.
Report an issue: GitHub.