Overall Statistics |
Total Trades 852 Average Win 0.10% Average Loss -0.28% Compounding Annual Return 2.919% Drawdown 26.600% Expectancy -0.339 Net Profit 9.171% Sharpe Ratio 0.226 Loss Rate 52% Win Rate 48% Profit-Loss Ratio 0.38 Alpha 0.157 Beta -7.02 Annual Standard Deviation 0.174 Annual Variance 0.03 Information Ratio 0.13 Tracking Error 0.174 Treynor Ratio -0.006 Total Fees $864.43 |
from QuantConnect.Data.UniverseSelection import * import numpy as np import pandas as pd ### <summary> ### </summary> class FiftyTwoWeeksHighEffectInStocks(QCAlgorithm): def Initialize(self): self.SetStartDate(2015,10,15) #Set Start Date self.SetEndDate(2018,10,31) self.SetCash(100000) #Set Strategy Cash self.symbols = None self.rebalance = 1 self.windows = {} self.ratios = {} self.mktCap = {} self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol self.Schedule.On(self.DateRules.MonthStart(), self.TimeRules.At(9,31), self.month_rebalance) #We define self.symbols in FineSelectionFunction, if we use warmup then Ondata and OnSecuritiesChanged will have no idea what self.symbols is # self.SetWarmup(252) def CoarseSelectionFunction(self, coarse): if self.rebalance or self.symbols is None: selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 3)] filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in filtered[:500]] #Pick 1000 most liquid stocks else: return self.symbols def FineSelectionFunction(self, fine): if self.rebalance or self.symbols is None: #Group the stocks by industry sector self.BasicMaterialsIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.BasicMaterials] self.ConsumerCyclicalIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.ConsumerCyclical] self.FinancialServicesIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.FinancialServices] self.RealEstateIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.RealEstate] self.ConsumerDefensiveIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.ConsumerDefensive] self.HealthcareIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Healthcare] self.UtilitiesIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Utilities] self.CommunicationServicesIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.CommunicationServices] self.EnergyIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Energy] self.IndustrialsIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Industrials] self.TechnologyIndustry = [x for x in fine if x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology] self.pools = self.BasicMaterialsIndustry + self.ConsumerCyclicalIndustry + self.FinancialServicesIndustry + self.RealEstateIndustry + self.ConsumerDefensiveIndustry + self.HealthcareIndustry + self.UtilitiesIndustry + self.CommunicationServicesIndustry + self.EnergyIndustry + self.IndustrialsIndustry + self.TechnologyIndustry hist = self.History([i.Symbol for i in self.pools], 252, Resolution.Daily) #The hist.index is in this format: "X R735QTJ8XC9 for x in self.pools: self.mktCap[x.Symbol] = float(x.EarningReports.BasicAverageShares.ThreeMonths) * hist.loc[str(x.Symbol)]['close'][-1] #str(x.Symbol) is "X R735QTJ8XC9X" ; #x.Symbol.Value is "X" so we should use str(x.Symbol) here self.symbols = [x.Symbol for x in self.pools] for symbol in self.symbols: self.windows[symbol] = RollingWindow[Decimal](252) for historical_close in hist.loc[str(symbol)]['close']: self.windows[symbol].Add(historical_close) self.rebalance = 0 self.Log(f'{self.Time}:FineSelection is running! The size of self.symbols is {len(self.symbols)}') return self.symbols else: return self.symbols def OnData(self, data): #Update the rolling windows if self.symbols is not None: for symbol in self.symbols: self.Log(symbol) if data.ContainsKey(symbol) and data[symbol] is not None : self.Log(data[symbol].Close) self.windows[symbol].Add(data[symbol].Close) self.Log("02") else: self.windows[symbol].Add(0) self.Log("03") def month_rebalance(self): self.Log(f'{self.Time} month_rebalance is running') self.rebalance = 1 #Calculate the weighted close/52-week-high ratios for each sector industry_list = [self.BasicMaterialsIndustry, self.ConsumerCyclicalIndustry, self.FinancialServicesIndustry, self.RealEstateIndustry, self.ConsumerDefensiveIndustry, self.HealthcareIndustry, self.UtilitiesIndustry, self.CommunicationServicesIndustry, self.EnergyIndustry, self.IndustrialsIndustry, self.TechnologyIndustry] weighted_scores = [] for i in range(0,len(industry_list)): mktCap_sum = sum([self.mktCap[x.Symbol] for x in industry_list[i]]) #The sum of mkt capitalization of i-th sector self.Log(f"{self.Time} {i}-th maktCap is good") curPrice = np.array([self.windows[x.Symbol][0] for x in (industry_list[i])]) #The current prices of each stock in i-th sector # self.Log(curPrice) # self.Log(f'curPrice of {i}-th industry:{curPrice}') self.Log(f"{self.Time} {i}-th curPrice is good") fiftyTwoHigh = np.array( [ max(self.windows[x.Symbol]) for x in industry_list[i] ] ) #The 52-week high of each stock in i-th sector x = industry_list[i][0] # self.Log(f'fiftyTwoHigh of {i}-th industry:{fiftyTwoHigh}') self.Log(f'{self.Time} {i}-th fiftyTwoHigh is good') ratios = curPrice/fiftyTwoHigh # Ratio (current prices/52-week high) of each stock in i-th sector # self.Log(f'ratios of {i}-th industry:{ratios}}') self.Log(f'{self.Time} {i}-th ratios is good') weights = np.array([self.mktCap[x.Symbol]/mktCap_sum for x in industry_list[i]]) #Weighted ratio for i-th sector self.Log(f'{self.Time} {i}-th weights is good') weighted_scores.append(sum(ratios*weights)) #Add weighted ratio to weighted_scores -> list self.Log("OK1") # self.Log(f'weighted_scores: {weighted_scores}') long_industry = industry_list[np.argmax(weighted_scores)] short_industry = industry_list[np.argmin(weighted_scores)] self.Log('long_industry and short_industry is good') for x in long_industry: self.SetHoldings(x.Symbol, 0.5/len(long_industry)) for x in short_industry: self.SetHoldings(x.Symbol, -0.5/len(short_industry))