Consolidating Data
Consolidator History
Introduction
Consolidators keep a built-in RollingWindow of the bars they produce. This section explains how to access these historical consolidated bars.
Save Consolidated Bars
Every consolidator keeps a built-in RollingWindow of the bars it produces, so you can access recent consolidated bars without creating and maintaining your own RollingWindow. The window updates automatically each time the consolidator emits a bar. By default, it holds the 2 most recent bars.
// Create and register a consolidator.
_consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(10));
SubscriptionManager.AddConsolidator("SPY", _consolidator); # Create and register a consolidator.
self._consolidator = TradeBarConsolidator(timedelta(minutes=10))
self.subscription_manager.add_consolidator("SPY", self._consolidator)
To access the trailing consolidated bars, use reverse list access semantics with positive and negative indexing. The current (most recent) bar is at index 0 or the Currentcurrent property, the previous bar is at index 1 or the Previousprevious property, and so on until the length of the window. Before the consolidator emits its first bar, Currentcurrent is nullNone, so check the Window.Countwindow.count or the window's IsReadyis_ready flag first.
The window stores IBaseData, so cast each element to the consolidated bar type to access its properties.
var currentBar = _consolidator.Current as TradeBar; // or _consolidator[0] var previousBar = _consolidator.Previous as TradeBar; // or _consolidator[1] var barCount = _consolidator.Window.Count;
current_bar = self._consolidator.current # or self._consolidator[0] previous_bar = self._consolidator.previous # or self._consolidator[1] bar_count = self._consolidator.window.count
To access all the consolidated bars in the window, iterate through the consolidator.
foreach (TradeBar bar in _consolidator)
{
Log(bar.ToString());
} for bar in self._consolidator:
self.log(f"{bar}")
To keep more than the 2 most recent bars, set the Sizesize of the window.
_consolidator.Window.Size = 10;
self._consolidator.window.size = 10
Get Historical Bars
The consolidator's built-in Windowwindow is a RollingWindow, so you can use negative indexing to get the oldest bar it holds.
var oldestBar = _consolidator.Window[-1];
oldest_bar = self._consolidator.window[-1]
To get the consolidated bar that was most recently removed from the window, use the MostRecentlyRemovedmost_recently_removed property.
var removedBar = _consolidator.Window.MostRecentlyRemoved;
removed_bar = self._consolidator.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 create a five-minute trade bar consolidator and increase its built-in rolling window to hold 3 trade bars to check if the trade conditions are fulfilled.
public class ConsolidatorHistoryAlgorithm : QCAlgorithm
{
private Symbol _spy;
// The 5-minute consolidator keeps a built-in window of consolidated bars.
private TradeBarConsolidator _consolidator;
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
// 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.
_consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(5));
// Keep the last 3 consolidated bars to identify a breakout pattern.
_consolidator.Window.Size = 3;
// 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)
{
// Inside the handler, the built-in window is already updated, so bar is _consolidator[0].
if (!IsWarmingUp && _consolidator.Window.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 secondLast = (TradeBar)_consolidator[1];
var thirdLast = (TradeBar)_consolidator[2];
var secondLastRange = secondLast.Close - secondLast.Open;
if (bar.Close > secondLast.Close && secondLast.Close > thirdLast.Close &&
thirdLast.Close > thirdLast.Open && secondLast.Close > secondLast.Open && bar.Close > bar.Open &&
thirdLast.Close - thirdLast.Open > secondLastRange && bar.Close > secondLast.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):
def initialize(self) -> None:
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
# 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.
self._consolidator = TradeBarConsolidator(timedelta(minutes=5))
# Keep the last 3 consolidated bars to identify a breakout pattern.
self._consolidator.window.size = 3
# Subscribe for automatically updating the consolidator with SPY data.
self.subscription_manager.add_consolidator(self.spy, self._consolidator)
# Add a consolidator handler to check that the breakout condition is fulfilled and traded.
self._consolidator.data_consolidated += self.on_consolidated
self.set_warm_up(timedelta(1))
def on_consolidated(self, sender: object, bar: TradeBar) -> None:
# Inside the handler, the built-in window is already updated, so bar is self._consolidator[0].
if not self.is_warming_up and self._consolidator.window.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 = self._consolidator[1]
third_last = self._consolidator[2]
second_last_range = second_last.close - second_last.open
if bar.close > second_last.close and second_last.close > third_last.close and\
third_last.close > third_last.open and second_last.close > second_last.open and\
bar.close > bar.open and third_last.close - third_last.open > second_last_range and\
bar.close > second_last.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()