Overall Statistics |
Total Orders 6 Average Win 0.14% Average Loss 0% Compounding Annual Return 17.985% Drawdown 0.200% Expectancy 0 Start Equity 100000 End Equity 100348 Net Profit 0.348% Sharpe Ratio 12.248 Sortino Ratio 0 Probabilistic Sharpe Ratio 100.000% Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha 0.081 Beta 0.004 Annual Standard Deviation 0.006 Annual Variance 0 Information Ratio 2.933 Tracking Error 0.123 Treynor Ratio 21.05 Total Fees $27.00 Estimated Strategy Capacity $0 Lowest Capacity Asset SPY YI9D6FI4Z6YU|SPY R735QTJ8XC9X Portfolio Turnover 0.10% |
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. # Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from AlgorithmImports import * from QuantConnect.Algorithm.CSharp import * ### <summary> ### Regression algorithm asserting we can specify a custom option exercise model ### </summary> class CustomOptionExerciseModelRegressionAlgorithm(OptionAssignmentRegressionAlgorithm): def Initialize(self): self.SetSecurityInitializer(self.CustomSecurityInitializer) super().Initialize() def CustomSecurityInitializer(self, security): if Extensions.IsOption(security.Symbol.SecurityType): security.SetOptionExerciseModel(CustomExerciseModel()) def OnData(self, data): super().OnData(data) class CustomExerciseModel(DefaultExerciseModel): def OptionExercise(self, option: Option, order: OptionExerciseOrder): order_event = OrderEvent( order.Id, option.Symbol, Extensions.ConvertToUtc(option.LocalTime, option.Exchange.TimeZone), OrderStatus.Filled, Extensions.GetOrderDirection(order.Quantity), 0.0, order.Quantity, OrderFee.Zero, "Tag" ) order_event.IsAssignment = False return [ order_event ]
#region imports from AlgorithmImports import * #endregion import pandas as pd import random from AlgorithmImports import * from QuantConnect.Algorithm.CSharp import * class CustomExerciseModel(DefaultExerciseModel): def OptionExercise(self, option: Option, order: OptionExerciseOrder): order_event = OrderEvent( order.Id, option.Symbol, Extensions.ConvertToUtc(option.LocalTime, option.Exchange.TimeZone), OrderStatus.Filled, Extensions.GetOrderDirection(order.Quantity), 0.0, order.Quantity, OrderFee.Zero, "Tag" ) order_event.IsAssignment = False return [ order_event ] class VirtualYellowGiraffe(QCAlgorithm): def Initialize(self): self.SetStartDate(2024, 4, 23) self.SetEndDate(2024, 4, 30) self.SetCash(100000) self.equity = self.AddEquity("SPY", Resolution.Minute) self.symbol = self.equity.Symbol self.InitOptionsAndGreeks(self.equity) ## Margin model #self.Portfolio.MarginCallModel = DefaultMarginCallModel(self.Portfolio, self.DefaultOrderProperties) self.Portfolio.MarginCallModel = MarginCallModel.Null self.SetSecurityInitializer(self.CustomSecurityInitializer) ## Options to target self.targetDELTA_STO = 0.03 self.targetExpiryDTE_STO = 14 self.minExpiryDTE = 7 self.contracts = 10 self.call_trade = False self.put_trade = False def CustomSecurityInitializer(self, security): if Extensions.IsOption(security.Symbol.SecurityType): security.SetOptionExerciseModel(CustomExerciseModel()) ## 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 10 days self.SetWarmup(15, 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, not invested if (not self.IsWarmingUp) and (not self.Portfolio.Invested) and (data.Bars.ContainsKey(self.symbol)): if (self.call_trade==False) and (self.put_trade==False) and (self.Time.hour >= 15): DTE_STO = self.targetExpiryDTE_STO ## Get contracts at delta=targetDELTA and DTE=targetExpiryDTE self.call_STO = self.SelectContract(self.equity.Symbol, self.targetDELTA_STO, 'delta', DTE_STO, OptionRight.Call) self.put_STO = self.SelectContract(self.equity.Symbol, self.targetDELTA_STO, 'delta', DTE_STO, OptionRight.Put) self.order(self.call_STO, self.contracts, 'STO') self.order(self.put_STO, self.contracts, 'STO') self.call_trade = True self.put_trade = True elif (not self.IsWarmingUp) and (self.Portfolio.Invested) and (data.Bars.ContainsKey(self.symbol)): if (self.call_trade==True) and (self.put_trade==True): ## Liquidate if expiry of option is close if ((self.call_STO.Expiry - self.Time).days + 1 <= self.minExpiryDTE) and (self.Time.hour >= 14): self.order(self.call_STO, self.contracts, 'BTC') self.order(self.put_STO, self.contracts, 'BTC') self.call_trade = False self.put_trade = False ## Underlying symbol, delta, days till expiration, Option right (put or call) ## ============================================================================ def order(self, symbolArg, contracts, ordertype= 'STO'): #self.current_delta = self.Get_Current_Delta(symbolArg.Symbol) ## construct an order message -- good for debugging and order records if ordertype == 'STO': orderMessage = f"Stock @ ${self.CurrentSlice[self.equity.Symbol].Close} |" + \ f"STO {symbolArg.Symbol} " elif ordertype == 'BTC': orderMessage = f"Stock @ ${self.CurrentSlice[self.equity.Symbol].Close} |" + \ f"BTC {symbolArg.Symbol} " #self.Debug(f"{self.Time} {orderMessage}") if ordertype == 'STO': self.Order(symbolArg.Symbol, -contracts, False, orderMessage) elif ordertype == 'BTC': self.Order(symbolArg.Symbol, contracts, False, orderMessage) ## Get an options contract that matches the specified criteria: ## Underlying symbol, delta, days till expiration, Option right (put or call) ## ============================================================================ def SelectContract(self, symbolArg, strikeArg, strikeArgName, 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+timedelta(hours=16)) - 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 if strikeArgName == 'delta': closestContract = min(contractsMatchingExpiryDTE, key=lambda x: abs(abs(x.Greeks.Delta)-strikeArg)) return closestContract ## The options filter function. ## Filter the options chain so we only have relevant strikes & expiration dates. ## ============================================================================= def OptionsFilterFunction(self, optionsContractsChain): strikeCount = 500 # no of strikes around underyling price => for universe selection min_Expiry_DTE = self.minExpiryDTE # min num of days to expiration => for uni selection max_Expiry_DTE = self.targetExpiryDTE_STO # 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))
#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))