| Overall Statistics |
|
Total Trades 66825 Average Win 0.07% Average Loss -0.05% Compounding Annual Return 1.369% Drawdown 54.000% Expectancy 0.014 Net Profit 12.604% Sharpe Ratio 0.172 Probabilistic Sharpe Ratio 0.325% Loss Rate 57% Win Rate 43% Profit-Loss Ratio 1.36 Alpha 0.038 Beta 0.036 Annual Standard Deviation 0.247 Annual Variance 0.061 Information Ratio -0.292 Tracking Error 0.286 Treynor Ratio 1.181 Total Fees $502169.34 |
from QuantConnect.Data.UniverseSelection import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
class CapitalizationUniverseModel(FundamentalUniverseSelectionModel):
"""
Universe model that selects stocks by capitalization. It performs an initial
selection by traded volume to improve algorithm speed.
"""
def __init__(self, filterFineData=True, universeSettings=None, securityInitializer=None):
'''
Initializes a new default instance of the Capitalization Universe Model.
'''
super().__init__(filterFineData, universeSettings, securityInitializer)
self.numberOfSymbolsCoarse = 2000
self.num_symbols = 50
self.price_limit = 1
self.last_month = -1 # Start first period
self.is_take_top = False
def SelectCoarse(self, algorithm, coarse):
'''
Drop securities which have no fundamental data or have too low prices.
Select those by dollar volume ordered accorsing to self.is_take_top.
'''
if algorithm.Time.month == self.last_month:
return Universe.Unchanged
self.last_month = algorithm.Time.month
selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > self.price_limit],
key=lambda x: x.DollarVolume, reverse=self.is_take_top)
return [x.Symbol for x in selected[:self.numberOfSymbolsCoarse]]
def SelectFine(self, algorithm, fine):
''' Selects the stocks by market cap '''
sorted_market_cap = sorted([x for x in fine if x.MarketCap > 0],
key=lambda x: x.MarketCap, reverse = self.is_take_top)
return [x.Symbol for x in sorted_market_cap[:self.num_symbols]]from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from datetime import datetime, timedelta
from CapitalizationUniverseModel import CapitalizationUniverseModel
from QuantConnect.Indicators import Momentum
class MomentumLaboratory(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2012, 1, 1)
self.SetEndDate(datetime.today())
self.SetCash(1000000)
self.AddEquity("SPY", Resolution.Daily)
self.SetBenchmark("SPY")
self.SetUniverseSelection(CapitalizationUniverseModel())
self.UniverseSettings.Resolution = Resolution.Daily
self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.TotalReturn))
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
self.AddAlpha(MomentumReversalAlphaModel())
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
class MomentumReversalAlphaModel(AlphaModel):
"""
Demostration Momentum Indicator Alpha Model.
"""
def __init__(self):
self.Name = 'MomentumReversalAlphaModel'
self.is_reversal = True
# Re-balance every week.
self.rebalancing_period = 30
# Initiate first day of operation.
self.next_rebalance = False
# EMA period, in days.
self.indicator_period = 30
# Gain Target:
self.gain_target = 0.05
self.max_positions = 15
# Dictionary to hold symbol data:
self.symbol_data = {}
# Flag to show first day of work completed.
self.start_flag = False
self.directions = [InsightDirection.Down, InsightDirection.Up]
def Update(self, algorithm, data):
# Clear counters and insights, return if data slice is empty.
insights = []
up_count = False
if not data.HasData:
return []
# If this is the first day of operation, lower the flag and set
# next re-balance time for today.
if self.start_flag is False:
self.next_rebalance = algorithm.Time
self.start_flag = True
if algorithm.Time < self.next_rebalance:
return[]
# Obtain a list of securities both in the indicator dictionary and in
# the data slice.
common_list = list(set(data.Keys).intersection(self.symbol_data.keys()))
ordered_list = sorted(common_list, key=lambda x: self.symbol_data[x].indicator.Current.Value, reverse=True)
up_momentum = [symbol for symbol in ordered_list][:self.max_positions]
down_momentum = [symbol for symbol in ordered_list][-self.max_positions:]
# Emit the insight for rebalance period.
for symbol in up_momentum + down_momentum:
# Avoid SPY
if symbol == algorithm.Symbol("SPY"):
continue
if data[symbol]:
if symbol in up_momentum:
direction = True
if symbol in down_momentum:
direction = False
if self.is_reversal:
direction = not direction
direction = self.directions[direction]
insight = Insight(symbol,
timedelta(days=self.rebalancing_period),
InsightType.Price,
direction, self.gain_target, 1, self.Name, 1)
insights.append(insight)
# Set next re-balancing date.
self.next_rebalance = algorithm.Time + timedelta(days=self.rebalancing_period)
return Insight.Group(insights)
def OnSecuritiesChanged(self, algorithm, changes):
# Generate and warm-up indicator for added securities:
for security in changes.AddedSecurities:
if security.Symbol not in self.symbol_data:
self.symbol_data[security.Symbol] = SymbolData(security.Symbol,
algorithm,
self.indicator_period)
for symbol in self.symbol_data.keys():
history = algorithm.History(security.Symbol,
self.indicator_period,
Resolution.Daily)
if not history.empty:
self.symbol_data[security.Symbol].WarmUpIndicators(history)
for security in changes.RemovedSecurities:
if security.Symbol in self.symbol_data:
algorithm.SubscriptionManager.RemoveConsolidator(security.Symbol, self.symbol_data[security.Symbol].consolidator)
self.symbol_data.pop(security.Symbol)
return
# Class to hold required symbol data
# Adapted from EMACrossOverUniverseSelectionModel
class SymbolData(object):
def __init__(self, symbol, algorithm, period):
self.symbol = symbol
self.indicator = Momentum(period)
self.consolidator = algorithm.RegisterIndicator(self.symbol,
self.indicator,
Resolution.Daily)
def WarmUpIndicators(self, history):
for time, row in history.loc[self.symbol].iterrows():
self.indicator.Update(time, row["close"])