Problem Description
Solution
To check if you already have a position in a stock within QuantConnect's Lean engine, you should access the self.portfolio object.
The most efficient way is to use the .invested property of the specific security holding. This boolean property returns True if you hold a non-zero quantity (either Long or Short) and False if you are flat.
Key Methods
- Check if Invested (Any Direction):
self.portfolio[symbol].invested - Check Specific Direction:
self.portfolio[symbol].is_long # True if Quantity > 0 self.portfolio[symbol].is_short # True if Quantity < 0 - Check Exact Quantity:
self.portfolio[symbol].quantity
Full Algorithm Example
Below is a complete, runnable algorithm demonstrating how to check for existing positions before placing a trade. This prevents the algorithm from continuously buying more shares every time on_data is called.
from AlgorithmImports import *
class PositionCheckAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
# Add an equity and save the Symbol object
self.spy = self.add_equity("SPY", Resolution.DAILY).symbol
def on_data(self, data: Slice):
# Ensure data exists for the symbol before accessing it
if not data.contains_key(self.spy):
return
# ---------------------------------------------------------
# METHOD 1: The .invested property (Recommended)
# ---------------------------------------------------------
# Check if we do NOT have a position
if not self.portfolio[self.spy].invested:
self.log("No position detected. Placing Buy Order.")
self.set_holdings(self.spy, 1.0)
else:
# We are already invested
current_qty = self.portfolio[self.spy].quantity
avg_price = self.portfolio[self.spy].average_price
self.log(f"Already invested. Quantity: {current_qty} @ {avg_price}")
# Example: Exit if price is 5% above average cost
current_price = data[self.spy].close
if current_price > avg_price * 1.05:
self.liquidate(self.spy, "Take Profit")
def on_order_event(self, order_event):
if order_event.status == OrderStatus.FILLED:
self.debug(f"Order Filled: {order_event}")
Important Considerations
-
Pending Orders vs. Filled Positions:
Theself.portfolio[symbol].investedproperty only reflects filled orders. If you place aMarketOrderin backtesting, it fills synchronously, so.investedupdates immediately. However, if you place aLimitOrderor are trading in Live Mode, the order might be "Submitted" but not yet "Filled."- Risk: If you don't check for pending orders, your algorithm might submit multiple orders for the same signal before the first one fills.
- Solution: You can check
self.transactions.get_open_orders(symbol)to see if there are pending orders.
-
PEP8 Compliance:
As per the latest QuantConnect standards, ensure you use lowercase properties (e.g.,.invested,.quantity,.average_price) rather than the older PascalCase versions (.Invested).
Q&A
Q: Does self.portfolio[symbol].invested return True if I have a Short position?
A: Yes. .invested returns True if the quantity is anything other than 0 (positive or negative). If you specifically need to know if you are Long or Short, use .is_long or .is_short.
Q: How do I check if I have any positions in the entire portfolio?
A: You can check self.portfolio.invested. This is a portfolio-level property that returns True if you hold any security in the entire algorithm.
Q: Can I check the position of a symbol I haven't subscribed to?
A: No. You must subscribe to the data using self.add_equity, self.add_forex, etc., to create the security object in the portfolio. Accessing a key in self.portfolio that doesn't exist will raise a KeyError.