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 SMAsma 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 Initializeinitialize 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 Namename 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 CloseOPEN, 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.AutomaticIndicatorWarmUpself.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 WarmUpIndicatorwarm_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 TimeSpantimedelta argument to the WarmUpIndicatorwarm_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 WarmUpIndicatorwarm_up_indicator method uses the default Valuevalue 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 SetWarmUpset_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 DeregisterIndicatorderegister_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 IsReadyis_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 Updateupdate 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 Updateupdate 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)

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: