QuantConnect/Lean · error · AssertionError
Expected position group buying power model type: OptionStrat
Error message
Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel. Actual: {type(position_group.buying_power_model).__name__} What it means
In Lean, when legs of a recognized option strategy are traded together, the portfolio groups them into a PositionGroup whose buying power is governed by an OptionStrategyPositionGroupBuyingPowerModel (so margin is computed on the combo, not leg-by-leg). This assertion, run in on_data after the strategy is invested, verifies the first position group's buying_power_model is of that exact type. Failure means the combo was not recognized as a strategy group — the legs were booked as independent positions with a different (e.g. SecurityPositionGroupBuyingPowerModel) model.
Source
Thrown at Algorithm.Python/OptionStrategyFactoryMethodsBaseAlgorithm.py:45
option = self.add_option("GOOG")
self._option_symbol = option.symbol
option.set_filter(lambda u: u.standards_only().strikes(-2, +2).expiration(0, 180))
self.set_benchmark("GOOG")
def on_data(self, slice):
if not self.portfolio.invested:
chain = slice.option_chains.get(self._option_symbol)
if chain is not None:
self.trade_strategy(chain, self._option_symbol)
else:
# Verify that the strategy was traded
position_group = list(self.portfolio.positions.groups)[0]
buying_power_model = position_group.buying_power_model
if not isinstance(buying_power_model, OptionStrategyPositionGroupBuyingPowerModel):
raise AssertionError("Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel. "
f"Actual: {type(position_group.buying_power_model).__name__}")
self.assert_strategy_position_group(position_group, self._option_symbol)
# Now we should be able to close the position
self.liquidate_strategy()
# We can quit now, no more testing required
self.quit()
def on_end_of_algorithm(self):
if self.portfolio.invested:
raise AssertionError("Expected no holdings at end of algorithm")
orders_count = len(list(self.transactions.get_orders(lambda order: order.status == OrderStatus.FILLED)))
if orders_count != self.expected_orders_count():
raise AssertionError(f"Expected {self.expected_orders_count()} orders to have been submitted and filled, "
f"half for buying the strategy and the other half for the liquidation. Actual {orders_count}")View on GitHub (pinned to d2c3659f87)
Solutions
- Ensure all strategy legs are submitted as a single combo order via OptionStrategies (e.g. self.Buy(option_strategy, quantity)) so they form one position group.
- Check the subclass's trade_strategy implementation uses the correct OptionStrategies factory method matching a recognized canonical strategy.
- If you changed Lean's grouping logic, re-register the strategy so its group resolves to OptionStrategyPositionGroupBuyingPowerModel.
- Inspect self.portfolio.positions.groups count and contents to confirm legs were grouped rather than split.
Example fix
# before: legs booked separately -> SecurityPositionGroupBuyingPowerModel self.buy(call.symbol, 1); self.sell(put.symbol, 1) # after: submit as a recognized strategy combo so the group uses the strategy BPM strategy = OptionStrategies.Straddle(self._option_symbol) self.buy(strategy, 1)
Defensive patterns
Strategy: type-guard
Validate before calling
# Before asserting, confirm a strategy group exists and inspect its BPM
from QuantConnect.Securities.Positions import OptionStrategyPositionGroupBuyingPowerModel
groups = list(self.portfolio.positions.groups)
if not groups:
return # nothing invested yet
bpm = groups[0].buying_power_model
if not isinstance(bpm, OptionStrategyPositionGroupBuyingPowerModel):
# legs were not grouped as a strategy; resubmit as a combo
self.log(f'unexpected BPM: {type(bpm).__name__}') Type guard
from QuantConnect.Securities.Positions import OptionStrategyPositionGroupBuyingPowerModel
def is_strategy_group(group) -> bool:
return isinstance(group.buying_power_model,
OptionStrategyPositionGroupBuyingPowerModel) Prevention
- Submit all strategy legs in a single combo order via OptionStrategies so they form one group.
- Match a canonical strategy definition; ad-hoc leg combos may not get the strategy BPM.
- After trading, inspect portfolio.positions.groups before asserting on the BPM.
- Re-run the strategy regression after any Lean position-grouping change.
When it happens
Trigger: After the algorithm is invested, reading list(self.portfolio.positions.groups)[0].buying_power_model and finding it is not an OptionStrategyPositionGroupBuyingPowerModel. Occurs when the option strategy legs do not match a canonical strategy definition, when the strategy factory method changed, or when position-grouping/buying-power-model assignment logic in Lean changed.
Common situations: Subclassing this base algorithm and calling a trade method whose legs no longer satisfy a registered OptionStrategy pattern; a Lean engine refactor of position grouping or buying-power-model resolution; trading legs in separate orders so they never form one group.
Related errors
- Unexpected order event symbol!
- Expected no holdings at end of algorithm
- Expected {self.expected_orders_count()} orders to have been
- AssertStrategyPositionGroup method is not implemented
- Index is not tradable.
AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13).
Data as JSON: /api/errors/bdf004d3fc573827.
Report an issue: GitHub.