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

  1. Verify the instrument ID passed to the greeks calculation is an option and that its definition includes the underlying symbol
  2. Re-load or re-register the instrument with its underlying field populated (check the adapter/provider data source)
  3. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/3ae416021d7415dc. Report an issue: GitHub.