QuantConnect / QuantConnect/Lean
Actual portfolio weight exceeds target percentage when using `PortfolioTarget.Percent`
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 21.7k
- Forks
- 5.3k
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 34
Description
Expected Behavior
The portfolio weight is less or equal to the target percentage at the time of order fill.
Actual Behavior
The portfolio weight far exceeds the target percentage when using PortfolioTarget.Percent.

Potential Solution
I think this happens since LEAN uses security.Price to determine the initial margin requirements in line 230 of the buying power model which affects the result of PortfolioTarget.Percent via line 269 in the position group buying power model.
Instead we should use security.AskPrice if target.Quantity > security.Holdings.Quantity (i.e. delta > 0) and otherwise security.BidPrice; at least if quote bars are available for the given security and we therefore can assume to get a fill at the ask/bid price. It also explains why the discrepancy is proportional to the bid-ask spread.
Reproducing the Problem
Run this backtest and view the order history and log file.
It is intended to buy 10% of a QQQ option contract, but instead we get 14% which is a huge discrepancy.
from AlgorithmImports import *
class DemoAlgoPortfolioTargetPercentBug(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 3, 15)
self.SetEndDate(2020, 4, 1)
self.SetCash(1_000_000)
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
self.Settings.FreePortfolioValuePercentage = 0
self.targetPercent = 0.1
self.AddEquity("QQQ", Resolution.Minute)
self.symbol = Symbol.CreateOption("QQQ", Market.USA, OptionStyle.American, OptionRight.Call, 174, datetime(2020, 4, 17))
self.AddOptionContract(self.symbol, Resolution.Minute)
def OnData(self, data: Slice):
if not self.Portfolio.Invested:
if self.symbol in data.QuoteBars:
target = PortfolioTarget.Percent(self, self.symbol, self.targetPercent)
self.MarketOrder(self.symbol, target.Quantity)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
portfolioWeight = self.Portfolio[orderEvent.Symbol].HoldingsCost / self.Portfolio.TotalPortfolioValue
self.Debug(f"\nActual portfolio weight of {orderEvent.Symbol.Value} after order fill: {portfolioWeight:.2%}. Target percent was {self.targetPercent:.2%}.\n")
System Information
QC Cloud
Checklist
- I have completely filled out this template
- I have confirmed that this issue exists on the current
masterbranch - I have confirmed that this is not a duplicate issue by searching issues
- I have provided detailed steps to reproduce the issue
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Common/Securities/BuyingPowerModel.cs at line 230 and Common/Securities/Positions/PositionGroupBuyingPowerModel.cs at line 269. Run the linked QQQ option backtest or the supplied Python algorithm, then compare the target percentage with the portfolio weight after the order fill. Done means the reproduced order no longer produces the reported discrepancy and the relevant behavior is covered by an appropriate test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, python
- Domain
- backend, fintech-quant
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 28/100