Indicators

Manual Indicators

Introduction

Manual indicators are indicators that don't automatically update from the underlying security data. Manual updates let you customize how you update the indicator, such as using a custom field or using Renko Bars. It's not always necessary to create manual indicators though. Automatic indicators are easier to create and update.

Naming Convention

The class name to create a manual indicator is the indicator name spelled in pascal case. For example, to create a manual simple moving average indicator, use the SimpleMovingAverage class.

Create Manual Indicators

To create manual indicators, instantiate an indicator with its constructor. To view all of the available indicators and their constructors, see Supported Indicators.

// Create an indicator
private Delay _delay;
_delay = new Delay(20);

// Create a candlestick pattern
private TwoCrows _twoCrows;
_twoCrows = new TwoCrows();
# Create an indicator
self.delay = Delay(20)

# Create a candlestick pattern
self.two_crows = TwoCrows()

You can track indicators by their name. To name a manual indicator, pass a string as the first argument to the constructor.

// Name an indicator
_delay = new Delay("AAPL Past Price", 20);
Log(_delay.Name);

// Name a candlestick pattern
_twoCrows = new TwoCrows("Two Crows Pattern");
Log(_twoCrows.Name);
# Name an indicator
self.delay = Delay("AAPL Past Price", 20)
self.log(self.delay.name)

# Name a candlestick pattern
self.two_crows = TwoCrows("Two Crows Pattern")
self.log(self.two_crows.name)

If you don't name an indicator, it's given a default name. For example, the default name for a Delay indicator is "Delay(period)".

Manual Updates

With manual updates, you control what data you use to update the indicator. For instance, you can use the 3:30 PM price in your daily moving average instead of the daily closing price or you can use the maximum temperature of the past 10 cloudy days.

To update an indicator, call the Updateupdate method. The Updateupdate method expects one of the following arguments:

  • Time/decimal pair
  • IndicatorDataPoint
  • QuoteBar
  • TradeBar
  • Custom data bar

To view what data type you should use to update an indicator, see Supported Indicators.

You can update indicators at any point in your algorithm, but the most common places are during the OnDataon_data event handler or during a consolidation event handler.

def initialize(self) -> None:
    self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol

    self.rsi = RelativeStrengthIndex(10, MovingAverageType.SIMPLE)
    self.ad = AccumulationDistribution()

    self.consolidator = TradeBarConsolidator(timedelta(days=3))
    self.consolidator = QuoteBarConsolidator(timedelta(days=3))
    self.consolidator = RenkoBarConsolidator(1)     # Renko consolidator that emits a bar when the price moves $1
    # Update the AD indicator with the consolidated bar
    self.consolidator.data_consolidated += (lambda _, bar : self.ad.update(bar))
    self.subscription_manager.add_consolidator(self.symbol, self.consolidator)

def on_data(self, slice: Slice) -> None:
    # Update the RSI indicator value with the new input close price every day
    if slice.bars.contains_key(self.symbol):
        bar = slice.bars[self.symbol]
        self.rsi.update(bar.end_time, bar.close)
private Symbol _symbol;
private RelativeStrengthIndex _rsi;
private AccumulationDistribution _ad;

public override void Initialize()
{
    _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
    
    _rsi = new RelativeStrengthIndex(10, MovingAverageType.Simple);
    _ad = new AccumulationDistribution();

    var consolidator = new TradeBarConsolidator(TimeSpan.FromDays(3));
    consolidator = new QuoteBarConsolidator(TimeSpan.FromDays(3));
    consolidator = new RenkoBarConsolidator(1);     // Renko consolidator that emits a bar when the price moves $1
    //Update the AD indicator with the consolidated bar
    consolidator.DataConsolidated += (_, bar) => _ad.Update(bar);
    SubscriptionManager.AddConsolidator(_symbol, consolidator);
}

public override void OnData(Slice slice)
{
    // Update the RSI indicator value with the new input close price every day
    if (slice.Bars.ContainsKey(_symbol))
    {
        var bar = slice.Bars[_symbol];
        _rsi.Update(bar.EndTime, bar.Close);
    }
}

Automatic Updates

With automatic updates, your indicators automatically update with the security data on a schedule you set. To configure automatic updates, create a consolidator and then call the RegisterIndicator method. If you register an indicator for automatic updates, don't call the indicator's Updateupdate method or else the indicator will receive double updates.

# Create a security subscription 
self._symbol = self.add_equity("SPY", Resolution.MINUTE).symbol

# Create a manual indicator
self.indicator = RelativeStrengthIndex(10, MovingAverageType.SIMPLE)

# Create a consolidator
consolidator = TradeBarConsolidator(1)
consolidator = QuoteBarConsolidator(1)
consolidator = RenkoConsolidator(1)     # Renko consolidator that emits a bar when the price moves $1

# Register the indicator to update with the consolidated data
self.register_indicator(self.symbol, self.indicator, consolidator)
// Create a security subscription 
_symbol = AddEquity("SPY", Resolution.Hour);

// Create a manual indicator
_indicator = new RelativeStrengthIndex(10, MovingAverageType.Simple);

// Create a consolidator
var consolidator = new TradeBarConsolidator(1);
consolidator = new QuoteBarConsolidator(1);
consolidator = new RenkoConsolidator(1);    // Renko consolidator that emits a bar when the price moves $1

// Register the indicator to update with the consolidated data
RegisterIndicator(_symbol, _indicator, consolidator);

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, provide a selector argument to the RegisterIndicator method.

self.register_indicator(self.symbol, self.indicator, consolidator, Field.HIGH)
RegisterIndicator(_symbol, _rsi, consolidator, Field.High);

The Field class has the following selector properties:

To create a custom selector, define a function that calculates the value.

RegisterIndicator(_symbol, _indicator, _consolidator, x =>
{
    var bar = x as IBaseDataBar;
    return (bar.Low + bar.High) / 2;
});

To stop automatically updating an indicator, pass the indicator to the DeregisterIndicator method.

DeregisterIndicator(_indicator);
// Alias:
// UnregisterIndicator(_indicator);
self.deregister_indicator(self.indicator)
# Alias:
# self.unregister_indicator(self.indicator)

Warm Up Indicators

Indicators use historical data to compute their value. Before you start trading with an indicator, warm it up. There are several ways to warm-up manual indicators.

Manual Indicator Warm-up

If you have access to the QCAlgorithm object, you can manually warmup indicators with a history request.

private SimpleMovingAverage _sma;

_sma = SimpleMovingAverage(20);
var history = algorithm.History(_symbol, 20, Resolution.Daily);
foreach (var bar in history)
{
    sma.Update(bar.Time, bar.Close);
}
self.sma = SimpleMovingAverage(20)
history = algorithm.history(self.symbol, 20, Resolution.DAILY)
if not history.empty:
    for time, row in history.loc[self.symbol].iterrows():
        self.sma.update(time, row.close)

Warm-up Helper Method

If an indicator inherits the IIndicatorWarmUpPeriodProvider class, you can warm it up with the WarmUpIndicator method.

_sma = SimpleMovingAverage(20);
algorithm.WarmUpIndicator(_symbol, _sma);
self.sma = SimpleMovingAverage(20)
algorithm.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 WarmUpIndicator 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.

// Warm-up with daily bars
algorithm.WarmUpIndicator(_symbol, _sma, Resolution.Daily);

// Warm-up with 3-day bars
algorithm.WarmUpIndicator(_symbol, _sma, TimeSpan.FromDays(3));
# Warm-up with daily bars
algorithm.warm_up_indicator(self.symbol, self.sma, Resolution.DAILY)

# Warm-up with 3-day bars
algorithm.warm_up_indicator(self.symbol, self.sma, timedelta(days=3))

The WarmUpIndicator method uses the default 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.

algorithm.WarmUpIndicator(_symbol, _sma, Resolution.Daily, Field.High);
algorithm.warm_up_indicator(self.symbol, self.sma, Resolution.DAILY, Field.HIGH)

Algorithm Warm-up

If you create indicators at the beginning of your algorithm, 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 of the algorithm. To ensure that all the indicators are ready after the algorithm warm-up period, choose a lookback period that contains sufficient data.

private SimpleMovingAverage _sma;

// In Initialize
var symbol = AddEquity("SPY", Resolution.Daily).Symbol;
_sma = SimpleMovingAverage(20);
SetWarmUp(20);

// In OnData
_sma.Update(data["SPY"]); // Delete this line if you registered the indicator for automatic updates
if (IsWarmingUp)
{ return; }
# In Initialize
self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
self.sma = SimpleMovingAverage(20)
self.set_warm_up(20)

# In OnData
self.sma.update(data[self.symbol]) # Delete this line if you registered the indicator for automatic updates
if self.is_warming_up:
    return

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. Furthermore, if you set up a consolidator to update the indicator, the consolidator 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.

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: