{"record":{"id":"bdf004d3fc573827","repo":"QuantConnect/Lean","slug":"expected-position-group-buying-power-model-type-o","errorCode":null,"errorMessage":"Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel. Actual: {type(position_group.buying_power_model).__name__}","messagePattern":"Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel\\. Actual: (.+?)","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"Algorithm.Python/OptionStrategyFactoryMethodsBaseAlgorithm.py","lineNumber":45,"sourceCode":"        option = self.add_option(\"GOOG\")\n        self._option_symbol = option.symbol\n\n        option.set_filter(lambda u: u.standards_only().strikes(-2, +2).expiration(0, 180))\n\n        self.set_benchmark(\"GOOG\")\n\n    def on_data(self, slice):\n        if not self.portfolio.invested:\n            chain = slice.option_chains.get(self._option_symbol)\n            if chain is not None:\n                self.trade_strategy(chain, self._option_symbol)\n        else:\n            # Verify that the strategy was traded\n            position_group = list(self.portfolio.positions.groups)[0]\n\n            buying_power_model = position_group.buying_power_model\n            if not isinstance(buying_power_model, OptionStrategyPositionGroupBuyingPowerModel):\n                raise AssertionError(\"Expected position group buying power model type: OptionStrategyPositionGroupBuyingPowerModel. \"\n                                f\"Actual: {type(position_group.buying_power_model).__name__}\")\n\n            self.assert_strategy_position_group(position_group, self._option_symbol)\n\n            # Now we should be able to close the position\n            self.liquidate_strategy()\n\n            # We can quit now, no more testing required\n            self.quit()\n\n    def on_end_of_algorithm(self):\n        if self.portfolio.invested:\n            raise AssertionError(\"Expected no holdings at end of algorithm\")\n\n        orders_count = len(list(self.transactions.get_orders(lambda order: order.status == OrderStatus.FILLED)))\n        if orders_count != self.expected_orders_count():\n            raise AssertionError(f\"Expected {self.expected_orders_count()} orders to have been submitted and filled, \"\n                            f\"half for buying the strategy and the other half for the liquidation. Actual {orders_count}\")","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/QuantConnect/Lean/blob/d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892/Algorithm.Python/OptionStrategyFactoryMethodsBaseAlgorithm.py#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: legs booked separately -> SecurityPositionGroupBuyingPowerModel\nself.buy(call.symbol, 1); self.sell(put.symbol, 1)\n# after: submit as a recognized strategy combo so the group uses the strategy BPM\nstrategy = OptionStrategies.Straddle(self._option_symbol)\nself.buy(strategy, 1)","handlingStrategy":"type-guard","validationCode":"# Before asserting, confirm a strategy group exists and inspect its BPM\nfrom QuantConnect.Securities.Positions import OptionStrategyPositionGroupBuyingPowerModel\ngroups = list(self.portfolio.positions.groups)\nif not groups:\n    return  # nothing invested yet\nbpm = groups[0].buying_power_model\nif not isinstance(bpm, OptionStrategyPositionGroupBuyingPowerModel):\n    # legs were not grouped as a strategy; resubmit as a combo\n    self.log(f'unexpected BPM: {type(bpm).__name__}')","typeGuard":"from QuantConnect.Securities.Positions import OptionStrategyPositionGroupBuyingPowerModel\n\ndef is_strategy_group(group) -> bool:\n    return isinstance(group.buying_power_model,\n                      OptionStrategyPositionGroupBuyingPowerModel)","tryCatchPattern":null,"preventionTips":["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."],"tags":["options","position-group","buying-power","regression-test","quantconnect","option-strategy"],"backgroundTag":null,"analyzedSha":"d2c3659f877bfc2b5d9dc0fc89a9c7566f45e892","analyzedAt":"2026-08-13T13:52:21.013Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}