| Overall Statistics |
|
Total Orders 2 Average Win 0% Average Loss 0% Compounding Annual Return 4.844% Drawdown 0.000% Expectancy 0 Start Equity 100000 End Equity 100060.5 Net Profit 0.060% Sharpe Ratio 4.448 Sortino Ratio 0 Probabilistic Sharpe Ratio 75.127% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.015 Beta 0.021 Annual Standard Deviation 0.003 Annual Variance 0 Information Ratio 0.251 Tracking Error 0.084 Treynor Ratio 0.671 Total Fees $2.00 Estimated Strategy Capacity $73000.00 Lowest Capacity Asset SPY WMDHMEFALEG6|SPY R735QTJ8XC9X Portfolio Turnover 0.23% |
# region imports
from AlgorithmImports import *
# endregion
"""
https://www.quantconnect.com/forum/discussion/3245/using-option-greeks-to-select-option-contracts-to-trade
if you need Greeks:
A) Filter and B) AddOption
more efficient than
C) OptionChainProvider and D) AddOptionContract
"""
from QuantConnect.Securities.Option import OptionPriceModels
#from datetime import timedelta
import datetime
import decimal as d
class DeltaHedgedStraddleAlgo(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 4, 17)
self.SetEndDate(2017, 4, 22)
self.SetCash(100000)
self.equity = self.AddEquity("SPY", Resolution.HOUR)
self.symbol = self.equity.Symbol
self._assignedOption = False
self.CloseOnDTE = timedelta(days=21)
self.SellGutsDelta = 0.5
self.OpenDTE = 90
self.InitOptionsAndGreeks(self.equity)
# -----------------------------------------------------------------------------
# scheduled functions
# -----------------------------------------------------------------------------
""" self.Schedule.On(self.DateRules.EveryDay(self.symbol),
self.TimeRules.BeforeMarketClose(self.symbol, 10),
Action(self.close_options)) """
def InitOptionsAndGreeks(self, theEquity ):
## 1. Specify the data normalization mode (must be 'Raw' for options)
theEquity.SetDataNormalizationMode(DataNormalizationMode.Raw)
## 2. Set Warmup period of at least 30 days
self.SetWarmup(30, Resolution.HOUR)
## 3. Set the security initializer to call SetMarketPrice
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
## 4. Subscribe to the option feed for the symbol
self.theOptionSubscription = self.AddOption(theEquity.Symbol)
## 5. set the pricing model, to calculate Greeks and volatility
self.theOptionSubscription.PriceModel = OptionPriceModels.CrankNicolsonFD() # both European & American, automatically
## 6. Set the function to filter out strikes and expiry dates from the option chain
self.theOptionSubscription.SetFilter(self.OptionsFilterFunction)
def on_securities_changed(self, changes):
for security in [x for x in changes.added_securities if x.type == SecurityType.OPTION]:
if security.cache.properties.contains_key('iv'):
continue
symbol = security.symbol
right = OptionRight.CALL if symbol.id.option_right == OptionRight.PUT else OptionRight.PUT
mirror_symbol = Symbol.create_option(symbol.id.underlying.symbol, symbol.id.market, symbol.id.option_style, right, symbol.id.strike_price, symbol.id.date)
security.iv = self.iv(symbol, mirror_symbol, resolution=Resolution.HOUR)
security.d = self.d(symbol, mirror_symbol, resolution=Resolution.HOUR)
security.g = self.g(symbol, mirror_symbol, resolution=Resolution.HOUR)
security.v = self.v(symbol, mirror_symbol, resolution=Resolution.HOUR)
security.r = self.r(symbol, mirror_symbol, resolution=Resolution.HOUR)
security.t = self.t(symbol, mirror_symbol, resolution=Resolution.HOUR)
## Initialize Options settings, chain filters, pricing models, etc
## ====================================================================
def OnData(self, data):
if self.IsWarmingUp: return
# 1. If we're done warming up, and not invested, Sell a put and a call.
if (not self.Portfolio.Invested) and self.HourMinuteIs(11, 00):
if data.Bars.ContainsKey(self.symbol):
self.SellAnOTMPut(data)
self.Debug("invested")
## ==================================================================
def SellAnOTMPut(self,data):
## Sell a 50 delta put expiring in 30 days
putContract = self.SelectContractByDelta(self.equity.Symbol,data, self.SellGutsDelta, self.OpenDTE, OptionRight.Put)
callContract = self.SelectContractByDelta(self.equity.Symbol,data, self.SellGutsDelta, self.OpenDTE, OptionRight.Call)
## construct an order message -- good for debugging and order rrecords
orderMessage = f"Stock @ ${self.CurrentSlice[self.equity.Symbol].Close} |" + \
f"Sell {putContract.Symbol} "+ \
f"({round(putContract.Greeks.Gamma,2)} Gamma)"+ \
f"({round(putContract.Greeks.Delta,2)} Delta)"+ \
f"Sell {callContract.Symbol} "+ \
f"({round(callContract.Greeks.Gamma,2)} Gamma)"+ \
f"({round(callContract.Greeks.Delta,2)} Delta)"
self.Debug(f"{self.Time} {orderMessage}")
self.Order(putContract.Symbol, -1, False, orderMessage )
self.Order(callContract.Symbol, -1, False, orderMessage )
self.GetGreeks(data,callContract.Symbol)
self.debug("SPY gamma {}" .format(self.gamma))
self.debug("SPY delta {}" .format(self.delta))
## Get an options contract that matches the specified criteria:
## Underlying symbol, delta, days till expiration, Option right (put or call)
## ============================================================================
def SelectContractByDelta(self, symbolArg,data, strikeDeltaArg, expiryDTE, optionRightArg= OptionRight.Call):
#canonicalSymbol = self.AddOption(symbolArg)
theOptionChain = data.OptionChains[self.theOptionSubscription.Symbol]
theExpiryDate = self.Time + timedelta(days=expiryDTE)
## Filter the Call/Put options contracts
filteredContracts = [x for x in theOptionChain if x.Right == optionRightArg]
## Sort the contracts according to their closeness to our desired expiry
contractsSortedByExpiration = sorted(filteredContracts, key=lambda p: abs(p.Expiry - theExpiryDate), reverse=False)
closestExpirationDate = contractsSortedByExpiration[0].Expiry
## Get all contracts for selected expiration
contractsMatchingExpiryDTE = [contract for contract in contractsSortedByExpiration if contract.Expiry == closestExpirationDate]
## Get the contract with the contract with the closest delta
closestContract = min(contractsMatchingExpiryDTE, key=lambda x: abs(abs(x.Greeks.Delta)-strikeDeltaArg))
return closestContract
def GetGreeks(self,data,symb):
chain = data.option_chains.get(self.theOptionSubscription.symbol)
if chain:
option = self.securities[symb]
self.Debug("cont symb {}" .format(option.symbol))
iv = option.iv.current.value
self.delta = option.d.current.value
self.gamma = option.g.current.value
vega = option.v.current.value
rho = option.r.current.value
theta = option.t.current.value
## The options filter function.
## Filter the options chain so we only have relevant strikes & expiration dates.
## =============================================================================
def OptionsFilterFunction(self, optionsContractsChain):
strikeCount = 10 # no of strikes around underyling price => for universe selection
minExpiryDTE = 0 # min num of days to expiration => for uni selection
maxExpiryDTE = self.OpenDTE + 30 # max num of days to expiration => for uni selection
return optionsContractsChain.IncludeWeeklys()\
.Strikes(-strikeCount, strikeCount)\
.Expiration(timedelta(minExpiryDTE), timedelta(maxExpiryDTE))
def HourMinuteIs(self, hour, minute):
return self.Time.hour == hour and self.Time.minute == minute
def OnAssignmentOrderEvent(self, assignmentEvent):
self.Log(str(assignmentEvent))
self._assignedOption = True