Problem Description
Solution
To access current price and asset data in QuantConnect, you primarily use the on_data(self, slice) event handler. There are two distinct ways to access this data depending on your needs:
- Using the
sliceobject: Best for reacting to new data arriving at that specific moment (e.g., the close of a minute bar). - Using
self.securities: Best for accessing the last known state of an asset (price, holdings, margin) anywhere in your algorithm, even if no new data arrived in the current time step.
Complete Strategy Example
The following code demonstrates how to access Price (Close), Open, High, Low, Volume, and Bid/Ask data using both methods.
# region imports
from AlgorithmImports import *
# endregion
class DataAccessAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 1, 5)
self.set_cash(100000)
# Add an equity and keep a reference to the Symbol
# Extended Market Hours is set to True to potentially see pre/post market data
equity = self.add_equity("SPY", Resolution.MINUTE, extended_market_hours=True)
self.spy_symbol = equity.symbol
def on_data(self, slice: Slice):
"""
OnData is the primary event handler for financial data.
The 'slice' object contains all data for the current moment in time.
"""
# ---------------------------------------------------------
# METHOD 1: Accessing data via the 'slice' object
# ---------------------------------------------------------
# This is the preferred method to ensure you are acting on NEW data.
# Check if TradeBar data (OHLCV) exists for this symbol in this slice
if self.spy_symbol in slice.bars:
trade_bar = slice.bars[self.spy_symbol]
current_close = trade_bar.close
current_open = trade_bar.open
current_high = trade_bar.high
current_low = trade_bar.low
current_volume = trade_bar.volume
self.log(f"Slice - Close: {current_close}, Vol: {current_volume}")
# Check if QuoteBar data (Bid/Ask) exists (requires high resolution or tick data)
if self.spy_symbol in slice.quote_bars:
quote_bar = slice.quote_bars[self.spy_symbol]
ask_price = quote_bar.ask.close
bid_price = quote_bar.bid.close
self.log(f"Slice - Bid: {bid_price}, Ask: {ask_price}")
# ---------------------------------------------------------
# METHOD 2: Accessing data via 'self.securities'
# ---------------------------------------------------------
# This accesses the global state of the security.
# It returns the last known value, even if the asset didn't trade in this specific slice.
security = self.securities[self.spy_symbol]
# Get the current price (usually the last trade or last quote depending on asset type)
last_known_price = security.price
# Access holdings information
is_invested = security.invested
quantity_held = security.holdings.quantity
unrealized_profit = security.holdings.unrealized_profit
# Example logic: Buy if price is below specific value and not invested
if last_known_price > 0 and not is_invested:
self.log(f"Securities State - Price: {last_known_price}")
self.set_holdings(self.spy_symbol, 1.0)
def on_end_of_day(self, symbol):
# You can also access self.securities here, outside of OnData
price = self.securities[symbol].price
self.log(f"End of Day Price for {symbol}: {price}")
Key Data Access Methods
1. The slice Object (Event-Driven)
The slice object represents a "slice" of time. It contains dictionaries for different data types.
slice.bars[symbol]: Returns aTradeBarobject (Open, High, Low, Close, Volume). Used for Equities, Futures, Crypto.slice.quote_bars[symbol]: Returns aQuoteBarobject (Bid, Ask, BidSize, AskSize). Used for Forex, Options, and Crypto.slice[symbol]: A dynamic helper that returns the default data type for that asset.
Note: Always check if symbol in slice.bars: before accessing to avoid "KeyError" exceptions, as data might not exist for every single minute (e.g., if there were no trades).
2. The self.securities Object (State-Based)
The self.securities dictionary holds the entire state of your universe.
self.securities[symbol].price: The most recent price. If no trade occurred in the current minute, it holds the price from the previous minute.self.securities[symbol].close: Same as price.self.securities[symbol].holdings: Access portfolio data likequantity,average_price, andtotal_sale_volume.
Q&A
Q: What is the difference between slice[symbol].Close and self.securities[symbol].Close?
A: slice[symbol] contains data only if a trade occurred during that specific time interval. If the stock was illiquid and didn't trade for a minute, slice will not contain that symbol. self.securities[symbol].Close always returns the most recent known closing price, carrying forward the previous value if necessary.
Q: How do I get the Bid and Ask prices?
A: You must ensure you are subscribed to a resolution that provides quotes (Tick, Second, or Minute). In on_data, access slice.quote_bars[symbol].bid.close and slice.quote_bars[symbol].ask.close.
Q: Why do I get a KeyError when doing slice[symbol]?
A: This happens when there is no data for that symbol in the current time step. Always wrap your data access in a check: if slice.contains_key(symbol): or if symbol in slice.bars:.
Q: How do I get historical data (e.g., yesterday's close)?
A: on_data only provides the current moment. To get past data, use the History request in initialize or inside on_data:
# Get last 10 days of daily data
history = self.history(self.spy_symbol, 10, Resolution.DAILY)
yesterday_close = history['close'].iloc[-1]