| Overall Statistics |
|
Total Trades 21358 Average Win 0.10% Average Loss -0.10% Compounding Annual Return 11.315% Drawdown 57.000% Expectancy 0.154 Net Profit 408.706% Sharpe Ratio 0.543 Probabilistic Sharpe Ratio 1.882% Loss Rate 41% Win Rate 59% Profit-Loss Ratio 0.95 Alpha 0.13 Beta -0.113 Annual Standard Deviation 0.219 Annual Variance 0.048 Information Ratio 0.067 Tracking Error 0.295 Treynor Ratio -1.049 Total Fees $23548.13 Estimated Strategy Capacity $140.00 |
from datetime import timedelta
from QuantConnect.Data.UniverseSelection import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
class TheStockPicker(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2006, 1, 1) #15 year backtest
#self.SetEndDate(2020, 7, 15)
self.SetCash(100000)
self.SetBenchmark('SPY')
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
self.UniverseSettings.Resolution = Resolution.Daily
#self.UniverseSettings.Leverage = 1
self.AddUniverseSelection(StockPickerUniverseSelectionModel())
#spy = [ Symbol.Create("SPY", SecurityType.Equity, Market.USA) ]
#self.AddUniverseSelection( ManualUniverseSelectionModel(spy) )
self.SetAlpha(ROELongShortAlphaModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetRiskManagement(TrailingStopRiskManagementModel(0.05)) #parameter n1 step 0.01 from 0.00 to 0.10
self.SetExecution(VolumeWeightedAveragePriceExecutionModel())
#self.SetExecution(ImmediateExecutionModel())
class StockPickerUniverseSelectionModel(FundamentalUniverseSelectionModel):
def __init__(self):
super().__init__(True, None, None)
self.lastMonth = -1
self.dollar_volume_count = 1000 #parameter n2 step 1 start from 500 to 1500
self.by_mom_count = 200 #parameter n3 step 1 start from 100 to 300
self.by_roe_count = 60 #parameter n4 step 1 start from 0 to 20
self.mom = {}
def SelectCoarse(self, algorithm, coarse):
selected = []
if self.lastMonth == algorithm.Time.month:
return Universe.Unchanged
self.lastMonth = algorithm.Time.month
universe = sorted([x for x in coarse if x.HasFundamentalData and x.Volume > 0 and x.Price > 0],
key=lambda x: x.DollarVolume, reverse=True)
universe = [x for x in universe[:self.dollar_volume_count]]
if len(universe) == 0:
return Universe.Unchanged
for coarse in universe:
symbol = coarse.Symbol
if symbol not in self.mom:
history = algorithm.History(symbol, 20, Resolution.Daily)
self.mom[symbol] = SelectionData(history)
self.mom[symbol].update(algorithm.Time, coarse.AdjustedPrice)
if self.mom[symbol].is_ready():
selected.append({"symbol":symbol, "indicator": self.mom[symbol].mom})
ordered = sorted(selected, key=lambda kv: kv["indicator"], reverse=True)
orderedlist = []
for i in range(len(ordered)):
orderedlist.append(ordered[i]['symbol'])
return orderedlist[:self.by_mom_count]
def SelectFine(self, algorithm, fine):
'''Performs fine selection by Return On Capital Employed
The company's headquarter must in the U.S.
The stock must be traded on either the NYSE or NASDAQ
At least half a year since its initial public offering
The stock's market cap must be greater than 500 million'''
sortedByRoe = sorted([x for x in fine if x.CompanyReference.CountryId == "USA"
and x.CompanyReference.PrimaryExchangeID in ["NYS","NAS"]
and (algorithm.Time - x.SecurityReference.IPODate).days > 180
and x.MarketCap > 5e8],
key = lambda x: x.OperationRatios.ROE.Value, reverse=True)
count = len(sortedByRoe)
if count == 0:
return Universe.Unchanged
# Update self.lastMonth after all QC500 criteria checks passed
self.lastMonth = algorithm.Time.month
universe = sortedByRoe[:self.by_roe_count]
return [f.Symbol for f in universe]
class SelectionData():
def __init__(self, history):
self.mom = Momentum(14) #parameter n5 step 1 start from 2 to 26
for bar in history.itertuples():
self.mom.Update(bar.Index[1], bar.close)
def is_ready(self):
return self.mom.IsReady
def update(self, time, price):
self.mom.Update(time, price)
# Define the ROELongShortAlphaModel class
class ROELongShortAlphaModel(AlphaModel):
def __init__(self):
self.lastMonth = -1
self.removed = []
self.added = []
def OnSecuritiesChanged(self, algorithm, changes):
for security in changes.AddedSecurities:
self.added.append(security.Symbol)
for security in changes.RemovedSecurities:
self.removed.append(security.Symbol)
def Update(self, algorithm, data):
insights = []
if self.lastMonth == algorithm.Time.month:
return insights
if algorithm.Time.month==8:
return insights
self.lastMonth = algorithm.Time.month
#emit insights with insight directions based on momentum
for symbol in self.added:
insights.append(Insight.Price(symbol, timedelta(28), 1))
for symbol in self.removed:
insights.append(Insight.Price(symbol, timedelta(28), 0))
self.removed.clear()
self.added.clear()
return insights