---
title: "Manual Indicators"
description: "Manual indicators are indicators that don't automatically update from the underlying security data."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/manual-indicators
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/manual-indicators.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/28%20Indicators/03%20Manual%20Indicators
modified: 2026-11-02
index: https://www.quantconnect.com/llms.txt
---

# Manual Indicators

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Indicators > Manual Indicators

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## 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](https://www.quantconnect.com/docs/v2/writing-algorithms/consolidating-data/consolidator-types/renko-consolidators/renko-consolidators.md). It's not always necessary to create manual indicators though. [Automatic indicators](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/automatic-indicators.md) 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](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/simple-moving-average.md), 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](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators.md).

```python
# 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.

```python
# 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 `update` method. The `update` 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](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators.md).

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

```python
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(3))   # 10 x 3 daily trade bars to warm up the RSI.
    self.consolidator = QuoteBarConsolidator(timedelta(3))   # 10 x 3 daily quote bars to warm up the RSI.
    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 += self.on_data_consolidated
    self.subscription_manager.add_consolidator(self._symbol, self.consolidator)

def on_data_consolidated(self, sender, bar):
    self.ad.update(bar)

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)
```

## 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](https://www.quantconnect.com/docs/v2/writing-algorithms/consolidating-data/getting-started.md) and then call the `register_indicator` method. If you register an indicator for automatic updates, don't call the indicator's `update` method or else the indicator will receive double updates.

```python
# 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(3)            # 10 x 3 min trade bars to warm up the RSI.
consolidator = QuoteBarConsolidator(4)            # 10 x 4 min quote bars to warm up the RSI
consolidator = RenkoConsolidator(1)               # Renko consolidator that emits a bar when the price moves $1
consolidator = IdentityDataConsolidator[TradeBar]()   # Identity consolidator that passes through each TradeBar data point

# Register the indicator to update with the consolidated data
self.register_indicator(self._symbol, self.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 `register_indicator` method.

```python
self.register_indicator(self._symbol, self.indicator, consolidator, Field.HIGH)
```

The `Field` class has the following `selector` properties:

*Members of `QuantConnect.Field` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Field&language=python*

To stop automatically updating an indicator, pass the indicator to the `deregister_indicator` property method.

```python
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](https://www.quantconnect.com/docs/v2/writing-algorithms/historical-data/history-requests.md).

```python
# Create a manual indicator.
self._sma = SimpleMovingAverage(20)
# Get 20 bars of daily data.
history = algorithm.history[TradeBar](self._symbol, 20, Resolution.DAILY)
# Manually warm up the indicator with the historical close prices.
for bar in history:
    self._sma.update(bar.end_time, bar.close)
```

### Warm-up Helper Method

If an indicator inherits (implements) the `IIndicatorWarmUpPeriodProvider` interface, you can warm it up with the `warm_up_indicator` method.

```python
# Warm up the indicator using the warm-up helper.
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 `timedelta` argument to the `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.

```python
# 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 `warm_up_indicator` 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 an alternative price field, pass a `Field` argument to the method.

```python
# Warm up the indicator using daily high prices instead of close prices.
algorithm.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](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/beta.md)). In this case, pass a list of the `Symbol` objects to the method.

```python
# Use the warm-up helper for an indicator that needs the data of 2 assets.
self._spy = self.add_equity("SPY").symbol
self._aapl = self.add_equity('AAPL').symbol
self._beta = Beta("", self._aapl, self._spy, 21)
self.warm_up_indicator([self._aapl, self._spy], self._beta, Resolution.DAILY)
```

### Algorithm Warm-up

If you create indicators at the beginning of your algorithm, you can set an [algorithm warm-up period](https://www.quantconnect.com/docs/v2/writing-algorithms/historical-data/warm-up-periods.md) 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.

```python
# In the initialize() method, request data and construct the indicators you need.
self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
self._sma = SimpleMovingAverage(20)
self.set_warm_up(20)

# In the on_data event handler, update your indicators.
# Use the self.is_warming_up flag to skip some code execution.
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 `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. 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](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/simple-moving-average.md) 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](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/true-range.md) indicator), the open, high, and low values of the first consolidated bar may be incorrect, causing the initial indicator values to be incorrect.

### Liquidity Considerations

When LEAN makes a history request to warm up an indicator, it may not return enough data to warm up the indicator. After warming up an indicator, [check for readiness](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/key-concepts.md#07-Check-Readiness), [reset it](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/key-concepts.md#11-Reset-Indicators), and increase the lookback period for a new history request if necessary.

```python
# In Initialize
symbol = self.add_equity("SPY").symbol
self._sma = SimpleMovingAverage(20)
self.warm_up_indicator(symbol, self._sma, Resolution.DAILY)
if not self._sma.is_ready:
    # The history request didn't return enough data to warm up the indicator.
    # Reset it, and increase the lookback period and try again.
    self._sma.reset()
    self.indicator_history(self._sma, symbol, 40, Resolution.DAILY)
```

## Delay Indicator

The `Delay` indicator delays its input by a specified number of bars. It is mainly used to combine indicators, enabling you to set a delay without using built-in [rolling windows](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/rolling-window.md). For example, you can create a displaced moving average by applying a `Delay` to a `SimpleMovingAverage`.

```python
# Create a 20-bar Delay indicator
self._delay = Delay(20)

# Create a displaced SMA by combining Delay with SMA
sma = SimpleMovingAverage(10)
displaced_sma = IndicatorExtensions.of(Delay(5), sma)
```

For a full example of using the `Delay` indicator to create a displaced SMA ribbon strategy, see [Combining Indicators Examples](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/combining-indicators.md#99-Examples).

For more information about this indicator, see the [class reference](https://www.lean.io/docs/v2/lean-engine/class-reference/py/QuantConnect/Indicators/Delay/) and [implementation](https://github.com/QuantConnect/Lean/blob/master/Indicators/Delay.cs).

## Examples

The following examples demonstrate some common practices for using manual indicators.

### Example 1: Universe Selection

The following algorithm selects the stocks within 5% of the 1-year maximum price among the top 100 liquid stocks. To do so, we make use of [Maximum](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/maximum.md) indicator to do so. Then, we hold the stocks with price > [EMA](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/exponential-moving-average.md) > [SMA](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/simple-moving-average.md), which indicates an upward accelerating trend.

```python
class ManualIndicatorAlgorithm(QCAlgorithm):
    maximum_by_symbol = {}

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)

        # Select a popularity-based universe with indicators in a Selection function.
        self._universe = self.add_universe(self.selection)

        # Set a scheduled event to rebalance daily on the daily indicator signals.
        self.schedule.on(
            self.date_rules.every_day(),
            self.time_rules.at(9, 31),
            self.rebalance
        )

    def selection(self, fundamentals: list[Fundamental]) -> list[Symbol]:
        selected = []

        # Initially filtered for the top 100 liquid stocks first.
        filtered = sorted(fundamentals, key=lambda f: f.dollar_volume, reverse=True)[:100]

        for f in filtered:
            if f.symbol not in self.maximum_by_symbol:
                self.maximum_by_symbol[f.symbol] = Maximum(252)
                # Warm up the Maximum indicator to its readiness to use immediately.
                history = self.history[TradeBar](f.symbol, 252, Resolution.DAILY)
                for bar in history:
                    self.maximum_by_symbol[f.symbol].update(bar.end_time, bar.close)
            else:
                # Update the indicator with the last known adjusted price daily.
                self.maximum_by_symbol[f.symbol].update(f.end_time, f.adjusted_price)

            # Select to trade if the current price is within 5% of the maximum price of the last year.
            # Close to the maximum price provides evidence of high popularity for the fund to support the trend.
            if f.adjusted_price >= self.maximum_by_symbol[f.symbol].current.value * 0.95:
                selected.append(f.symbol)

        return selected

    def rebalance(self) -> None:
        def to_buy(symbol):
            security = self.securities[symbol]
            # Buy the stocks whose prices are above the EMA and the EMA is above the SMA, meaning their trend is upward accelerating.
            return security.price > security.ema.current.value > security.sma.current.value

        symbols_to_buy = [symbol for symbol in self._universe.selected if to_buy(symbol)]

        # Equally invest in the selected stocks to dissipate the capital risk evenly.
        count = len(symbols_to_buy)
        if count > 0:
            targets = [PortfolioTarget(symbol, 1 / count) for symbol in symbols_to_buy]
            # Liquidate the positions that are not on an upward trend or are not popular anymore.
            self.set_holdings(targets, liquidate_existing_holdings=True)

    def on_securities_changed(self, changes: SecurityChanges) -> None:
        for added in changes.added_securities:
            symbol = added.symbol
            # Create an EMA and an SMA manual indicator for trade signal generation.
            added.ema = ExponentialMovingAverage(20)
            added.sma = SimpleMovingAverage(20)

            # Warm up the indicators to ensure their readiness to use them immediately.
            self.warm_up_indicator(symbol, added.ema, Resolution.DAILY)
            self.warm_up_indicator(symbol, added.sma, Resolution.DAILY)

            # Subscribe to the indicators to update daily price data for updated trade signal generation.
            self.register_indicator(symbol, added.ema, Resolution.DAILY)
            self.register_indicator(symbol, added.sma, Resolution.DAILY)

        for removed in changes.removed_securities:
            # Cancel data subscription to release computation resources when leaving the universe.
            self.deregister_indicator(removed.ema)
            self.deregister_indicator(removed.sma)
```
