nautechsystems/nautilus_trader · error

Failed to convert Decimal to f64

Error message

Failed to convert Decimal to f64

What it means

Position adjustments carry an optional quantity_change as a Decimal; apply_adjustment_state converts it to f64 to update signed_qty and panics if the Decimal cannot be represented (e.g. beyond f64 range). Conversion to f64 fails only for extreme magnitudes since Decimal supports far wider ranges.

Source

Thrown at crates/model/src/position.rs:672

    ///
    /// # Panics
    ///
    /// Panics if the adjustment's `quantity_change` cannot be converted to f64.
    pub fn apply_adjustment(&mut self, adjustment: PositionAdjusted) {
        self.apply_adjustment_state(adjustment, true);
    }

    fn apply_adjustment_state(&mut self, adjustment: PositionAdjusted, record_replay: bool) {
        if record_replay {
            self.replay_events
                .push(PositionReplayEvent::Adjusted(adjustment));
        }

        // Apply quantity change if present
        if let Some(quantity_change) = adjustment.quantity_change {
            self.signed_qty += quantity_change
                .to_f64()
                .expect("Failed to convert Decimal to f64");

            self.quantity = Quantity::new(self.signed_qty.abs(), self.size_precision);

            if self.quantity > self.peak_qty {
                self.peak_qty = self.quantity;
            }
        }

        // Apply PnL change if present
        if let Some(pnl_change) = adjustment.pnl_change {
            self.realized_pnl = Some(match self.realized_pnl {
                Some(current) => current + pnl_change,
                None => pnl_change,
            });
        }

        // Update position state based on quantity (source of truth for zero check)
        // This handles floating-point precision edge cases

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Sanity-check adjustment.quantity_change magnitude before applying (must be within f64 range, realistically within the instrument's size limits)
  2. Construct quantity_change from a proper Quantity/domain type rather than an arbitrary Decimal
  3. Use try_from-style conversion with explicit error handling in custom adjustment code instead of expect

Example fix

// before
self.signed_qty += quantity_change.to_f64().expect("Failed to convert Decimal to f64");
// after
let delta = quantity_change.to_f64().filter(|v| v.is_finite()).ok_or_else(|| anyhow::anyhow!("quantity_change out of range"))?;
self.signed_qty += delta;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: before apply_adjustment
let q = adjustment.quantity_change.map(|d| d.to_f64());
if let Some(Some(v)) = q { assert!(v.is_finite() && v.abs() < 1e15, "adjustment out of range"); }

Type guard

fn valid_qty_change(d: &Decimal) -> Option<f64> { d.to_f64().filter(|v| v.is_finite()) }

Prevention

When it happens

Trigger: Applying a PositionAdjustment (via apply_adjustment, purge_events_for_order, rebuild_from_replay, or apply_base_commission_adjustment) whose quantity_change is too large for f64 — astronomically large position sizes/quantities.

Common situations: Unit bugs creating adjustments with raw/unsealed Decimal values (e.g. passing raw scaled integers as quantity_change); corrupted adjustment data from external reconciliation; wrong scale/precision when constructing the Decimal.

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