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
- 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.
- 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.
- 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
- Always compare closing_qty against position/fill size before flipping
- Use plain close paths when the intent is to fully flatten
- Derive closing quantities from instrument size increments to avoid residual-zero splits
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
- AX requires whole contract quantities, was {}
- Order quantity must be at least 1 contract
- Cannot update position {}: not found in cache
- Cannot update position {position_id}: not found in cache
- Duplicate position fill
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f7377c34c8ef1ae0.
Report an issue: GitHub.