{"record":{"id":"55e0b7e43f9a30a3","repo":"nautechsystems/nautilus_trader","slug":"cfd-swap-batch-end-date-was-not-recorded","errorCode":null,"errorMessage":"CFD swap batch end date was not recorded","messagePattern":"CFD swap batch end date was not recorded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/modules/cfd_swap.rs","lineNumber":472,"sourceCode":"                .ok_or_else(|| anyhow::anyhow!(\"CFD swap rollover day is not initialized\"))?;\n            let adjustment_count = day\n                .pending_adjustments\n                .as_ref()\n                .ok_or_else(|| anyhow::anyhow!(\"no completed CFD swap batch to acknowledge\"))?\n                .len();\n            anyhow::ensure!(\n                outcomes.len() == adjustment_count,\n                \"CFD swap acknowledgement count {}, expected {}\",\n                outcomes.len(),\n                adjustment_count\n            );\n            let adjustments = day\n                .pending_adjustments\n                .take()\n                .ok_or_else(|| anyhow::anyhow!(\"no completed CFD swap batch to acknowledge\"))?;\n            let end_date = day\n                .pending_end_date\n                .ok_or_else(|| anyhow::anyhow!(\"CFD swap batch end date was not recorded\"))?;\n            (adjustments, end_date)\n        };\n\n        let mut failed = Vec::new();\n\n        for (adjustment, outcome) in adjustments.into_iter().zip(outcomes) {\n            match outcome {\n                AccountAdjustmentOutcome::Applied => {\n                    let mut totals = self.swap_totals.borrow_mut();\n                    let total = totals.entry(adjustment.amount.currency).or_default();\n                    *total = total\n                        .checked_add(adjustment.amount.as_decimal())\n                        .ok_or_else(|| anyhow::anyhow!(\"CFD swap diagnostic total overflow\"))?;\n                }\n                AccountAdjustmentOutcome::Failed(error) if error.is_retryable() => {\n                    log::warn!(\n                        \"Cannot apply CFD swap adjustment for {} on {}: {error}\",\n                        adjustment.amount.currency,","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/modules/cfd_swap.rs#L454-L490","documentation":"Raised in `acknowledge` when the rollover day exists but `pending_end_date` is None, i.e. the module recorded pending adjustments but never recorded the batch's end date. Internally this indicates the two fields were not set together, so the acknowledgement cannot determine the batch period it is closing.","triggerScenarios":"Calling `acknowledge` when pending_adjustments was populated through a path that skipped setting `pending_end_date`, or acknowledging a partially-initialized rollover day (day exists, batch metadata incomplete).","commonSituations":"Custom or modified rollover pipelines that set pending adjustments manually; deserialized/restored backtest state where the end date was not persisted; a previous successful acknowledgement cleared `pending_end_date` but a second ack still sees stale adjustments.","solutions":["Ensure the batch completion step sets both `pending_adjustments` and `pending_end_date` atomically.","Treat this as a module-internal invariant break: check the version/patch level of the backtest crate and update.","If restoring saved backtest state, persist and restore both fields together.","Avoid calling acknowledge twice; the first ack clears pending_end_date on success."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure batch metadata set together before ack\nanyhow::ensure!(engine.pending_batch_ready(), \"pending adjustments and end date must both be set\");","typeGuard":null,"tryCatchPattern":"match engine.acknowledge(&outcomes) {\n    Err(e) if e.to_string().contains(\"end date was not recorded\") => {\n        log::error!(\"module state inconsistent; rebuild module or upgrade crate\");\n    }\n    other => other?,\n}","preventionTips":["Do not manually construct or mutate rollover day internals","Persist and restore both pending_adjustments and pending_end_date together","Keep the backtest crate up to date"],"tags":["backtest","cfd","internal-state"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}