nautechsystems/nautilus_trader · error

cannot calculate CFD swap for {instrument_id}: midpoint over

Error message

cannot calculate CFD swap for {instrument_id}: midpoint overflow

What it means

The CFD swap module computes the settlement price as the midpoint of the instrument's bid/ask using checked Decimal arithmetic. If bid+ask overflows the Decimal range (or division by two fails), it cannot form a midpoint and throws this error rather than producing a corrupted price.

Source

Thrown at crates/backtest/src/modules/cfd_swap.rs:188

    }

    fn settlement_price(
        ctx: &ExchangeContext,
        instrument_id: InstrumentId,
    ) -> anyhow::Result<Option<Price>> {
        let Some(matching_engine) = ctx.matching_engines.get(&instrument_id) else {
            return Ok(None);
        };
        let book = matching_engine.get_book();

        match (book.best_bid_price(), book.best_ask_price()) {
            (Some(bid), Some(ask)) => {
                let midpoint = bid
                    .as_decimal()
                    .checked_add(ask.as_decimal())
                    .and_then(|sum| sum.checked_div(Decimal::TWO))
                    .ok_or_else(|| {
                        anyhow::anyhow!(
                            "cannot calculate CFD swap for {instrument_id}: midpoint overflow"
                        )
                    })?;
                Ok(Some(Price::from_decimal(midpoint)?))
            }
            (Some(price), None) | (None, Some(price)) => Ok(Some(price)),
            (None, None) => Ok(None),
        }
    }

    fn log_calculation_failure(
        &self,
        booking_date: Date,
        instrument_id: InstrumentId,
        kind: CfdSwapFailureKind,
        message: &str,
    ) {
        let first_failure = self

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the instrument's price_precision/size and the data feed's price scaling; correct mis-scaled bid/ask data
  2. Clamp or normalize extreme bid/ask values before the bar/quote reaches the module
  3. Check upstream data for corruption (e.g. concatenated digits) and filter bad ticks

Example fix

// before: feeding raw prices with wrong scale
let bid = Price::from_raw(79228162514264337593543950335); // near Decimal::MAX
// after: validate magnitude before use
assert!(bid.as_decimal() < Decimal::from(1_000_000), "bid out of expected range");
Defensive patterns

Strategy: validation

Validate before calling

// sanity-check quote magnitudes before the module computes midpoints
fn valid_price(p: Price) -> bool {
    let d = p.as_decimal();
    d > Decimal::ZERO && d < Decimal::from(1_000_000)
}
assert!(valid_price(bid) && valid_price(ask));

Type guard

fn sane_price(p: &Price) -> bool {
    p.as_decimal() > Decimal::ZERO && p.as_decimal() < Decimal::from(1_000_000)
}

Try / catch

match module.settlement_price(&instrument_id, ts) {
    Ok(price) => /* use price */,
    Err(e) if e.to_string().contains("midpoint overflow") => log::warn!("bad quote data: {e:#}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling settlement_price for an instrument whose bid and ask Decimals are so large their sum exceeds Decimal's maximum (e.g. mis-scaled prices like 1e28 for a low-precision instrument), when both bid and ask are present.

Common situations: Instruments configured with wrong precision/scale so prices parse as enormous Decimals; corrupted data where bid/ask carry garbage magnitudes; mixing price scales across data sources.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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