QuantConnect/Lean · error · RegressionTestException
Expected cash {expectedCash} but got {Portfolio.Cash}
Error message
Expected cash {expectedCash} but got {Portfolio.Cash} What it means
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.
Source
Thrown at Algorithm.CSharp/CustomMarginInterestRateModelAlgorithm.cs:83
var security = Securities[_spy];
var marginInterestRateModel = security.MarginInterestRateModel as CustomMarginInterestRateModel;
if (marginInterestRateModel == null)
{
throw new RegressionTestException("CustomMarginInterestRateModel was not set");
}
if (marginInterestRateModel.CallCount == 0)
{
throw new RegressionTestException("CustomMarginInterestRateModel was not called");
}
var expectedCash = _cashAfterOrder * (decimal)Math.Pow(1 + (double)marginInterestRateModel.InterestRate, marginInterestRateModel.CallCount);
// add a tolerance since using Math.Pow(double, double) given the lack of a decimal overload
if (Math.Abs(Portfolio.Cash - expectedCash) > 1e-10m)
{
throw new RegressionTestException($"Expected cash {expectedCash} but got {Portfolio.Cash}");
}
}
public class CustomMarginInterestRateModel : IMarginInterestRateModel
{
public decimal InterestRate { get; } = 0.01m;
public int CallCount { get; private set; }
public void ApplyMarginInterestRate(MarginInterestRateParameters marginInterestRateParameters)
{
var security = marginInterestRateParameters.Security;
var positionValue = security.Holdings.GetQuantityValue(security.Holdings.Quantity);
if (positionValue.Amount > 0)
{
positionValue.Cash.AddAmount(InterestRate * positionValue.Cash.Amount);
CallCount++;View on GitHub (pinned to d2c3659f87)
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.
Example fix
// before: interest added on a different base than the formula assumes positionValue.Cash.AddAmount(InterestRate * Portfolio.Cash); // after: add on the same principal the closed-form uses var principal = _cashAfterOrder * Math.Pow(1 + (double)InterestRate, CallCount); positionValue.Cash.AddAmount(InterestRate * positionValue.Cash.Amount);
Defensive patterns
Strategy: validation
Validate before calling
// Reconcile cash against the model's recorded applications
var applied = marginInterestRateModel.CallCount;
var expected = _cashAfterOrder * (decimal)Math.Pow(1 + (double)marginInterestRateModel.InterestRate, applied);
if (Math.Abs(Portfolio.Cash - expected) > 1e-10m)
Log($"Cash drift detected: expected {expected}, actual {Portfolio.Cash}"); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unexpected cached margin interest rate for {interestRate.Key
- CustomMarginInterestRateModel was not set
- CustomMarginInterestRateModel was not called
- {Time} - Unexpected symbol changed event old symbol: {change
- {Time} - Unexpected symbol changed event new symbol: {change
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/6e163691c1775510.
Report an issue: GitHub.