Problem Description
Solution
To get the latest price for a specific stock in the on_data function, you can access the data through the slice object passed to the method, or via the self.securities collection.
Here are the two most common methods, formatted in PEP8 as per the latest QuantConnect API standards.
Method 1: Using the slice Object (Recommended)
The slice object contains the data packets for the current moment in time. This is the preferred method for event-driven logic because it ensures you are acting on new data arriving in that specific time step.
def on_data(self, slice: Slice) -> None:
# Check if the symbol exists in the current slice to avoid KeyErrors
if self.symbol in slice:
# Access the TradeBar or QuoteBar directly
current_price = slice[self.symbol].close
self.log(f"Current price via Slice: {current_price}")
Method 2: Using self.securities (State-based)
The self.securities object holds the state of your entire universe. The .price property returns the last known price. This is useful if the asset did not trade in the current minute (and therefore isn't in slice) but you still need its most recent valuation.
def on_data(self, slice: Slice) -> None:
# Access the security object directly
# This works even if there is no new data in the current slice (uses fill-forward)
last_known_price = self.securities[self.symbol].price
self.log(f"Last known price via Securities: {last_known_price}")
Complete Algorithm Example
Below is a fully functional algorithm demonstrating both approaches.
from AlgorithmImports import *
class PriceAccessAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 1, 5)
self.set_cash(100000)
# Add Equity and save the Symbol object
self.spy = self.add_equity("SPY", Resolution.MINUTE).symbol
def on_data(self, slice: Slice) -> None:
# Method A: Accessing via Slice (Data available in this specific time step)
# We check 'if self.spy in slice' to ensure data arrived this minute
if self.spy in slice.bars:
bar = slice.bars[self.spy]
trade_price = bar.close
high_price = bar.high
self.log(f"Slice - Close: {trade_price}, High: {high_price}")
# Method B: Accessing via Securities (State of the security)
# This is useful for portfolio calculations regardless of whether a trade occurred this second
security_price = self.securities[self.spy].price
# Example logic: Buy if price is below 380
if not self.portfolio.invested and security_price < 380:
self.set_holdings(self.spy, 1.0)
self.debug(f"Purchased at {security_price}")
Key Differences
| Feature | slice[symbol] |
self.securities[symbol] |
|---|---|---|
| Data Source | Current time step only. | Global state (Last known value). |
| Availability | Only exists if a trade/quote occurred in this resolution step. | Always available after initialization. |
| Use Case | Triggering trade logic based on new movement. | Portfolio valuation, margin checks, or accessing price when data is sparse. |
| Properties | Access to .open, .high, .low, .close, .volume. |
Access to .price (current value), .holdings, .buying_power_model. |
Q&A
Q: What happens if I try to access slice[symbol] when there is no data?
A: It will raise a KeyError. You should always check if symbol in slice: or if slice.contains_key(symbol): before accessing it via the slice object.
Q: How do I get the Bid and Ask prices instead of the last trade price?
A: If you have subscribed to high-resolution data or Forex/CFDs, you can access slice.quote_bars.
if self.symbol in slice.quote_bars:
quote = slice.quote_bars[self.symbol]
bid = quote.bid.close
ask = quote.ask.close
Q: Does self.securities[symbol].price include extended market hours data?
A: Yes, if you set extended_market_hours=True when calling add_equity, the self.securities price will update during pre- and post-market hours. If set to False, it will hold the market close price until the next market open.