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 Securitiessecurities 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 OnDataon_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 | Barsbars |
QuoteBar | QuoteBarsquote_bars |
Tick | Ticksticks |
// 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 GetLastDataget_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 Cachecache 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]()
Market Session
The sessionSession property of a Security object contains the open, high, low, close, volume, and open interest of trading sessions. The Session object is a RollingWindow object with size of zero by default. To enable market session tracking, set the size of the Session object to 2, so the previous session data is at index 1.
var session = Securities[_symbol].Session;
session.Size = 2;
var todayOpen = session.Open;
var todayHigh = session.High;
var todayLow = session.Low;
var todayCumulativeVolume = session.Volume;
if (session.IsReady)
{
var yesterdayClose = session[1].Close;
} session = self.securities[self._symbol].session
session.size = 2
today_open = session.open
today_high = session.high
today_low = session.low
today_cumulative_volume = session.volume
if session.is_ready:
yesterday_close = session[1].close
Increase the size of the Session object to store more session data. You can use cached data in the sessionSession property instead of requesting historical daily data.
var equity = AddEquity("SPY");
// Increase the session lookback to 252 from default (0)
equity.Session.Size = 252;
// Get all closing prices in the session history
var closes = session.OrderBy(x => x.EndTime).Select(x => x.Close);
equity = self.add_equity("SPY")
# Increase the session lookback to 252 from default (0)
equity.session.size = 252
# Get all closing prices in the session history
closes = [x.close for x in session][::-1]
For more information about rolling windows, see Rolling Window.
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(2024, 9, 1);
SetEndDate(2024, 12, 31);
_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))
{
return;
}
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);
// Wait for the algorithm and indicator to be warmed up.
if (IsWarmingUp || !_indicator.IsReady)
{
return;
}
// Trade SMA cross strategy.
SetHoldings(_symbol, _indicator > midPrice ? -0.5m : 0.5m);
}
} class HandlingSecuritiesDataAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
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 self._symbol not in slice.quote_bars:
return
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)
# Wait for the algorithm and indicator to be warmed up.
if self.is_warming_up or not self._indicator.is_ready:
return
# Trade SMA cross strategy.
weight = -0.5 if self._indicator.current.value > mid_price else 0.5
self.set_holdings(self._symbol, weight)
Example 2: Get Tick Data
The following algorithm calculates the bid and ask sizes of the latest SPY quote ticks and then trades at the top of each hour. During each rebalance, it buys SPY if bid size is greater than ask size since supply is greater than demand in this case. If bid size is smaller than ask size, it sells SPY since demand is greater than supply in this case.
public class BidAskSizeTickAlgorithm : QCAlgorithm
{
private Symbol _symbol;
// Set up variables to save the bid and ask sizes.
private decimal _bidSize = 0m, _askSize = 0m;
private int _hour = -1;
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 9, 10);
// Seed the price of each asset with its last known price to
// avoid trading errors.
SetSecurityInitializer(
new BrokerageModelSecurityInitializer(
BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)
)
);
_symbol = AddEquity("SPY", Resolution.Tick).Symbol;
}
public override void OnData(Slice slice)
{
// Check if the Ticks object contain SPY tick data.
if (slice.Ticks.TryGetValue(_symbol, out var ticks))
{
// Iterate all the ticks to calculate the bid and ask sizes.
foreach (var tick in ticks.Where(tick => tick.TickType == TickType.Quote))
{
// Update the bid or ask size.
_bidSize += tick.BidSize;
_askSize += tick.AskSize;
}
}
// Trade at the top of each hour.
if (slice.Time.Hour != _hour)
{
// Invest based on supply-demand relationship from all ticks.
// If bid size is above ask size, supply is greater than
// demand, which drives the price up. Otherwise, supply is
// lower than demand, which drives the price down.
SetHoldings(_symbol, _bidSize > _askSize ? 0.5m : -0.5m);
// Reset the bid and ask size variables.
_bidSize = 0;
_askSize = 0;
_hour = slice.Time.Hour;
}
}
} class BidAskSizeTickAlgorithm(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(2024, 9, 1)
self.set_end_date(2024, 9, 10)
# Seed the price of each asset with its last known price to
# avoid trading errors.
self.set_security_initializer(
BrokerageModelSecurityInitializer(
self.brokerage_model,
FuncSecuritySeeder(self.get_last_known_prices)
)
)
self._symbol = self.add_equity('SPY', Resolution.TICK).symbol
def on_data(self, slice: Slice) -> None:
# Check if the Slice contain SPY tick data.
ticks = slice.ticks.get(self._symbol)
if ticks:
# Iterate all the 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 and ask sizes.
self._bid_size += tick.bid_size
self._ask_size += tick.ask_size
# Trade at the top of each hour.
if slice.time.hour != self._hour:
# Invest based on supply-demand relationship from all ticks.
# If bid size is above ask size, supply is greater than
# demand, which drives the price up. Otherwise, supply is
# lower than demand, which drives the price down.
self.set_holdings(
self._symbol, 0.5 if self._bid_size > self._ask_size else -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(2024, 9, 1);
SetEndDate(2024, 12, 31);
// 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(2024, 9, 1)
self.set_end_date(2024, 12, 31)
# 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(2024, 9, 1);
SetEndDate(2024, 12, 31);
// 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(2024, 9, 1)
self.set_end_date(2024, 12, 31)
# 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.greeks.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. It simply buys AAPL when the sentiment is positive and liquidates the position when the sentiment is negative.
public class HandlingSecuritiesDataAlgorithm : QCAlgorithm
{
private Symbol _symbol, _dataset7DaySymbol;
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
// Seed the price of AAPL with its last known price to avoid trading errors.
SetSecurityInitializer(
new BrokerageModelSecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices))
);
// Add the 7-day sentiment data for AAPL.
_symbol = AddEquity("AAPL", Resolution.Daily).Symbol;
_dataset7DaySymbol = AddData<BrainSentimentIndicator7Day>(_symbol).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(_symbol, 1);
}
// Liquidate otherwise.
else if (dataPoint.Sentiment < 0m && Portfolio[_symbol].Invested)
{
Liquidate(_symbol);
}
}
}
} class HandlingSecuritiesDataAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
# Seed the price of AAPL with its last known price to avoid trading errors.
self.set_security_initializer(
BrokerageModelSecurityInitializer(
self.brokerage_model,
FuncSecuritySeeder(self.get_last_known_prices)
)
)
# Add the 7-day sentiment data for AAPL.
self._symbol = self.add_equity("AAPL", Resolution.DAILY).symbol
self._dataset_7day_symbol = self.add_data(BrainSentimentIndicator7Day, self._symbol).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._symbol, 1)
# Liquidate otherwise.
elif data_point.sentiment < 0 and self.portfolio[self._symbol].invested:
self.liquidate(self._symbol)
Example 6: Market Session as History
Using Session instead of history requests or rolling window to cache the last 252 OHLCV bars, the following algorithm buys SPY accordingly when the market opens above the 252-day max of closing prices.
public class MarketSessionAsDailyHistory : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
var equity = AddEquity("SPY");
// Increase the session lookback to 252 from default (0)
// since our strategy break out the 252-day max of closing prices
equity.Session.Size = 252;
// Warm up with daily data for speed
SetWarmUp(equity.Session.Size, Resolution.Daily);
Schedule.On(DateRules.EveryDay("SPY"), TimeRules.AfterMarketOpen("SPY", 1), () =>
{
// Break-out strategy. If the market opens above the 252-day max of closing prices,
// allocate all in SPY. Liquidate if it opens below the mean.
var session = Securities["SPY"].Session;
// The session is ready when the number of samples is greater than the size (252)
if (IsWarmingUp || !session.IsReady)
return;
var closes = session.OrderBy(x => x.EndTime).Select(x => x.Close);
var open = session.Open;
var max = closes.Max();
var mean = closes.Average();
// Invest all in SPY if the open break out the 252-day max and liquidate below the mean
if (open > max)
SetHoldings("SPY", 1, tag: $"_open={open:F2}, _max={max:F2}");
if (open < mean)
Liquidate(symbol: "SPY", tag:$"_open={open:F2}, _mean={mean:F2}");
});
}
} class MarketSessionAsDailyHistory(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
equity = self.add_equity("SPY")
# Increase the session lookback to 252 from default (0)
# since our strategy break out the 252-day max of closing prices
equity.session.size = 252
# Warm up with daily data for speed
self.set_warm_up(equity.session.size, Resolution.DAILY)
self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.after_market_open("SPY", 1), self.trade_break_out)
def trade_break_out(self):
'''Break-out strategy. If the market opens above the 252-day max of closing prices,
allocate all in SPY. Liquidate if it opens below the mean.
'''
session = self.securities["SPY"].session
# The session is ready when the number of samples is greater than the size (252)
if self.is_warming_up or not session.is_ready:
return
# Create a pandas Series of the closing prices of session
# to use the max and mean methods
closes = pd.Series([x.close for x in session][::-1])
_open, _max, _mean = session.open, closes.max(), closes.mean()
# Invest all in SPY if the open break out the 252-day max and liquidate below the mean
if _open > _max:
self.set_holdings("SPY", 1, tag=f'{_open=:.2f}, {_max=:.2f}')
if _open < _mean:
self.liquidate("SPY", tag=f'{_open=:.2f}, {_mean=:.2f}')