| Overall Statistics |
|
Total Trades 2 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $2.00 Estimated Strategy Capacity $3900000.00 Lowest Capacity Asset SPY 327EYLVZFOQJQ|SPY R735QTJ8XC9X Portfolio Turnover 0.12% |
#region imports
from AlgorithmImports import *
#endregion
class VirtualYellowGiraffe(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 5, 8)
self.SetEndDate(2023, 5, 9)
self.SetCash(100000)
self.equity = self.AddEquity("SPY", Resolution.Hour)
self.symbol = self.equity.Symbol
self.InitOptionsAndGreeks(self.equity)
#self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 30), self.daily_check)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose('SPY', 60), self.daily_check)
## Options to target
self.targetDELTA = 0.5
#self.targetExpiryDTE = 45
self.targetExpiryDTE = 0
## Initialize Options settings, chain filters, pricing models, etc
## ====================================================================
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 leasr 30 days
self.SetWarmup(30, Resolution.Daily)
## 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
theOptionSubscription = self.AddOption(theEquity.Symbol)
## 5. set the pricing model, to calculate Greeks and volatility
theOptionSubscription.PriceModel = OptionPriceModels.CrankNicolsonFD() # both European & American, automatically
## 6. Set the function to filter out strikes and expiry dates from the option chain
theOptionSubscription.SetFilter(self.OptionsFilterFunction)
def Get_Current_Delta(self, symbol):
for kvp in self.CurrentSlice.OptionChains:
chain = kvp.Value
contracts = [i for i in chain if i.Symbol==symbol]
if contracts[0]:
return contracts[0].Greeks.Delta
return None
def daily_check(self):
## If we're done warming up, and not invested, Sell put/call
if (not self.IsWarmingUp) and (not self.Portfolio.Invested):
self.put = self.SellAnOTMPut(self.targetDELTA, self.targetExpiryDTE)
self.call = self.SellAnOTMCall(self.targetDELTA, self.targetExpiryDTE)
## Sell an OTM Put Option.
## Use Delta to select a put contract to sell
## ==================================================================
def SellAnOTMPut(self, target_DELTA, target_DTE):
## Sell a put with delta and expiry days......???not clear if days are calendar days or trading days
putContract = self.SelectContractByDelta(self.equity.Symbol, target_DELTA, target_DTE, OptionRight.Put)
## construct an order message -- good for debugging and order records
orderMessage = f"Stock @ ${self.CurrentSlice[self.equity.Symbol].Close} |" + \
f"STO {putContract.Symbol} "+ \
f"({round(putContract.Greeks.Delta,2)} Delta)"
self.Debug(f"{self.Time} {orderMessage}")
self.Order(putContract.Symbol, -1, False, orderMessage)
return putContract
## Sell an OTM Call Option.
## Use Delta to select a call contract to sell
## ==================================================================
def SellAnOTMCall(self, target_DELTA, target_DTE):
## Sell a call with delta and expiry days......???not clear if days are calendar days or trading days
callContract = self.SelectContractByDelta(self.equity.Symbol, target_DELTA, target_DTE, OptionRight.Call)
## construct an order message -- good for debugging and order records
orderMessage = f"Stock @ ${self.CurrentSlice[self.equity.Symbol].Close} |" + \
f"STO {callContract.Symbol} "+ \
f"({round(callContract.Greeks.Delta,2)} Delta)"
self.Debug(f"{self.Time} {orderMessage}")
self.Order(callContract.Symbol, -1, False, orderMessage)
return callContract
## Get an options contract that matches the specified criteria:
## Underlying symbol, delta, days till expiration, Option right (put or call)
## ============================================================================
def SelectContractByDelta(self, symbolArg, strikeDeltaArg, expiryDTE, optionRightArg= OptionRight.Call):
canonicalSymbol = self.AddOption(symbolArg)
theOptionChain = self.CurrentSlice.OptionChains[canonicalSymbol.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
## 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
min_Expiry_DTE = self.targetExpiryDTE # min num of days to expiration => for uni selection
max_Expiry_DTE = self.targetExpiryDTE+45 # max num of days to expiration => for uni selection
## Select options including both monthly and weekly
return optionsContractsChain.IncludeWeeklys()\
.Strikes(-strikeCount, strikeCount)\
.Expiration(timedelta(min_Expiry_DTE), timedelta(max_Expiry_DTE))
## Select options including only monthly
#return optionsContractsChain.StandardsOnly()\
# .Strikes(-strikeCount, strikeCount)\
# .Expiration(timedelta(min_Expiry_DTE), timedelta(max_Expiry_DTE))
#region imports
from AlgorithmImports import *
#endregion
class VirtualYellowGiraffe(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 4, 17)
self.SetEndDate(2021, 2, 17)
self.SetCash(100000)
self.equity = self.AddEquity("SPY", Resolution.Minute)
self.InitOptionsAndGreeks(self.equity)
## Initialize Options settings, chain filters, pricing models, etc
## ====================================================================
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 leasr 30 days
self.SetWarmup(30, Resolution.Daily)
## 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
theOptionSubscription = self.AddOption(theEquity.Symbol)
## 5. set the pricing model, to calculate Greeks and volatility
theOptionSubscription.PriceModel = OptionPriceModels.CrankNicolsonFD() # both European & American, automatically
## 6. Set the function to filter out strikes and expiry dates from the option chain
theOptionSubscription.SetFilter(self.OptionsFilterFunction)
def OnData(self, data):
## If we're done warming up, and not invested, Sell a put.
if (not self.IsWarmingUp) and (not self.Portfolio.Invested):
self.SellAnOTMPut()
## Sell an OTM Put Option.
## Use Delta to select a put contract to sell
## ==================================================================
def SellAnOTMPut(self):
## Sell a 20 delta put expiring in 2 weeks (14 days)
putContract = self.SelectContractByDelta(self.equity.Symbol, .30, 10, OptionRight.Put)
## 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.Delta,2)} Delta)"
self.Debug(f"{self.Time} {orderMessage}")
self.Order(putContract.Symbol, -1, False, orderMessage )
## Get an options contract that matches the specified criteria:
## Underlying symbol, delta, days till expiration, Option right (put or call)
## ============================================================================
def SelectContractByDelta(self, symbolArg, strikeDeltaArg, expiryDTE, optionRightArg= OptionRight.Call):
canonicalSymbol = self.AddOption(symbolArg)
theOptionChain = self.CurrentSlice.OptionChains[canonicalSymbol.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
## The options filter function.
## Filter the options chain so we only have relevant strikes & expiration dates.
## =============================================================================
def OptionsFilterFunction(self, optionsContractsChain):
strikeCount = 100 # no of strikes around underyling price => for universe selection
minExpiryDTE = 10 # min num of days to expiration => for uni selection
maxExpiryDTE = 40 # max num of days to expiration => for uni selection
return optionsContractsChain.IncludeWeeklys()\
.Strikes(-strikeCount, strikeCount)\
.Expiration(timedelta(minExpiryDTE), timedelta(maxExpiryDTE))