nautechsystems/nautilus_trader · error

Cannot apply betting account state: balance would be negativ

Error message

Cannot apply betting account state: balance would be negative {} {} ({})

What it means

BettingAccountState::apply rejects an AccountState event whose balance total is negative, because a betting account cannot hold a negative balance. The library bails with anyhow::Error naming the total, currency, and account id. It guards against corrupt or miscomputed account event data.

Source

Thrown at crates/model/src/accounts/betting.rs:177

}

impl Account for BettingAccount {
    impl_account_base_members!();

    fn is_cash_account(&self) -> bool {
        true
    }

    fn is_margin_account(&self) -> bool {
        false
    }

    fn apply(&mut self, event: AccountState) -> anyhow::Result<()> {
        self.check_event_account_id(&event)?;

        for balance in &event.balances {
            if balance.total.raw < 0 {
                anyhow::bail!(
                    "Cannot apply betting account state: balance would be negative {} {} ({})",
                    balance.total.as_decimal(),
                    balance.currency.code,
                    self.id
                );
            }
        }

        if event.is_reported {
            self.balances_locked.clear();
        }

        self.base_apply(event);
        Ok(())
    }

    fn calculate_balance_locked(
        &self,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the AccountState event balances and correct the negative total values at the source before applying
  2. If the negative balance is legitimate, use a cash/margin account with allow_borrowing instead of a betting account
  3. Verify upstream balance computation (deposits/withdrawals/adjustments) for sign errors

Example fix

// before: event with negative total
let mut state = AccountState::new(...);
state.balances = vec![AccountBalance::new(Money::from(-100.0, AUD), ...)];
account.apply(state)?; // bails
// after: ensure non-negative total
assert!(total.as_decimal() >= Decimal::ZERO);
account.apply(state)?;
Defensive patterns

Strategy: validation

Validate before calling

if event.balances.iter().any(|b| b.total.raw < 0) { return Err(anyhow!("event has negative balance")); }
account.apply(event)?;

Type guard

fn has_non_negative_balances(event: &AccountState) -> bool {
    event.balances.iter().all(|b| b.total.raw >= 0)
}

Try / catch

match account.apply(event) {
    Err(e) if e.to_string().contains("balance would be negative") => { /* fix event data */ }
    other => other?,
}

Prevention

When it happens

Trigger: Calling apply() (or py_apply / betting_account_from_account_events) with an AccountState whose event.balances contains any balance with total.raw < 0.

Common situations: Feeding incorrectly signed balance data from a backtest fill model, converting balances from a broker export with wrong signs, or constructing AccountState events by hand with negative totals.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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