nautechsystems/nautilus_trader · error

CFD swap acknowledgement count {}, expected {}

Error message

CFD swap acknowledgement count {}, expected {}

What it means

This error is raised in the CFD swap module's `acknowledge` method when the number of account adjustment outcomes passed back by the caller does not match the number of pending swap adjustments recorded when the rollover batch completed. The backtest module requires a one-to-one acknowledgement of every adjustment it applied. A count mismatch means the driver loop submitted an outcome vector that does not correspond to the current batch.

Source

Thrown at crates/backtest/src/modules/cfd_swap.rs:460

            .as_mut()
            .ok_or_else(|| anyhow::anyhow!("CFD swap rollover day is not initialized"))?;
        day.pending_adjustments = Some(batch);
        day.pending_end_date = Some(batch_end_date);
        Ok(SimulationModuleResult::Completed(adjustments))
    }

    fn acknowledge(&self, outcomes: &[AccountAdjustmentOutcome]) -> anyhow::Result<()> {
        let (adjustments, batch_end_date) = {
            let mut day = self.rollover_day.borrow_mut();
            let day = day
                .as_mut()
                .ok_or_else(|| anyhow::anyhow!("CFD swap rollover day is not initialized"))?;
            let adjustment_count = day
                .pending_adjustments
                .as_ref()
                .ok_or_else(|| anyhow::anyhow!("no completed CFD swap batch to acknowledge"))?
                .len();
            anyhow::ensure!(
                outcomes.len() == adjustment_count,
                "CFD swap acknowledgement count {}, expected {}",
                outcomes.len(),
                adjustment_count
            );
            let adjustments = day
                .pending_adjustments
                .take()
                .ok_or_else(|| anyhow::anyhow!("no completed CFD swap batch to acknowledge"))?;
            let end_date = day
                .pending_end_date
                .ok_or_else(|| anyhow::anyhow!("CFD swap batch end date was not recorded"))?;
            (adjustments, end_date)
        };

        let mut failed = Vec::new();

        for (adjustment, outcome) in adjustments.into_iter().zip(outcomes) {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the caller collects exactly one AccountAdjustmentOutcome per pending adjustment in the same order the adjustments were generated.
  2. Call `acknowledge` exactly once per completed swap batch; do not re-call after a partial acknowledgement.
  3. If filtering outcomes, keep placeholders (e.g. Failed outcomes) rather than removing entries so the count stays aligned.
  4. Log both lengths (outcomes.len() and the pending count) at the call site to identify where the divergence occurs.

Example fix

// before
let outcomes: Vec<_> = results.into_iter().filter(|r| r.is_ok()).collect();
engine.acknowledge(&outcomes)?;

// after
let outcomes: Vec<_> = results.into_iter().collect(); // keep 1:1 with adjustments
engine.acknowledge(&outcomes)?;
Defensive patterns

Strategy: validation

Validate before calling

let pending_count = engine.pending_adjustment_count()?;
anyhow::ensure!(outcomes.len() == pending_count, "outcome count {} != pending {}", outcomes.len(), pending_count);
engine.acknowledge(&outcomes)?;

Try / catch

match engine.acknowledge(&outcomes) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("acknowledgement count") => log::error!("outcome/batch misalignment: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `acknowledge` with an `outcomes` slice whose length differs from `day.pending_adjustments.len()`. Typically happens when outcomes are collected across multiple days, filtered (e.g. dropping failed adjustments), duplicated, or `acknowledge` is called twice for one batch.

Common situations: Backtest drivers that pre-allocate or truncate outcome vectors, or that batch acknowledgements after retry loops which skip some outcomes. Also occurs if `begin_rollover`/batch completion was re-run between generating outcomes and acknowledging them, replacing the pending batch.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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