nautechsystems/nautilus_trader · error

No option expirations for {currency}

Error message

No option expirations for {currency}

What it means

After fetching expirations, request_option_expirations looks up the per-currency entry via expirations_for_currency and requires its `option` expirations. When the currency key is missing from the response map there is no Option expirations list, so the call fails. This is a lookup miss on a response that did return a result.

Source

Thrown at crates/adapters/deribit/src/http/client.rs:1955

    /// # Errors
    ///
    /// Returns an error if the request fails.
    pub async fn request_option_expirations(
        &self,
        currency: DeribitCurrency,
    ) -> anyhow::Result<Vec<String>> {
        let params = GetExpirationsParams::new(currency.as_str(), DeribitExpirationKind::Option);
        let full_response = self
            .inner
            .get_expirations(params)
            .await
            .map_err(|e| anyhow::anyhow!(e))?;
        let response = full_response
            .result
            .ok_or_else(|| anyhow::anyhow!("No result in expirations response"))?;
        let expirations = response
            .expirations_for_currency(currency.as_str())
            .ok_or_else(|| anyhow::anyhow!("No option expirations for {currency}"))?;

        Ok(expirations.option.clone())
    }

    /// Requests position status reports for reconciliation.
    ///
    /// Fetches positions from Deribit and converts them to Nautilus [`PositionStatusReport`].
    ///
    /// # Strategy
    /// - Uses `currency=any` to fetch all positions in one call
    /// - Filters by instrument_id if provided
    ///
    /// # Errors
    ///
    /// Returns an error if the request fails or parsing fails.
    pub async fn request_position_status_reports(
        &self,
        account_id: AccountId,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use the canonical uppercase Deribit currency code matching the response keys.
  2. Confirm the currency actually lists option instruments; otherwise handle the absence gracefully.
  3. Log available keys from the response to see which currencies were returned.

Example fix

// before
let expirations = response
    .expirations_for_currency(currency.as_str())
    .ok_or_else(|| anyhow::anyhow!("No option expirations for {currency}"))?;
// after
let expirations = response.expirations_for_currency(currency.as_str()).ok_or_else(|| {
    anyhow::anyhow!(
        "No option expirations for {currency}; available: {:?}",
        response.available_currencies()
    )
})?;
Defensive patterns

Strategy: fallback

Validate before calling

// Guard against non-canonical currency strings before the call
let currency = currency.to_uppercase();
assert!(!currency.is_empty());

Try / catch

match client.request_option_expirations(currency).await {
    Ok(x) => handle(x),
    Err(e) if e.to_string().contains("No option expirations for") => Vec::new(), // fall back to empty
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling request_option_expirations(currency) where the returned expirations map has no entry for that exact currency string (case mismatch or currency not present in the payload).

Common situations: Passing a lowercase or non-canonical currency symbol, or a currency that lists expirations only for futures but not options.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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