Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 |
from System import * from QuantConnect import * from QuantConnect.Orders import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Execution import * from QuantConnect.Algorithm.Framework.Risk import * from QuantConnect.Algorithm.Framework.Selection import * from HistoricalReturnsAlphaModel import HistoricalReturnsAlphaModel from BlackLittermanPortfolioConstructionModel import BlackLittermanPortfolioConstructionModel from datetime import timedelta import numpy as np import pandas as pd from scipy.optimize import minimize from CustomFundamentalPortfolioSelectionModel import CustomFundamentalPortfolioSelectionModel from MacdAlphaModel import MacdAlphaModel class MeanVarianceOptimizationAlgorithm(QCAlgorithmFramework): '''Mean Variance Optimization Algorithm.''' def Initialize(self): ''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.''' # Set requested data resolution self.UniverseSettings.Resolution = Resolution.Minute # self.DebugMode = True self.SetStartDate(2015, 10, 1) #Set Start Date self.SetEndDate(2015, 10, 6) #Set End Date self.SetCash(10000000) #Set Strategy Cash # tickers = ['IWD', 'MTUM', 'IWN', 'IWM', 'EFA', 'EEM']#, 'IEF', 'SPY', 'LQD', 'TLT', 'DBC', 'GLD', 'VNQ'] # symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers ] self.UniverseSettings.Resolution = Resolution.Daily self.minimum_weight = -1 self.maximum_weight = 1 # set algorithm framework models # self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) ) self.SetUniverseSelection(CustomFundamentalPortfolioSelectionModel()) # self.SetAlpha( HistoricalReturnsAlphaModel(resolution = Resolution.Daily) ) # self.SetAlpha(CompositeAlphaModel([ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(minutes = 60), 0.025, None), # HistoricalReturnsAlphaModel(resolution = Resolution.Daily), # MacdAlphaModel()])) self.SetAlpha(CompositeAlphaModel( [ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(minutes = 60), 0.0025, None), #ConstantAlphaModel(InsightType.Price, InsightDirection.Down, timedelta(minutes = 60), 0.001, None),])) #MacdAlphaModel(10, 20, 6, MovingAverageType.Simple, Resolution.Daily), HistoricalReturnsAlphaModel(resolution = Resolution.Daily), MacdAlphaModel()])) self.SetPortfolioConstruction( BlackLittermanPortfolioConstructionModel() ) self.SetExecution( ImmediateExecutionModel() ) self.SetRiskManagement( NullRiskManagementModel() ) #def OnOrderEvent(self, orderEvent): # if orderEvent.Status == OrderStatus.Filled: # self.Debug(orderEvent.ToString()) def maximum_sharpe_ratio(self, returns): '''Maximum Sharpe Ratio optimization method''' # Objective function fun = lambda weights: -self.sharpe_ratio(returns, weights) # Constraint #1: The weights can be negative, which means investors can short a security. constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}] size = returns.columns.size x0 = np.array(size * [1. / size]) bounds = tuple((self.minimum_weight, self.maximum_weight) for x in range(size)) opt = minimize(fun, # Objective function x0, # Initial guess method='SLSQP', # Optimization method: Sequential Least SQuares Programming bounds = bounds, # Bounds for variables constraints = constraints) # Constraints definition weights = pd.Series(opt['x'], index = returns.columns) return opt, weights def sharpe_ratio(self, returns, weights): annual_return = np.dot(np.matrix(returns.mean()), np.matrix(weights).T).item() annual_volatility = np.sqrt(np.dot(weights.T, np.dot(returns.cov(), weights))) return annual_return/annual_volatility
# 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") AddReference("QuantConnect.Indicators") from System import * from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget from datetime import timedelta import numpy as np import pandas as pd from scipy.optimize import minimize from itertools import groupby from numpy import dot, transpose from numpy.linalg import inv ### <summary> ### Provides an implementation of Mean-Variance portfolio optimization based on modern portfolio theory. ### The interval of weights in optimization method can be changed based on the long-short algorithm. ### The default model uses the last three months daily price to calculate the optimal weight ### with the weight range from -1 to 1 and minimize the portfolio variance with a target return of 2% ### </summary> class BlackLittermanPortfolioConstructionModel: def __init__(self, *args, **kwargs): """Initialize the model Args: lookback(int): Historical return lookback period resolution: The resolution of the history price minimum_weight(float): The lower bounds on portfolio weights maximum_weight(float): The upper bounds on portfolio weights risk_free_rate(float): The risk free rate self.tau(float): A scalar number indicating the uncertainty of the CAPM prior """ self.lookback = kwargs['lookback'] if 'lookback' in kwargs else 63 # self.period = kwargs['period'] if 'period' in kwargs else 15 self.resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.Daily self.minimum_weight = kwargs['minimum_weight'] if 'minimum_weight' in kwargs else -1 self.maximum_weight = kwargs['maximum_weight'] if 'maximum_weight' in kwargs else 1 self.risk_free_rate = kwargs['risk_free_rate'] if 'risk_free_rate' in kwargs else 0 self.tau = kwargs['risk_free_rate'] if 'risk_free_rate' in kwargs else 0.025 self.symbolDataDict = {} self.pendingRemoval = [] def CreateTargets(self, algorithm, insights): """ Create portfolio targets from the specified insights Args: algorithm: The algorithm instance insights: The insights to create portoflio targets from Returns: An enumerable of portoflio targets to be sent to the execution model """ algorithm.Log("Begin Try Catch") try: algorithm.Lom("CHANGE:: Purposefully calling undefined method") except: algorithm.Error(str(sys.exc_info()[0])) algorithm.Log("End Try Catch") for symbol in self.pendingRemoval: yield PortfolioTarget.Percent(algorithm, symbol, 0) self.pendingRemoval.clear() price = {} for symbol, data in self.symbolDataDict.items(): price[str(symbol)] = data.PriceSeries() df_price = pd.DataFrame(price)#.astype(float) returns = df_price.pct_change().dropna() symbols = list(returns.columns) # market capitalization weight W = np.array([1/len(symbols)]*len(symbols)) # annualized return annual_return = np.sum(((1 + returns.mean())**252 - 1) * W) algorithm.Log("annual_return "+ str(annual_return)) # annualized variance of return annual_variance = dot(W.T, dot(returns.cov()*252, W)) # the risk aversion coefficient risk_aversion = (annual_return - self.risk_free_rate ) / annual_variance algorithm.Log("risk_aversion " + str(risk_aversion)) # the covariance matrix of excess returns (N x N matrix) cov = returns.cov()*252 # the implied excess equilibrium return Vector (N x 1 column vector) equilibrium_return = dot(dot(risk_aversion, cov), W) algorithm.Log("equilibrium_return " + str(equilibrium_return)) # generate the link matrix of views P view = {} insights = sorted(insights, key = lambda x: x.SourceModel) for model, group in groupby(insights, lambda x: x.SourceModel): view[model] = {str(symbol): 0 for symbol in symbols} for insight in group: view[model][str(insight.Symbol)] = insight.Direction view = pd.DataFrame(view).T algorithm.Log("This is view "+str(view.values)) # normalize the view matrix by row up_view = view[view>0].fillna(0) down_view = view[view<0].fillna(0) normalize_up_view = up_view.apply(lambda x: x/sum(x), axis=1).fillna(0) normalize_down_view = down_view.apply(lambda x: -x/sum(x), axis=1).fillna(0) # link matrix: a matrix that identifies the assets involved in the views (K x N matrix) P = normalize_up_view + normalize_down_view # drop the rows with all zero views (flat direction) P = P[~(P == 0).all(axis=1)] algorithm.Log("This is P "+str(P.values)) # the estimated return vector for every different view (K x 1 column vector) Q = [] for model, group in groupby(insights, lambda x: x.SourceModel): if model in P.index: Q.append(sum([P.loc[model][str(insight.Symbol)]*insight.Magnitude for insight in group])) P = np.array(P) Q = np.array(Q) algorithm.Log("This is Q "+str(Q)) # The diagonal covariance matrix of error terms from the expressed views omega = dot(dot(dot(self.tau, P), cov), transpose(P)) algorithm.Log("This is omega "+str(omega)) if np.linalg.det(omega) == 0: expected_return = equilibrium_return algorithm.Log("expected return is equilibrium_return "+str(expected_return)) else: A = inv(dot(self.tau, cov)) + dot(dot(np.transpose(P), inv(omega)), P) B = dot(inv(dot(self.tau, cov)), equilibrium_return) + dot(dot(np.transpose(P), inv(omega)), Q) # The new combined expected return vector expected_return = dot(inv(A), B) algorithm.Log("expected return "+str(expected_return)) # The optimization method processes the data frame opt, weights = self.maximum_sharpe_ratio(expected_return, returns) algorithm.Log("opt "+str(weights)) # Create portfolio targets from the specified insights for insight in insights: weight = weights[str(insight.Symbol)] yield PortfolioTarget.Percent(algorithm, insight.Symbol, weight) 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''' # clean up data for removed securities for removed in changes.RemovedSecurities: self.pendingRemoval.append(removed.Symbol) symbolData = self.symbolDataDict.pop(removed.Symbol, None) for symbol in self.symbolDataDict.keys(): self.symbolDataDict[symbol].Add(algorithm.Time, algorithm.Securities[symbol].Close) # initialize data for newly added securities symbols = [ x.Symbol for x in changes.AddedSecurities] history = algorithm.History(symbols, self.lookback, self.resolution) for symbol in symbols: if symbol not in self.symbolDataDict.keys(): symbolData = SymbolData(symbol, self.lookback) self.symbolDataDict[symbol] = symbolData symbolData.WarmUpHisotryWindow(history.loc[str(symbol)]) def maximum_sharpe_ratio(self, expected_return, returns): '''Maximum Sharpe Ratio optimization method''' # Objective function fun = lambda weights: -self.sharpe_ratio(expected_return, returns, weights) # Constraint #1: The weights can be negative, which means investors can short a security. constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}] size = returns.columns.size x0 = np.array(size * [1. / size]) bounds = tuple((self.minimum_weight, self.maximum_weight) for x in range(size)) opt = minimize(fun, # Objective function x0, # Initial guess method='SLSQP', # Optimization method: Sequential Least SQuares Programming bounds = bounds, # Bounds for variables constraints = constraints) # Constraints definition weights = pd.Series(opt['x'], index = returns.columns) return opt, weights def sharpe_ratio(self, expected_return, returns, weights): annual_return = np.dot(np.matrix(expected_return), np.matrix(weights).T).item() annual_volatility = np.sqrt(np.dot(weights.T, np.dot(returns.cov(), weights))) return annual_return/annual_volatility class SymbolData: '''Contains data specific to a symbol required by this model''' def __init__(self, symbol, lookback): self.Symbol = symbol self.Window = RollingWindow[IndicatorDataPoint](lookback) def WarmUpHisotryWindow(self, history): for tuple in history.itertuples(): item = IndicatorDataPoint(self.Symbol, tuple.Index, float(tuple.close)) self.Window.Add(item) def Add(self, time, value): item = IndicatorDataPoint(self.Symbol, time, value) self.Window.Add(item) def PriceSeries(self): data = [float(x.Value) for x in self.Window] index = [x.EndTime for x in self.Window] return pd.Series(data, index=index)
from clr import AddReference AddReference("System") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Common") AddReference("QuantConnect.Indicators") from System import * from QuantConnect import * from QuantConnect.Util import PythonUtil from QuantConnect.Orders import * from QuantConnect.Indicators import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Execution import * from QuantConnect.Algorithm.Framework.Portfolio import * from QuantConnect.Algorithm.Framework.Risk import * from QuantConnect.Algorithm.Framework.Selection import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Data.UniverseSelection import * from datetime import timedelta import numpy as np class CustomFundamentalPortfolioSelectionModel: def CreateUniverses(self, algorithm): algorithm.AddUniverse(self.SelectCoarse, self.SelectFine) return [] def SelectCoarse(self, coarse): sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True) return [ x.Symbol for x in sortedByDollarVolume[:20] ] def SelectFine(self, fine): sortedByEarningYield = sorted(fine, key=lambda f: f.ValuationRatios.EarningYield, reverse=True) # algorithm.Log("universe "+str([x.Symbol.Value for x in sortedByEarningYield[:5])) return [ x.Symbol for x in sortedByEarningYield[:5] ]
# 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("QuantConnect.Algorithm.Framework") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Common") from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm.Framework.Alphas import * from datetime import timedelta class HistoricalReturnsAlphaModel: '''Uses Historical returns to create insights.''' def __init__(self, *args, **kwargs): '''Initializes a new default instance of the HistoricalReturnsAlphaModel class. Args: lookback(int): Historical return lookback period resolution: The resolution of historical data''' self.lookback = kwargs['lookback'] if 'lookback' in kwargs else 1 self.resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.Daily self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.lookback) self.symbolDataBySymbol = {} 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: The new insights generated''' insights = [] for symbol, symbolData in self.symbolDataBySymbol.items(): if symbolData.CanEmit: direction = InsightDirection.Flat magnitude = symbolData.Return if magnitude > 0: direction = InsightDirection.Up if magnitude < 0: direction = InsightDirection.Down insights.append(Insight.Price(symbol, self.predictionInterval, direction, magnitude, None)) 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''' # clean up data for removed securities for removed in changes.RemovedSecurities: symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None) if symbolData is not None: symbolData.RemoveConsolidators(algorithm) # initialize data for added securities symbols = [ x.Symbol for x in changes.AddedSecurities ] history = algorithm.History(symbols, self.lookback, self.resolution) if history.empty: return tickers = history.index.levels[0] for ticker in tickers: symbol = SymbolCache.GetSymbol(ticker) if symbol not in self.symbolDataBySymbol: symbolData = SymbolData(symbol, self.lookback) self.symbolDataBySymbol[symbol] = symbolData symbolData.RegisterIndicators(algorithm, self.resolution) symbolData.WarmUpIndicators(history.loc[ticker]) class SymbolData: '''Contains data specific to a symbol required by this model''' def __init__(self, symbol, lookback): self.Symbol = symbol self.ROC = RateOfChange('{}.ROC({})'.format(symbol, lookback), lookback) self.Consolidator = None self.previous = 0 def RegisterIndicators(self, algorithm, resolution): self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution) algorithm.RegisterIndicator(self.Symbol, self.ROC, self.Consolidator) def RemoveConsolidators(self, algorithm): if self.Consolidator is not None: algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator) def WarmUpIndicators(self, history): for tuple in history.itertuples(): self.ROC.Update(tuple.Index, tuple.close) @property def Return(self): return float(self.ROC.Current.Value) @property def CanEmit(self): if self.previous == self.ROC.Samples: return False self.previous = self.ROC.Samples return self.ROC.IsReady def __str__(self, **kwargs): return '{}: {:.2%}'.format(self.ROC.Name, (1 + self.Return)**252 - 1)
# 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("QuantConnect.Common") AddReference("QuantConnect.Algorithm.Framework") AddReference("QuantConnect.Indicators") from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm.Framework.Alphas import * class MacdAlphaModel: '''Defines a custom alpha model that uses MACD crossovers. The MACD signal line is used to generate up/down insights if it's stronger than the bounce threshold. If the MACD signal is within the bounce threshold then a flat price insight is returned.''' def __init__(self, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9, movingAverageType = MovingAverageType.Exponential, resolution = Resolution.Daily): ''' Initializes a new instance of the MacdAlphaModel class Args: fastPeriod: The MACD fast period slowPeriod: The MACD slow period</param> signalPeriod: The smoothing period for the MACD signal movingAverageType: The type of moving average to use in the MACD''' self.fastPeriod = fastPeriod self.slowPeriod = slowPeriod self.signalPeriod = signalPeriod self.movingAverageType = movingAverageType self.resolution = resolution self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), fastPeriod) self.bounceThresholdPercent = 0.01 self.symbolData = {}; resolutionString = Extensions.GetEnumString(resolution, Resolution) movingAverageTypeString = Extensions.GetEnumString(movingAverageType, MovingAverageType) self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageTypeString, resolutionString) def Update(self, algorithm, data): ''' Determines an insight for each security based on it's current MACD signal Args: algorithm: The algorithm instance data: The new data available Returns: The new insights generated''' for key, sd in self.symbolData.items(): if sd.Security.Price == 0: continue direction = InsightDirection.Flat normalized_signal = sd.MACD.Signal.Current.Value / sd.Security.Price # algorithm.Log(str(algorithm.Time) + ":" + str(normalized_signal) + ":" + str(sd.MACD.Signal.Current.Value)) if normalized_signal > self.bounceThresholdPercent: direction = InsightDirection.Up elif normalized_signal < -self.bounceThresholdPercent: direction = InsightDirection.Down # ignore signal for same direction as previous signal if direction == sd.PreviousDirection: continue insight = Insight.Price(sd.Security.Symbol, self.insightPeriod, direction) sd.PreviousDirection = insight.Direction yield insight def OnSecuritiesChanged(self, algorithm, changes): '''Event fired each time the we add/remove securities from the data feed. This initializes the MACD for each added security and cleans up the indicator for each removed security. Args: algorithm: The algorithm instance that experienced the change in securities changes: The security additions and removals from the algorithm''' for added in changes.AddedSecurities: self.symbolData[added.Symbol] = SymbolData(algorithm, added, self.fastPeriod, self.slowPeriod, self.signalPeriod, self.movingAverageType, self.resolution) for removed in changes.RemovedSecurities: data = self.symbolData.pop(removed.Symbol, None) if data is not None: # clean up our consolidator algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator) class SymbolData: def __init__(self, algorithm, security, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution): self.Security = security self.MACD = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType) self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution) algorithm.RegisterIndicator(security.Symbol, self.MACD, self.Consolidator) self.PreviousDirection = None