I am constantly referring back to the Boot Camp, but it is difficult to access the information in its interactive format. I have copied the text of the lesson here for quick access and review. (Do the boot camp before using this for review.) -GK ......
Opening Range BreakoutIn this lesson we will implement an opening range breakout strategy. This strategy predicts a breakout if the price leaves the range of the first 30 minutes of trading.
What You'll Learn- Using a consolidator to create a time bar
- Creating an event handler
- Taking a position depending on the high and low of the opening range
- Using a scheduled event to close a position
We suggest you complete Momentum Based Tactical Allocation before starting this lesson.
-----------------------------------
Creating a Consolidator- Opening Range Breakout
- Creating a Consolidator
Opening range breakout uses a defined period of time to set a price-range, and trades on leaving that range. To achieve this we will start by consolidating the first 30 minutes of data.
ConsolidatorsConsolidators are used to combine smaller data points into larger bars. We commonly use consolidators to create 5, 10 or 15-minute bars from minute data.
The self.Consolidate()
method takes a ticker
, timedelta
, and event handler
. It can also take a CalendarType
or a Resolution
parameter.
Consolidate
should be called in your Initialize()
method so it is initialized only once. You must request data smaller than the bar you aim to consolidate. For example, it is not possible to consolidate daily data into minute bars.
Consolidators require an accompanying event handler to receive the output data. The consolidator event handlers are functions which are called when a new bar is created. We can name the event handler anything as long as it is passed into our Consolidate
method.
Task Objectives
- Create a 30 minute consolidator, which calls an event handler
self.OnDataConsolidated
. - Create a function
OnDataConsolidated()
and save the bar toself.currentBar
.
class OpenRangeBreakout(QCAlgorithm):
openingBar = None
currentBar = None
def Initialize(self):
self.SetStartDate(2018, 7, 10) # Set Start Date
self.SetEndDate(2019, 6, 30) # Set End Date
self.SetCash(100000) # Set Strategy Cash
# Subscribe to TSLA with Minute Resolution
self.symbol = self.AddEquity("TSLA", Resolution.Minute)
#1. Create our consolidator with a timedelta of 30 min
self.Consolidate("TSLA", timedelta(minutes=30), self.OnDataConsolidated)
def OnData(self, data):
pass
#2. Create a function OnDataConsolidator which saves the currentBar as bar
def OnDataConsolidated(self, bar):
self.currentBar = bar
- Opening Range Breakout
- Bar Data and Bar Time
The type of data created by a consolidator depends on the type of underlying data of your requested data. QuantConnect has specific data for each asset type:
Asset TypeData AvailableConsolidated OutputEquityTrades (Tick, TradeBar)TradeBarForex/CfdQuotes (Tick, QuoteBar)QuoteBarCryptoTrades and Quotes (Tick, TradeBar, and QuoteBar)TradeBar, QuoteBarFuturesTrades and Quotes (Tick, TradeBar, and QuoteBar)TradeBar, QuoteBarOptionsTrades and Quotes (Tick, TradeBar, and QuoteBar)TradeBar, QuoteBar Checking Our TimeRegardless of the resulting bar type, all data contains a few common properties: bar.Time
, and bar.Price
. You can use bar.Time
to get the current moment the bar was created and bar.EndTime
to get the time at the end of the bar.
Keep in mind bar is a variable that can go by any name. It is defined in the parameters of our event handler function.
Task Objectives
Let's save the first bar of the day to use it's range later for trading sigals.
- Save the first bar of the trading day to the class variable:
self.openingBar
. The first bar starts at 9:30am ET and ends at 10:00am ET.
bar.Time.hour
is the time at the start of your bar and bar.EndTime.hour
is the time at the end of your bar. The two are not interchangeable. We only need to check for one of the two. Code:class OpeningRangeBreakout(QCAlgorithm):openingBar = None
def Initialize(self):
self.SetStartDate(2018, 7, 10)
self.SetEndDate(2019, 6, 30)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
self.Consolidate("TSLA", timedelta(minutes=30), self.OnDataConsolidated)
def OnData(self, data):
pass
def OnDataConsolidated(self, bar):
#1. Check the time, we only want to work with the first 30min after Market Open
if bar.Time.hour == 9 and bar.Time.minute == 30:
#2. Save one bar as openingBar
self.openingBar = bar -----------------------------Using the Output of the Consolidator
- Opening Range Breakout
- Using the Output of the Consolidator
As we have requested equities data, the consolidated bar's type is TradeBar
. It has the properties Open, High, Low, Close and Volume for a given period of time.
Short positions are when you are betting the market will fall in value. When you place a "short trade" you are borrowing someone else's stocks for a while to sell them. When you "close" a short position you are buying back the stocks you borrowed at the new market price.
Going Short In QuantConnectIn QuantConnect, we don't need to place a separate order type to enter a short position. We can use a scale of 1 to -1 in self.SetHoldings
to place a long or short order. Going long is denoted by ordering a positive number, and short a negative one. QC does not support hedging (long and short at the same time).
Task Objectives
The breakout point is when the price breaks past the high or low of the market opening range.
- If we have already invested, or if the
openingBar
is None, exit OnData. - If the close price of TSLA is greater than the openingBar.High price, go 100% long on TSLA.
- If the close price of TSLA is less than the openingBar.Low price, go 100% short on TSLA.
openingBar = None
def Initialize(self):
self.SetStartDate(2018, 7, 10)
self.SetEndDate(2019, 6, 30)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
self.Consolidate("TSLA", timedelta(minutes=30), self.OnDataConsolidated)
def OnData(self, data):
#1. If self.Portfolio.Invested is true, or if the openingBar is None, return
if self.Portfolio.Invested or self.openingBar is None:
return
#2. Check if the close price is above the high price, if so go 100% long on TSLA
if data["TSLA"].Close > self.openingBar.High:
self.SetHoldings("TSLA", 1)
#3. Check if the close price is below the low price, if so go 100% short on TSLA
elif data["TSLA"].Close < self.openingBar.Low:
self.SetHoldings("TSLA", -1)
def OnDataConsolidated(self, bar):
if bar.Time.hour == 9 and bar.Time.minute == 30:
self.openingBar = bar --------------------------------Scheduling Events
- Opening Range Breakout
- Scheduling Events
Scheduled events allow you to trigger code blocks for execution at specific times according to rules you set. We initialize scheduled events in the Initialize
method so they are only executed once.
We use self.Schedule.On()
to coordinate your algorithm activities and perform analysis at regular intervals while letting the trading engine take care of market holidays.
Scheduled events need a DateRules
and TimeRules
pair to set a specific time, and an action that you want to complete. When the event is triggered the action block (or function) is executed. We include our asset ticker in our EveryDay("ticker")
object to specifify that we are calling days there is data for the ticker. You can learn more in the documentation.
Liquidate("ticker")
closes any open positions with a market order, and closes any open limit orders.
Task Objectives
The breakout signal is strongest in the morning. Let's use a scheduled event to close our position each day at 13:30 ET.
- Create an event handler for our scheduled event named
def ClosePositions(self):
- Inside your ClosePositions function set self.openingBar to None and liquidate TSLA.
- Create a scheduled event triggered at 13:30 ET calling the ClosePositions function.
You can use TimeRules.At(int hour, in min)
helper to trigger at a specific time each day. Keep in mind we are operating on a 24-hour clock so 4pm ET is hour 16.
For the Schedule.On() call, make sure you are using self.ClosePositions not self.ClosePositions(), the parentheses execute the function.
Code:class OpeningRangeBreakout(QCAlgorithm):
openingBar = None
def Initialize(self):
self.SetStartDate(2018, 7, 10)
self.SetEndDate(2019, 6, 30)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
self.Consolidate("TSLA", timedelta(minutes=30), self.OnDataConsolidated)
#3. Create a scheduled event triggered at 13:30 calling the ClosePositions function
self.Schedule.On(self.DateRules.EveryDay("TSLA"), self.TimeRules.At(13, 30), self.ClosePositions)
def OnData(self, data):
if self.Portfolio.Invested or self.openingBar is None:
return
if data["TSLA"].Close > self.openingBar.High:
self.SetHoldings("TSLA", 1)
elif data["TSLA"].Close < self.openingBar.Low:
self.SetHoldings("TSLA", -1)
def OnDataConsolidated(self, bar):
if bar.Time.hour == 9 and bar.Time.minute == 30:
self.openingBar = bar
#1. Create a function named ClosePositions(self)
def ClosePositions(self):
#2. Set self.openingBar to None, and liquidate TSLA
self.openingBar = None
self.Liquidate("TSLA")
Greg Kendall
Here is the backtest with code….
Greg Kendall
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!