Overall Statistics |
Total Trades 72 Average Win 1.05% Average Loss -0.45% Compounding Annual Return -9.517% Drawdown 19.700% Expectancy 0.522 Net Profit -3.164% Sharpe Ratio -0.243 Probabilistic Sharpe Ratio 22.098% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 2.33 Alpha -0.107 Beta 0.291 Annual Standard Deviation 0.254 Annual Variance 0.065 Information Ratio -0.688 Tracking Error 0.317 Treynor Ratio -0.212 Total Fees $68.50 |
from QuantConnect.Data.Custom.CBOE import CBOE from math import sqrt,floor class ModifiedCollar(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 2) # Set Start Date # self.SetEndDate(2020, 4, 1) self.SetCash(100000) # Set Strategy Cash self.SetBenchmark("SWAN") self.stock = self.AddEquity("SPY", Resolution.Minute) self.stockSymbol = self.stock.Symbol self.stock.SetDataNormalizationMode(DataNormalizationMode.Raw) self.vixSymbol = self.AddData(CBOE, "VIX").Symbol self.vix = 0 self.simpleMoving = SimpleMovingAverage(60) self.vixSMA = self.SMA(self.vixSymbol, 60, Resolution.Daily) self.simpleMoving.Updated += self.OnVixSMA self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 10), self.LogData) self.length = None self.callContract = self.putContract = None self.maxPutPrice = self.callPrice = 0 self.callTicket = self.putTicket = None self.optionPercent = .025 self.optionPercentOfStock = self.optionPercent / (1 - self.optionPercent) self.putsPerHundred, self.protectionNumber = 2, 1 self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) self.SetWarmUp(timedelta(days=60)) def OnVixSMA(self, sender, updated): return def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Argumen data: Slice object keyed by symbol containing the stock data ''' self.Log("hello") for volatility in data.Get(CBOE).Values: self.vix = volatility.Value self.simpleMoving.Update(self.Time, self.vix) if self.IsWarmingUp: return if (not self.Portfolio[self.stockSymbol].Invested): number = self.CalculateOrderQuantity(self.stockSymbol, 1-self.optionPercent) self.protectionNumber = number // 100 number = self.protectionNumber * 100 self.ticket = self.MarketOrder(self.stockSymbol, number) # if (self.Portfolio.Cash * (1-self.optionPercent) /100 // self.stock.Price >= 1): # number = (self.Portfolio.Cash * (1-self.optionPercent) / self.stock.Price // 100) # self.protectionNumber += number # self.MarketOrder(self.stockSymbol, number) if (self.putContract is None): self.putContract = self.GetPutContract() if (self.callContract is None): self.callContract = self.GetCallContract() putBid = self.Securities[self.putContract.Symbol].BidPrice putAsk = self.Securities[self.putContract.Symbol].AskPrice callBid = self.Securities[self.callContract.Symbol].BidPrice callAsk = self.Securities[self.callContract.Symbol].AskPrice if (self.putContract is not None and not self.Portfolio[self.putContract.Symbol].Invested): self.putTicket = self.MarketOrder(self.putContract.Symbol, self.protectionNumber * self.putsPerHundred) self.Debug("bought") self.maxPutPrice = self.putTicket.AverageFillPrice # self.Debug("bought price: {}".format(self.maxPutPrice)) if (self.callContract is not None and not self.Portfolio[self.callContract.Symbol].Invested): self.callTicket = self.MarketOrder(self.callContract.Symbol, -self.protectionNumber) self.Debug(self.callTicket) self.callPrice = self.callTicket.AverageFillPrice if (callBid > self.callPrice * 2): self.Liquidate(self.callContract.Symbol) self.callContract = None if (putBid > self.maxPutPrice): self.maxPutPrice = putBid if (self.maxPutPrice * .7 >= putAsk): self.putTicket = self.MarketOrder(self.putContract.Symbol, -self.protectionNumber * self.putsPerHundred) self.Debug("sold") # self.Debug("sold price: {}".format(self.putTicket.AverageFillPrice)) self.RemoveSecurity(self.putContract.Symbol) self.maxPutPrice = 0 self.putContract = None def GetPutContract(self): max_price = self.Portfolio.TotalPortfolioValue * self.optionPercent/ 100 / (self.protectionNumber * self.putsPerHundred) contracts = self.OptionChainProvider.GetOptionContractList(self.stock.Symbol, self.Time) x = 1 contracts = [x for x in contracts if 330 <= (x.ID.Date - self.Time).days <= 400] x = 2 contracts = [x for x in contracts if x.ID.StrikePrice < self.stock.Price] contracts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put] contracts = sorted(sorted(contracts, key = lambda x: x.ID.StrikePrice, reverse = True), key = lambda x: abs(x.ID.Date - self.Time)) for i in contracts: contract = self.AddOptionContract(i) if contract.AskPrice < max_price: return contract # else: # self.RemoveSecurity(i) return None def GetCallContract(self): # if (self.vix >= self.vixSMA.Current.Value * 1.5): # return None max_price = floor((1 + (self.vix/100 / sqrt(52) )) * self.Securities[self.stockSymbol].Price) contracts = self.OptionChainProvider.GetOptionContractList(self.stock.Symbol, self.Time) contracts = [x for x in contracts if 5 <= (x.ID.Date - self.Time).days <= 7] contracts = [x for x in contracts if x.ID.StrikePrice > self.stock.Price] contracts = [x for x in contracts if x.ID.OptionRight == OptionRight.Call] contracts = sorted(sorted(contracts, key = lambda x: x.ID.StrikePrice, reverse = False), key = lambda x: x.ID.Date) for i in contracts: if i.ID.StrikePrice >= max_price: call = self.AddOptionContract(i) return call return None def LogData(self): if self.putContract is not None and self.callContract is not None: self.Debug("time: {}, stock price: {}, ask price: {}, bid price: {}, max price: {}, protection number: {}, cash, unsettled: {}, {}, call ask: {}, call bid{}".format( self.Time, self.stock.Price, self.Securities[self.putContract.Symbol].AskPrice, self.Securities[self.putContract.Symbol].BidPrice, self.maxPutPrice, self.protectionNumber, self.Portfolio.Cash, self.Portfolio.UnsettledCash, self.Securities[self.callContract.Symbol].AskPrice, self.Securities[self.callContract.Symbol].BidPrice))