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
- 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.
- If you added a custom data type or brokerage, register MarginInterestRate handling so Lean's SubscriptionDataReaderConsumer caches it like other BaseData.
- Run the regression under a debugger and inspect whether cachedInterestRate is null (cache miss) or a distinct instance (duplicate allocation) to locate the divergence.
- 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
- Do not write MarginInterestRate instances directly to the slice; route them through the security cache so the two stay identical.
- When adding a brokerage/data type, register it in Lean's cache pipeline so Cache.GetData<T> resolves.
- In regression tests, compare with ReferenceEquals to catch instance divergence early.
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
- Expected positive buying power from BNFCR, got {buyingPower.
- Order rejected — BNFCR collateral should cover margin
- Expected positive TotalMarginUsed, got {Portfolio.TotalMargi
- ETHUSDC buying power ({ethBuyingPower.Value}) must be less t
- Expected an open position at end of algorithm
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/9c03b666d1b0eec4.
Report an issue: GitHub.