nautechsystems/nautilus_trader · error

Interest rate for location '{}' at '{}' must be finite, was

Error message

Interest rate for location '{}' at '{}' must be finite, was {}

What it means

Validation error thrown by `InterestRateRecord::validate` when an interest-rate record's `value` is not finite (NaN or infinity). FX rollover rate data must contain well-formed finite percentages; the module rejects such records before use rather than producing NaN downstream calculations.

Source

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

    pyo3::pyclass(module = "nautilus_trader.backtest", from_py_object)
)]
#[cfg_attr(
    feature = "python",
    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.backtest")
)]
pub struct InterestRateRecord {
    /// OECD location code using ISO 3166 alpha-3 (e.g., "AUS", "USA") or "EA19".
    /// Records with unsupported codes are ignored.
    pub location: String,
    /// Time period key (e.g., "2024-01" for monthly, "2024-Q1" for quarterly).
    pub time: String,
    /// Interest rate value as a percentage (e.g., 5.25 means 5.25%). Must be finite.
    pub value: f64,
}

impl InterestRateRecord {
    pub(crate) fn validate(&self) -> anyhow::Result<()> {
        anyhow::ensure!(
            self.value.is_finite(),
            "Interest rate for location '{}' at '{}' must be finite, was {}",
            self.location,
            self.time,
            self.value
        );
        Ok(())
    }
}

/// Calculates overnight rollover interest rates for FX currency pairs.
///
/// Uses short-term interest rate data (OECD format) to compute the daily
/// differential between base and quote currency rates.
#[derive(Debug, Clone)]
pub struct RolloverInterestCalculator {
    // currency code -> {time_key -> rate_percentage}
    rates: AHashMap<String, AHashMap<String, f64>>,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the rate data file at the reported location/time and fix or remove the non-finite value.
  2. Add pre-load validation that parses each value with strict parsing (reject empty/NaN/inf) before building InterestRateRecords.
  3. Fill missing rate entries with a defined fallback (e.g. previous value or zero) via your data pipeline, not NaN.
  4. Re-export the data source avoiding formulas that can yield errors or infinities.

Example fix

// before
let value: f64 = row.get("rate").unwrap().parse().unwrap_or(f64::NAN);

// after
let raw = row.get("rate").ok_or("missing rate")?;
let value: f64 = raw.trim().parse()?;
anyhow::ensure!(value.is_finite(), "non-finite rate {raw}");
Defensive patterns

Strategy: validation

Validate before calling

let value: f64 = raw.trim().parse()?;
if !value.is_finite() {
    return Err(anyhow::anyhow!("non-finite interest rate for {} at {}: {raw}", location, time));
}

Type guard

fn is_valid_rate(v: f64) -> bool { v.is_finite() && v.abs() < 1000.0 }

Try / catch

let records: Vec<InterestRateRecord> = load_records(path)?;
for r in &records {
    if let Err(e) = r.validate() {
        log::error!("bad rate record: {e}");
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Loading an FX/interest-rate data file containing NaN, Infinity, or a parse that yields non-finite floats (empty fields coerced to NaN, scientific overflow like 1e400).

Common situations: Malformed CSV/parquet rate files with blank values; data exported from spreadsheets with #DIV/0! or overflowed cells; hand-edited rate tables with typos.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — 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/2578eccc59684375. Report an issue: GitHub.