QuantConnect/Lean · error · RegressionTestException

Expected positive TotalMarginUsed, got {Portfolio.TotalMargi

Error message

Expected positive TotalMarginUsed, got {Portfolio.TotalMarginUsed}

What it means

Thrown when Portfolio.TotalMarginUsed is zero or negative after the ADAUSDT buy order fills. TotalMarginUsed should reflect the maintenance margin consumed by the open ADAUSDT futures position. A zero value means the margin tracking system failed to register the position's margin requirement.

Source

Thrown at Algorithm.CSharp/BinanceCryptoFutureBnfcrCollateralRegressionAlgorithm.cs:79

            // 1. BNFCR collateral must produce positive buying power (USDT is zero)
            var buyingPower = _adaUsdt.BuyingPowerModel.GetBuyingPower(new BuyingPowerParameters(Portfolio, _adaUsdt, OrderDirection.Buy));
            if (buyingPower.Value <= 0)
            {
                throw new RegressionTestException($"Expected positive buying power from BNFCR, got {buyingPower.Value}");
            }

            // 2. Order must not be rejected
            var ticket = Buy(_adaUsdt.Symbol, 1000);
            _orderPlaced = true;
            if (ticket.Status == OrderStatus.Invalid)
            {
                throw new RegressionTestException("Order rejected — BNFCR collateral should cover margin");
            }

            // 3. Margin must be tracked
            if (Portfolio.TotalMarginUsed <= 0)
            {
                throw new RegressionTestException($"Expected positive TotalMarginUsed, got {Portfolio.TotalMarginUsed}");
            }

            // 4. Shared collateral: ETHUSDC (different quote currency) must deduct ADAUSDT margin
            _ethUsdc.SetMarketPrice(new TradeBar { Time = Time, Symbol = _ethUsdc.Symbol, Close = 1600 });

            var ethBuyingPower = _ethUsdc.BuyingPowerModel.GetBuyingPower(new BuyingPowerParameters(Portfolio, _ethUsdc, OrderDirection.Buy));
            var adaBuyingPower = _adaUsdt.BuyingPowerModel.GetBuyingPower(new BuyingPowerParameters(Portfolio, _adaUsdt, OrderDirection.Buy));

            // ETHUSDC must see less buying power than ADAUSDT - ADAUSDT maintenance margin
            // is deducted from ETHUSDC's shared pool, but ADAUSDT skips itself.
            if (ethBuyingPower.Value >= adaBuyingPower.Value)
            {
                throw new RegressionTestException(
                    $"ETHUSDC buying power ({ethBuyingPower.Value}) must be less than ADAUSDT ({adaBuyingPower.Value}) " +
                    $"— shared BNFCR pool must deduct ADAUSDT maintenance margin");
            }
        }

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Verify the ADAUSDT position is actually open: check Portfolio[_adaUsdt.Symbol].Quantity and .HoldingsValue.
  2. Inspect SecurityMarginModel.GetReservedMargin to confirm it returns a positive value for the open position.
  3. Check that Portfolio.ProcessFill or the fill event handler triggers margin recalculation for CryptoFuture.
  4. Log Securities[_adaUsdt.Symbol].BuyMarginModel and .MaintenanceMarginModel outputs.
  5. Ensure the crypto future price feed is active so the margin model has a current price to compute against.

Example fix

// before
if (Portfolio.TotalMarginUsed <= 0)
{
    throw new RegressionTestException($"Expected positive TotalMarginUsed, got {Portfolio.TotalMarginUsed}");
}

// after — diagnose margin components
if (Portfolio.TotalMarginUsed <= 0)
{
    var sec = Securities[_adaUsdt.Symbol];
    Log($"Position: qty={Portfolio[_adaUsdt.Symbol].Quantity}, " +
        $"margin model={sec.BuyingPowerModel.GetType().Name}");
    throw new RegressionTestException(
        $"Expected positive TotalMarginUsed, got {Portfolio.TotalMarginUsed}");
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate margin tracking before asserting
var qty = Portfolio[_adaUsdt.Symbol].Quantity;
Log($"ADAUSDT position: {qty}, TotalMarginUsed: {Portfolio.TotalMarginUsed}");
if (Portfolio.TotalMarginUsed <= 0 && qty != 0)
{
    Log($"Position open but margin not tracked — check margin model");
}

Prevention

When it happens

Trigger: The CryptoFutureSecurityMarginModel did not update TotalMarginUsed after the fill, the position was not actually opened despite the order being accepted, or the margin model returns zero for this position configuration.

Common situations: Margin model bugs where crypto future positions are not counted, Holding objects not updated after fills, or Portfolio.ProcessFill not invoking the margin model correctly for CryptoFuture security type.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/39f43cee8e592013. Report an issue: GitHub.