| Overall Statistics |
|
Total Trades 466 Average Win 0.17% Average Loss -0.13% Compounding Annual Return 17.567% Drawdown 32.900% Expectancy 0.099 Net Profit 17.619% Sharpe Ratio 0.636 Probabilistic Sharpe Ratio 32.302% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.30 Alpha 0.247 Beta -0.221 Annual Standard Deviation 0.317 Annual Variance 0.101 Information Ratio -0.002 Tracking Error 0.483 Treynor Ratio -0.911 Total Fees $466.05 Estimated Strategy Capacity $42000000.00 Lowest Capacity Asset AGO SY2SA4YZ4UW5 |
from AlphaModel import *
class VerticalTachyonRegulators(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetEndDate(2021, 1, 1)
self.SetCash(100000)
# Execution model
self.SetExecution(ImmediateExecutionModel())
# Portfolio construction model
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(rebalance=timedelta(weeks=13)))
# Risk model
self.SetRiskManagement(NullRiskManagementModel())
# Universe selection
self.num_coarse = 500
self.rebalanceTime = datetime.min
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.sectors = set([MorningstarSectorCode.FinancialServices, MorningstarSectorCode.RealEstate, MorningstarSectorCode.Healthcare, MorningstarSectorCode.Utilities, MorningstarSectorCode.Technology])
self.period = timedelta(weeks=13)
# Alpha Model
self.AddAlpha(FundamentalFactorAlphaModel(self.period, self.sectors))
def CoarseSelectionFunction(self, coarse):
# If not time to rebalance, keep the same universe
if self.Time <= self.rebalanceTime:
return Universe.Unchanged
self.rebalanceTime = self.Time + self.period
# Select only those with fundamental data and a sufficiently large price
# Sort by top dollar volume: most liquid to least liquid
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):
# Filter the fine data for equities that IPO'd more than 5 years ago in given sector
filtered_fine = [x.Symbol for x in fine if x.SecurityReference.IPODate + timedelta(365*5) < self.Time
and x.AssetClassification.MorningstarSectorCode in self.sectors
and x.OperationRatios.ROE.Value > 0
and x.OperationRatios.NetMargin.Value > 0
and x.ValuationRatios.PERatio > 0]
return filtered_fine
from datetime import timedelta
class FundamentalFactorAlphaModel(AlphaModel):
def __init__(self, period, sectors):
self.rebalanceTime = datetime.min
self.sectors = {}
for sector in sectors:
self.sectors[sector] = set()
self.period = period
def Update(self, algorithm, data):
'''Updates this alpha model with the latest data from the algorithm.
This is called each time the algorithm receives data for subscribed securities
Args:
algorithm: The algorithm instance
data: The new data available
Returns:
New insights'''
# Return no insights if it's not time to rebalance
if algorithm.Time <= self.rebalanceTime:
return []
self.rebalanceTime = algorithm.Time + self.period
# Dictionary holding a dictionary of scores for each sector
sector_scores = {}
for sector in self.sectors:
securities = self.sectors[sector]
sortedByROE = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.ROE.Value, reverse=True)
sortedByPM = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.NetMargin.Value, reverse=True)
sortedByPE = sorted(securities, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=False)
scores = {}
for security in securities:
score = sum([sortedByROE.index(security), sortedByPM.index(security), sortedByPE.index(security)])
scores[security] = score
sector_scores[sector] = scores
# Set of symbols that we want to buy
longs = set()
for sector in self.sectors:
# add best 20% of each sector to longs set (minimum 1)
length = max(int(len(sector_scores[sector])/5), 1)
for security in sorted(sector_scores[sector].items(), key=lambda x: x[1], reverse=False)[:length]:
longs.add(security[0].Symbol)
# Insights of the form: Insight(symbol, timedelta, type, direction, magnitude, confidence, sourceModel, weight)
insights = []
# Close old positions if they aren't in longs set
for security in algorithm.Portfolio.Values:
if security.Invested and security.Symbol not in longs:
insights.append(Insight(security.Symbol, Expiry.EndOfDay, InsightType.Price, InsightDirection.Flat))
# Emit buy insight for all symbols in longs set
for symbol in longs:
insights.append(Insight(symbol, self.period, InsightType.Price, InsightDirection.Up))
return insights
def OnSecuritiesChanged(self, algorithm, changes):
'''Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm'''
# Remove security from sector set
for security in changes.RemovedSecurities:
for sector in self.sectors:
if security in self.sectors[sector]:
self.sectors[sector].remove(security)
# Add security to corresponding sector set
for security in changes.AddedSecurities:
self.sectors[security.Fundamentals.AssetClassification.MorningstarSectorCode].add(security)