Overall Statistics |
Total Trades 8444 Average Win 0.07% Average Loss -0.06% Compounding Annual Return -3.952% Drawdown 27.600% Expectancy -0.083 Net Profit -20.288% Sharpe Ratio -0.764 Probabilistic Sharpe Ratio 0.004% Loss Rate 55% Win Rate 45% Profit-Loss Ratio 1.06 Alpha -0.04 Beta 0.009 Annual Standard Deviation 0.05 Annual Variance 0.003 Information Ratio -0.896 Tracking Error 0.191 Treynor Ratio -4.088 Total Fees $8714.81 |
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. # Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from clr import AddReference AddReference("System") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Common") from System import * from QuantConnect import * from QuantConnect.Algorithm import * from IntradayMomentumAlphaModel import IntradayMomentumAlphaModel from CloseOnCloseExecutionModel import CloseOnCloseExecutionModel class IntradayETFMomentumAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 1, 1) self.SetEndDate(2020, 8, 16) self.SetCash(50000) tickers = ['SPY', # S&P 500 'IWM', # Russell 2000 'IYR' # Real Estate ETF ] symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers ] self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) ) self.UniverseSettings.Resolution = Resolution.Minute self.SetAlpha(IntradayMomentumAlphaModel(self)) self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: None)) self.SetExecution(CloseOnCloseExecutionModel())
class IntradayMomentumAlphaModel(AlphaModel): """ This class emits insights to take positions for the last `return_bar_count` minutes of the day in the direction of the return for the first `return_bar_count` minutes of the day. """ intraday_momentum_by_symbol = {} sign = lambda _, x: int(x and (1, -1)[x < 0]) def __init__(self, algorithm, return_bar_count = 30): """ Input: - return_bar_count Number of minutes to calculate the morning return over and the number of minutes to hold before the close (0 < return_bar_count < 195) """ if return_bar_count <= 0 or return_bar_count >= 195: algorithm.Quit(f"Requirement violated: 0 < return_bar_count < 195") self.return_bar_count = return_bar_count def Update(self, algorithm, slice): """ Called each time our alpha model receives a new data slice. Input: - algorithm Algorithm instance running the backtest - data A data structure for all of an algorithm's data at a single time step Returns a list of Insights to the portfolio construction model """ insights = [] for symbol, intraday_momentum in self.intraday_momentum_by_symbol.items(): if slice.ContainsKey(symbol) and slice[symbol] is not None: intraday_momentum.bars_seen_today += 1 # End of the morning return if intraday_momentum.bars_seen_today == self.return_bar_count: intraday_momentum.morning_return = (slice[symbol].Close - intraday_momentum.yesterdays_close) / intraday_momentum.yesterdays_close ## Beginning of the close next_close_time = intraday_momentum.exchange.Hours.GetNextMarketClose(slice.Time, False) mins_to_close = int((next_close_time - slice.Time).total_seconds() / 60) if mins_to_close == self.return_bar_count + 1: insight = Insight.Price(intraday_momentum.symbol, next_close_time, self.sign(intraday_momentum.morning_return)) insights.append(insight) continue # End of the day if not intraday_momentum.exchange.DateTimeIsOpen(slice.Time): intraday_momentum.yesterdays_close = slice[symbol].Close intraday_momentum.bars_seen_today = 0 return insights def OnSecuritiesChanged(self, algorithm, changes): """ Called each time our universe has changed. Input: - algorithm Algorithm instance running the backtest - changes The additions and subtractions to the algorithm's security subscriptions """ for security in changes.AddedSecurities: self.intraday_momentum_by_symbol[security.Symbol] = IntradayMomentum(security, algorithm) for security in changes.RemovedSecurities: self.intraday_momentum_by_symbol.pop(security.Symbol, None) class IntradayMomentum: """ This class manages the data used for calculating the morning return of a security. """ def __init__(self, security, algorithm): """ Input: - security The security to trade - algorithm Algorithm instance running the backtest """ self.symbol = security.Symbol self.exchange = security.Exchange self.bars_seen_today = 0 self.yesterdays_close = algorithm.History(self.symbol, 1, Resolution.Daily).loc[self.symbol].close[0] self.morning_return = 0
class CloseOnCloseExecutionModel(ExecutionModel): """ Provides an implementation of IExecutionModel that immediately submits a market order to achieve the desired portfolio targets and an associated market on close order. """ def __init__(self): self.targetsCollection = PortfolioTargetCollection() self.invested_symbols = [] def Execute(self, algorithm, targets): """ Immediately submits orders for the specified portfolio targets. Input: - algorithm Algorithm instance running the backtest - targets The portfolio targets to be ordered """ # for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call self.targetsCollection.AddRange(targets) if self.targetsCollection.Count > 0: for target in self.targetsCollection.OrderByMarginImpact(algorithm): # calculate remaining quantity to be ordered quantity = OrderSizing.GetUnorderedQuantity(algorithm, target) if quantity == 0: continue algorithm.MarketOrder(target.Symbol, quantity) algorithm.MarketOnCloseOrder(target.Symbol, -quantity) self.targetsCollection.ClearFulfilled(algorithm)