book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Consolidating Data

Updating Indicators

Introduction

You can use consolidators to automatically update indicators in your algorithms. The consolidators can update your indicators at each time step or with aggregated bars. By default, LEAN updates data point indicators with the close price of the consolidated bars, but you can change it to a custom data field.

Standard Indicator Periods

If your algorithm has a static universe, you can create automatic indicators in just one line of code. When you create an automatic indicator, LEAN creates a consolidator, hooks it up for automatic updates, and then updates the indicator with the consolidated bars.

Select Language:
 # Consolidate minute SPY data into 14-bar daily indicators
ema = self.ema("SPY", 14, Resolution.DAILY)
sma = self.sma("SPY", 14, Resolution.DAILY)

If your algorithm has a dynamic universe, create a manual indicator and then create a time period consolidator that updates the indicator at each time step. Keep a reference to the consolidator so you can remove it when your algorithm removes the security from the universe.

Select Language:
def initialize(self):
    self._ema = ExponentialMovingAverage(14)
    self._consolidator = TradeBarConsolidator(1)
    self._consolidator.data_consolidated += self._on_data_consolidated
    self.subscription_manager.add_consolidator(self._symbol, self._consolidator)

# Define consolidator handler function as a class method
def _on_data_consolidated(self, sender, bar):
    self._ema.update(bar.end_time, bar.close)

Custom Indicator Periods

It's common to update indicators with price data that spans a normal time period like one minute or one day. It's less common to update an indicator with exotic time periods (for example, a 7-minute consolidated bar), so these types of indicators may provide more opportunities for alpha. To update indicators with exotic data, create a manual indicator and then call the register_indicator method. The register_indicator method wires up the indicator for automatic updates at the time interval you provide.

Select Language:
# Calculate the SMA with 10 7-minute bars
self._symbol = self.add_equity("SPY", Resolution.MINUTE).symbol
self._indicator = SimpleMovingAverage(10)
self.register_indicator(self._symbol, self._indicator, timedelta(minutes=7))

The register_indicator method can accept a timedelta, Resolution, or an unregistered consolidator. If you apply the indicator to a security in a dynamic universe, provide a consolidator so that you can remove it when your algorithm removes the security from the universe.

Select Language:
# timedelta
self.register_indicator(self._symbol, self._indicator, timedelta(minutes=7))

# Resolution
self.register_indicator(self._symbol, self._indicator, Resolution.HOUR)

# Consolidator
self._consolidator = TradeBarConsolidator(35)
self.register_indicator(self._symbol, self._indicator, self._consolidator)

Custom Indicator Values

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.

Select Language:
# Define a 10-period RSI with indicator constructor
self._rsi = RelativeStrengthIndex(10, MovingAverageType.SIMPLE)

# Register the daily High price data to automatically update the indicator
self.register_indicator(self._symbol, self._rsi, Resolution.DAILY, Field.HIGH)

The register_indicator method can accept a timedelta, Resolution, or an unregistered consolidator. If you apply the indicator to a security in a dynamic universe, provide a consolidator so that you can remove it when your algorithm removes the security from the universe.

The Field class has the following selector properties:

Examples

The following examples demonstrate common practices for updating indicators using consolidators.

Example 1: Hammer Pattern With 5-Minute Consolidator

The following algorithm trades Hammer and Inverted Hammer candlestick patterns. The candles are constructed with five-minute SPY trade bars consolidated by a five-minute TradeBarConsolidator , while automatically updating with the register_indicator method. Both patterns are bullish, so we buy SPY if the pattern is signaled.

Select Language:
from QuantConnect.Indicators.CandlestickPatterns import *
class ConsolidatorUpdatingIndicatorsAlgorithm(QCAlgorithm):
    hammer = Hammer()
    inverted_hammer = InvertedHammer()

    def initialize(self) -> None:
        self.set_start_date(2021, 10, 1)
        self.set_end_date(2022, 1, 1)
        
        # Request SPY data for signal generation and trading.
        self.spy = self.add_equity("SPY", Resolution.MINUTE).symbol

        # The candlestick patterns are based on a 5-minute consolidated trade bar.
        consolidator = TradeBarConsolidator(timedelta(minutes=5))
        # Subscribe for automatically updating the indicators with the 5-minute consolidator.
        self.register_indicator(self.spy, self.hammer, consolidator)
        self.register_indicator(self.spy, self.inverted_hammer, consolidator)
        # Add an event handler on candlestick indicators that are updated to trade the pattern.
        self.hammer.updated += self.on_updated
        self.inverted_hammer.updated += self.on_updated

        self.set_warm_up(1)

    def on_updated(self, sender: object, point: IndicatorDataPoint) -> None:
        # Both the hammer and inverted hammer patterns indicate bullish movement, so we buy SPY.
        if point.value == 1 and not self.portfolio[self.spy].is_long:
            self.set_holdings(self.spy, 0.5)

    def on_order_event(self, order_event: OrderEvent) -> None:
        if order_event.status == OrderStatus.FILLED:
            if order_event.ticket.order_type == OrderType.MARKET:
                # Stop loss order at 1%.
                stop_price = order_event.fill_price * (0.99 if order_event.fill_quantity > 0 else 1.01)
                self.stop_market_order(self.spy, -self.portfolio[self.spy].quantity, stop_price)
                # Take profit order at 2%.
                take_profit_price = order_event.fill_price * (1.02 if order_event.fill_quantity > 0 else 0.98)
                self.limit_order(self.spy, -self.portfolio[self.spy].quantity, take_profit_price)
            elif order_event.ticket.order_type == OrderType.STOP_MARKET or order_event.ticket.order_type == OrderType.LIMIT:
                # Cancel any open order if stop loss or take profit order filled.
                self.transactions.cancel_open_orders()

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: