Securities
Handling Data
Introduction
If you create a data subscription, your algorithm receives data updates for that security or custom data source.
Get Prices
LEAN uses data updates to set the current prices of a security and compute the contribution of securities you hold to the portfolio value.
To get the current prices of a security, find the Security
object in the Securities
securities
dictionary and access the price attributes.
// Use the Securities object as a dictionary to get the current ask and bid price of the SPY. var security = Securities["SPY"]; var price = security.Price; var bidPrice = security.BidPrice; var askPrice = security.AskPrice;
# Use the securities object as a dictionary to get the current ask and bid price of the SPY. security = self.securities["SPY"] price = security.price bid_price = security.bid_price ask_price = security.ask_price
Data Events
The Slice
that LEAN passes to the OnData
on_data
method represents all of the data for your subscriptions at a single point in time. The Slice
object contains data like Tick
objects, TradeBar
objects, QuoteBar
objects, corporate actions, and chains for Option and Future contracts. You can use the data in the Slice
to make trading decisions.
To access data in the Slice
, index it with the security Symbol
. If you use the security ticker instead of the Symbol
, LEAN automatically finds the Symbol
.
// Use the OnData method to get the data for a symbol in the current time slice. public override void OnData(Slice slice) { if (slice.ContainsKey(_symbol)) { var myData = slice[_symbol]; } }
# Use the OnData method to get the data for a symbol in the current time slice. def on_data(self, slice: Slice) -> None: if slice.contains_key(self._symbol): my_data = slice[self._symbol]
The following table shows the Slice
property to index based on the data format you want:
Data Format | Slice Property |
---|---|
TradeBar | Bars bars |
QuoteBar | QuoteBars quote_bars |
Tick | Ticks ticks |
// Get the tradebar from the slice. public override void OnData(Slice slice) { if (slice.ContainsKey(_symbol)) { var bar = slice.Bars[_symbol]; } }
# Get the tradebar from the slice. def on_data(self, slice: Slice) -> None: if slice.contains_key(self._symbol): bar = slice.bars[self._symbol]
If you just index the Slice
instead of the preceding properties, it returns the correct object. For instance, if your data subscription provides QuoteBar
objects and you index the Slice
with the security Symbol
, it returns the QuoteBar
.
For more information about the Slice
class, see Timeslices.
Cache
To get the most recent data for a security, call the GetLastData
get_last_data
method on the Security
object.
// Get the most recent data for a security. var data = Securities["SPY"].GetLastData();
# Get the most recent data for a security. data = self.securities["SPY"].get_last_data()
The Cache
cache
property of a Security
object provides additional price information since you can access TradeBar
, QuoteBar
, and Tick
data.
// Get additional price information for the security. var cache = Securities["SPY"].Cache; var tradeBar = cache.GetData<TradeBar>(); var quoteBar = cache.GetData<QuoteBar>(); var ticks = cache.GetAll<Tick>();
# Get additional price information for the security. cache = self.securities["SPY"].cache trade_bar = cache.get_data[TradeBar]() quote_bar = cache.get_data[QuoteBar]() ticks = cache.get_all[Tick]()
Examples
The following examples demonstrate some common practices for handling data.
Example 1: Get QuoteBar Data
The following algorithm updates an indicator with the mid-price of a quote bar to increase the accuracy of the indicator values for illiquid assets. Then, make use of the SMA indicator to trade the SMA cross strategy.
public class HandlingSecuritiesDataAlgorithm : QCAlgorithm { private Symbol _symbol; // Initialize a new instance of a SimpleMovingAverage object. private SimpleMovingAverage _indicator = new(250); public override void Initialize() { SetStartDate(2022, 1, 1); SetEndDate(2022, 5, 1); _symbol = AddEquity("SPY").Symbol; SetWarmUp(TimeSpan.FromDays(350)); } public override void OnData(Slice slice) { // Check if the QuoteBars contain SPY quote data. if (slice.QuoteBars.ContainsKey(_symbol)) { var quoteBar = slice.QuoteBars[_symbol]; // Calculate the mid price by averaging the bid and ask price. var midPrice = (quoteBar.Bid.Close + quoteBar.Ask.Close) * 0.5m; // Update the SMA indicator with the mid price. _indicator.Update(quoteBar.EndTime, midPrice); // Trade SMA cross strategy. if (_indicator.IsReady) { if (_indicator > midPrice) { SetHoldings(_symbol, -0.5m); } else { SetHoldings(_symbol, 0.5m); } } } } }
class HandlingSecuritiesDataAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2022, 1, 1) self.set_end_date(2022, 5, 1) self._symbol = self.add_equity("SPY").symbol self._indicator = SimpleMovingAverage(250) self.set_warm_up(timedelta(350)) def on_data(self, slice: Slice) -> None: # Check if the QuoteBars contain SPY quote data. if slice.quote_bars.contains_key(self._symbol): quote_bar = slice.quote_bars[self._symbol] # Calculate the mid price by averaging the bid and ask price. mid_price = (quote_bar.bid.close + quote_bar.ask.close) * 0.5 # Update the SMA indicator with the mid price. self._indicator.update(quote_bar.end_time, mid_price) # Trade SMA cross strategy. if self._indicator.is_ready: if self._indicator.current.value > mid_price: self.set_holdings(self._symbol, -0.5) else: self.set_holdings(self._symbol, 0.5)
Example 2: Get Tick Data
The following algorithm calculates the bid and ask sizes of the latest SPY quote ticks. Then, buy SPY if bid size is greater than ask size, indicating supply is greater than demand. Else, sell if bid size is smaller than ask size, indicating supply is smaller than supply.
public class HandlingSecuritiesDataAlgorithm : QCAlgorithm { private Symbol _symbol; // Set up variables to save the bid and ask sizes. private decimal _bidSize = 0m; private decimal _askSize = 0m; private int _hour = -1; public override void Initialize() { SetStartDate(2019, 1, 1); SetEndDate(2019, 1, 5); _symbol = AddEquity("SPY", Resolution.Tick).Symbol; } public override void OnData(Slice slice) { // Check if the Ticks object contain SPY tick data. if (slice.Ticks.ContainsKey(_symbol)) { var ticks = slice.Ticks[_symbol]; // Iterate all related ticks to calculate the bid and ask sizes. // Make sure the tick data is a quote instead of a trade. foreach (var tick in ticks.Where(tick => tick.TickType == TickType.Quote)) { // Update the bid or ask size. _bidSize += tick.BidSize; _askSize += tick.AskSize; } } // Invest according to aggregated hour bid ask sizes. if (slice.Time.Hour != _hour) { // Invest based on supply-demand relationship from all ticks. // If bid size is above ask size, meaning supply is greater than demand, which drives the price up. if (_bidSize > _askSize) { SetHoldings(_symbol, 0.5m); } // Else, meaning supply is lower than demand, which drives the price down. else { SetHoldings(_symbol, -0.5m); } // Reset the bid and ask size variables. _bidSize = 0; _askSize = 0; _hour = slice.Time.Hour; } } }
class HandlingSecuritiesDataAlgorithm(QCAlgorithm): # Set up variables to save the bid and ask sizes. _bid_size = 0 _ask_size = 0 _hour = -1 def initialize(self) -> None: self.set_start_date(2019, 1, 1) self.set_end_date(2019, 1, 5) self._symbol = self.add_equity("SPY", Resolution.TICK).symbol def on_data(self, slice: Slice) -> None: # Check if the Ticks object contain SPY tick data. if slice.ticks.contains_key(self._symbol): ticks = slice.ticks[self._symbol] # Iterate all related ticks to calculate the bid and ask sizes. for tick in ticks: # Make sure the tick data is a quote instead of a trade. if tick.tick_type == TickType.QUOTE: # Update the bid or ask size. self._bid_size += tick.bid_size self._ask_size += tick.ask_size # Invest according to aggregated hour bid ask sizes. if slice.time.hour != self._hour: # Invest based on supply-demand relationship from all ticks. # If bid size is above ask size, meaning supply is greater than demand, which drives the price up. if self._bid_size > self._ask_size: self.set_holdings(self._symbol, 0.5) # Else, meaning supply is lower than demand, which drives the price down. else: self.set_holdings(self._symbol, -0.5) # Reset the bid and ask size variables. self._bid_size = 0 self._ask_size = 0 self._hour = slice.time.hour
Example 3: Get US Equity Fundamental Data
The following algorithm gets the latest fundamental data of all the US Equities in the algorithm. We invest in the stocks with the highest PE Ratio equally to ride on the popularity.
public class HandlingSecuritiesDataAlgorithm : QCAlgorithm { public override void Initialize() { SetStartDate(2021, 1, 1); SetEndDate(2022, 1, 1); // Request data for a selected list of equities for trading. foreach (var ticker in new[] { "AAPL", "MSFT", "TSLA", "GOOG", "AMZN" }) { AddEquity(ticker); } } public override void OnData(Slice slice) { var peRatios = new Dictionary<Symbol, double>(); // Iterate active security objects. foreach (var security in ActiveSecurities.Values) { // Get the Fundamental cache. var fundamental = security.Fundamentals; // Get the sector code. var sectorCode = fundamental.AssetClassification.MorningstarSectorCode; peRatios[security.Symbol] = fundamental.ValuationRatios.PERatio; } // Sort by PE ratio to get the most popular stocks. var sortedByPeRatio = peRatios.OrderByDescending(x => x.Value) .ToDictionary(x => x.Key, x => x.Value); var targets = sortedByPeRatio .Take(3) .Select(x => new PortfolioTarget(x.Key, 1m / 3m)) .ToList(); targets.AddRange(sortedByPeRatio .TakeLast(2) .Select(x => new PortfolioTarget(x.Key, 0m)) .ToList()); // Invest equally in the highest PE Ratio stocks for evenly dissipate capital risk. SetHoldings(targets); } }
class HandlingSecuritiesDataAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2021, 1, 1) self.set_end_date(2022, 1, 1) # Request data for a selected list of equities for trading. for ticker in ["AAPL", "MSFT", "TSLA", "GOOG", "AMZN"]: self.add_equity(ticker) def on_data(self, slice: Slice) -> None: pe_ratios = {} # Iterate active security objects. for kvp in self.active_securities: symbol = kvp.key security = kvp.value # Get the Fundamental cache. fundamental = security.fundamentals # Get the sector code. sector_code = fundamental.asset_classification.morningstar_sector_code pe_ratios[symbol] = fundamental.valuation_ratios.pe_ratio # Sort by PE ratio to get the most popular stocks. sorted_by_pe_ratio = sorted(pe_ratios.items(), key=lambda x: x[1]) targets = [PortfolioTarget(x[0], 1/3) for x in sorted_by_pe_ratio[-3:]] targets.extend([PortfolioTarget(x[0], 0) for x in sorted_by_pe_ratio[:2]]) # Invest equally in the highest PE Ratio stocks for evenly dissipate capital risk. self.set_holdings(targets)
Example 4: Get Option Greeks
The following algorithm gets SPY Option Greek values. Sell a straddle with the ATM options, selected by Option Delta (closest to 0.5).
public class HandlingSecuritiesDataAlgorithm : QCAlgorithm { private Symbol _symbol; public override void Initialize() { SetStartDate(2020, 1, 1); SetEndDate(2020, 4, 1); // Add an SPY Option universe. var option = AddOption("SPY"); // Set the Option universe filter. option.SetFilter(x => x.IncludeWeeklys().Strikes(-5, 5).Expiration(7, 60)); // Save a reference of the canonical symbol. _symbol = option.Symbol; } public override void OnData(Slice slice) { // Try to get the Option contracts within the Option chain. if (!Portfolio.Invested && slice.OptionChains.TryGetValue(_symbol, out var chain)) { foreach (var contract in chain) { // Get the implied volatility and greeks of each Option contract. var iv = contract.ImpliedVolatility; var greeks = contract.Greeks; var delta = greeks.Delta; var gamma = greeks.Gamma; var vega = greeks.Vega; var theta = greeks.Theta; var rho = greeks.Rho; } // Invest in a straddle strategy to capitalize the volatility // Trade the ATM options with the closest expiry after 7 days. var expiry = chain.Min(x => x.Expiry); var selected = chain.Where(x => x.Expiry == expiry) .MinBy(x => Math.Abs(x.Greeks.Delta - 0.5m)); var optionStrategy = OptionStrategies.Straddle(selected.Symbol.Canonical, selected.Strike, selected.Expiry); Sell(optionStrategy, 1); } } }
class HandlingSecuritiesDataAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2020, 1, 1) self.set_end_date(2020, 4, 1) # Add an SPY Option universe. option = self.add_option("SPY") # Set the Option universe filter. option.set_filter(lambda x: x.include_weeklys().strikes(-5, 5).expiration(0, 60)) # Save a reference of the canonical symbol. self._symbol = option.symbol def on_data(self, slice: Slice) -> None: # Try to get the Option contracts within the Option chain. chain = slice.option_chains.get(self._symbol) if not self.portfolio.invested and chain: for contract in chain: # Get the implied volatility and greeks of each Option contract. iv = contract.implied_volatility greeks = contract.greeks delta = greeks.delta gamma = greeks.gamma vega = greeks.vega theta = greeks.theta rho = greeks.rho # Invest in a straddle strategy to capitalize the volatility # Trade the ATM options with the closest expiry after 7 days. expiry = min(x.expiry for x in chain) selected = sorted([x for x in chain if x.expiry == expiry], key=lambda x: abs(x.greek.delta - 0.5))[0] option_strategy = OptionStrategies.straddle(selected.symbol.canonical, selected.strike, selected.expiry) self.sell(option_strategy, 1)
Example 5: Get Asset Sentiment Values
The following example gets sentiment values from the Brain Sentiment Indicator dataset. We long AAPL if its sentiment is positive.
public class HandlingSecuritiesDataAlgorithm : QCAlgorithm { private Symbol _dataset7DaySymbol; public override void Initialize() { SetStartDate(2022, 1, 1); SetEndDate(2022, 4, 1); // Add the 7-day sentiment data for AAPL. var aapl = AddEquity("AAPL", Resolution.Daily).Symbol; _dataset7DaySymbol = AddData<BrainSentimentIndicator7Day>(aapl).Symbol; } public override void OnData(Slice slice) { // Check if the current slice contains the 7-day sentiment data. if (slice.ContainsKey(_dataset7DaySymbol)) { var dataPoint = slice[_dataset7DaySymbol]; // Log the sentiment value. Log($"{_dataset7DaySymbol} sentiment at {slice.Time}: {dataPoint.Sentiment}"); // Invest if the sentiment score is above 0, which indicates positive sentiment. if (dataPoint.Sentiment > 0m) { MarketOrder(dataPoint.Symbol, 1); } // Liquidate otherwise. else if (dataPoint.Sentiment < 0m && Portfolio[dataPoint.Symbol].Invested) { Liquidate(dataPoint.Symbol); } } } }
class HandlingSecuritiesDataAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2022, 1, 1) self.set_end_date(2022, 4, 1) # Add the 7-day sentiment data for AAPL. self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol self.dataset_7day_symbol = self.add_data(BrainSentimentIndicator7Day, self.aapl).symbol def on_data(self, slice: Slice) -> None: # Check if the current slice contains the 7-day sentiment data. if slice.contains_key(self.dataset_7day_symbol): data_point = slice[self.dataset_7day_symbol] # Log the sentiment value. self.log(f"{self.dataset_7day_symbol} sentiment at {slice.time}: {data_point.sentiment}") # Invest if the sentiment score is above 0, which indicates positive sentiment. if data_point.sentiment > 0: self.market_order(self.aapl, 1) # Liquidate otherwise. elif data_point.sentiment < 0 and self.portfolio[self.aapl].invested: self.liquidate(self.aapl)