Overall Statistics |
Total Trades 198 Average Win 4.69% Average Loss -1.66% Compounding Annual Return 50.362% Drawdown 14.900% Expectancy 1.442 Net Profit 843.029% Sharpe Ratio 2.053 Probabilistic Sharpe Ratio 97.495% Loss Rate 36% Win Rate 64% Profit-Loss Ratio 2.82 Alpha 0.439 Beta -0.08 Annual Standard Deviation 0.209 Annual Variance 0.044 Information Ratio 1.094 Tracking Error 0.278 Treynor Ratio -5.339 Total Fees $2451.08 |
from QuantConnect.Data.UniverseSelection import * import math import numpy as np import pandas as pd import scipy as sp import statistics class MomentumEffectAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 7, 1) # Set Start Date self.SetEndDate(2020, 12, 28) # Set Start Date self.SetCash(100000) # Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily self.mom = {} # Dict of Momentum indicator keyed by Symbol self.lookback = 252 # Momentum indicator lookback period self.num_coarse = 100 # Number of symbols selected at Coarse Selection self.num_fine = 50 # Number of symbols selected at Fine Selection self.num_long = 5 # Number of symbols with open positions self.month = -1 self.rebalance = False self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(30))) self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) def CoarseSelectionFunction(self, coarse): '''Drop securities which have no fundamental data or have too low prices. Select those with highest by dollar volume''' if self.month == self.Time.month: return Universe.Unchanged self.rebalance = True self.month = self.Time.month selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5], key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in selected[:self.num_coarse]] def FineSelectionFunction(self, fine): filtered_fine = [x for x in fine if x.OperationRatios.OperationMargin.Value and x.ValuationRatios.PriceChange1M and x.ValuationRatios.BookValuePerShare] # rank stocks by three factor. sortedByfactor1 = sorted(filtered_fine, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=True) sortedByfactor2 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=True) sortedByfactor3 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True) stock_dict = {} # assign a score to each stock, you can also change the rule of scoring here. for i,ele in enumerate(sortedByfactor1): rank1 = i rank2 = sortedByfactor2.index(ele) rank3 = sortedByfactor3.index(ele) score = sum([rank1*0.1,rank2*0.8,rank3*0.1]) stock_dict[ele] = score # sort the stocks by their scores self.sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1],reverse=True) sorted_symbol = [x[0] for x in self.sorted_stock][:20] '''Select security with highest market cap''' fine = [f for f in fine if f.ValuationRatios.PERatio > 0 and f.EarningReports.BasicEPS.TwelveMonths > 0 and f.EarningReports.BasicAverageShares.ThreeMonths > 0] lowest_market_cap= sorted(fine, key=lambda f: f.ValuationRatios.PERatio * f.EarningReports.BasicEPS.TwelveMonths * f.EarningReports.BasicAverageShares.ThreeMonths, reverse=True) turnoverss=[] norma=[] for i in lowest_market_cap[:]: hist = self.History([i.Symbol], timedelta(days=10)) if not hist.empty: norma.append(i.FinancialStatements.IncomeStatement.NetIncome.Value) norma = [float(j)/sum(norma) for j in norma] j=0 for i in lowest_market_cap[:]: hist = self.History([i.Symbol], timedelta(days=10)) if not hist.empty: i.inc=norma[j] j=j+1 i.Turnover=(hist["close"].iloc[0]) i.compare= ((statistics.mean(hist["close"]))-(statistics.mean(hist["open"])))/(statistics.stdev(hist["close"]-(statistics.mean(hist["open"])))) turnoverss.append(i.compare) else: lowest_market_cap.remove(i) lowest_market_cappp=sorted(lowest_market_cap,key=lambda f:f.compare,reverse=False) lowest_market_capp=lowest_market_cappp[:5] turnovers=turnoverss[:3] som=sum(turnovers) self.long = [x.Symbol for x in set(lowest_market_capp).intersection(sorted_symbol)] self.weights = {} for i in lowest_market_capp: self.weights[str(i.Symbol)] = i.compare+i.inc*i.inc*i.inc*i.inc+i.inc return self.long def OnData(self, data): if not self.rebalance: return if self.long: 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.Liquidate(i) # goes long on stocks with the lowest turnover for i in self.long: self.SetHoldings(i,-self.weights[str(i)]) # short on stocks with the highest turnover self.rebalance = False