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"])