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
- Add rate data for the missing currency to the dataset supplied to the FX rollover module.
- Verify currency code casing/format matches exactly between instrument definitions and the rate table keys.
- Restrict the backtest universe to instruments whose currencies are covered by your rate data.
- 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
- Cross-check instrument currencies against rate-file keys before the run
- Normalize currency code casing and suffixes consistently
- Ship a complete rate dataset covering every traded currency
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
- Missing ask quote for pair {pair}
- {FAILED}: {e}
- Either venue_order_id or client_order_id must be provided
- Missing data in SetFeeProtocol event log
- Missing data in CollectProtocol event log
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/61ca285a76cfd345.
Report an issue: GitHub.