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

Consolidating Data

Consolidator History

Introduction

This page explains how to save and access historical consolidated bars.

Save Consolidated Bars

To access historical bars that were passed to your consolidation handler, save the bars as you receive them. You can use a RollingWindow to save the consolidated bars and easily access them later on in your algorithm.

// Create a class member to store the RollingWindow
_window = new RollingWindow<TradeBar>(2);

// In the consolidation handler, add consolidated bars to the RollingWindow
private void ConsolidationHandler(object sender, TradeBar consolidatedBar)
{
    _window.Add(consolidatedBar);
}
# Create a class member to store the RollingWindow
self._window = RollingWindow[TradeBar](2)

# In the consolidation handler, add consolidated bars to the RollingWindow
def _consolidation_handler(self, sender: object, consolidated_bar: TradeBar) -> None:
    self._window.add(consolidated_bar)

Get Historical Bars

If you save consolidated bars in a RollingWindow, you can access them by indexing the RollingWindow. RollingWindow objects operate on a first-in, first-out process to allow for reverse list access semantics. Index 0 refers to the most recent item in the window and the largest index refers to the last item in the window.

var mostRecentBar = _window[0];
var previousBar = _window[1];
var oldestBar = _window[_window.Count-1];
most_recent_bar = self._window[0]
previous_bar = self._window[1]
oldest_bar = self._window[self._window.count-1]

To get the consolidated bar that was most recently removed from the RollingWindow, use the MostRecentlyRemovedmost_recently_removed property.

var removedBar = _window.MostRecentlyRemoved;
removed_bar = self._window.most_recently_removed

Examples

The following examples demonstrate some common practices for consolidator history.

Example 1: Price Action

The following algorithm trades breakout price action on the SPY five-minute trade bar. To do so, we must create a five-minute trade bar consolidator and a rolling window to hold 3 trade bars to check if the trade conditions are fulfilled.

public class ConsolidatorHistoryAlgorithm : QCAlgorithm
{
    private Symbol _spy;
    // To hold 3 consolidated trade bars to identify a breakout pattern.
    private RollingWindow<TradeBar> _windows = new(3);

    public override void Initialize()
    {
        SetStartDate(2021, 10, 1);
        SetEndDate(2022, 1, 1);
        
        // Request SPY data for signal generation and trading.
        _spy = AddEquity("SPY", Resolution.Minute).Symbol;

        // The breakout is based on a 5-minute consolidated trade bar.
        var consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(5));
        // Subscribe for automatically updating the consolidator with SPY data.
        SubscriptionManager.AddConsolidator(_spy, consolidator);
        // Add a consolidator handler to check that the breakout condition is fulfilled and traded.
        consolidator.DataConsolidated += OnConsolidated;

        SetWarmUp(TimeSpan.FromDays(1));
    }

    private void OnConsolidated(object sender, TradeBar bar)
    {
        _windows.Add(bar);

        if (_windows.IsReady)
        {
            // Buy if the breakout price action is fulfilled.
            // 1. Increasing price trend.
            // 2. The last 3 bars are green.
            // 3. The 3rd and 2nd last bars range is decreasing.
            // 4. The last bar exceeds the 2nd last bar by double the 2nd last bar's range.
            var secondLastRange = _windows[1].Close - _windows[1].Open;
            if (bar.Close > _windows[1].Close && _windows[1].Close > _windows[2].Close &&
            _windows[2].Close > _windows[2].Open && _windows[1].Close > _windows[1].Open && _windows[0].Close > _windows[0].Open &&
            _windows[2].Close - _windows[2].Open > secondLastRange && bar.Close > _windows[1].Close + 2 * secondLastRange)
            {
                SetHoldings(_spy, 0.5m);
            }
        }
    }

    public override void OnOrderEvent(OrderEvent orderEvent)
    {
        if (orderEvent.Status == OrderStatus.Filled)
        {
            if (orderEvent.Ticket.OrderType == OrderType.Market)
            {
                // Stop loss order at 1%.
                var stopPrice = orderEvent.FillQuantity > 0m ? orderEvent.FillPrice * 0.99m : orderEvent.FillPrice * 1.01m;
                StopMarketOrder(_spy, -Portfolio[_spy].Quantity, stopPrice);
                // Take profit order at 2%.
                var takeProfitPrice = orderEvent.FillQuantity > 0m ? orderEvent.FillPrice * 1.02m : orderEvent.FillPrice * 0.98m;
                LimitOrder(_spy, -Portfolio[_spy].Quantity, takeProfitPrice);
            }
            else if (orderEvent.Ticket.OrderType == OrderType.StopMarket || orderEvent.Ticket.OrderType == OrderType.Limit)
            {
                // Cancel any open order if stop loss or take profit order filled.
                Transactions.CancelOpenOrders();
            }
        }
    }
}
class ConsolidatorHistoryAlgorithm(QCAlgorithm):
    # To hold 3 consolidated trade bars to identify a breakout pattern.
    windows = RollingWindow[TradeBar](3)

    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 breakout is based on a 5-minute consolidated trade bar.
        consolidator = TradeBarConsolidator(timedelta(minutes=5))
        # Subscribe for automatically updating the consolidator with SPY data.
        self.subscription_manager.add_consolidator(self.spy, consolidator)
        # Add a consolidator handler to check that the breakout condition is fulfilled and traded.
        consolidator.data_consolidated += self.on_consolidated

        self.set_warm_up(timedelta(15))

    def on_consolidated(self, sender: object, bar: TradeBar) -> None:
        self.windows.add(bar)

        if self.windows.is_ready:
            # Buy if the breakout price action is fulfilled.
            # 1. Increasing price trend.
            # 2. The last 3 bars are green.
            # 3. The 3rd and 2nd last bars range is decreasing.
            # 4. The last bar exceeds the 2nd last bar by double the 2nd last bar's range.
            second_last_range = self.windows[1].close - self.windows[1].open
            if bar.close > self.windows[1].close and self.windows[1].close > self.windows[2].close and\
            self.windows[2].close > self.windows[2].open and self.windows[1].close > self.windows[1].open and\
            self.windows[0].close > self.windows[0].open and self.windows[2].close - self.windows[2].open > second_last_range and\
            bar.close > self.windows[1].close + 2 * second_last_range:
                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 order_event.fill_price * 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 order_event.fill_price * 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: