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

Renko Consolidators

Volume Renko Consolidators

Introduction

Volume Renko consolidators aggregate bars based on a fixed trading volume. These types of bars are commonly known as volume bars. A VolumeRenkoConsolidator with a bar size of 10,000 produces a new bar every time 10,000 units of the security trades in the market. If the trading volume of a single time step exceeds two step sizes (i.e. >20,000), the VolumeRenkoConsolidator produces multiple bars in a single time step.

Consolidate Trade Bars

TradeBar consolidators aggregate TradeBar objects into VolumeRenkoBar objects. Follow these steps to create and manage a TradeBar consolidator based on custom set volume traded:

  1. Create the consolidator.
  2. To create a Volume Renko consolidator, pass the bar size to the VolumeRenkoConsolidator constructor.

    // Create a Volume Renko consolidator that emits a bar every time 10,000 units trade
    _consolidator = new VolumeRenkoConsolidator(10000m);
    # Create a Volume Renko consolidator that emits a bar every time 10,000 units trade
    self._consolidator = VolumeRenkoConsolidator(10000)
  3. Add an event handler to the consolidator.
  4. _consolidator.DataConsolidated += ConsolidationHandler;
    self._consolidator.data_consolidated += self._consolidation_handler

    LEAN passes consolidated bars to the consolidator event handler in your algorithm. The most common error when creating consolidators is to put parenthesis () at the end of your method name when setting the event handler of the consolidator. If you use parenthesis, the method executes and the result is passed as the event handler instead of the method itself. Remember to pass the name of your method to the event system. Specifically, it should be ConsolidationHandlerself._consolidation_handler, not ConsolidationHandler()self._consolidation_handler().

  5. Define the consolidation handler.
  6. void ConsolidationHandler(object sender, VolumeRenkoBar consolidatedBar)
    {
    
    }
    def _consolidation_handler(self, sender: object, consolidated_bar: VolumeRenkoBar) -> None:
        pass

    The consolidation event handler receives bars when the trading volume forms a new Volume Renko bar.

  7. Update the consolidator.
  8. You can automatically or manually update the consolidator.

    • Automatic Updates
    • To automatically update a consolidator with data from the security subscription, call the AddConsolidatoradd_consolidator method of the Subscription Manager.

      self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
      SubscriptionManager.AddConsolidator(_symbol, _consolidator);
    • Manual Updates
    • Manual updates let you control when the consolidator updates and what data you use to update it. If you need to warm up a consolidator with data outside of the warm-up period, you can manually update the consolidator. To manually update a consolidator, call its Updateupdate method with a TradeBar object. You can update the consolidator with data from the Slice object in the OnDataon_data method or with data from a history request.

      # Example 1: Update the consolidator with data from the Slice object
      def on_data(self, slice: Slice) -> None:
          trade_bar = slice.bars[self._symbol]
          self._consolidator.update(trade_bar)
      
      # Example 2: Update the consolidator with data from a history request
      history = self.history[TradeBar](self._symbol, 30, Resolution.MINUTE)
      for trade_bar in history:
          self._consolidator.update(trade_bar)
      // Example 1: Update the consolidator with data from the Slice object
      public override void OnData(Slice slice)
      {
          var tradeBar = slice.Bars[_symbol];
          _consolidator.Update(tradeBar);
      }
      
      // Example 2: Update the consolidator with data from a history request
      var history = History<TradeBar>(_symbol, 30, Resolution.Minute);
      foreach (var tradeBar in history)
      {
          _consolidator.Update(tradeBar);
      }
  9. If you create consolidators for securities in a dynamic universe and register them for automatic updates, remove the consolidator when the security leaves the universe.
  10. SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
    self.subscription_manager.remove_consolidator(self._symbol, self._consolidator)

    If you have a dynamic universe and don't remove consolidators, they compound internally, causing your algorithm to slow down and eventually die once it runs out of RAM. For an example of removing consolidators from universe subscriptions, see the GasAndCrudeOilEnergyCorrelationAlphaGasAndCrudeOilEnergyCorrelationAlpha in the LEAN GitHub repository.

Consolidate Trade Ticks

Tick consolidators aggregate Tick objects into VolumeRenkoBar objects. Follow these steps to create and manage a Tick consolidator based on custom set volume traded:

  1. Create the consolidator.
  2. To create a Volume Renko consolidator, pass the bar size to the VolumeRenkoConsolidator constructor.

    // Create a Volume Renko consolidator that emits a bar every time 10,000 units trade
    _consolidator = new VolumeRenkoConsolidator(10000m);
    # Create a Volume Renko consolidator that emits a bar every time 10,000 units trade
    self._consolidator = VolumeRenkoConsolidator(10000)
  3. Add an event handler to the consolidator.
  4. _consolidator.DataConsolidated += ConsolidationHandler;
    self._consolidator.data_consolidated += self._consolidation_handler

    LEAN passes consolidated bars to the consolidator event handler in your algorithm. The most common error when creating consolidators is to put parenthesis () at the end of your method name when setting the event handler of the consolidator. If you use parenthesis, the method executes and the result is passed as the event handler instead of the method itself. Remember to pass the name of your method to the event system. Specifically, it should be ConsolidationHandlerself._consolidation_handler, not ConsolidationHandler()self._consolidation_handler().

  5. Define the consolidation handler.
  6. void ConsolidationHandler(object sender, VolumeRenkoBar consolidatedBar)
    {
    
    }
    def _consolidation_handler(self, sender: object, consolidated_bar: VolumeRenkoBar) -> None:
        pass

    The consolidation event handler receives bars when the trading volume forms a new Volume Renko bar.

  7. Update the consolidator.
  8. You can automatically or manually update the consolidator.

    • Automatic Updates
    • To automatically update a consolidator with data from the security subscription, call the AddConsolidatoradd_consolidator method of the Subscription Manager.

      self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
      SubscriptionManager.AddConsolidator(_symbol, _consolidator);
    • Manual Updates
    • Manual updates let you control when the consolidator updates and what data you use to update it. If you need to warm up a consolidator with data outside of the warm-up period, you can manually update the consolidator. To manually update a consolidator, call its Updateupdate method with a Tick object. You can update the consolidator with data from the Slice object in the OnDataon_data method or with data from a history request.

      # Example 1: Update the consolidator with data from the Slice object
      def on_data(self, slice: Slice) -> None:
          ticks = slice.ticks[self._symbol]
          for tick in ticks:
              self._consolidator.update(tick)
      
      # Example 2: Update the consolidator with data from a history request
      ticks = self.history[Tick](self._symbol, timedelta(minutes=3), Resolution.TICK)
      for tick in ticks:
          self._consolidator.update(tick)
      // Example 1: Update the consolidator with data from the Slice object
      public override void OnData(Slice slice)
      {
          var ticks = slice.Ticks[_symbol];
          foreach (var tick in ticks)
          {
          	_consolidator.Update(tick);
          }
      }
      
      // Example 2: Update the consolidator with data from a history request
      var ticks = History<Tick>(_symbol, TimeSpan.FromMinutes(3), Resolution.Tick);
      foreach (var tick in ticks)
      {
          _consolidator.Update(tick);
      }
  9. If you create consolidators for securities in a dynamic universe and register them for automatic updates, remove the consolidator when the security leaves the universe.
  10. SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
    self.subscription_manager.remove_consolidator(self._symbol, self._consolidator)

    If you have a dynamic universe and don't remove consolidators, they compound internally, causing your algorithm to slow down and eventually die once it runs out of RAM. For an example of removing consolidators from universe subscriptions, see the GasAndCrudeOilEnergyCorrelationAlphaGasAndCrudeOilEnergyCorrelationAlpha in the LEAN GitHub repository.

Examples

The following examples demonstrate common practices for implementing volume renko consolidator.

Example 1: Bollinger Bands From Volume Renko Bar

The following algorithm uses 100,000-share volume Renko bars to spot mean reversal opportunities on SPY with Bollinger Bands. Equal volume bars could provide more equal information in each bar, so the standard deviation and trade opportunities are more credible.

public class VolumeRenkoBarConsolidatorAlgorithm : QCAlgorithm
{
    private Symbol _spy;
    private VolumeRenkoConsolidator _consolidator;
    // We use a Bollinger band to signal mean reversal opportunities.
    private BollingerBands _bbands = new(20, 2, MovingAverageType.Exponential);

    public override void Initialize()
    {
        SetStartDate(2020, 4, 1);
        SetEndDate(2020, 10, 1);
        
        // Request SPY data to feed for the consolidators, indicators, and trade signal generation.
        _spy = AddEquity("SPY", Resolution.Minute).Symbol;

        // Create a 100,000-share bar of SPY to obtain a more equally-informed bar.
        _consolidator = new VolumeRenkoConsolidator(100000m);

        // Subscribe the consolidators to SPY data to automatically update the indicator.
        RegisterIndicator(_spy, _bbands, _consolidator);
        
        // Warm up the indicator for immediate use.
        SetWarmUp(20);
    }

    public override void OnData(Slice slice)
    {
        if (_bbands.IsReady & slice.Bars.TryGetValue(_spy, out var bar))
        {
            // Trade the mean reversal by short-selling SPY if the current price exceeds the upper threshold.
            if (bar.Close > _bbands.UpperBand && !Portfolio[_spy].IsShort)
            {
                SetHoldings(_spy, -0.5m);
            }
            // Trade the mean reversal by buying SPY if the current price is below the lower threshold.
            else if (bar.Close < _bbands.LowerBand && !Portfolio[_spy].IsLong)
            {
                SetHoldings(_spy, 0.5m);
            }
            // Exit position if the price is mean-reverted that crosses the EMA reference.
            else if ((Portfolio[_spy].Quantity > 0 && bar.Close > _bbands.MiddleBand) ||
                (Portfolio[_spy].Quantity < 0 && bar.Close < _bbands.MiddleBand))
            {
                Liquidate(_spy);
            }
        }
    }
}
class VolumeRenkoBarConsolidatorAlgorithm(QCAlgorithm):
    # We use a Bollinger band to signal mean reversal opportunities.
    _bbands = BollingerBands(20, 2, MovingAverageType.EXPONENTIAL)

    def initialize(self) -> None:
        self.set_start_date(2020, 4, 1)
        self.set_end_date(2020, 10, 1)
        
        # Request SPY data to feed for the consolidators, indicators, and trade signal generation.
        self._spy = self.add_equity("SPY", Resolution.MINUTE).symbol

        # Create a 100,000-share bar of SPY to obtain a more equally-informed bar.
        self._consolidator = VolumeRenkoConsolidator(100000)

        # Subscribe the consolidators to SPY data to automatically update the indicator.
        self.register_indicator(self._spy, self._bbands, self._consolidator)

        # Warm up the indicator for immediate use.
        self.set_warm_up(20)

    def on_data(self, slice: Slice) -> None:
        bar = slice.bars.get(self._spy)
        if self._bbands.is_ready and bar:
            upper_band = self._bbands.upper_band.current.value
            middle_band = self._bbands.middle_band.current.value
            lower_band = self._bbands.lower_band.current.value

            # Trade the mean reversal by short-selling SPY if the current price exceeds the upper threshold.
            if bar.close > upper_band and not self.portfolio[self._spy].is_long:
                self.set_holdings(self._spy, -0.5)
            # Trade the mean reversal by buying SPY if the price is below the lower threshold.
            elif bar.close < lower_band and not self.portfolio[self._spy].is_short:
                self.set_holdings(self._spy, 0.5)
            # Exit position if the price is mean-reverted that crosses the EMA reference.
            elif (self.portfolio[self._spy].quantity > 0 and bar.close > middle_band)\
            or (self.portfolio[self._spy].quantity < 0 and bar.close < middle_band):
                self.liquidate(self._spy)

Other Examples

For more examples, see the following algorithms:

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: