Overall Statistics |
Total Trades 70920 Average Win 0.11% Average Loss -0.03% Compounding Annual Return -100% Drawdown 100% Expectancy -0.969 Net Profit -100% Sharpe Ratio -0.142 Probabilistic Sharpe Ratio 0% Loss Rate 99% Win Rate 1% Profit-Loss Ratio 3.28 Alpha -1.314 Beta 9.581 Annual Standard Deviation 7.018 Annual Variance 49.256 Information Ratio -0.148 Tracking Error 6.994 Treynor Ratio -0.104 Total Fees $70916.00 Estimated Strategy Capacity $0 Lowest Capacity Asset SDC 31RZMH42OKLOM|SDC X7SGNKRL4WV9 |
class SwimmingFluorescentOrangePanda(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 9, 20) # Set Start Date self.SetCash(1000000) # Set Strategy Cash self.AddEquity("SPY") self.rankFactor = {} self.selected = [] self.selectedOptionSymbols = [] self.AddUniverse(self.CustomCoarseUniverseSelection) self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw)) def CustomCoarseUniverseSelection(self, coarse): # Assume only equities within the top 200 dollar volume have options sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)[:200] for c in sortedByDollarVolume: if c.Symbol not in self.rankFactor: self.rankFactor[c.Symbol] = SymbolData(self, c.Symbol) self.rankFactor[c.Symbol].Update(c.Price) sortedByRankFactor = sorted(self.rankFactor.items(), key=lambda x: x[1].factor, reverse=True)[:10] # Option selection self.selectedOptionSymbols = self.OptionSelection(sortedByRankFactor) return self.selectedOptionSymbols def OptionSelection(self, dict_): optionsList = [] for symbol, symbolData in dict_: contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time) if not contracts: continue # We only want close-expiratory options within 30 days contractList = [i for i in contracts if (i.ID.Date.date() - self.Time.date()).days <= 30] # find the strike price of ATM option atmStrike = sorted(contractList, key = lambda x: abs(x.ID.StrikePrice - symbolData.price))[0].ID.StrikePrice # limit OTM and ITM range filteredContracts = [i for i in contractList if (i.ID.StrikePrice >= atmStrike * 0.99 and i.ID.StrikePrice <= atmStrike * 1.05 and i.ID.OptionRight == OptionRight.Call) or (i.ID.StrikePrice >= atmStrike * 0.95 and i.ID.StrikePrice <= atmStrike * 1.01 and i.ID.OptionRight == OptionRight.Put)] optionsList += filteredContracts if optionsList: history = self.History(optionsList, timedelta(days=3), Resolution.Minute) if history.empty: return [] history = history.close history.index = pd.MultiIndex.from_tuples([x[-2:] for x in history.index], names=["symbol", "time"]) history = history.unstack(0).resample("D").last() history = history.loc[~(history==0).all(axis=1)] pctChg = history.pct_change().iloc[-1] sortByPctChg = pctChg.sort_values(axis=0, ascending=False) # select top 20 selectedOptionSymbols = sortByPctChg.iloc[:min(20, len(sortByPctChg))].index return [self.AddOptionContract(x, Resolution.Minute).Symbol for x in selectedOptionSymbols] return [] def OnData(self, data): self.Liquidate() for symbol in self.selectedOptionSymbols: self.Buy(symbol, 1) class SymbolData: def __init__(self, algorithm, symbol): self.window = RollingWindow[float](253) self.price = 0 self.factor = 0 history = algorithm.History(symbol, 253, Resolution.Daily) for bar in history.iloc[:-1].itertuples(): self.window.Add(bar.close) def Update(self, close): self.price = close self.window.Add(close) if self.window.IsReady: returns = pd.Series(self.window).pct_change().iloc[1:] volatility = float(returns.std()) priceChange = float(returns.iloc[-1]) self.factor = volatility * abs(priceChange)