nautechsystems/nautilus_trader · error

no completed CFD swap batch to acknowledge

Error message

no completed CFD swap batch to acknowledge

What it means

acknowledge() requires that a completed batch of pending adjustments exists to match the supplied outcomes against. If rollover_day exists but pending_adjustments is None, there is no completed batch to acknowledge and this error is thrown.

Source

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

        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"))?;
        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();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Acknowledge exactly once per Completed result from process()
  2. Track batch lifecycle in your engine wrapper: only call acknowledge when the preceding process returned Completed
  3. If retrying after a failure, re-run process() to regenerate the batch before acknowledging

Example fix

// before: double acknowledge
module.acknowledge(&outcomes)?;
module.acknowledge(&outcomes)?; // pending already consumed
// after
module.acknowledge(&outcomes)?; // once per Completed batch
Defensive patterns

Strategy: validation

Validate before calling

// guard: only acknowledge a batch you actually received
let batch: Option<Vec<Money>> = match module.process(ts_now)? {
    SimulationModuleResult::Completed(a) => Some(a),
    _ => None,
};
if let Some(amounts) = batch { /* acknowledge */ }

Type guard

fn batch_ready(r: &SimulationModuleResult) -> Option<&[Money]> {
    match r { SimulationModuleResult::Completed(a) => Some(a), _ => None }
}

Try / catch

match module.acknowledge(&outcomes) {
    Err(e) if e.to_string().contains("no completed CFD swap batch") => {
        log::warn!("acknowledge called without a completed batch");
        Ok(())
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling acknowledge() either before any process() returned Completed, or a second time after the first acknowledge consumed pending_adjustments, or with outcomes for a batch that was never produced.

Common situations: Retry logic re-acknowledging the same batch; engine event ordering delivering acknowledge before the module's process completed; tests acknowledging with fabricated outcomes without running process first.

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/90346c8be45777b8. Report an issue: GitHub.