Overall Statistics |
Total Trades 4613 Average Win 0.15% Average Loss -0.14% Compounding Annual Return 7.884% Drawdown 17.600% Expectancy 0.092 Net Profit 30.405% Sharpe Ratio 0.476 Probabilistic Sharpe Ratio 11.444% Loss Rate 49% Win Rate 51% Profit-Loss Ratio 1.12 Alpha 0.039 Beta 0.244 Annual Standard Deviation 0.133 Annual Variance 0.018 Information Ratio -0.181 Tracking Error 0.194 Treynor Ratio 0.258 Total Fees $11244.04 Estimated Strategy Capacity $29000000.00 Lowest Capacity Asset ADRO VZQ7GYGNVHB9 Portfolio Turnover 5.55% |
#region imports from AlgorithmImports import * #endregion class ShortTimeReversal(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) #self.SetEndDate(2021, 1, 1) self.SetCash(1000000) self.leverage = .33 self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.SelectCoarse) self.dollar_volume_selection_size = 100 self.roc_selection_size = int(0.1 * self.dollar_volume_selection_size) self.lookback = 22 self.roc_by_symbol = {} self.week = 0 def SelectCoarse(self, coarse): # We should keep a dictionary for all securities that have been selected for cf in coarse: symbol = cf.Symbol if symbol in self.roc_by_symbol: self.roc_by_symbol[symbol].Update(cf.EndTime, cf.AdjustedPrice) # Refresh universe each week week_number = self.Time.date().isocalendar()[1] if week_number == self.week: return Universe.Unchanged self.week = week_number # sort and select by dollar volume sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True) selected = {cf.Symbol: cf for cf in sortedByDollarVolume[:self.dollar_volume_selection_size]} # New selections need a history request to warm up the indicator symbols = [k for k in selected.keys() if k not in self.roc_by_symbol or not self.roc_by_symbol[k].IsReady] if symbols: history = self.History(symbols, self.lookback+1, Resolution.Daily) if history.empty: self.Log(f'No history for {", ".join([x.Value for x in symbols])}') history = history.close.unstack(0) for symbol in symbols: symbol_id = symbol.ID.ToString() if symbol_id not in history: continue # Create and warm-up the RateOfChange indicator roc = RateOfChange(self.lookback) for time, price in history[symbol_id].dropna().iteritems(): roc.Update(time, price) if roc.IsReady: self.roc_by_symbol[symbol] = roc # Sort the symbols by their ROC values selectedRateOfChange = {} for symbol in selected.keys(): if symbol in self.roc_by_symbol: selectedRateOfChange[symbol] = self.roc_by_symbol[symbol] sortedByRateOfChange = sorted(selectedRateOfChange.items(), key=lambda kv: kv[1], reverse=True) # Define the top and the bottom to buy and sell self.rocTop = [x[0] for x in sortedByRateOfChange[:self.roc_selection_size]] self.rocBottom = [x[0] for x in sortedByRateOfChange[-self.roc_selection_size:]] return self.rocTop + self.rocBottom def OnData(self, data): # Rebalance for symbol in self.rocTop: self.SetHoldings(symbol, -0.5 * self.leverage/len(self.rocTop)) for symbol in self.rocBottom: self.SetHoldings(symbol, 0.5 * self.leverage/len(self.rocBottom)) # Clear the list of securities we have placed orders for # to avoid new trades before the next universe selection self.rocTop.clear() self.rocBottom.clear() def OnSecuritiesChanged(self, changes): for security in changes.RemovedSecurities: self.Liquidate(security.Symbol, 'Removed from Universe')