QuantConnect/Lean · error · RegressionTestException

Unexpected cached margin interest rate for {interestRate.Key

Error message

Unexpected cached margin interest rate for {interestRate.Key}!

What it means

Thrown by a regression test in OnData to assert cache consistency: every MarginInterestRate object delivered in a Slice must be the same object cached on its Security. Lean pushes MarginInterestRate into both the time slice and Securities[symbol].Cache; if the two diverge, the data pipeline has a bug (cache not refreshed, or a stale/duplicate object). It is an internal integrity check, not a runtime contract a user code path normally hits.

Source

Thrown at Algorithm.CSharp/BybitCryptoFuturesRegressionAlgorithm.cs:81

            _interestPerSymbol[_btcUsd.Symbol] = 0;

            // the amount of USDT we need to hold to trade 'BTCUSDT'
            _btcUsdt.QuoteCurrency.SetAmount(200);
            // the amount of BTC we need to hold to trade 'BTCUSD'
            _btcUsd.BaseCurrency.SetAmount(0.005m);
        }

        public override void OnData(Slice slice)
        {
            var interestRates = slice.Get<MarginInterestRate>();
            foreach (var interestRate in interestRates)
            {
                _interestPerSymbol[interestRate.Key]++;

                var cachedInterestRate = Securities[interestRate.Key].Cache.GetData<MarginInterestRate>();
                if (cachedInterestRate != interestRate.Value)
                {
                    throw new RegressionTestException($"Unexpected cached margin interest rate for {interestRate.Key}!");
                }
            }

            if (!_slow.IsReady)
            {
                return;
            }

            if (_fast > _slow)
            {
                if (!Portfolio.Invested && Transactions.OrdersCount == 0)
                {
                    var ticket = Buy(_btcUsd.Symbol, 1000);
                    if (ticket.Status != OrderStatus.Invalid)
                    {
                        throw new RegressionTestException($"Unexpected valid order {ticket}, should fail due to margin not sufficient");
                    }

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Ensure the same MarginInterestRate instance is written to the Security cache (Security.Cache.Store(data)) in the same step it is added to the slice.
  2. If you added a custom data type or brokerage, register MarginInterestRate handling so Lean's SubscriptionDataReaderConsumer caches it like other BaseData.
  3. Run the regression under a debugger and inspect whether cachedInterestRate is null (cache miss) or a distinct instance (duplicate allocation) to locate the divergence.
  4. Confirm the data builder for BybitCryptoFutures produces one MarginInterestRate per symbol and forwards it through BaseData.Cache properly.

Example fix

// before: slice value and cache hold different instances
slice.Add(key, rate);
// security.Cache never updated -> divergence

// after: cache the exact instance emitted in the slice
Security.Cache.Store(rate);
slice.Add(rate.Symbol, rate);
Defensive patterns

Strategy: validation

Validate before calling

// Before trusting the cached rate, verify cache/slice consistency defensively:
var sliceRates = slice.Get<MarginInterestRate>();
foreach (var kv in sliceRates)
{
    var cached = Securities[kv.Key].Cache.GetData<MarginInterestRate>();
    if (cached == null || !ReferenceEquals(cached, kv.Value))
    {
        // log and skip rather than throw in user code
        Log($"Cache/slice mismatch for {kv.Key}; skipping");
        continue;
    }
    // use the rate
}

Type guard

bool IsCacheConsistent(Security security, MarginInterestRate sliceRate) =>
    ReferenceEquals(security.Cache.GetData<MarginInterestRate>(), sliceRate);

Prevention

When it happens

Trigger: Calling slice.Get<MarginInterestRate>() and comparing each value to Securities[interestRate.Key].Cache.GetData<MarginInterestRate>(); the comparison fails when the cache holds a different/null MarginInterestRate than the slice. Happens with crypto-futures margin rate ingestion where the rate is produced by a broker/builder that bypasses the cache write.

Common situations: Changes to the MarginInterestRate builder/serializer, a new brokerage whose margin-rate data type is not registered in the cache, or an engine refactor that stopped updating Security.Cache before emitting the slice. Also seen after upgrading Lean versions that altered data-cache semantics.

Related errors


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