Overall Statistics |
Total Trades 1839 Average Win 0.45% Average Loss -0.45% Compounding Annual Return 13.751% Drawdown 15.200% Expectancy 0.351 Net Profit 317.712% Sharpe Ratio 1.571 Probabilistic Sharpe Ratio 95.671% Loss Rate 32% Win Rate 68% Profit-Loss Ratio 0.99 Alpha 0.082 Beta 0.26 Annual Standard Deviation 0.073 Annual Variance 0.005 Information Ratio -0.084 Tracking Error 0.131 Treynor Ratio 0.443 Total Fees $5951.24 |
# https://quantpedia.com/Screener/Details/7 # The investment universe consists of global large cap stocks (or US large cap stocks). # At the end of the each month, sort large dollar volume traded stocks, # then sort them by total yield and ROE, then rank them by the last 100 days volatility. # Go long stocks with the lowest volatility. from QuantConnect.Data.UniverseSelection import * import math import numpy as np import pandas as pd import scipy as sp class ShortTermReversalAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 1, 1) # Set Start Date #self.SetEndDate(2011, 1, 1) # Set Start Date self.SetCash(100000) # Set Strategy Cash self.lookback = 100 self.coarselist = 250 self.finelist = 60 self.stocks = 8 self.UniverseSettings.Resolution = Resolution.Hour self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.symbolDataDict = {} self.AddEquity("SPY", Resolution.Hour) self.AddEquity("UST", Resolution.Hour) # 7-10 yr treasury 2x start 2/1/2010 self.Schedule.On(self.DateRules.MonthEnd("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30), self.rebalance) #self.SetRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(0.2)) def CoarseSelectionFunction(self, coarse): selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 5)] # rank the stocks by dollar volume filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) return [ x.Symbol for x in filtered[:self.coarselist]] def FineSelectionFunction(self, fine): #top = sorted(fine, key = lambda x: x.ValuationRatios.TotalYield, reverse=True) #top = sorted(fine, key = lambda x: x.EarningReports.DividendPerShare.Value, reverse=True) '''top = sorted(fine, key=lambda x: x.ValuationRatios.TotalYield and x.OperationRatios.ROE.Value, reverse=True) return [x.Symbol for x in top[:self.finelist]]''' security_filter = [x for x in fine if x.CompanyReference.CountryId == "USA" and x.SecurityReference.SecurityType == 'ST00000001' # stock is common stock, ST00000002 is preferred stock and x.SecurityReference.IsPrimaryShare and x.CompanyReference.IsLimitedPartnership == 0 and x.SecurityReference.IsDepositaryReceipt == 0] top = sorted(security_filter, key=lambda x: x.ValuationRatios.TotalYield and x.OperationRatios.ROE.Value, reverse=True) return [x.Symbol for x in top[:self.finelist]] def rebalance(self): sorted_symbolData = sorted(self.symbolDataDict, key=lambda x: self.symbolDataDict[x].Volatility()) # pick the stocks with the lowest volatility long_stocks = sorted_symbolData[:self.stocks] stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the list for i in stocks_invested: if i not in long_stocks: self.Liquidate(i) # long stocks with the lowest volatility for i in long_stocks: self.SetHoldings(i, 0.5/len(long_stocks)) self.SetHoldings('UST', 0.5) invested = [ x.Symbol.Value for x in self.Portfolio.Values if x.Invested ] # create list of current positions invested.sort () # sort positions alphabetically for easier reading in the logs invested.remove('UST') # remove UST from the list since its the hedge and not a stock self.Debug("Stay long UST at 50% allocation. These are the current stock positions: " + str(invested)) # print current stock positions def OnData(self, data): for symbol, symbolData in self.symbolDataDict.items(): # update the indicator value for newly added securities if symbol not in self.addedSymbols: symbolData.Price.Add(IndicatorDataPoint(symbol, self.Time, self.Securities[symbol].Close)) self.addedSymbols = [] self.removedSymbols = [] def OnSecuritiesChanged(self, changes): # clean up data for removed securities self.removedSymbols = [x.Symbol for x in changes.RemovedSecurities] for removed in changes.RemovedSecurities: symbolData = self.symbolDataDict.pop(removed.Symbol, None) # warm up the indicator with history price for newly added securities self.addedSymbols = [ x.Symbol for x in changes.AddedSecurities if x.Symbol.Value != "SPY"] history = self.History(self.addedSymbols, self.lookback+1, Resolution.Daily) for symbol in self.addedSymbols: if symbol not in self.symbolDataDict.keys() and symbol.Value != 'UST' and symbol.Value != 'SPY': symbolData = SymbolData(symbol, self.lookback) self.symbolDataDict[symbol] = symbolData if str(symbol) in history.index: symbolData.WarmUpIndicator(history.loc[str(symbol)]) class SymbolData: '''Contains data specific to a symbol required by this model''' def __init__(self, symbol, lookback): self.symbol = symbol self.Price = RollingWindow[IndicatorDataPoint](lookback) def WarmUpIndicator(self, history): # warm up the RateOfChange indicator with the history request for tuple in history.itertuples(): item = IndicatorDataPoint(self.symbol, tuple.Index, float(tuple.close)) self.Price.Add(item) def Volatility(self): data = [float(x.Value) for x in self.Price] # time = [x.EndTime for x in self.Price] # price_series = pd.Series(data, index=time) return np.std(data)