Overall Statistics |
Total Trades 1676 Average Win 0.90% Average Loss -0.48% Compounding Annual Return 25.610% Drawdown 27.500% Expectancy 0.422 Net Profit 364.047% Sharpe Ratio 0.898 Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.90 Alpha 0.214 Beta 0.003 Annual Standard Deviation 0.239 Annual Variance 0.057 Information Ratio 0.397 Tracking Error 0.272 Treynor Ratio 73.679 Total Fees $9058.34 |
from QuantConnect.Data.UniverseSelection import * import operator from math import ceil,floor from datetime import datetime import pandas as pd import numpy as np from scipy.optimize import minimize class CoarseFineFundamentalComboAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2011,2,01) #Set Start Date self.SetEndDate(datetime.now()) #Set End Date self.SetCash(100000) #Set Strategy Cash self.rebalence_flag = 0 self.first_month_trade_flag = 1 self.trade_flag = 0 self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.AddEquity("SPY") self.number_coarse = 2000 self.num_portfolios = 6 self.num_long = 8 self.num_short = 6 self.long = None self.short = None self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.Rebalancing)) def CoarseSelectionFunction(self, coarse): if self.rebalence_flag or self.first_month_trade_flag: CoarseWithFundamental = [x for x in coarse if x.HasFundamentalData] sortedByDollarVolume = sorted(CoarseWithFundamental, key=lambda x: x.DollarVolume, reverse=True) top = sortedByDollarVolume[:self.number_coarse] return [i.Symbol for i in top] else: return [] def FineSelectionFunction(self, fine): if self.rebalence_flag or self.first_month_trade_flag: filtered_fine = [x for x in fine if x.ValuationRatios.PriceChange1M and x.EarningReports.TotalDividendPerShare.ThreeMonths and x.ValuationRatios.PERatio and x.ValuationRatios.BookValueYield] sortedByfactor1 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=False) sortedByfactor2 = sorted(filtered_fine, key=lambda x: x.EarningReports.TotalDividendPerShare.ThreeMonths, reverse=True) sortedByfactor3 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PERatio, reverse=False) sortedByfactor4 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.BookValueYield, reverse=True) num_stocks = floor(len(filtered_fine)/self.num_portfolios) stock_dict = {} for i,ele in enumerate(sortedByfactor1): rank1 = i rank2 = sortedByfactor2.index(ele) rank3 = sortedByfactor3.index(ele) rank4 = sortedByfactor4.index(ele) score = [ceil(rank1/num_stocks), ceil(rank2/num_stocks), ceil(rank3/num_stocks), ceil(rank4/num_stocks)] score = sum(score) stock_dict[ele] = score self.sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1],reverse=True) sorted_symbol = [self.sorted_stock[i][0] for i in xrange(len(self.sorted_stock))] self.long = sorted_symbol[:self.num_long] self.short = sorted_symbol[-self.num_short:] self.rebalence_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return [i.Symbol for i in (self.long+self.short)] else: return [] def OnData(self, data): if self.long is None and self.short is None: return if self.trade_flag or self.first_month_trade_flag: holdings = self.long + self.short for i in self.Portfolio.Values: if (i.Invested) and (i.Symbol not in holdings): self.Liquidate(i.Symbol) symbols = [ x.Symbol.Value for x in self.long] for i in symbols: self.AddEquity(i, Resolution.Daily) history = self.History(symbols, 100, Resolution.Daily) if history is None: return data = {} for i in symbols: if i in history.index.levels[0]: data[i] = history.loc[i]['close'] df_price = pd.DataFrame(data,columns=data.keys()) log_return = np.log(df_price / df_price.shift(1)).dropna() a = PortfolioOptimization(log_return, 0, len(data)) opt_weight = a.opt_portfolio() for i in range(len(data)): self.SetHoldings(df_price.columns[i],opt_weight[i]) # # Equally weighted # for i in self.long: # self.SetHoldings(i.Symbol,1.0/self.num_long) if self.num_short != 0: for i in self.short: self.SetHoldings(i.Symbol,-0.2/self.num_short) self.trade_flag = 0 def Rebalancing(self): self.rebalence_flag = 1 class PortfolioOptimization(object): import numpy as np import pandas as pd def __init__(self, df_return, risk_free_rate, num_assets): self.log_return = df_return self.risk_free_rate = risk_free_rate self.n = num_assets # numbers of risk assets in portfolio def annual_port_return(self, weights): # calculate the annual return of portfolio return np.sum(self.log_return.mean() * weights) * 252 def annual_port_vol(self, weights): # calculate the annual volatility of portfolio return np.sqrt(np.dot(weights.T, np.dot(self.log_return.cov() * 252, weights))) def mc_mean_var(self): # apply monte carlo method to search for the feasible region of return returns = [] vols = [] for i in range(200): weights = np.random.rand(n) weights /= np.sum(weights) returns.append(self.annual_port_return(weights)) vols.append(self.annual_port_vol(weights)) return returns, vols def min_func(self, weights): return - self.annual_port_return(weights) / self.annual_port_vol(weights) def opt_portfolio(self): # maximize the sharpe ratio to find the optimal weights cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) bnds = tuple((0, 1) for x in range(self.n)) opt = minimize(self.min_func, np.array(self.n * [1. / self.n]), method='SLSQP', bounds=bnds, constraints=cons) opt_weights = opt['x'] return opt_weights