Overall Statistics |
Total Trades 0 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 $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset |
# 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 * ### <summary> ### This example demonstrates how to add and trade SPX index weekly options ### </summary> ### <meta name="tag" content="using data" /> ### <meta name="tag" content="options" /> ### <meta name="tag" content="indexes" /> class BasicTemplateSPXWeeklyIndexOptionsAlgorithm(QCAlgorithm): def Initialize(self): # self.SetStartDate(2020, 1, 17) #Set Start Date # self.SetEndDate(2020, 1, 18) #Set End Date # self.SetCash(50000) #Set Strategy Cash # equity = self.AddEquity("GOOG", Resolution.Minute) # Add the underlying stock: Google # option = self.AddOption("GOOG", Resolution.Minute) # Add the option corresponding to underlying stock # self.symbol = option.Symbol # option.SetFilter(-10, 10, timedelta(0), timedelta(days = 1)) self.SetStartDate(2022, 9, 1) self.SetEndDate(2022, 9, 30) self.SetCash(1000000) self.underlying = self.AddIndex("SPX", Resolution.Minute) self.underlying.SetDataNormalizationMode(DataNormalizationMode.Raw) self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) self.spx = self.underlying.Symbol # regular option SPX contracts self.spxOptions = self.AddIndexOption(self.spx, Resolution.Minute) self.spxOptions.SetFilter(lambda u: (u.Strikes(-1, -1).Expiration(0, 0))) # weekly option SPX contracts spxw = self.AddIndexOption(self.spx, "SPXW", Resolution.Minute) # set our strike/expiry filter for this option chain spxw.SetFilter(lambda u: (u.Strikes(-1, 1) # single week ahead since there are many SPXW contracts and we want to preserve performance .Expiration(0, 1) .IncludeWeeklys())) self.spxw_option = spxw.Symbol self.SetSecurityInitializer(self.securityInitializer) self.currentDate = None # Called every time a security (Option or Equity/Index) is initialized def securityInitializer(self, security): security.SetDataNormalizationMode(DataNormalizationMode.Raw) security.SetMarketPrice(self.GetLastKnownPrice(security)) if security.Type in [SecurityType.Option, SecurityType.IndexOption]: security.SetFillModel(BetaFillModel(self)) #security.SetFillModel(MidPriceFillModel(self)) security.SetFeeModel(TastyWorksFeeModel()) def OnData(self,slice): # if it's the same day then stop. if self.Time.date().strftime("%Y-%m-%d") == self.currentDate: return for i in slice.OptionChains: if i.Key != self.spxw_option: self.Log(f"\n({self.Time.strftime('%A')}) The `{i.Key}` does not match SPXW so we skip.") continue optionchain = i.Value df = pd.DataFrame([[x.Expiry] for x in optionchain], index=[x.Symbol.Value for x in optionchain], columns=['expiry']) if df.shape[0] > 0: self.Log(f"\n{df.to_string()}") # we found data so we consider this day complete self.currentDate = self.Time.date().strftime("%Y-%m-%d") else: self.Log(f"\n({self.Time.strftime('%A')}) No options on this date") # self.Quit() # if self.Portfolio.Invested: return # chain = slice.OptionChains.GetValue(self.spxw_option) # if chain is None: # return # # we sort the contracts to find at the money (ATM) contract with closest expiration # contracts = sorted(sorted(sorted(chain, \ # key = lambda x: x.Expiry), \ # key = lambda x: abs((chain.Underlying.Price + 20) - x.Strike)), \ # key = lambda x: x.Right, reverse=True) # # select just calls # contracts = [x for x in contracts if x.Right == OptionRight.Call] # # if found, buy until it expires # if len(contracts) == 0: return # symbol = contracts[0].Symbol # self.MarketOrder(symbol, -1) def OnOrderEvent(self, orderEvent): self.Debug(str(orderEvent)) class TastyWorksFeeModel: def GetOrderFee(self, parameters): optionFee = min(10, parameters.Order.AbsoluteQuantity * 0.5) transactionFee = parameters.Order.AbsoluteQuantity * 0.14 return OrderFee(CashAmount(optionFee + transactionFee, 'USD')) # Custom Fill model based on Beta distribution: # - Orders are filled based on a Beta distribution skewed towards the mid-price with Sigma = bidAskSpread/6 (-> 99% fills within the bid-ask spread) class BetaFillModel(ImmediateFillModel): # Initialize Random Number generator with a fixed seed (for replicability) random = np.random.RandomState(1234) def __init__(self, context): self.context = context def MarketFill(self, asset, order): # Start the timer # self.context.executionTimer.start() # Get the random number generator random = BetaFillModel.random # Compute the Bid-Ask spread bidAskSpread = abs(asset.AskPrice - asset.BidPrice) # Compute the Mid-Price midPrice = 0.5*(asset.AskPrice + asset.BidPrice) # Call the parent method fill = super().MarketFill(asset, order) # Setting the parameters of the Beta distribution: # - The shape parameters (alpha and beta) are chosen such that the fill is "reasonably close" to the mid-price about 96% of the times # - How close -> The fill price is within 15% of half the bid-Ask spread if order.Direction == OrderDirection.Sell: # Beta distribution in the range [Bid-Price, Mid-Price], skewed towards the Mid-Price # - Fill price is within the range [Mid-Price - 0.15*bidAskSpread/2, Mid-Price] with about 96% probability offset = asset.BidPrice alpha = 20 beta = 1 else: # Beta distribution in the range [Mid-Price, Ask-Price], skewed towards the Mid-Price # - Fill price is within the range [Mid-Price, Mid-Price + 0.15*bidAskSpread/2] with about 96% probability offset = midPrice alpha = 1 beta = 20 # Range (width) of the Beta distribution range = bidAskSpread/2.0 # Compute the new fillPrice (centered around the midPrice) fillPrice = round(offset + range * random.beta(alpha, beta), 2) # Update the FillPrice attribute fill.FillPrice = fillPrice # Stop the timer # self.context.executionTimer.stop() # Return the fill return fill