{"record":{"id":"6e163691c1775510","repo":"QuantConnect/Lean","slug":"expected-cash-expectedcash-but-got-portfolio-ca","errorCode":null,"errorMessage":"Expected cash {expectedCash} but got {Portfolio.Cash}","messagePattern":"Expected cash (.+?) but got (.+?)","errorType":"exception","errorClass":"RegressionTestException","httpStatus":null,"severity":"error","filePath":"Algorithm.CSharp/CustomMarginInterestRateModelAlgorithm.cs","lineNumber":83,"sourceCode":"            var security = Securities[_spy];\n            var marginInterestRateModel = security.MarginInterestRateModel as CustomMarginInterestRateModel;\n\n            if (marginInterestRateModel == null)\n            {\n                throw new RegressionTestException(\"CustomMarginInterestRateModel was not set\");\n            }\n\n            if (marginInterestRateModel.CallCount == 0)\n            {\n                throw new RegressionTestException(\"CustomMarginInterestRateModel was not called\");\n            }\n\n            var expectedCash = _cashAfterOrder * (decimal)Math.Pow(1 + (double)marginInterestRateModel.InterestRate, marginInterestRateModel.CallCount);\n\n            // add a tolerance since using Math.Pow(double, double) given the lack of a decimal overload\n            if (Math.Abs(Portfolio.Cash - expectedCash) > 1e-10m)\n            {\n                throw new RegressionTestException($\"Expected cash {expectedCash} but got {Portfolio.Cash}\");\n            }\n        }\n\n        public class CustomMarginInterestRateModel : IMarginInterestRateModel\n        {\n            public decimal InterestRate { get; } = 0.01m;\n\n            public int CallCount { get; private set; }\n\n            public void ApplyMarginInterestRate(MarginInterestRateParameters marginInterestRateParameters)\n            {\n                var security = marginInterestRateParameters.Security;\n                var positionValue = security.Holdings.GetQuantityValue(security.Holdings.Quantity);\n\n                if (positionValue.Amount > 0)\n                {\n                    positionValue.Cash.AddAmount(InterestRate * positionValue.Cash.Amount);\n                    CallCount++;","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/QuantConnect/Lean/blob/d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892/Algorithm.CSharp/CustomMarginInterestRateModelAlgorithm.cs#L65-L101","documentation":"Asserts compounding correctness. expectedCash = _cashAfterOrder * (1 + InterestRate)^CallCount, compared to Portfolio.Cash within 1e-10. A mismatch means the model applied interest a different number of times, on a different principal, or the cash accounting diverged from the simple compounding assumption.","triggerScenarios":"ApplyMarginInterestRate ran CallCount times, but Portfolio.Cash does not equal the closed-form compound of _cashAfterOrder. Divergence arises if interest is applied to a changing principal, CallCount increments inconsistently, or AddAmount uses the wrong base.","commonSituations":"The model adds interest on positionValue.Cash.Amount (margin cash) which differs from Portfolio.Cash; position closed/reopened mid-run so _cashAfterOrder no longer represents the principal; or CallCount counts only some applications. Floating double-vs-decimal rounding is explicitly tolerated (1e-10), so larger gaps indicate logic errors.","solutions":["Ensure ApplyMarginInterestRate applies exactly InterestRate * base consistently and increments CallCount on the same condition each time.","Confirm _cashAfterOrder is captured exactly once at the fill and the position is held flat afterward so the principal does not change.","Reconcile whether interest should compound on positionValue.Cash.Amount vs Portfolio.Cash and make the closed-form formula match the actual base used.","Log each ApplyMarginInterestRate call (principal, delta, cumulative) to find where accounting diverges."],"exampleFix":"// before: interest added on a different base than the formula assumes\npositionValue.Cash.AddAmount(InterestRate * Portfolio.Cash);\n\n// after: add on the same principal the closed-form uses\nvar principal = _cashAfterOrder * Math.Pow(1 + (double)InterestRate, CallCount);\npositionValue.Cash.AddAmount(InterestRate * positionValue.Cash.Amount);","handlingStrategy":"validation","validationCode":"// Reconcile cash against the model's recorded applications\nvar applied = marginInterestRateModel.CallCount;\nvar expected = _cashAfterOrder * (decimal)Math.Pow(1 + (double)marginInterestRateModel.InterestRate, applied);\nif (Math.Abs(Portfolio.Cash - expected) > 1e-10m)\n    Log($\"Cash drift detected: expected {expected}, actual {Portfolio.Cash}\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Apply interest on the same principal the closed-form formula assumes.","Capture _cashAfterOrder exactly once at fill and keep the position flat afterward.","Log each ApplyMarginInterestRate call to reconcile principal and delta."],"tags":["regression-test","margin-interest-rate","cash-accounting","compounding"],"backgroundTag":null,"analyzedSha":"d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892","analyzedAt":"2026-08-13T13:52:21.013Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}