Indicators
Automatic Indicators
Introduction
Automatic indicators are indicators that automatically update from the underlying security data. Automatic indicators do everything that manual indicators do, but with automatic updates registered for you.
Naming Convention
The method to create an automatic indicator is usually named after the acronym of the indicator name. For example, to create an automatic simple moving average indicator, use the SMA
sma
method.
Create Automatic Indicators
To create automatic indicators, call the indicator helper method from the QCAlgorithm
class. The indicator helper methods create an indicator object, hooks it up for automatic updates, and returns it so you can used it in your algorithm. To view the helper method for each indicator, see Supported Indicators. In most cases, you should call the helper method in the Initialize
initialize
method.
The indicator resolution must be greater than or equal to the resolution of the security subscription. For instance, if your security subscription is for minute resolution data, the indicator resolution should be minute, hour, or daily resolution.
private SimpleMovingAverage _sma; private TwoCrows _twoCrows; // In Initialize() var symbol = AddEquity("SPY").Symbol; // - Create an indicator _sma = SMA(symbol, 20, Resolution.Daily); // - Create a candlestick pattern var patterns = new CandlestickPatterns(this); _twoCrows = patterns.TwoCrows(_symbol);
# In Initialize() symbol = self.add_equity("SPY").symbol # - Create an indicator self._sma = self.sma(symbol, 20, Resolution.DAILY) # - Create a candlestick pattern patterns = CandlestickPatterns(self) self.two_crows = patterns.two_crows(self._symbol)
When you create an indicator with a helper method, the indicator is given a default name. You can track indicators by their name. The default name for the SimpleMovingAverage
indicator is "SMAsma(period,ticker_resolution)". For example, in the preceding code snippet, the SimpleMovingAverage
indicator is named "SMAsma(20,SPY_day)". To get the name of an indicator, inspect its Name
name
property.
Log(_sma.Name); Log(_twoCrows.Name);
self.log(self.sma.name) self.log(self.two_crows.name)
Alternative Price Fields
Data point indicators use only a single price data in their calculations. By default, those indicators use the closing price. For
assets with TradeBar
data, that price is the TradeBar
close price. For assets with QuoteBar
data, that price is the mid-price of the bid closing price and the ask closing price. To create an indicator with the other fields like the Open
, High
, Low
, or Close
OPEN
, HIGH
, LOW
, or CLOSE
, provide a selector
argument to the indicator helper method.
# Define a 10-period daily RSI indicator with shortcut helper method # Select the Open price to update the indicator self._rsi = self.rsi("SPY", 10, MovingAverageType.SIMPLE, Resolution.DAILY, Field.OPEN)
// Define a 10-period daily RSI indicator with shortcut helper method // Select the Open price to update the indicator _rsi = RSI("SPY", 10, MovingAverageType.Simple, Resolution.Daily, Field.Open);
The Field
class has the following selector
properties:
To create a custom selector
, define a function that calculates the value.
_rsi = RSI("SPY", 10, MovingAverageType.Simple, Resolution.Daily, x => { var bar = x as IBaseDataBar; return (bar.Low + bar.High) / 2; });
Warm Up Indicators
Indicators compute their value based on historical data. Before you start trading with an indicator, warm it up. There are several ways to warm-up automatic indicators.
Algorithm Warm-up
You can set an algorithm warm-up period to warm up the indicators. When you set an algorithm warm-up period, the engine pumps data in and automatically updates all the indicators from before the start date. To ensure that all the indicators are ready after the algorithm warm-up period, choose a look-back period that contains sufficient data.
private SimpleMovingAverage _sma; // In Initialize var symbol = AddEquity("SPY").Symbol; _sma = SMA(symbol, 20, Resolution.Daily); SetWarmUp(20, Resolution.Daily); // In OnData if (IsWarmingUp)
{ return; }
# In Initialize symbol = self.add_equity("SPY").symbol self._sma = self.sma(symbol, 20, Resolution.DAILY) self.set_warm_up(20, Resolution.DAILY) # In OnData if self.is_warming_up: return
Manual Indicator Warm-up
You can manually warm up indicators with a history request.
private SimpleMovingAverage _sma; // In Initialize var symbol = AddEquity("SPY").Symbol; _sma = SMA(symbol, 20, Resolution.Daily); var history = History(symbol, 20, Resolution.Daily); foreach (var bar in history) { sma.Update(bar.Time, bar.Close); }
# In Initialize symbol = self.add_equity("SPY").symbol self._sma = self.sma(symbol, 20, Resolution.DAILY) history = self.history(symbol, 20, Resolution.DAILY) if not history.empty: for time, row in history.loc[symbol].iterrows(): self._sma.update(time, row.close)
Automatic Indicator Warm-up
You can set the Settings.AutomaticIndicatorWarmUp
self.settings.automatic_indicator_warm_up
property to true before you create most indicators to automatically warm them up. This technique doesn't work for all indicators.
private SimpleMovingAverage _sma; // In Initialize var symbol = AddEquity("SPY").Symbol; Settings.AutomaticIndicatorWarmUp = true; _sma = SMA(symbol, 20, Resolution.Daily);
# In Initialize symbol = self.add_equity("SPY").symbol self.settings.automatic_indicator_warm_up = True self._sma = self.sma(symbol, 20, Resolution.DAILY)
Warm-up Helper Method
If an indicator inherits the IIndicatorWarmUpPeriodProvider
class, you can warm it up with the WarmUpIndicator
warm_up_indicator
method.
_sma = SMA(_symbol, 20); WarmUpIndicator(_symbol, _sma);
self._sma = self.sma(self._symbol, 20) self.warm_up_indicator(self._symbol, self._sma)
To warm up the indicator with a resolution that's different from the security resolution, pass a resolution or TimeSpan
timedelta
argument to the WarmUpIndicator
warm_up_indicator
method. The resolution you provide should be greater than or equal to the security resolution. For example, if the security has minute resolution data, you should warm up the indicator with data that spans at least one minute. If the indicator resolution is different from the security resolution, provide the indicator resolution as the argument to properly warm up the indicator.
// Warm up with daily bars WarmUpIndicator(_symbol, _sma, Resolution.Daily); // Warm up with 3-day bars WarmUpIndicator(_symbol, _sma, TimeSpan.FromDays(3));
# Warm up with daily bars self.warm_up_indicator(self._symbol, self._sma, Resolution.DAILY) # Warm up with 3-day bars self.warm_up_indicator(self._symbol, self._sma, timedelta(days=3))
The WarmUpIndicator
warm_up_indicator
method uses the default Value
value
of the historical data to warm up the indicator. In most cases, this is the closing price. To warm up the indicator with a different data field, pass a Field
argument to the method.
WarmUpIndicator(_symbol, _sma, Resolution.Daily, Field.High);
self.warm_up_indicator(self._symbol, self._sma, Resolution.DAILY, Field.HIGH)
Some indicators require the prices of two assets to compute their value (for example, Beta).
In this case, pass a list of the Symbol
objects to the method.
var spy = AddEquity("SPY").Symbol; var aapl = AddEquity("AAPL").Symbol; var beta = B(aapl, spy, 21, Resolution.Daily); WarmUpIndicator(new[] {aapl, spy}, beta, Resolution.Daily);
self._spy = self.add_equity("SPY").symbol self._aapl = self.add_equity('AAPL').symbol self._beta = self.B(self._aapl, self._spy, 21, Resolution.DAILY) self.warm_up_indicator([self._aapl, self._spy], self._beta, Resolution.DAILY)
Timing Considerations
In some cases, you might create and warm up the indicator during its sampling period. For example, say the security resolution is minute, the indicator resolution is daily, and you create and warm-up the indicator at noon without using the SetWarmUp
set_warm_up
method. In this example, the history request that gathers data to warm up the indicator won't contain any data from the current day and the consolidator that updates the indicator also won't aggregate any data from before noon. It doesn't cause issues if the indicator only uses the close price to calculate the indicator value (like the simple moving average indicator) because the first consolidated bar that updates the indicator will have the correct close price. However, if the indicator uses more than just the close price to calculate its value (like the True Range indicator), the open, high, and low values of the first consolidated bar may be incorrect, causing the initial indicator values to be incorrect.
Deregister Indicators
To stop the automatic updates of an indicator, pass the indicator to the DeregisterIndicator
deregister_indicator
property method.
DeregisterIndicator(_sma); // Alias: // UnregisterIndicator(_sma);
self.deregister_indicator(self._sma) # Alias: # self.unregister_indicator(self._sma)
Common Mistakes
Avoid the following common mistakes when you use automatic indicators.
Using Indicator Values Before the Indicator Is Warmed Up
Indicators can provide inaccurate output values before they are warmed up. To avoid issues, always check the IsReady
is_ready
flag before you use indicator output values.
// IsReady signals whether the indicator values are accurate and ready to use. if (_indicator.IsReady) { var value = _indicator.Current.Value; }
# is_ready signals whether the indicator values are accurate and ready to use. if self.indicator.is_ready: value = self.indicator.current.value
Manually Registering an Automatic Indicator for Updates
If you create an automatic indicator and then register it for automatics updates or call the Update
update
method, the indicator receives multiple input values during each update cycle. To avoid issues, create automatic indicators but don't register them for automatic updates or call the Update
update
method.
// Automatic indicator short-cuts create the indicator and register it for data updates. _indicator = SMA(_symbol, 5); // Don't register the automatic indicators for data. They automatically get data. // RegisterIndicator(_symbol, _indicator, Resolution.Daily)
# Automatic indicator short-cuts create the indicator and register it for data updates. self.indicator = self.sma(self._symbol, 5) # Don't register the automatic indicators for data. They automatically get data. # self.register_indicator(self._symbol, self.indicator, Resolution.DAILY)
Examples
The following examples demonstrate some common practices for using automatic indicators.
Example 1: Trade With Automatic Indicators in a Dynamic Universe
The following algorithm selects a universe of the 20 most liquid US Equities each day. At the start of each trading session, it then forms an equal-weighted portfolio of the stocks that have price > EMA > SMA, which indicates an upward accelerating trend. As assets enter the universe, it creates automatic indicators for them. As assets leave the universe, it deregisters the indicators.
public class AutomaticIndicatorAlgorithm : QCAlgorithm { private Dictionary<Symbol, SymbolData> _symbolData = new(); public override void Initialize() { SetStartDate(2021, 1, 1); SetEndDate(2021, 2, 1); // Add a universe of the 20 most liquid stocks to trade trends more efficiently. AddUniverse(Universe.DollarVolume.Top(20)); // Set a Scheduled Event to rebalance at the start of each day with the daily indicator signals. Schedule.On(DateRules.EveryDay(), TimeRules.At(9, 31), Rebalance); } private void Rebalance() { // Buy the stocks where price is above EMA and EMA is above SMA, meaning its trend is upward and accelerating. var symbolsToBuy = _symbolData.Where( kvp => kvp.Value.IsReady && Securities[kvp.Key].Price > kvp.Value.Ema && kvp.Value.Ema > kvp.Value.Sma ) .Select(kvp => kvp.Key) .ToList(); // Equally invest in the selected stocks to evenly distribute the capital risk. var count = symbolsToBuy.Count; if (count > 0) { var targets = symbolsToBuy.Select(symbol => new PortfolioTarget(symbol, 1m / count)).ToList(); // Liquidate the positions that are no longer popular or in an uptrend. SetHoldings(targets, liquidateExistingHoldings: true); } } public override void OnSecuritiesChanged(SecurityChanges changes) { foreach (var added in changes.AddedSecurities) { // Create a SymbolData object for this asset, which is an object that contains the indicators. _symbolData[added.Symbol] = new SymbolData(this, added.Symbol); } foreach (var removed in changes.RemovedSecurities) { // Stop updating the indicators of assets that leave the universe to release computation resources. if (_symbolData.Remove(removed.Symbol, out var symbolData)) { symbolData.Dispose(); } } } private class SymbolData { private QCAlgorithm _algorithm; public Symbol Symbol { get; set; } public ExponentialMovingAverage Ema { get; set; } public SimpleMovingAverage Sma { get; set; } public bool IsReady => Ema.IsReady && Sma.IsReady; public SymbolData(QCAlgorithm algorithm, Symbol symbol) { _algorithm = algorithm; Symbol = symbol; // Create EMA & SMA indicators to generate trade signals. Ema = algorithm.EMA(symbol, 20, Resolution.Daily); Sma = algorithm.SMA(symbol, 20, Resolution.Daily); // Warm up the indicators so they are ready to use immediately. algorithm.WarmUpIndicator(symbol, Ema, Resolution.Daily); algorithm.WarmUpIndicator(symbol, Sma, Resolution.Daily); } public void Dispose() { // Stop updating the indicators of assets that leave the universe to release computation resources. _algorithm.DeregisterIndicator(Ema); _algorithm.DeregisterIndicator(Sma); } } }
class AutomaticIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2021, 1, 1) self.set_end_date(2021, 2, 1) # Add a universe of the 20 most liquid stocks to trade trends more efficiently. self._universe = self.add_universe(self.universe.dollar_volume.top(20)) # Set a Scheduled Event to rebalance at the start of each day with the daily indicator signals. self.schedule.on(self.date_rules.every_day(), self.time_rules.at(9, 31), self._rebalance) def _rebalance(self) -> None: # Buy the stocks where price is above EMA and EMA is above SMA, meaning its trend is upward and accelerating. securities = [self.securities[symbol] for symbol in self._universe.selected] symbols_to_buy = [ security.symbol for security in securities if security.price > security.ema.current.value > security.sma.current.value ] # Equally invest in the selected stocks to evenly distribute the capital risk. count = len(symbols_to_buy) if count: targets = [PortfolioTarget(symbol, 1 / count) for symbol in symbols_to_buy] # Liquidate the positions that are no longer popular or in an uptrend. self.set_holdings(targets, liquidate_existing_holdings=True) def on_securities_changed(self, changes: SecurityChanges) -> None: for security in changes.added_securities: symbol = security.symbol # Create EMA & SMA indicators to generate trade signals. Use duck typing to save the indicator instances. security.ema = self.ema(symbol, 20, Resolution.DAILY) security.sma = self.sma(symbol, 20, Resolution.DAILY) # Warm up the indicators so they are ready to use immediately. self.warm_up_indicator(symbol, security.ema, Resolution.DAILY) self.warm_up_indicator(symbol, security.sma, Resolution.DAILY) for security in changes.removed_securities: # Stop updating the indicators of assets that leave the universe to release computation resources. self.deregister_indicator(security.ema) self.deregister_indicator(security.sma)