| Overall Statistics |
|
Total Trades 7687 Average Win 0.17% Average Loss -0.24% Compounding Annual Return 8.391% Drawdown 57.500% Expectancy 0.126 Net Profit 293.817% Sharpe Ratio 0.48 Probabilistic Sharpe Ratio 0.752% Loss Rate 34% Win Rate 66% Profit-Loss Ratio 0.71 Alpha 0.094 Beta -0.084 Annual Standard Deviation 0.179 Annual Variance 0.032 Information Ratio -0.041 Tracking Error 0.248 Treynor Ratio -1.028 Total Fees $8223.67 |
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
class QuantumTransdimensionalComputer(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2003, 1, 1) # Set Start Date
self.SetEndDate(2019, 12, 31) # Set End Date
self.SetCash(100000) # Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Daily
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
# Momentum parameters
self.mom = {} # Dict of Momentum indicator keyed by Symbol
self.lookback = 90 # Momentum indicator lookback period
self.num_long = 30 # Number of symbols with open positions
# Trade control Parameters
self.month = -1 # month indicator
self.rebalance = False # rebalance indicator
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
# Update the indicator
for symbol, mom in self.mom.items():
# Update also works for an IndicatorDataPoint object
mom.Update(self.Time, self.Securities[symbol].Close)
if not self.rebalance:
return
# Selects the securities with highest momentum
# Note that sorted_mom and selected are lists of symbols
sorted_mom = sorted([k for k,v in self.mom.items() if v.IsReady],
key=lambda x: self.mom[x].Current.Value, reverse=True)
selected = sorted_mom[:self.num_long]
# Liquidate securities that are not in the list
for symbol, mom in self.mom.items():
if symbol not in selected:
self.Liquidate(symbol, 'Not selected')
# Buy selected securities
for symbol in selected:
self.SetHoldings(symbol, 1/self.num_long)
self.rebalance = False
def CoarseSelectionFunction(self, coarse):
'''Drop securities which have no fundamental data or have too low prices.
Select those with highest by dollar volume'''
# Only one rebalance per mounth
if self.month == self.Time.month:
return Universe.Unchanged
# Set rebalance indicator and reset month variable
self.rebalance = True
self.month = self.Time.month
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]
def FineSelectionFunction(self, fine):
'''Select security with highest market cap'''
fine = [f for f in fine if f.ValuationRatios.PERatio > 0
and f.EarningReports.BasicEPS.TwelveMonths > 0
and f.EarningReports.BasicAverageShares.ThreeMonths > 0]
# This is how Market Cap is calculated
selected = [ f for f in fine if f.ValuationRatios.PERatio *
f.EarningReports.BasicEPS.TwelveMonths *
f.EarningReports.BasicAverageShares.ThreeMonths > 8200000000]
return [x.Symbol for x in selected]
def OnSecuritiesChanged(self, changes):
# Clear removed securities
for security in changes.RemovedSecurities:
symbol = security.Symbol
if self.mom.pop(symbol, None) is not None:
self.Liquidate(symbol, 'Removed from universe')
# Create indicators for the new secuirities (NOT UPDATING YET)
for security in changes.AddedSecurities:
symbol = security.Symbol
if symbol not in self.mom:
self.mom[symbol] = Momentum(self.lookback)
# Warm up the indicator with history price if it is not ready
addedSymbols = [k for k,v in self.mom.items() if not v.IsReady]
# Get historical prices for the newly added securities
# Use 1 + self.lookback as the window to be safer
history = self.History(addedSymbols, 1 + self.lookback, Resolution.Daily)
# The unstack changes a multiindex data frame to a single index data frame
# Level = 0 keeps inner index and create columns based on the outer index
history = history.close.unstack(level=0)
# Manually update the indicator for the new securiites
for symbol in addedSymbols:
ticker = str(symbol)
if ticker in history:
# history[ticker] selects a *column*(result of unstack) based on the ticker
for time, value in history[ticker].items():
# Manually create an indicator data point object and update with it
item = IndicatorDataPoint(symbol, time, value)
self.mom[symbol].Update(item)