I have spent quite some time trying to get a basic volatility trading algo working using options. For some reason I have hit an error that I cannot resolve. I have attached a backtest below, any help would be appreciated.
For some reason I was not able to attach a backtest, so the code for the algo is below:
from datetime import timedelta
class OptionsAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 01, 01)
self.SetEndDate(2017, 01, 8)
self.SetCash(100000)
equity = self.AddEquity("VXX", Resolution.Minute)
self.symbol = equity.Symbol
self.last_slice = None
# warm up for 121 days
self.SetWarmUp(timedelta(121))
# filter the option contracts we will trade
option = self.AddOption(str(self.symbol));
option.SetFilter(-2, +20, TimeSpan.FromDays(5),TimeSpan.FromDays(9));
# run rebalance every day before market close
self.Schedule.On(self.DateRules.Every([DayOfWeek.Friday]), self.TimeRules.BeforeMarketClose(str(self.symbol), 2), Action(self.rebalance))
def OnData(self,slice):
if self.IsWarmingUp: return
# if self.Time.weekday() != 4 or self.Time.hour != 12 or self.Time.minute != 58: return
self.last_slice = slice
def rebalance(self):
self.Debug("rebalance being called")
# sell everything first
self.Liquidate()
for i in self.last_slice.OptionChains:
chain = i.Value
# differentiate the call and put options
calls = [x for x in chain if x.Right == 0]
puts = [x for x in chain if x.Right == 1]
# get all OTM calls
otm_calls = [x for x in calls if x.UnderlyingLastPrice - x.Strike < 0]
# get closest ATM call
atm_call = sorted(otm_calls, key = lambda x: x.UnderlyingLastPrice - x.Strike)[0]
# get OTM call at least 10% OTM
fotm_calls = [x for x in otm_calls if float(x.UnderlyingLastPrice)*float(1.15) - float(x.Strike) < 0]
fotm_call = sorted(fotm_calls, key = lambda x: x.UnderlyingLastPrice - x.Strike)[0]
# get all OTM puts
otm_puts = [x for x in puts if x.UnderlyingLastPrice - x.Strike > 0]
# get 2nd closest ATM put
put = sorted(otm_puts, key = lambda x: x.Strike, reverse = True)[1]
# sell 80% ATM calls
atm_call_amount = float(self.Portfolio.TotalPortfolioValue) * -0.8
atm_call_contracts = atm_call_amount / (float(atm_call.Underlying.Price)*100.0)
self.MarketOrder(atm_call.Symbol, atm_call_contracts)
# buy 80% Far out of the money calls
fotm_call_amount = float(self.Portfolio.TotalPortfolioValue) * 0.8
fotm_call_contracts = fotm_call_amount / (float(fotm_call.Underlying.Price)*100.0)
self.MarketOrder(fotm_call.Symbol, fotm_call_contracts)
# buy 32% puts
put_amount = float(self.Portfolio.TotalPortfolioValue) * 0.32
put_contracts = put_amount / (float(put.Underlying.Price)*100.0)
self.MarketOrder(put.Symbol, put_contracts)
Jing Wu
Hi mohammad, there might be no contracts satisfying your filtering conditions at some time as the SetFilter function is strict with the maturity and the strike. You can add the weekly options contracts in SetFilter method to include more contracts
option.SetFilter(self.UniverseFunc)
def UniverseFunc(self, universe):
return universe.IncludeWeeklys().Strikes(-2, 20).Expiration(TimeSpan.FromDays(5),TimeSpan.FromDays(9))
Secondly, 'OptionContract' object has no attribute 'Underlying' , it is an attribute of OptionChain. For 'OptionContract' you can use 'UnderlyingLastPrice' to get the underlying price.
Mohammad Forouzani
Thanks Jing, I really appreciate your help (as you can tell my code was already based on your option tutorial :)
I was not aware that the weeklys were not included by default, but now I know.
Thanks again!
Jing Wu
You could also try the OptionChainProvider() which includes the weekly contract data, the backtest is much faster using OptionChainProvider() than the SetFilter()
Patrick.chan
Thanks Jing for the helpful example! Could you also provide a Research jupyter notebook example of using OptionChainProvider() to get weekly options contracts?
Thanks!
Mohammad Forouzani
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!