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 ......
Fading The Gap
In this lesson we will use scheduled events to monitor for overnight price gaps in the market and trade on abnormal activity. Additionally, we will use a standard deviation indicator to reduce a parameter.
What You'll Learn- Creating Scheduled Events
- Creating a Rolling Window
- Accessing a Rolling Window
- Reducing a Parameter
We recommend completing the Liquid Universe Selection lesson and Opening Range Breakout lesson before starting this lesson.
Creating Scheduled Events- Fading The Gap
- Creating Scheduled Events
The difference between the close price of the previous day and the opening price of the current day is referred to as a gap. Fading the gap is a strategy that monitors for a large gap down and buys stock assuming it will rebound.
Using Scheduled EventsScheduled events trigger a method at a specific date and time. Scheduled events have three parameters: a DateRule
, a TimeRule
, and an action parameter. Our action parameter should be set to the name of a method.
Setting Event Time
Our time rules AfterMarketOpen
and BeforeMarketClose
take a symbol, and minute period. Both trigger an event a for a specific symbol's market hours.
Setting Event Action
def Initialize(self): # Schedule an event to fire every trading day for a security. # Remember to schedule events in initialize of your algorithm. # The time rule here tells the algorithm to fire 1 minute after TSLA's market open self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 1), self.FadeTheGap) def FadeTheGap(self): passYou must schedule events in your algorithm Initialize
method.
Task Objectives CompletedContinue
Let's set up our data schedule.
- Create an empty method
ClosingBar
. Create a scheduled event to run every day at TSLA market close that callsClosingBar
. - Create an empty method
OpeningBar
. Create a scheduled event to run every day at 1 minute after TSLA market open that callsOpeningBar
. - Create a method
ClosePositions
and liquidate our position to limit our exposure and keep our holding period short. Create a scheduled event to run every day at 45 minutes after TSLA market open that callsClosePositions
.
Are you using AfterMarketOpen
with the TimeRules
class? If you're rusty brush up on the documentation here.
Scheduled Events was introduced in the Opening Range Bootcamp.
Code:
class FadingTheGap(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 11, 1)
self.SetEndDate(2018, 7, 1)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
#1. Create a scheduled event to run every day at "0 minutes" before
# TSLA market close that calls the method ClosingBar
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("TSLA", 0), self.ClosingBar)
#2. Create a scheduled event to run every day at 1 minute after
# TSLA market open that calls the method OpeningBar
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 1), self.OpeningBar)
#3. Create a scheduled event to run every day at 45 minutes after
# TSLA market open that calls the method ClosePositions
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 45), self.ClosePositions)
#1. Create an empty method ClosingBar
def ClosingBar(self):
pass
#2. Create an empty method OpeningBar
def OpeningBar(self):
pass
#3. Create a method ClosePositions
# Liquidate our position to limit our exposure and keep our holding period short
def ClosePositions(self):
self.Liquidate()
-------------------------------------
Creating a Rolling Window
- Fading The Gap
- Creating a Rolling Window
A RollingWindow
holds a set of the most recent entries of data. As we move from time t=0 forward, our rolling window will shuffle data further along to a different index until it leaves the window completely.
<see chart in lesson - chart/graphic of a rollling window>
Setting A Window Type and LengthWhen we create a rolling window we need to set the type of data it will use. See more about the data types available in QuantConnect such as float, TradeBar, and QuoteBar here.
The length of the window is specified in parentheses after the data type. Keep in mind the window is indexed from 0 up to Count-1.
# Rolling window of 4 TradeBars (indexed from 0: 0, 1, 2, 3) self.window = RollingWindow[TradeBar](4) Adding Entries To A WindowWe update our rolling array with the Add()
method, which adds a new element at the beginning of the window. The objects inserted must match the type of the RollingWindow
.
The latest data point is accessible with the algorithm self.CurrentSlice
property. This is the same Slice
object from OnData
. See more about slices in the documentation.
Task Objectives CompletedContinue
Let's set up our rolling window to get the final bar of yesterday, and the first bar of today into our window.
- In
Initialize()
, create aRollingWindow
with type TradeBar and length of 2 asself.window
- In the
self.ClosingBar
method, add the current TradeBar of TSLA to our rolling window. - In the
self.OpeningBar
method, add the current TradeBar of TSLA to our rolling window.
self.CurrentSlice["TSLA"]
to get the latest TradeBar for TSLA. Code: class FadingTheGap(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 11, 1)
self.SetEndDate(2018, 7, 1)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("TSLA", 0), self.ClosingBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 1), self.OpeningBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 45), self.ClosePositions)
#1. Save a RollingWindow with type TradeBar and length of 2 as self.window
self.window = RollingWindow[TradeBar](2)
def ClosingBar(self):
#2. Add the final bar of TSLA to our rolling window
self.window.Add(self.CurrentSlice["TSLA"])
def OpeningBar(self):
#3. If "TSLA" is in the current slice, add the current slice to the window
if "TSLA" in self.CurrentSlice.Bars:
self.window.Add(self.CurrentSlice["TSLA"])
def ClosePositions(self):
self.Liquidate()
---------------------------------
Accessing a Rolling Window
- Fading The Gap
- Accessing a Rolling Window
Our window is not full when we first create it. QuantConnect provides a shortcut to check the if the rolling window is full("ready"): window.IsReady
which will return true when all slots of the window have data.
The object in the window with index [0] refers to the most recent item. The length-1 in the window is the oldest object.
# In a rolling window with len=2 # access the latest object # from the rolling window with index 0 self.window[0] # In a rolling window with len=2 # access the last object # from the rolling window with index 1 self.window[1]Task Objectives CompletedContinue
We will use our rolling window to decide when to place an order in the OpeningBar
method.
- If our window is not full yet use
return
to wait for tomorrow. - Calculate the change in overnight price using index values from our rolling window (ie. today's open - yesterday's close). Save the value to
delta
. - If delta is less than -$2.50,
SetHoldings()
to 100% TSLA.
Remember our RollingWindow
is saved to the variable window
. Our rolling windows are reverse indexed so 0 is the most recent item.
Remember you need to put an "m" on the end of decimal type numbers. "-2.5m"
Code:
class FadingTheGap(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 11, 1)
self.SetEndDate(2018, 7, 1)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("TSLA", 0), self.ClosingBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 1), self.OpeningBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 45), self.ClosePositions)
self.window = RollingWindow[TradeBar](2)
def ClosingBar(self):
self.window.Add(self.CurrentSlice["TSLA"])
def OpeningBar(self):
if "TSLA" in self.CurrentSlice.Bars:
self.window.Add(self.CurrentSlice["TSLA"])
#1. If our window is not full use return to wait for tomorrow
if not self.window.IsReady:
return
#2. Calculate the change in overnight price
delta = self.window[0].Open - self.window[1].Close
#3. If delta is less than -$2.5, SetHoldings() to 100% TSLA
if delta < -2.5:
self.SetHoldings("TSLA", 1)
def ClosePositions(self):
self.Liquidate()
--------------
Reducing a Parameter
- Fading The Gap
- Reducing a Parameter
Modern quant research suggests we should use as few parameters as possible. In this task we'll replace the arbitary -$10 move with a measurement of the standard deviation to replace a parameter with a statistical "confidence".
Assuming asset prices follow a normal distribution, we can use standard deviation to measure an asset's volatility. In a normal distribution, 68% of values are within 1 standard deviation of the mean, 95% of values are within 2 standard deviations of the mean, and 99% of values are within 3 standard deviations of the mean.
Creating an Indicator ManuallyQuantConnect provides the ability to add indicators manually. When we create an indicator manually, we control what data goes into it. Each manual indicator is slightly different, but the standard deviation takes a name and period. More on this topic can be found in the documentation here.
# Create an indicator StandardDeviation # for the symbol TSLA with a 30 "value" period self.volatilty = StandardDeviation("TSLA", 30) self.rsi = RelativeStrengthIndex("MyRSI", 200) self.ema = ExponentialMovingAverage("SlowEMA", 200)Updating Our IndicatorWhen we create a manual indicator object, we need to update it in order to create valid indicator values. All manual indicators have an Update
method which takes a time and a value.
When you're using the helper methods the updates are done automatically (e.g. RSI(), EMA()). When using a combination of OnData and Scheduled Events keep in mind that scheduled events trigger before our OnData
events, so your indicator might not be updated yet.
Manually created indicators values are accessed just like their automatic counterparts.
# Accessing current value of standard deviation x = self.volatility.Current.ValueTask Objectives CompletedContinue
In the previous task, we traded based on an arbitrary price setting. In this task, we will be replacing the setting with a manually created standard deviation indicator.
- In
Initialize()
create a manualStandardDeviation()
indicator to calculate TSLA volatility for a 60 minute period. Save it to the variableself.volatility
. - In
OnData()
, update ourStandardDeviation()
indicator manually with algorithm time usingself.Time
and TSLA's close price. - In OpeningBar, use
IsReady
to check if both volatility and the window are ready, or usereturn
to wait for tomorrow. - Save the number of standard deviations of the delta to the variable
deviations
for use when we set holdings.# Gap as a number of standard deviations self.deviations = delta / self.volatility.Current.Value SetHoldings()
to 100% TSLA ifdeviations
is less than -3 standard deviations (i.e. when we are 99% confident this gap is an anomaly).
StandardDeviation("TSLA", 60)
to self.volatility
. Code: class FadingTheGap(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 11, 1)
self.SetEndDate(2018, 7, 1)
self.SetCash(100000)
self.AddEquity("TSLA", Resolution.Minute)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("TSLA", 0), self.ClosingBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 1), self.OpeningBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("TSLA", 45), self.ClosePositions)
self.window = RollingWindow[TradeBar](2)
#1. Create a manual Standard Deviation indicator to track recent volatility
self.volatility = StandardDeviation("TSLA", 60)
def OnData(self, data):
if data["TSLA"] is not None:
#2. Update our standard deviation indicator manually with algorithm time and TSLA's close price
self.volatility.Update(self.Time, data["TSLA"].Close)
def OpeningBar(self):
if "TSLA" in self.CurrentSlice.Bars:
self.window.Add(self.CurrentSlice["TSLA"])
#3. Use IsReady to check if both volatility and the window are ready, if not ready 'return'
if not self.window.IsReady or not self.volatility.IsReady:
return
delta = self.window[0].Open - self.window[1].Close
#4. Save an approximation of standard deviations to our deviations variable by dividing delta by the current volatility value:
# Normally this is delta from the mean, but we'll approximate it with current value for this lesson.
deviations = delta / self.volatility.Current.Value
#5. SetHoldings to 100% TSLA if deviations is less than -3 standard deviations from the mean:
if deviations < -3:
self.SetHoldings("TSLA", 1)
def ClosePositions(self):
self.Liquidate()
def ClosingBar(self):
self.window.Add(self.CurrentSlice["TSLA"])
Anderson Wang
@Greg Kendall
Are you able to pass the task below? This is one of the tasks that you did and you pasted the code above.
Lesson 6 05/05: Fading The Gap / Reducing a Parameter
I tried to submit solution.py, but it can't pass either.
This is not the first task which solution.py cannot pass. I hope @Quantconnect people can fix them soon.
Greg Kendall
I was able to do it. It was a few months a go. Maybe they changed something. When you figure it out … let us know what change is required.
Varad Kabade
Hi Anderson Wang,We could not reproduce the issue. We used the following solution:class FadingTheGap(QCAlgorithm):
We recommend trying this solution.
Best,
Varad kabade
Anderson Wang
I was doing wrong, so please ignore my question above.
When I am on solution.py and press Submit, it still submits main.py actually.
Louis Szeto
Hi Anderson
Please copy the solution.py and paste it on the main.py tab
Best
Louis Szeto
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.
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!