QuantConnect/Lean · error · InvalidOperationException

VolumeShareSlippageModel.GetSlippageApproximation(): Cannot

Error message

VolumeShareSlippageModel.GetSlippageApproximation(): Cannot use this model with market data type {data.GetType()}

What it means

VolumeShareSlippageModel.get_slippage_approximation computes slippage from the ratio of order size to bar volume, so it needs a volume figure. It reads asset.get_last_data() and only knows how to extract volume from MarketDataType.TRADE_BAR (uses .volume) and MarketDataType.QUOTE_BAR (uses last_bid_size/last_ask_size). For any other data type (Tick, Base, Auxiliary, OptionChain, FuturesChain) it raises InvalidOperationException naming the runtime type. The error is thrown by the slippage model during fill processing, not at setup.

Source

Thrown at Common/Orders/Slippage/VolumeShareSlippageModel.py:44

    def get_slippage_approximation(self, asset: Security, order: Order) -> float:
        '''Slippage Model. Return a decimal cash slippage approximation on the order.
        Args:
            asset: The Security instance of the security of the order.
            order: The Order instance being filled.'''
        last_data = asset.get_last_data()
        if not last_data:
           return 0

        bar_volume = 0
        slippage_percent = self.volume_limit * self.volume_limit * self.price_impact

        if last_data.data_type == MarketDataType.TRADE_BAR:
            bar_volume = last_data.volume
        elif last_data.data_type == MarketDataType.QUOTE_BAR:
            bar_volume = last_data.last_bid_size if order.direction == OrderDirection.BUY else last_data.last_ask_size
        else:
           raise InvalidOperationException(Messages.VolumeShareSlippageModel.invalid_market_data_type(last_data))

        # If volume is zero or negative, we use the maximum slippage percentage since the impact of any quantity is infinite
        # In FX/CFD case, we issue a warning and return zero slippage
        if bar_volume <= 0:
            security_type = asset.symbol.id.security_type
            if security_type == SecurityType.CFD or security_type == SecurityType.FOREX or security_type == SecurityType.CRYPTO:
                Log.error(Messages.VolumeShareSlippageModel.volume_not_reported_for_market_data_type(security_type))
                return 0

            Log.error(Messages.VolumeShareSlippageModel.negative_or_zero_bar_volume(bar_volume, slippage_percent))
        else:
            # Ratio of the order to the total volume
            volume_share = min(order.absolute_quantity / bar_volume, self.volume_limit)

            slippage_percent = volume_share * volume_share * self.price_impact

        return slippage_percent * last_data.Value;

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Switch the security to a bar-based resolution (Minute/Hour/Daily) so the last data is a TradeBar/QuoteBar that carries volume.
  2. Replace the slippage model with one that handles your data type: security.set_slippage_model(ConstantSlippageModel(...)) or a custom ISlippageModel that handles Tick/Base data.
  3. If you must keep tick data, implement a custom slippage model that derives volume from the Tick (last_bid_size/last_ask_size) instead of asserting.
  4. Confirm the security's data subscription actually produces TradeBar/QuoteBar; add the fill-forward/quote configuration if needed.

Example fix

# before — default slippage model fails on tick-resolution data
self.add_equity("SPY", Resolution.TICK)
# fill triggers: InvalidOperationException ... market data type Tick

# after — use a slippage model that does not require bar volume
self.add_equity("SPY", Resolution.TICK).set_slippage_model(
    ConstantSlippageModel(0.01))
# or switch to a bar resolution so VolumeShareSlippageModel has volume
self.add_equity("SPY", Resolution.MINUTE)
Defensive patterns

Strategy: validation

Validate before calling

# Before trading, ensure the slippage model can handle the security's data type
from QuantConnect.Orders.Slippage import VolumeShareSlippageModel
sec = self.add_equity('SPY', Resolution.TICK)
last = sec.get_last_data()
uses_bars = last is not None and last.data_type in (MarketDataType.TRADE_BAR, MarketDataType.QUOTE_BAR)
if isinstance(sec.slippage_model, VolumeShareSlippageModel) and not uses_bars:
    sec.set_slippage_model(ConstantSlippageModel(0.01))

Type guard

def slippage_model_supports_data(slippage_model, last_data):
    """True when the configured slippage model can consume the security's last data type."""
    if isinstance(slippage_model, VolumeShareSlippageModel):
        return last_data is None or last_data.data_type in (
            MarketDataType.TRADE_BAR, MarketDataType.QUOTE_BAR)
    return True  # assume non-volume-share models are permissive

Prevention

When it happens

Trigger: A fill is evaluated for a security whose last cached data is not a TradeBar or QuoteBar — most commonly a Tick (the security is subscribed at tick resolution, or the last data is a Tick) or a custom/Base data point. The model's if/elif/else falls through to the else branch and raises. This is typical when a user sets Resolution.TICK (or add_equity with tick data) while keeping the default VolumeShareSlippageModel, or trades a custom-data security.

Common situations: Trading at tick resolution with the default slippage model. Trading custom/Base data (no volume concept). A security whose cache's last data is an Auxiliary event (e.g. dividend/split) at the moment of filling. Subscribing a security type whose fill data is Tick rather than bars.

Related errors


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