Overall Statistics |
Total Trades 88 Average Win 1.75% Average Loss -1.84% Compounding Annual Return -6.730% Drawdown 24.800% Expectancy -0.024 Net Profit -15.961% Sharpe Ratio -0.356 Probabilistic Sharpe Ratio 0.450% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 0.95 Alpha -0.016 Beta -0.245 Annual Standard Deviation 0.114 Annual Variance 0.013 Information Ratio -0.862 Tracking Error 0.166 Treynor Ratio 0.166 Total Fees $982.66 Estimated Strategy Capacity $9600000.00 Lowest Capacity Asset HAE R735QTJ8XC9X |
#region imports from AlgorithmImports import * #endregion # https://quantpedia.com/Screener/Details/18 import math import scipy as sp class LiquidityEffectAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2016, 1, 1) self.SetEndDate(2018, 7, 1) self.SetCash(1000000) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.AddEquity("SPY", Resolution.Daily) self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY"), self.rebalance) # Count the number of months that have passed since the algorithm starts self.months = -1 self.yearly_rebalance = True self.num_coarse = 500 self.long = None self.short = None def CoarseSelectionFunction(self, coarse): if self.yearly_rebalance: # drop stocks which have no fundamental data or have low price selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.AdjustedPrice) > 5)] # rank the stocks by dollar volume filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) self.filtered_coarse = [ x.Symbol for x in filtered[:self.num_coarse]] return self.filtered_coarse else: return Universe.Unchanged def FineSelectionFunction(self, fine): if self.yearly_rebalance: # The market capitalization must be no less than $10 million top_market_cap = list(filter(lambda x: x.MarketCap > 10000000, fine)) # Save all market cap values market_caps = [i.MarketCap for i in top_market_cap] # Calculate the lowest market-cap quartile lowest_quartile = np.percentile(market_caps, 25) # Filter stocks in the lowest market-cap quartile lowest_market_cap = list(filter(lambda x: x.MarketCap <= lowest_quartile, top_market_cap)) turnovers = [] # Divide into quartiles based on their turnover (the number of shares traded divided by the stock’s outstanding shares) in the last 12 months for i in lowest_market_cap[:]: hist = self.History([i.Symbol], 21*12, Resolution.Daily) if not hist.empty: mean_volume = np.mean(hist.loc[str(i.Symbol)]['volume']) i.Turnover = mean_volume / float(i.CompanyProfile.SharesOutstanding) turnovers.append(i.Turnover) else: lowest_market_cap.remove(i) bottom_turnover = np.percentile(turnovers, 5) top_turnover = np.percentile(turnovers, 95) self.long = [x.Symbol for x in lowest_market_cap if x.Turnover < bottom_turnover] self.short = [x.Symbol for x in lowest_market_cap if x.Turnover > top_turnover] return self.long + self.short else: return Universe.Unchanged def rebalance(self): # yearly rebalance self.months += 1 if self.months%12 == 0: self.yearly_rebalance = True def OnData(self, data): if not self.yearly_rebalance: return self.yearly_rebalance = False if self.long and self.short: stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the trading list for i in stocks_invested: if i not in self.long+self.short: self.Liquidate(i) # goes long on stocks with the lowest turnover for short_stock in self.short: self.SetHoldings(short_stock, -0.5/len(self.short)) # short on stocks with the highest turnover for long_stock in self.long: self.SetHoldings(long_stock, 0.5/len(self.long))