nautechsystems/nautilus_trader · error

closing quantity {closing_qty} must be smaller than fill qua

Error message

closing quantity {closing_qty} must be smaller than fill quantity {}

What it means

Position::split_for_position_flip splits a fill into a closing order and an opening order when flipping a position. The closing quantity must be strictly smaller than the fill quantity (some of the fill must remain to open the new side); equal or larger values would leave an empty or inverted split, so the library rejects it via anyhow::ensure!.

Source

Thrown at crates/model/src/events/order/filled.rs:169

    pub fn is_sell(&self) -> bool {
        self.order_side == OrderSide::Sell
    }

    /// Splits an overfill into the fragment which closes the current position and the
    /// fragment which opens the flipped position.
    ///
    /// # Errors
    ///
    /// Returns an error when `closing_qty` is zero, is not smaller than the fill quantity,
    /// or the proportional commission cannot be represented.
    pub fn split_for_position_flip(
        &self,
        closing_qty: Quantity,
        opening_position_id: Option<PositionId>,
        opening_event_id: UUID4,
    ) -> anyhow::Result<(Self, Self)> {
        anyhow::ensure!(!closing_qty.is_zero(), "closing quantity was zero");
        anyhow::ensure!(
            closing_qty.raw < self.last_qty.raw,
            "closing quantity {closing_qty} must be smaller than fill quantity {}",
            self.last_qty,
        );

        let opening_qty =
            Quantity::from_raw(self.last_qty.raw - closing_qty.raw, closing_qty.precision);
        let closing_fraction = closing_qty.as_decimal() / self.last_qty.as_decimal();
        let (closing_commission, opening_commission) = match self.commission {
            Some(commission) => {
                let closing = Money::from_decimal(
                    commission.as_decimal() * closing_fraction,
                    commission.currency,
                )?;
                (Some(closing), Some(commission - closing))
            }
            None => (None, None),
        };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass a closing_qty strictly less than the fill quantity; if you want to close the position entirely, use a plain close/flatten path instead of split_for_position_flip.
  2. Check position.qty (net exposure) before flipping and clamp closing_qty to min(position_qty, last_qty.raw - 1) in the instrument's size increment.
  3. If the fill already equals the intended closing amount, treat the flip as a full close: submit the opposite-side order without the split helper.

Example fix

// before
let (close_fill, open_fill) = filled
    .split_for_position_flip(filled.last_qty, None, open_event_id)?;
// after
let closing = Quantity::new(filled.last_qty.as_f64() - instrument.size_increment.as_f64(), filled.last_qty.precision);
let (close_fill, open_fill) = filled
    .split_for_position_flip(closing, None, open_event_id)?;
Defensive patterns

Strategy: validation

Validate before calling

if closing_qty.raw >= filled.last_qty.raw || closing_qty.is_zero() {
    // fall back to full close instead of flip split
}

Prevention

When it happens

Trigger: Calling split_for_position_flip with closing_qty equal to or greater than self.last_qty of the existing OrderFilled event. Reached indirectly via apply_orderless_flip_fill or flip_position when the flip quantity passed in does not leave a residual opening amount.

Common situations: Flattening a position exactly to zero with a flip helper (passing the full position size as closing qty), sizing a flip order from stale position data, or off-by-one/confusion between 'close all' and 'flip' semantics in strategy code.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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