Overall Statistics |
Total Trades 90 Average Win 1.08% Average Loss -0.74% Compounding Annual Return -1.758% Drawdown 3.500% Expectancy -0.063 Net Profit -1.474% Sharpe Ratio -0.769 Sortino Ratio -0.637 Probabilistic Sharpe Ratio 4.525% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 1.47 Alpha -0.015 Beta -0.022 Annual Standard Deviation 0.023 Annual Variance 0.001 Information Ratio -0.473 Tracking Error 0.31 Treynor Ratio 0.801 Total Fees $48.00 Estimated Strategy Capacity $490000.00 Lowest Capacity Asset AAPL XJ43QAQNONOM|AAPL R735QTJ8XC9X Portfolio Turnover 3.74% |
#region imports from AlgorithmImports import * #endregion class VirtualYellowGiraffe(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 17) self.SetEndDate(2020, 11, 17) self.SetCash(100000) self.equity = self.AddEquity("AAPL", 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) and ((data.Time.hour == 11) and (data.Time.minute == 00)): self.BuyCallSpread() ## Use Delta to select a put contract to sell ## ================================================================== def BuyCallSpread(self): ## Sell a 20 delta put expiring in 2 weeks (14 days) callContractTuple = self.SelectCallSpreadLegsByDelta(self.equity.Symbol, .70, 'Long', 12, OptionRight.Call) #putContract = self.SelectContractByDelta(self.equity.Symbol, .30, 10, OptionRight.Put) ## construct an order message -- good for debugging and order rrecords if callContractTuple is not None: orderMessage = f"Stock @ ${self.CurrentSlice[self.equity.Symbol].Close} |" + \ f"Buying Call Spread {callContractTuple[0].Strike} {callContractTuple[1].Strike} "+ \ f"For limit price of {round(callContractTuple[0].AskPrice - callContractTuple[1].AskPrice,2)} " + \ f"Underlying price {callContractTuple[0].UnderlyingLastPrice}" self.Debug(f"{self.Time} {orderMessage}") #self.Order(callContractTuple[0].Symbol, -1, False, orderMessage ) # Create order legs legs = [] quantities = [1, -1] for i, contract in enumerate(callContractTuple): legs.append(Leg.Create(contract.Symbol, quantities[i])) # Calculate limit price limit_price = callContractTuple[0].AskPrice - callContractTuple[1].AskPrice # Place order self.ComboLimitOrder(legs, 1, limit_price) ## Get an options contract that matches the specified criteria: ## Underlying symbol, delta, days till expiration, Option right (put or call) ## ============================================================================ # ============================================================================ def OptionsFilterFunction(self, optionsContractsChain): strikeCount = 1 # no of strikes around underyling price => for universe selection minExpiryDTE = 10 # min num of days to expiration => for uni selection maxExpiryDTE = 15 # max num of days to expiration => for uni selection return optionsContractsChain.IncludeWeeklys()\ .Strikes(-strikeCount, strikeCount)\ .Expiration(timedelta(minExpiryDTE), timedelta(maxExpiryDTE)) def SelectCallSpreadLegsByDelta(self, symbolArg, buystrikeDeltaArg, LongOrShortSpreadArg, expiryDTE, optionRightArg= OptionRight.Call): canonicalSymbol = self.AddOption(symbolArg) if not canonicalSymbol.Symbol in self.CurrentSlice.OptionChains: # self.Log('No option chains available at this time') return 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 applicableContractList = sorted(contractsMatchingExpiryDTE, key=lambda x: abs(abs(x.Greeks.Delta)-buystrikeDeltaArg), reverse=False) if len(applicableContractList) >= 2: #Got Enough contracts closestBuyContract = applicableContractList[0] pairedSellContract = applicableContractList[1] else: return return [pairedSellContract, closestBuyContract] ##================================================================================== ##================================================================================== def SelectContractByDelta(self, symbolArg, strikeDeltaArg, expiryDTE, optionRightArg= OptionRight.Call): canonicalSymbol = self.AddOption(symbolArg) #if self.CurrentSlice.OptionChains[canonicalSymbol.Symbol] 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 SellAnOTMPut(self): ## Sell a 20 delta put expiring in 2 weeks (14 days) putContract = self.SelectCallSpreadLegsByDelta(self.equity.Symbol, .30, 5, 'Long', 10, OptionRight.Put) #putContract = self.SelectContractByDelta(self.equity.Symbol, .30, 10, OptionRight.Put) ## construct an order message -- good for debugging and order rrecords if putContract is not None: 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 )