Problem Description
Solution
To create an order that targets a specific percentage of your portfolio (e.g., 5%), the most efficient method in QuantConnect is using the set_holdings method. This helper method automatically calculates the number of shares required based on the current price and your total portfolio value (Cash + Equity) and places a market order to adjust your position to the desired target.
Below are the two primary ways to achieve this: using set_holdings (recommended for market orders) and using calculate_order_quantity (recommended if you need Limit or Stop orders).
Method 1: Using set_holdings (Recommended)
This method automatically handles the math. If you already hold 2% and ask for 5%, it buys 3%. If you hold 10% and ask for 5%, it sells 5%.
class PortfolioTargetingAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
# Add an asset
self.spy = self.add_equity("SPY", Resolution.MINUTE).symbol
def on_data(self, data):
# Check if we have data for the symbol to avoid errors
if not data.contains_key(self.spy):
return
# Example: If we are not invested, buy 5% of Portfolio Value
if not self.portfolio.invested:
# 0.05 represents 5%
# liquidate_existing_holdings=False means it won't sell other assets to fund this
self.set_holdings(self.spy, 0.05)
# To Short 5%, use negative value:
# self.set_holdings(self.spy, -0.05)
Method 2: Manual Calculation (For Limit/Stop Orders)
If you need to place a Limit Order or Stop Market Order targeting 5% of the portfolio, set_holdings cannot be used because it forces a Market Order. Instead, use calculate_order_quantity to get the share count, then place the specific order type.
class ManualCalculationAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
self.spy = self.add_equity("SPY", Resolution.MINUTE).symbol
def on_data(self, data):
if not data.contains_key(self.spy):
return
if not self.portfolio.invested:
# 1. Calculate the number of shares that represent 5% of the portfolio
# This returns a signed integer (positive for long, negative for short)
quantity = self.calculate_order_quantity(self.spy, 0.05)
# 2. Place a Limit Order at 1% below current price
limit_price = data[self.spy].price * 0.99
self.limit_order(self.spy, quantity, limit_price)
# Or place a standard market order manually
# self.market_order(self.spy, quantity)
Key Considerations
- Buying Power: Both methods respect the margin model. If you request 5% but only have enough margin for 2%, the order may be rejected or reduced depending on the
BuyingPowerModelsettings. - Total Portfolio Value: The calculation is based on
self.portfolio.total_portfolio_value(Cash + Unrealized Profit/Loss + Equity), not just available cash. - Fractional Shares: By default, these methods calculate integer share counts. If your brokerage model supports fractional shares, the quantity might be a float.
Q&A
Q: Does set_holdings liquidate other positions?
A: By default, set_holdings only adjusts the specific symbol you pass to it. However, if you set the optional parameter liquidate_existing_holdings=True, it will liquidate all other positions in your portfolio before buying the target asset.
Q: How do I target 5% leverage (using margin)?
A: The percentage parameter represents the target leverage. If you want to use 2x leverage (200% of portfolio value), you would pass 2.0. For 5% of portfolio value (no leverage, small position), you pass 0.05.
Q: Why did set_holdings result in slightly less than 5%?
A: This usually happens due to two factors:
- Rounding: The algorithm usually rounds down to the nearest whole share to ensure you don't exceed available cash.
- Fees: The system estimates transaction fees and subtracts them from the buying power to prevent a negative cash balance.