QuantConnect/Lean · error · RegressionTestException
Index is not tradable.
Error message
Index is not tradable.
What it means
Identical assertion to error 21 but in BasicTemplateIndexOptionsAlgorithm. Thrown when Portfolio[_spx].TotalSaleVolume > 0, meaning the engine filled orders on the SPX index itself. Index symbols must remain non-tradable — only their options should generate fills.
Source
Thrown at Algorithm.CSharp/BasicTemplateIndexOptionsAlgorithm.cs:106
if (_emaFast > _emaSlow && contract.Right == OptionRight.Call)
{
Liquidate(InvertOption(contract.Symbol));
MarketOrder(contract.Symbol, 1);
}
else if (_emaFast < _emaSlow && contract.Right == OptionRight.Put)
{
Liquidate(InvertOption(contract.Symbol));
MarketOrder(contract.Symbol, 1);
}
}
}
}
public override void OnEndOfAlgorithm()
{
if (Portfolio[_spx].TotalSaleVolume > 0)
{
throw new RegressionTestException("Index is not tradable.");
}
if (Portfolio.TotalSaleVolume == 0)
{
throw new RegressionTestException("Trade volume should be greater than zero by the end of this algorithm");
}
AssertIndicators();
}
public Symbol InvertOption(Symbol symbol)
{
return QuantConnect.Symbol.CreateOption(
symbol.Underlying,
symbol.ID.Market,
symbol.ID.OptionStyle,
symbol.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call,
symbol.ID.StrikePrice,
symbol.ID.Date);
}View on GitHub (pinned to d2c3659f87)
Solutions
- Audit all order method calls (MarketOrder, LimitOrder, Liquidate, SetHoldings) to confirm they use option contract symbols, not _spx.
- Log Securities[_spx].IsTradable after Initialize to verify it is false.
- Check for InvertOption() misuse — ensure it returns option symbols, not the underlying index.
- Review the IndexSecurity/SecurityService code path for IsTradable default assignment.
Example fix
// before — Liquidate(_spx) is called somewhere Liquidate(_spx); // after — liquidate specific option positions Liquidate(contract.Symbol);
Defensive patterns
Strategy: validation
Validate before calling
// Verify index is non-tradable before running
if (Securities[_spx].IsTradable)
{
Log($"WARNING: {_spx} is tradable — index should not be");
}
// Verify no order calls reference _spx
foreach (var kvp in Securities)
{
if (kvp.Key == _spx && kvp.Value.Holdings.Quantity != 0)
Log($"Unexpected position on index {_spx}: {kvp.Value.Holdings.Quantity}");
} Type guard
bool IsTradableSecurity(Symbol sym) =>
Securities[sym].IsTradable && Securities[sym].Type != SecurityType.Index; Prevention
- Use only option contract symbols (from chain.Contracts) for order calls.
- Verify InvertOption() always returns option symbols, never the underlying.
- Log Securities[_spx].IsTradable after Initialize.
- Add unit tests that assert index symbols cannot receive fills.
When it happens
Trigger: The OnData logic calls MarketOrder on the _spx index symbol instead of a contract symbol, the engine's IsTradable flag for index securities was reset, or a Liquidate() call inadvertently targets the index.
Common situations: Code refactoring that passes _spx to MarketOrder instead of contract.Symbol, brokerage model changes affecting index tradability, or engine-level security initialization bugs.
Related errors
- Index is not tradable.
- Index is not tradable.
- Stock was not assigned
- Expected no holdings at end of algorithm
- Trade volume should be greater than zero by the end of this
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/0e3de66443516f8c.
Report an issue: GitHub.