Problem Description
Solution
To set the starting price (average cost) and quantity of a security in your portfolio within QuantConnect, you use the set_holdings method on the specific security's holding object.
This is typically done in the initialize method to simulate an existing position or to manually adjust the cost basis.
Implementation Details
set_holdings(average_price, quantity): This method manually updates the portfolio state. It does not place an order; it simply edits the ledger to reflect that you own the asset at the specified price.- Security Seeding: It is best practice to also use
set_security_initializerwithget_last_known_prices. This ensures that the security object itself has a valid market price immediately upon subscription, preventing errors where the price might be 0 before the first data slice arrives.
Code Example
Here is a complete algorithm demonstrating how to set a starting price of $400.00 for SPY with a quantity of 100 shares.
# region imports
from AlgorithmImports import *
# endregion
class SetStartingPriceAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 2, 1)
self.set_cash(100000)
# 1. Set the Security Initializer
# This ensures that when we add the equity, it immediately gets the last known market price
# instead of starting at 0. This is crucial for accurate initial calculations.
self.set_security_initializer(
BrokerageModelSecurityInitializer(
self.brokerage_model,
FuncSecuritySeeder(self.get_last_known_prices)
)
)
# 2. Add the Security
spy = self.add_equity("SPY", Resolution.MINUTE)
self._symbol = spy.symbol
# 3. Set the Starting Price (Cost Basis) and Quantity
# Syntax: self.portfolio[symbol].set_holdings(average_price, quantity)
initial_average_price = 400.00
initial_quantity = 100
self.portfolio[self._symbol].set_holdings(initial_average_price, initial_quantity)
# Debug print to verify
self.debug(f"Initial Holdings: {self.portfolio[self._symbol].quantity} shares @ ${self.portfolio[self._symbol].average_price}")
def on_data(self, data: Slice):
# Example logic: Liquidate if price rises 5% above our manually set starting price
if self.portfolio[self._symbol].invested:
current_price = self.securities[self._symbol].price
# Calculate profit based on the manual starting price
if current_price > self.portfolio[self._symbol].average_price * 1.05:
self.liquidate(self._symbol)
self.log(f"Target hit. Liquidated at {current_price}")
Key Components Explained
self.portfolio[self._symbol]: Accesses theSecurityHoldingobject for the specific symbol..set_holdings(average_price, quantity):average_price: The price at which you want the algorithm to believe you bought the shares.quantity: The number of shares held. Use a negative number for short positions.
FuncSecuritySeeder(self.get_last_known_prices): Without this,self.securities[symbol].pricemight return 0 until the first market bar is processed. Seeding ensures the market price is populated immediately, which is helpful if you calculate margin or value immediately after setting holdings.
Q&A
Q: Does set_holdings place a market order?
A: No. set_holdings strictly manipulates the internal portfolio ledger (accounting). It does not send an order to the brokerage or the backtesting fill model. It is used for setting initial states.
Q: Can I use set_holdings to set a short position?
A: Yes. To set a short position, provide a negative quantity (e.g., self.portfolio[symbol].set_holdings(400.00, -100)).
Q: Why do I need set_security_initializer?
A: When you add a security in initialize, it defaults to a price of $0. If you try to access self.securities[symbol].price or calculate portfolio value before the first data bar arrives, it may result in errors or incorrect calculations. The initializer fills the price with historical data immediately.
Q: Will this affect the cash balance?
A: set_holdings updates the portfolio value and holdings, but it does not automatically deduct cash from self.portfolio.cash in the same way a trade execution would. If you need the cash balance to reflect the purchase, you must manually adjust it using self.portfolio.cash_book[currency].add_amount(-cost).