nautechsystems/nautilus_trader · error

No rate data for currency {currency}

Error message

No rate data for currency {currency}

What it means

Raised by `lookup_rate` (used by `calc_overnight_rate`) when there is no rates entry at all for the requested currency in the loaded FX rollover rate table. The module cannot compute an overnight/rollover rate without baseline interest-rate data for that currency.

Source

Thrown at crates/backtest/src/modules/fx_rollover.rs:179

        let symbol = instrument_id.symbol.as_str();
        if symbol.len() < 6 {
            anyhow::bail!("FX symbol must be at least 6 characters: {symbol}");
        }

        let base_currency = &symbol[..3];
        let quote_currency = &symbol[symbol.len() - 3..];

        let base_rate = self.lookup_rate(base_currency, date)?;
        let quote_rate = self.lookup_rate(quote_currency, date)?;

        Ok((base_rate - quote_rate) / 365.0 / 100.0)
    }

    fn lookup_rate(&self, currency: &str, date: Date) -> anyhow::Result<f64> {
        let currency_rates = self
            .rates
            .get(currency)
            .ok_or_else(|| anyhow::anyhow!("No rate data for currency {currency}"))?;

        // Try monthly key first
        let monthly_key = format!("{}-{:02}", date.year(), date.month());
        if let Some(&rate) = currency_rates.get(&monthly_key) {
            return Ok(rate);
        }

        // Fall back to quarterly key
        let quarter = (date.month() - 1) / 3 + 1;
        let quarterly_key = format!("{}-Q{quarter}", date.year());
        if let Some(&rate) = currency_rates.get(&quarterly_key) {
            return Ok(rate);
        }

        anyhow::bail!("No rate data for {currency} at {monthly_key} or {quarterly_key}")
    }
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add rate data for the missing currency to the dataset supplied to the FX rollover module.
  2. Verify currency code casing/format matches exactly between instrument definitions and the rate table keys.
  3. Restrict the backtest universe to instruments whose currencies are covered by your rate data.
  4. Provide a default/override rate for unsupported currencies if your strategy tolerates approximation.

Example fix

// before
let rates = load_rates("rates_major.csv"); // missing SGD
let rate = lookup_rate("SGD", date)?;

// after
let rates = load_rates("rates_all.csv"); // includes SGD
anyhow::ensure!(rates.contains_key("SGD"), "SGD rates missing");
Defensive patterns

Strategy: validation

Validate before calling

let currencies: HashSet<_> = instruments.iter().map(|i| i.base_currency()).collect();
let missing: Vec<_> = currencies.filter(|c| !rates.contains_key(c.as_str())).collect();
anyhow::ensure!(missing.is_empty(), "rate data missing for: {missing:?}");

Type guard

fn has_rates(rates: &HashMap<String, _>, currency: &str) -> bool { rates.contains_key(currency) }

Try / catch

match calc_overnight_rate(currency, date) {
    Ok(rate) => rate,
    Err(e) if e.to_string().starts_with("No rate data") => {
        log::warn!("falling back to zero rate for {currency}");
        0.0
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Computing an overnight rate for a currency absent from the rates map — e.g. trading an instrument quoted in a currency not included in the provided rate dataset, or a currency-code mismatch (USD vs USDT vs USD.T).

Common situations: Backtesting instruments in exotic or crypto-settled currencies while the rate file only covers majors; passing currency codes with differing case or suffixes than the rate file keys.

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/61ca285a76cfd345. Report an issue: GitHub.