| Overall Statistics |
|
Total Trades 98 Average Win 0.09% Average Loss -0.06% Compounding Annual Return 1.665% Drawdown 2.400% Expectancy 0.594 Net Profit 0.285% Sharpe Ratio 0.331 Probabilistic Sharpe Ratio 38.140% Loss Rate 37% Win Rate 63% Profit-Loss Ratio 1.52 Alpha 0.035 Beta -0.109 Annual Standard Deviation 0.046 Annual Variance 0.002 Information Ratio -1.44 Tracking Error 0.112 Treynor Ratio -0.139 Total Fees $486.79 |
#This is a Template of dynamic stock selection.
#You can try your own fundamental factor and ranking method by editing the CoarseSelectionFunction and FineSelectionFunction
from QuantConnect.Data.UniverseSelection import *
from sklearn.datasets import load_iris
from sklearn import preprocessing
import subfactors
class BasicTemplateAlgorithm(QCAlgorithm):
def __init__(self):
# set the flag for rebalance
self.reb = 1
self.months = -1
# Number of stocks to pass CoarseSelection process
self.num_coarse = 400
# Number of stocks to long - not used
self.symbols = None
#Declare empty score lists
self.long_score_q = []
def Initialize(self):
self.SetCash(1000000)
self.SetStartDate(2005,12,29)
# if not specified, the Backtesting EndDate would be today
self.SetEndDate(2006,3,1)
#Set benchmark
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
self.SetBenchmark("SPY")
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
# Schedule the rebalance function to execute at the begining of each month or week
#self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday),
#self.TimeRules.AfterMarketOpen(self.spy,20), Action(self.rebalance))
self.Schedule.On(self.DateRules.MonthStart(self.spy),
self.TimeRules.AfterMarketOpen(self.spy,20), Action(self.rebalance))
def OnData(self, data):
pass
def CoarseSelectionFunction(self, coarse):
# if the rebalance flag is not 1, return null list to save time.
if self.reb != 1:
return self.longq + self.shortq
# make universe selection once a month
# drop stocks which have no fundamental data or have too low prices
selected = [x for x in coarse if (x.HasFundamentalData)
and (float(x.Price) > 1)]
sortedByDollarVolume = sorted(selected, key=lambda x: x.DollarVolume, reverse=True)
top = sortedByDollarVolume[:self.num_coarse]
#self.Log(top[-1].DollarVolume)
return [i.Symbol for i in top]
def FineSelectionFunction(self, fine):
# return null list if it's not time to rebalance
if self.reb != 1:
return self.longq + self.shortq
self.reb = 0
data_issues = {"MAA", "FR", "FRT"}
filtered_fine = [x for x in fine if x.OperationRatios.DebttoAssets.Value
and x.OperationRatios.ROA.Value
and x.OperationRatios.ROA5YrAvg.Value
and x.OperationRatios.OperationMargin.Value
and x.OperationRatios.OperationIncomeGrowth.FiveYears
and x.ValuationRatios.PERatio
and x.OperationRatios.CurrentRatio.Value
and x.AssetClassification.MorningstarIndustryGroupCode != MorningstarIndustryGroupCode.Banks
and x.AssetClassification.MorningstarIndustryGroupCode != MorningstarIndustryGroupCode.REITs
and (x.CompanyReference.CountryId == "USA")
and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
and (x.Symbol.Value not in data_issues)
]
bankscoarse = [x for x in fine if x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.Banks
and (not x.SecurityReference.IsDepositaryReceipt)
and (x.CompanyReference.CountryId == "USA")
and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
and (x.Symbol.Value not in data_issues)
]
banks = [x for x in bankscoarse if x.ValuationRatios.TangibleBookValuePerShare
and x.FinancialStatements.TotalRiskBasedCapital.Value
and x.OperationRatios.ROE.Value
and x.OperationRatios.ROE5YrAvg.Value
]
# put REITs in seperate category
REITs_coarse = [x for x in fine if x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.REITs
and (not x.SecurityReference.IsDepositaryReceipt)
and (x.CompanyReference.CountryId == "USA")
and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
and (x.Symbol.Value not in data_issues)
]
REITs = [x for x in REITs_coarse if x.ValuationRatios.FFOPerShare
and x.OperationRatios.FinancialLeverage.Value
and x.OperationRatios.ROE5YrAvg.Value
]
# Here I do things
#Put factors as scores
REITs_score = {}
bank_score = {}
all_score = {}
#Simplified
for i,ele in enumerate(filtered_fine):
all_score[ele] = i
for i,ele in enumerate(banks):
bank_score[ele] = i
for i,ele in enumerate(REITs):
REITs_score[ele] = i
min_all = min(all_score.values())
max_all = max(all_score.values())
for i,ele in enumerate(all_score):
all_score[ele] = (all_score[ele]-min_all) / (max_all - min_all)
min_bank = min(bank_score.values())
max_bank = max(bank_score.values())
for i,ele in enumerate(bank_score):
bank_score[ele] = (bank_score[ele]-min_bank) / (max_bank - min_bank)
min_REIT = min(REITs_score.values())
max_REIT = max(REITs_score.values())
for i,ele in enumerate(REITs_score):
REITs_score[ele] = (REITs_score[ele]-min_REIT) / (max_REIT - min_REIT)
all_stocks = {**bank_score, **REITs_score, **all_score}
# sort the stocks by their ranks low to high
self.sorted_stockvq = sorted(all_stocks.items(), key=lambda d:d[1],reverse=False)
sorted_score = [x[0] for x in self.sorted_stockvq]
sorted_vq_score = [x[1] for x in self.sorted_stockvq]
# sort the top stocks into each factor
sliced = int(round(len(sorted_score)/10))
self.longq = [x.Symbol for x in sorted_score[:sliced]]
self.long_score_q = [x for x in sorted_vq_score[:sliced]]
self.shortq = [x.Symbol for x in sorted_score[-sliced:]]
self.short_score_q = [x for x in sorted_vq_score[-sliced:]]
return self.longq + self.shortq
def rebalance(self):
# if this month the stock are not going to be long, liquidate it.
total_list = self.longq + self.shortq
long_score = self.long_score_q
total_list.clear()
long_list = self.longq
short_list = self.shortq
short_score = self.short_score_q
maxwgt = 0.2
slope = 0.55
gross_exposure = 0.85
gross_exp = gross_exposure
sum_long_score =0
sum_short_score =0
total_wgts = []
#long_wgts = []
i =0
num_short =0
"""while gross_exp > 0 and maxwgt-(slope*(long_score[i]**2)) > 0:
#for i in range(0, len(long_score)):
weight = maxwgt-(slope*(long_score[i]**2))
total_wgts.append(weight)
total_list.append(long_list[i])
gross_exp -= weight
i += 1"""
"""gross_exp = 0.85
k = len(short_score)-1
while gross_exp > 0 and maxwgt-(slope*(short_score[k]**2)) < 0:
weight = maxwgt-(slope*(short_score[k]**2))
total_wgts.append(weight)
total_list.append(short_list[k])
gross_exp += weight
k -= 1"""
#sum_short_score += max(short_score[i]**0.75,0.15)
for i in range(0, len(long_score)):
sum_long_score += (1/max(long_score[i]**0.75,0.15))
for i in range(0, len(long_score)):
total_wgts.append((1/max(long_score[i]**0.75,0.15))/sum_long_score)
"""for i in range(0, len(short_score)):
short_wgts.append(1/len(short_score))
#short_wgts.append(max(short_score[i]**0.75,0.15)/sum_short_score)
"""
total_list = long_list #+ short_list
for i in self.Portfolio.Values:
if (i.Invested) and (i.Symbol not in total_list) and (i.Symbol.Value != "SPY"):
self.Liquidate(i.Symbol)
# Assign each stock equally
k = 0
for i in total_list:
#self.Log(";Buying;" +str(long_wgts[k]) + ";of;"+ str(i) + ";with score;" + str(long_score[k]))
self.SetHoldings(i, total_wgts[k]*gross_exposure)
self.Log(i)
self.Log(total_wgts[k])
self.Log(long_score[k])
k+=1
#k = 0
"""for i in short_list:
#self.Log(";Shorting;" +str(short_wgts[k]) + ";of;"+ str(i) + ";with score;" + str(short_score[k]))
self.SetHoldings(i, -short_wgts[k]*gross_exposure)
sum_short += -short_wgts[k]*gross_exposure
k+=1"""
self.SetHoldings("SPY", -gross_exposure)
self.reb = 1class subfactor():
def normalise(data):
min_data = min(data)
max_data = max(data)
for i in range(0, len(data)):
data[i] = (data[i]-min_data) / (max_data - min_data)
return data
# Your New Python File