nautechsystems/nautilus_trader · error

position commission currency differs for fill {}

Error message

position commission currency differs for fill {}

What it means

While distributing a voided commission across the positions touched by a fill, the engine requires that every position fragment's recorded commission is denominated in the same currency as the commission being voided. If a fragment's commission currency differs from remaining_commission.currency, exact Money arithmetic would be undefined across currencies, so the engine raises this error.

Source

Thrown at crates/execution/src/engine/mod.rs:3942

                .and_modify(|allocation| allocation.0 = allocation.0 + removed)
                .or_insert((removed, None));
            remaining_qty = remaining_qty - removed;
        }
        anyhow::ensure!(
            remaining_qty.is_zero(),
            "position fragments do not cover voided quantity for fill {}",
            event.trade_id
        );

        if let Some(mut remaining_commission) = event.commission_voided {
            for (position_id, _, _, commission) in fragments.iter().rev() {
                if remaining_commission.is_zero() {
                    break;
                }
                let Some(commission) = commission else {
                    continue;
                };
                anyhow::ensure!(
                    commission.currency == remaining_commission.currency,
                    "position commission currency differs for fill {}",
                    event.trade_id
                );
                let removed_raw = remaining_commission.raw.abs().min(commission.raw.abs());
                let removed = Money::from_raw(
                    removed_raw * remaining_commission.raw.signum(),
                    remaining_commission.currency,
                );
                allocations
                    .entry(*position_id)
                    .and_modify(|allocation| {
                        allocation.1 = Some(
                            allocation
                                .1
                                .map_or(removed, |commission| commission + removed),
                        );
                    })

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the void event's commission currency matches the currency used when the original fill's commission was booked.
  2. Check strategy/instrument configuration so commission_currency is consistent for all fills of the instrument.
  3. If commissions genuinely span currencies, split the void per currency and apply separate void events per currency.
  4. Log each position's recorded commission currency at fill time and compare with the void event to locate the mismatched fragment.

Example fix

// before: voiding a USD commission against a fill booked in USD fee
let voided = Money::new(2.5, Currency::USD());
engine.apply_fill_void(FillVoided { commission_voided: Some(voided), .. })?;
// after: match the currency of the recorded commission
let voided = Money::new(2.5, recorded_fill.commission.currency);
engine.apply_fill_void(FillVoided { commission_voided: Some(voided), .. })?;
Defensive patterns

Strategy: validation

Validate before calling

// ensure the void commission currency matches the fill's recorded commission
if let Some(voided) = &event.commission_voided {
    assert_eq!(voided.currency, recorded_fill(event.trade_id).commission.currency,
        "void commission currency differs from booked commission");
}

Try / catch

match engine.apply_fill_void(event) {
    Err(e) if e.to_string().contains("commission currency differs") => {
        log::error!("currency mismatch voiding fill {}: {e}", event.trade_id);
        // re-emit the void with the fill's booked commission currency
    }
    other => other?,
}

Prevention

When it happens

Trigger: Applying a fill void whose commission_voided currency does not equal the currency of the commission recorded on one of the positions involved — e.g. commissions were booked in quote currency but the void event carries base-currency commission, or multi-currency instruments with mixed commission currencies per fill.

Common situations: Configuring a strategy's commission currency inconsistently between backtest and live; adapters reporting commissions in different currencies across partial fills; changing instrument or account currency config between runs while replaying cached events.

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