Consolidating Data
Consolidator History
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 MostRecentlyRemoved
most_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()