Overall Statistics |
Total Trades 1557 Average Win 0.35% Average Loss -0.17% Compounding Annual Return 10.530% Drawdown 17.100% Expectancy 0.793 Net Profit 190.876% Sharpe Ratio 1.34 Loss Rate 41% Win Rate 59% Profit-Loss Ratio 2.03 Alpha 0.063 Beta 2.004 Annual Standard Deviation 0.077 Annual Variance 0.006 Information Ratio 1.08 Tracking Error 0.077 Treynor Ratio 0.051 Total Fees $2545.60 |
from clr import AddReference AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm.Framework") from System import * from QuantConnect import * from QuantConnect.Orders import * from QuantConnect.Algorithm.Framework.Execution import ExecutionModel from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTargetCollection from datetime import datetime, timedelta from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) class Zero_Leverage_ExecutionModel(ExecutionModel): def __init__(self, resolution = Resolution.Daily, min_order = 0): self.targetsCollection = PortfolioTargetCollection() self.finished = False self.rebalancingTime = UTCMIN self.rebalancingPeriod = Extensions.ToTimeSpan(resolution) self.min_order_value = min_order def Execute(self, algorithm, targets): #Run until all orders are completed once per day if self.finished and algorithm.UtcTime <= self.rebalancingTime: return elif algorithm.UtcTime > self.rebalancingTime: self.finished = False self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod self.targetsCollection.AddRange(targets) sold = False bought = False for target in self.targetsCollection.OrderByMarginImpact(algorithm): open_quantity = sum([x.Quantity for x in algorithm.Transactions.GetOpenOrders(target.Symbol)]) existing = algorithm.Securities[target.Symbol].Holdings.Quantity + open_quantity qty_to_order = target.Quantity - existing value = abs(qty_to_order) * algorithm.Securities[target.Symbol].Price #Only sell if it exeeds min order value or if selling everything, to prevent being left with a tiny amount of stock unable to sell if qty_to_order <= -1.0 and (value >= self.min_order_value or target.Quantity == 0): algorithm.MarketOrder(target.Symbol, qty_to_order) #algorithm.Debug("Sell = " + str(target.Symbol) + " > " + str(quantity)) sold = True #Only buy if it exeeds min order value and no sells have also processed in this same instant to avoid leverage elif qty_to_order >= 1.0 and value >= self.min_order_value and sold == False: #Don't buy stocks if there are still open sell orders to prevent leverage all_open_sells_value = 0 for order in algorithm.Transactions.GetOpenOrders(): if order.Quantity < 0: all_open_sells_value += abs(order.Quantity) * algorithm.Securities[order.Symbol].Price if all_open_sells_value == 0: algorithm.MarketOrder(target.Symbol, qty_to_order) #algorithm.Debug("Buy = " + str(target.Symbol) + " > " + str(quantity)) bought = True else: bought = True if bought == False and sold == False: self.finished = True self.targetsCollection.ClearFulfilled(algorithm)
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 Utility_Functions import * from datetime import timedelta import numpy as np import pandas as pd import math import itertools from datetime import datetime, timedelta from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) class Alpha_Composite: def __init__(self, variables, *args, **kwargs): self.ACR_assets = [ "VOE", "VDC", "XLP", "IJR", ] self.ACR_bonds = [ "TLT", "TIP", "DBC", "SHY", ] self.ACR_sectors = [ "XLB", #Materials "XLY", #Consumer Cyclical "XLF", #Financials "IYR", #ISHARES Real Estate "XLP", #Consumer Defensive "XLV", #Healthcare "XLU", #Utilities "XLE", #Energy "XLI", #Industrials "XLK", #Tech ] self.ACR_fixed = [ "SPY", ] #self.Hedge = ["VXX",] self.ACR_stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed# + self.Hedge """ ACR (Asset Class Rotation) parameters """ self.ACR_sector_step = 13 #12% step change = all bonds if 9 of 11 sectors down self.ACR_asset_step = 20 #20% step change self.ACR_allocation = {} self.ACR_fixed_weight = [ 0.0, #SPY ] self.ACR_stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed# + self.Hedge #Variable Definitions self.resolution = Resolution.Daily self.rebalancingTime = UTCMIN self.days_predicted = 5 self.rebalancingPeriod = Extensions.ToTimeSpan(self.resolution)*self.days_predicted self.var = variables #for stock in self.ACR_sectors: #self.var.AddEquity(stock) self.symbolData = {} for symbol in self.ACR_stocks: self.ACR_allocation[symbol] = 0.0 """ VAA parameters """ self.periods = [42,21,63,126,252] self.weights = [0, 12, 6, 3, 1] self.GrowthSymbols = [ "SPY", #SPDR S&P 500 Trust ETF "VEA", #Vanguard FTSE Developed Markets ETF "VTI", #Vanguard Total Stock Market ETF "VT", #Vanguard Total World Stock Index Fund "VWO", #Vanguard FTSE Emerging Markets ETF "AGG", #iShares Barclays Aggregate Bond ETF ] self.SafetySymbols = [ "HYD", #VanEck Vectors High-Yield Municipal ETF "VMBS", #Vanguard Mortgage-Backed Securities ETF "BKLN", #Invesco Senior Loan ETF "JNK", #SPDR Barclays High Yield Bond ETF "TLT", #iShares 20+ Year Treasury Bond ETF ] #Variable Definitions self.symbolDataBySymbol = {} self.VAA_allocation = {} self.VAA_stocks = self.GrowthSymbols + self.SafetySymbols for symbol in self.VAA_stocks: self.VAA_allocation[symbol] = 0.0 ''' Zscore ''' #zscore assets self.ZScore_stocks = [ "SPY", "TLT", "XLP", #"ZIV", ] """ zscore parameters """ self.fixed_wt_pct = 0.0 #.50 self.fixed_wt = { "XLP": 0.50, "TLT": 0.40, #"ZIV": 0.10, } self.vol_factor = 0.50 #TLT = .5, SPY = 1 self.ext_factor = 4.0 #move too extreme, leave asset self.lookback = 150 self.ZScore_allocation = {} self.stocks = self.ZScore_stocks + self.VAA_stocks + self.ACR_stocks self.stocks = (list(set(self.stocks))) for symbol in self.ZScore_stocks: self.ZScore_allocation[symbol] = 0.0 def Update(self, algorithm, data): #algorithm.Debug("Updating Alpha Model.") insights = [] collect_insights = {} for stock in self.stocks: collect_insights[stock] = {} if (algorithm.UtcTime <= self.rebalancingTime): return insights for sid in self.ACR_stocks: if not sid in self.ACR_allocation: self.ACR_allocation[sid] = 0.0 ACR_assets_weight = np.zeros(len(self.ACR_assets)) ACR_bonds_data = pd.DataFrame(0, columns=['Weight','Ratio','20Day','60Day'],index=self.ACR_bonds) ACR_sectors_data = pd.DataFrame(0, columns=['Ratio','20Day','200Day'],index=self.ACR_sectors) """ Determine sector trends and calculate weight to assets/bonds """ ACR_sectors_data.loc[:,'20Day'] = algorithm.History(self.ACR_sectors, 20, Resolution.Daily)["close"].unstack(level=0).mean() ACR_sectors_data.loc[:, '200Day'] = algorithm.History(self.ACR_sectors, 200, Resolution.Daily)["close"].unstack(level=0).mean() ACR_sectors_data['Ratio'] = ACR_sectors_data['20Day']/ACR_sectors_data['200Day'] - 1 ACR_bonds_weight = len(ACR_sectors_data[ACR_sectors_data['Ratio'] < 0]) * self.ACR_sector_step/100.0 if ACR_bonds_weight > 1.0: ACR_bonds_weight = 1.0 ACR_bonds_weight = ACR_bonds_weight * (1-sum(self.ACR_fixed_weight)) """ Determine bond trends and which duration to be in """ if ACR_bonds_weight > 0.0: ACR_bonds_data.loc[:,'20Day'] = algorithm.History(self.ACR_bonds, 20, Resolution.Daily)["close"].unstack(level=0).mean() ACR_bonds_data.loc[:, '60Day'] = algorithm.History(self.ACR_bonds, 60, Resolution.Daily)["close"].unstack(level=0).mean() ACR_bonds_data['Ratio'] = ACR_bonds_data['20Day']/ACR_bonds_data['60Day'] - 1 ACR_bonds_data['Weight'] = 0 ACR_bonds_data.loc[ACR_bonds_data['Ratio'].idxmax(), 'Weight'] = ACR_bonds_weight #log.info(self.ACR_bonds_data) returns = algorithm.History(self.ACR_assets, 126, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna() + 1.0 expected = algorithm.History(self.ACR_stocks, 20, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna().mean() + 1.0 """ Create portfolio combinations """ n = len(self.ACR_assets) steps = [x/100.0 for x in range(0,101,int(self.ACR_asset_step))] a = [steps for x in range(n)] b = list(itertools.product(*a)) x = [sum(i) for i in b] port = pd.DataFrame(b) port['Sum'] = x port = port[port.Sum == 1] del port['Sum'] """ Score and Weight portoflio """ port_returns = pd.DataFrame(np.dot(returns, port.T), index=returns.index) port_metrics = self.ACR_get_specs(port_returns) port_metrics = self.ACR_score(port_metrics) port_metrics['Z3'] = port_metrics.ZMean\ -port_metrics.ZSTD\ -port_metrics.ZDownSide\ +port_metrics.ZAnnualized\ -port_metrics.ZMax_Draw\ -port_metrics.ZSTD10\ +port_metrics.ZMean10\ +port_metrics.ZMinimum15 port_metrics = port_metrics.sort_values(by='Z3', ascending=False) portfolios = port portfolios.columns = list(returns.columns.values) best = pd.concat([pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]).T]) #log.info(best.loc[:, (best != 0).any(axis=0)].T) best = pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]) #log.info(best) ACR_assets_weight = [i[0]*(1-ACR_bonds_weight-sum(self.ACR_fixed_weight)) for i in best.values] predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.days_predicted) allocation_temp = {} magnitude = {} direction = {} risk_weight = 0 for x in range(n): allocation_temp[self.ACR_assets[x]] = ACR_assets_weight[x]#*.95 #expected_temp[self.ACR_assets[x]] = risk_weight += ACR_assets_weight[x] for stock in self.ACR_stocks: magnitude[stock] = ((expected[stock]**self.days_predicted)-1.0)/5.0 #magnitude[stock] = 0.001 #algorithm.Debug(str(stock) + " magnitude = " + str(magnitude[stock])) if magnitude[stock] > -0.01: direction[stock] = InsightDirection.Up else: direction[stock] = InsightDirection.Flat magnitude[stock] = 0.001 #self.ACR_allocation[self.Hedge[0]] = risk_weight * .05 for stock in self.ACR_bonds: allocation_temp[stock] = ACR_bonds_data.loc[stock, 'Weight'] for x in range(len(self.ACR_fixed)): allocation_temp[self.ACR_fixed[x]] = self.ACR_fixed_weight[x] for symbol in self.ACR_allocation: if (self.ACR_allocation[symbol] != allocation_temp[symbol]) or (self.ACR_allocation[symbol] != 0.0) or (allocation_temp[symbol] != 0.0): #if allocation_temp[symbol] > self.ACR_allocation[symbol]: self.ACR_allocation[symbol] = allocation_temp[symbol] #insights.append(Insight(symbol, predictionInterval, InsightType.Price, InsightDirection.Up, magnitude[symbol], self.ACR_allocation[symbol], sourceModel="Test")) collect_insights[symbol]["ACR"] = [predictionInterval, InsightType.Price, InsightDirection.Up, magnitude[symbol], self.ACR_allocation[symbol]] #algorithm.Debug(str(symbol) + " = " + str(self.ACR_allocation[symbol])) ''' VAA ''' ##Using a weighted average, compute the score for each risky asset. growthdata = [] for symbol in self.GrowthSymbols: if not symbol in self.symbolDataBySymbol.keys(): continue temp_values = [] for i, period in enumerate(self.periods): if self.symbolDataBySymbol[symbol][i].CanEmit: temp_values.append(self.symbolDataBySymbol[symbol][i].Return) else: temp_values.append(0.0) score = sum([i*j for i,j in zip(temp_values,self.weights)]) growthdata.append([symbol, score, temp_values[0]]) orderedGrowthScores = sorted(growthdata, key=lambda x: x[1], reverse=True) #algorithm.Debug(orderedGrowthScores) ##Using a weighted average, compute the score for each risk-free asset. ##This approach overweights the front month momentum value and progressively underweights older momentum values safetydata = [] for symbol in self.SafetySymbols: #self.var.Debug(symbol) #self.var.Debug(self.symbolDataBySymbol.keys()) if not symbol in self.symbolDataBySymbol.keys(): continue #self.var.Debug("2") temp_values = [] for i, period in enumerate(self.periods): #self.var.Debug("3") if self.symbolDataBySymbol[symbol][i].CanEmit: #self.var.Debug("4") temp_values.append(self.symbolDataBySymbol[symbol][i].Return) else: temp_values.append(0.0) #self.var.Debug("5") #score = np.average(temp_values, weights = self.weights) score = sum([i*j for i,j in zip(temp_values,self.weights)]) safetydata.append([symbol, score, temp_values[0]]) orderedSafeScores = sorted(safetydata, key=lambda x: x[1], reverse=True) #algorithm.Debug(orderedSafeScores) ##Count the number of risky assets with negative momentum scores and store in N. If all four of the offensive assets exhibit positive momentum scores, ##select the offensive asset with the highest score and allocate 100% of the portfolio to that asset at the close count = 0 negative_flag = False for stock in orderedGrowthScores: if stock[1] <= 0.0: count += 1 if count > 0: negative_flag = True top_growth = orderedGrowthScores[0][0] second_growth = orderedGrowthScores[1][0] top_safe = orderedSafeScores[0][0] second_safe = orderedSafeScores[1][0] predictionInterval = Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), self.days_predicted) stock_data = {} for data in (orderedGrowthScores + orderedSafeScores): #stock_data[stock] = [score, momentum, magnitude, direction] score = data[1] if score == 0.0: #DATA ERROR continue momentum = data[2] magnitude = (data[2]/100*(self.days_predicted/self.periods[0])) if magnitude > -.005: direction = InsightDirection.Up #elif magnitude < -.005: #direction = InsightDirection.Down else: direction = InsightDirection.Flat stock_data[data[0]] = [score, momentum, magnitude, direction] for symbol in stock_data: weight = 0 if symbol == top_growth: if negative_flag: weight = 0.1 else: weight = 0.5 elif symbol == second_growth: if negative_flag: weight = 0.1 else: weight = 0.5 elif symbol == top_safe: if negative_flag: weight = 0.4 else: weight = 0.0 elif symbol == second_safe: if negative_flag: weight = 0.4 else: weight = 0.0 else: weight = 0 if self.VAA_allocation[symbol] != weight: #insights.append(Insight(symbol, predictionInterval, InsightType.Price, stock_data[symbol][3], stock_data[symbol][2], weight, sourceModel="Test")) collect_insights[symbol]["VAA"] = [predictionInterval, InsightType.Price, stock_data[symbol][3], stock_data[symbol][2], weight] self.VAA_allocation[symbol] = weight ''' ZScore ''' safestock = "TLT" history = algorithm.History([safestock], self.lookback, Resolution.Daily) mean = history['close'].mean() sigma = history['close'].std() price = float(algorithm.Securities[safestock].Price) if sigma != 0.0: z = (price - mean) / sigma**self.vol_factor else: z = 0.0 if -self.ext_factor <= z <= self.ext_factor: tlt_target = 1.0/(1+math.exp(-1.2*z)) # Pure momentum adding 1 to the sin wave to prevent shorting else: tlt_target = 0.0 spy_target = (1.0-tlt_target) allocation_temp = {} magnitude = {} direction = {} for sid in self.ZScore_stocks: allocation_temp[sid] = 0.0 if sid in self.fixed_wt: allocation_temp[sid] = self.fixed_wt[sid] * self.fixed_wt_pct allocation_temp[safestock] += tlt_target * (1.0 - self.fixed_wt_pct) allocation_temp["SPY"] += spy_target * (1.0 - self.fixed_wt_pct) predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.days_predicted) expected = algorithm.History(self.ZScore_stocks, 5, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna().mean() + 1.0 for stock in self.ZScore_stocks: magnitude[stock] = ((expected[stock]**self.days_predicted)-1.0)/2.0 if magnitude[stock] > -0.005: direction[stock] = InsightDirection.Up else: direction[stock] = InsightDirection.Flat for symbol in self.ZScore_allocation: if (self.ZScore_allocation[symbol] != allocation_temp[symbol]) or (self.ZScore_allocation[symbol] != 0.0) or (allocation_temp[symbol] != 0.0): self.ZScore_allocation[symbol] = allocation_temp[symbol] #insights.append(Insight(symbol, predictionInterval, InsightType.Price, direction[symbol], magnitude[symbol], self.ZScore_allocation[symbol], sourceModel="Test")) collect_insights[symbol]["ZScore"] = [predictionInterval, InsightType.Price, direction[symbol], magnitude[symbol], self.ZScore_allocation[symbol]] #algorithm.Debug(str(symbol) + " = " + str(self.ZScore_allocation[symbol])) ''' combined ''' for symbol in self.stocks: num_alphas = 3.0 magnitude = 0.0 allocation = 0.0 direction = InsightDirection.Flat if collect_insights[symbol] != {}: for alpha in collect_insights[symbol].keys(): allocation += collect_insights[symbol][alpha][4]/num_alphas magnitude += collect_insights[symbol][alpha][3]/num_alphas if magnitude > -0.005: direction = InsightDirection.Up else: direction = InsightDirection.Flat insights.append(Insight(symbol, predictionInterval, InsightType.Price, direction, magnitude, allocation)) self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod return Insight.Group( insights ) def ACR_drawdown(self, returns): mat = returns.cumprod().values [n, m] = np.shape(mat) maxes = np.maximum.accumulate(np.array(mat)) for i in range(0,n): for j in range(m): mat[i,j] = mat[i,j] / maxes[i,j] df = pd.DataFrame(mat) df[df > 1] = 1 return df def ACR_moving_returns(self, returns, w): mat = returns.values [n, m] = np.shape(mat) ret = np.zeros(shape = (n-w+1,m)) for i in range(w-1,n): for j in range(m): ret[i-w+1,j] = np.power(np.prod(mat[(i-w+1):i+1,j]),(1.0/w))- 1.0 return pd.DataFrame(ret) def ACR_get_specs(self, returns): metrics = pd.DataFrame((returns.mean()),columns=['Mean']) - 1.0 metrics['STD'] = pd.DataFrame((returns.std())) metrics['Annualized'] = np.power(returns.cumprod().values.tolist()[-1],1.0/len(returns))- 1.0 downside = returns.copy(deep=True) - 1 downside[downside > 0] = 0 downside = downside ** 2 metrics['DownSide'] = pd.DataFrame(downside.mean() ** 0.5) draw = self.ACR_drawdown(returns) metrics['Max_Draw'] = 1.0 - draw.min().values ret15 = self.ACR_moving_returns(returns,21) metrics['Minimum15'] = ret15.min().values ret10 = self.ACR_moving_returns(returns,21) metrics['Mean10'] = ret10.mean().values metrics['STD10'] = ret10.std().values return metrics def ACR_zscore(self, stocks, var, var_save): stocks[var_save] = (stocks[var] - stocks[var].mean())/stocks[var].std(ddof=0) return stocks def ACR_score(self, metrics): metrics = self.ACR_zscore(metrics, 'Mean', 'ZMean') metrics = self.ACR_zscore(metrics, 'STD', 'ZSTD') metrics = self.ACR_zscore(metrics, 'Annualized', 'ZAnnualized') metrics = self.ACR_zscore(metrics, 'DownSide', 'ZDownSide') metrics = self.ACR_zscore(metrics, 'Max_Draw', 'ZMax_Draw') metrics = self.ACR_zscore(metrics, 'Minimum15', 'ZMinimum15') metrics = self.ACR_zscore(metrics, 'STD10', 'ZSTD10') metrics = self.ACR_zscore(metrics, 'Mean10', 'ZMean10') return metrics def OnSecuritiesChanged(self, algorithm, changes): algorithm.Debug("Securities Changed. Added " + str(changes.AddedSecurities)) symbols = [ x.Symbol for x in changes.AddedSecurities if str(x.Symbol) in self.VAA_stocks ] history = algorithm.History(symbols, (np.max(self.periods)), Resolution.Daily) if history.empty: algorithm.Debug("History Error") return for removed in changes.RemovedSecurities: if removed.Symbol in self.VAA_stocks: symbolData_temp = self.symbolDataBySymbol.pop(removed.Symbol, None) if symbolData_temp is not None: symbolData_temp.RemoveConsolidators(algorithm) for stock in history.index.levels[0]: symbol = SymbolCache.GetSymbol(stock) algorithm.Debug("Getting Data for " + str(symbol)) if str(symbol) not in self.symbolDataBySymbol.keys(): algorithm.Debug("registering symboldata for " + str(symbol)) symbolData_temp = [] for period in self.periods: #algorithm.Debug("C") tempData = SymbolData(symbol, period) tempData.RegisterIndicators(algorithm, Resolution.Daily) tempData.WarmUpIndicators(history.loc[stock]) symbolData_temp.append(tempData) self.symbolDataBySymbol[str(symbol)] = symbolData_temp #algorithm.Debug(symbolData_temp) class SymbolData: def __init__(self, symbol, lookback): self.Symbol = symbol self.MOM = Momentum('{}.MOM({})'.format(symbol, lookback), lookback) self.Consolidator = None self.previous = 0 def RegisterIndicators(self, algorithm, resolution): #algorithm.Debug("Register Indicators. Alpha") self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution) algorithm.RegisterIndicator(self.Symbol, self.MOM, 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.MOM.Update(tuple.Index, tuple.close) @property def Return(self): return float(self.MOM.Current.Value) @property def CanEmit(self): if self.previous == self.MOM.Samples: return False self.previous = self.MOM.Samples return self.MOM.IsReady ''' def __str__(self, **kwargs): return '{}: {:.2%}'.format(self.MOM.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("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm.Framework") from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel from QuantConnect.Algorithm.Framework.Alphas import InsightCollection from datetime import datetime from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) class Drawdown_Risk_Model(RiskManagementModel): def __init__(self, max_drawdown = 0.025, security_stop = .025, multi_day_slide_stop = .025, wait_days = 5): self.insightCollection = InsightCollection() self.rebalancingTime = UTCMIN self.rebalancingPeriod = Extensions.ToTimeSpan(Resolution.Daily) self.max_drawdown = -abs(max_drawdown) self.sec_stop = -abs(security_stop) self.multi_day_slide_stop = -abs(multi_day_slide_stop) self.wait_days = int(wait_days) self.blacklist = {} self.ago_portfolio = .0001 self.yesterday_portfolio = .0001 self.today_portfolio = .0001 self.max_portfolio = .0001 self.waited_days = 0 self.port_liquidate = False def ManageRisk(self, algorithm, targets): targets = [] if algorithm.UtcTime > self.rebalancingTime: self.ago_portfolio = self.yesterday_portfolio self.yesterday_portfolio = self.today_portfolio self.today_portfolio = float(algorithm.Portfolio.TotalPortfolioValue) self.max_portfolio = max(self.max_portfolio, self.today_portfolio) for symbol in self.blacklist: self.blacklist[symbol] +=1 if self.port_liquidate: algorithm.Debug("Portfolio drop stop! Wait " + str(self.wait_days) + ". " + str(algorithm.UtcTime)) self.waited_days += 1 if self.waited_days > self.wait_days: self.port_liquidate = False self.waited_days = 0 self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod current_portfolio = float(algorithm.Portfolio.TotalPortfolioValue) if ((current_portfolio/self.max_portfolio)-1.0) < self.max_drawdown: self.max_portfolio = current_portfolio self.port_liquidate = True algorithm.Debug("Max drop stop! Wait " + str(self.wait_days) + ". " + str(algorithm.UtcTime)) if (((current_portfolio/self.ago_portfolio)-1.0) < self.multi_day_slide_stop) or (((current_portfolio/self.yesterday_portfolio)-1.0) < self.multi_day_slide_stop) or self.port_liquidate: self.port_liquidate = True for kvp in algorithm.Securities: security = kvp.Value if security.Invested: self.insightCollection.Clear([security.Symbol]) targets.append(PortfolioTarget(security.Symbol, 0)) for kvp in algorithm.Securities: security = kvp.Value liquidate = False pnl = 0.0 if security.Invested: pnl = security.Holdings.UnrealizedProfitPercent if pnl < self.sec_stop: liquidate = True if security.Symbol in self.blacklist: if self.blacklist[security.Symbol] > self.wait_days: self.blacklist.pop(security.Symbol, None) else: liquidate = True elif liquidate: algorithm.Debug("Stop on " + str(security.Symbol) + "! Wait " + str(self.wait_days) + ". " + str(algorithm.UtcTime)) self.blacklist[security.Symbol] = 0 if liquidate: self.insightCollection.Clear([security.Symbol]) targets.append(PortfolioTarget(security.Symbol, 0)) return targets
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 Utility_Functions import * from datetime import timedelta import numpy as np import pandas as pd import itertools from datetime import datetime, timedelta from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) class Alpha_ACR: def __init__(self, variables, *args, **kwargs): """ ACR (Asset Class Rotation) parameters """ self.ACR_sector_step = 13 #12% step change = all bonds if 9 of 11 sectors down self.ACR_asset_step = 20 #20% step change self.allocation = {} self.ACR_fixed_weight = [ 0.0, #SPY ] self.ACR_assets = [ "VOE", "VDC", "XLP", "IJR", ] self.ACR_bonds = [ "TLT", "TIP", "DBC", "SHY", ] self.ACR_sectors = [ "XLB", #Materials "XLY", #Consumer Cyclical "XLF", #Financials "IYR", #ISHARES Real Estate "XLP", #Consumer Defensive "XLV", #Healthcare "XLU", #Utilities "XLE", #Energy "XLI", #Industrials "XLK", #Tech ] self.ACR_fixed = [ "SPY", ] #self.Hedge = ["VXX",] self.stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed# + self.Hedge #Variable Definitions self.resolution = Resolution.Daily self.rebalancingTime = UTCMIN self.days_predicted = 5 self.rebalancingPeriod = Extensions.ToTimeSpan(self.resolution)*self.days_predicted self.var = variables #for stock in self.ACR_sectors: #self.var.AddEquity(stock) self.symbolData = {} for symbol in self.stocks: self.allocation[symbol] = 0.0 def Update(self, algorithm, data): #algorithm.Debug("Updating Alpha Model.") insights = [] if (algorithm.UtcTime <= self.rebalancingTime): return insights self.symbolData["sectors20"] = algorithm.History(self.ACR_sectors, 20, Resolution.Daily)["close"] self.symbolData["sectors200"] = algorithm.History(self.ACR_sectors, 200, Resolution.Daily)["close"] self.symbolData["bonds20"] = algorithm.History(self.ACR_bonds, 20, Resolution.Daily)["close"] self.symbolData["bonds60"] = algorithm.History(self.ACR_bonds, 60, Resolution.Daily)["close"] self.symbolData["assets126"] = algorithm.History(self.ACR_assets, 126, Resolution.Daily)["close"] self.symbolData["return5"] = algorithm.History(self.stocks, 5, Resolution.Daily)["close"] ACR_assets_weight = np.zeros(len(self.ACR_assets)) ACR_bonds_data = pd.DataFrame(0, columns=['Weight','Ratio','20Day','60Day'],index=self.ACR_bonds) ACR_sectors_data = pd.DataFrame(0, columns=['Ratio','20Day','200Day'],index=self.ACR_sectors) """ Determine sector trends and calculate weight to assets/bonds """ ACR_sectors_data.loc[:,'20Day'] = self.symbolData["sectors20"].unstack(level=0).mean() ACR_sectors_data.loc[:, '200Day'] = self.symbolData["sectors200"].unstack(level=0).mean() ACR_sectors_data['Ratio'] = ACR_sectors_data['20Day']/ACR_sectors_data['200Day'] - 1 ACR_bonds_weight = len(ACR_sectors_data[ACR_sectors_data['Ratio'] < 0]) * self.ACR_sector_step/100.0 if ACR_bonds_weight > 1.0: ACR_bonds_weight = 1.0 ACR_bonds_weight = ACR_bonds_weight * (1-sum(self.ACR_fixed_weight)) """ Determine bond trends and which duration to be in """ if ACR_bonds_weight > 0.0: ACR_bonds_data.loc[:,'20Day'] = self.symbolData["bonds20"].unstack(level=0).mean() ACR_bonds_data.loc[:, '60Day'] = self.symbolData["bonds60"].unstack(level=0).mean() ACR_bonds_data['Ratio'] = ACR_bonds_data['20Day']/ACR_bonds_data['60Day'] - 1 ACR_bonds_data['Weight'] = 0 ACR_bonds_data.loc[ACR_bonds_data['Ratio'].idxmax(), 'Weight'] = ACR_bonds_weight #log.info(self.ACR_bonds_data) returns = self.symbolData["assets126"].unstack(level=0).dropna().pct_change().dropna() + 1.0 expected = self.symbolData["return5"].unstack(level=0).dropna().pct_change().dropna().mean() + 1.0 #algorithm.Debug(expected) """ Create portfolio combinations """ n = len(self.ACR_assets) steps = [x/100.0 for x in range(0,101,int(self.ACR_asset_step))] a = [steps for x in range(n)] b = list(itertools.product(*a)) x = [sum(i) for i in b] port = pd.DataFrame(b) port['Sum'] = x port = port[port.Sum == 1] del port['Sum'] """ Score and Weight portoflio """ port_returns = pd.DataFrame(np.dot(returns, port.T), index=returns.index) port_metrics = self.ACR_get_specs(port_returns) port_metrics = self.ACR_score(port_metrics) port_metrics['Z3'] = port_metrics.ZMean\ -port_metrics.ZSTD\ -port_metrics.ZDownSide\ +port_metrics.ZAnnualized\ -port_metrics.ZMax_Draw\ -port_metrics.ZSTD10\ +port_metrics.ZMean10\ +port_metrics.ZMinimum15 port_metrics = port_metrics.sort_values(by='Z3', ascending=False) portfolios = port portfolios.columns = list(returns.columns.values) best = pd.concat([pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]).T]) #log.info(best.loc[:, (best != 0).any(axis=0)].T) best = pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]) #log.info(best) ACR_assets_weight = [i[0]*(1-ACR_bonds_weight-sum(self.ACR_fixed_weight)) for i in best.values] allocation_temp = {} magnitude = {} direction = {} risk_weight = 0 for x in range(n): allocation_temp[self.ACR_assets[x]] = ACR_assets_weight[x]#*.95 #expected_temp[self.ACR_assets[x]] = risk_weight += ACR_assets_weight[x] predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.days_predicted) for stock in self.stocks: magnitude[stock] = ((expected[stock]**self.days_predicted)-1.0) if magnitude[stock] > 0.0: direction[stock] = InsightDirection.Up elif magnitude[stock] < 0.0: direction[stock] = InsightDirection.Down else: direction[stock] = InsightDirection.Flat #self.allocation[self.Hedge[0]] = risk_weight * .05 for stock in self.ACR_bonds: allocation_temp[stock] = ACR_bonds_data.loc[stock, 'Weight'] for x in range(len(self.ACR_fixed)): allocation_temp[self.ACR_fixed[x]] = self.ACR_fixed_weight[x] for symbol in self.allocation: if (self.allocation[symbol] != allocation_temp[symbol]) or (self.allocation[symbol] != 0.0) or (allocation_temp[symbol] != 0.0): self.allocation[symbol] = allocation_temp[symbol] insights.append(Insight(symbol, predictionInterval, InsightType.Price, direction[symbol], magnitude[symbol], self.allocation[symbol], sourceModel="Test")) #algorithm.Debug(str(symbol) + " = " + str(self.allocation[symbol])) #insights = [] #Remove me #return insights self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod return Insight.Group( insights ) def ACR_drawdown(self, returns): mat = returns.cumprod().values [n, m] = np.shape(mat) maxes = np.maximum.accumulate(np.array(mat)) for i in range(0,n): for j in range(m): mat[i,j] = mat[i,j] / maxes[i,j] df = pd.DataFrame(mat) df[df > 1] = 1 return df def ACR_moving_returns(self, returns, w): mat = returns.values [n, m] = np.shape(mat) ret = np.zeros(shape = (n-w+1,m)) for i in range(w-1,n): for j in range(m): ret[i-w+1,j] = np.power(np.prod(mat[(i-w+1):i+1,j]),(1.0/w))- 1.0 return pd.DataFrame(ret) def ACR_get_specs(self, returns): metrics = pd.DataFrame((returns.mean()),columns=['Mean']) - 1.0 metrics['STD'] = pd.DataFrame((returns.std())) metrics['Annualized'] = np.power(returns.cumprod().values.tolist()[-1],1.0/len(returns))- 1.0 downside = returns.copy(deep=True) - 1 downside[downside > 0] = 0 downside = downside ** 2 metrics['DownSide'] = pd.DataFrame(downside.mean() ** 0.5) draw = self.ACR_drawdown(returns) metrics['Max_Draw'] = 1.0 - draw.min().values ret15 = self.ACR_moving_returns(returns,21)#21 metrics['Minimum15'] = ret15.min().values ret10 = self.ACR_moving_returns(returns,21)#21 metrics['Mean10'] = ret10.mean().values metrics['STD10'] = ret10.std().values return metrics def ACR_zscore(self, stocks, var, var_save): stocks[var_save] = (stocks[var] - stocks[var].mean())/stocks[var].std(ddof=0) return stocks def ACR_score(self, metrics): metrics = self.ACR_zscore(metrics, 'Mean', 'ZMean') metrics = self.ACR_zscore(metrics, 'STD', 'ZSTD') metrics = self.ACR_zscore(metrics, 'Annualized', 'ZAnnualized') metrics = self.ACR_zscore(metrics, 'DownSide', 'ZDownSide') metrics = self.ACR_zscore(metrics, 'Max_Draw', 'ZMax_Draw') metrics = self.ACR_zscore(metrics, 'Minimum15', 'ZMinimum15') metrics = self.ACR_zscore(metrics, 'STD10', 'ZSTD10') metrics = self.ACR_zscore(metrics, 'Mean10', 'ZMean10') return metrics def OnSecuritiesChanged(self, algorithm, changes): pass
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 QuantConnect.Algorithm.Framework.Alphas import * from Alpha_VAA import Alpha_VAA from Alpha_ACR_2 import Alpha_ACR from Alpha_ZScore import Alpha_ZScore from Alpha_Composite import Alpha_Composite from Confidence_Weighted_Portfolio import Confidence_Weighted_Portfolio from Zero_Leverage_ExecutionModel import Zero_Leverage_ExecutionModel from Drawdown_Risk_Model import Drawdown_Risk_Model from Utility_Functions import Utilities import numpy as np import pandas as pd from scipy.optimize import minimize class Modular_Framework(QCAlgorithmFramework): '''Mean Variance Optimization Algorithm.''' def Initialize(self): """ General Algorithm Parameters """ self.UniverseSettings.Resolution = Resolution.Minute #Set Data Resolution self.SetStartDate(2008, 1, 1) #Set Start Date self.SetEndDate(2018, 8, 28) #Set End Date self.SetCash(100000) #Set Strategy Cash self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) #Set Brokerage Model """ Portfolio Construction Parameters """ self.max_leverage = 1.0 #highest combined leverage self.reserved = 0.0 #Tell algo to reserve (won't trade with) this amount of cash #self.normalize = True #noramalize leverage (attempt to stay at max leverage always) """ Portfolio Execution Parameters """ self.Minimum_order_value = 2500 #This reduces fees by trading only when a large reblance is needed """ Portfolio Risk Management Parameters """ self.max_drawdown = .04 self.security_stop = .04 self.multi_day_slide_stop = .04 self.wait_days = 5 """ Universe """ self.assets_VAA = [ "SPY", #SPDR S&P 500 Trust ETF "VEA", #Vanguard FTSE Developed Markets ETF "VTI", #Vanguard Total Stock Market ETF "AGG", #iShares Barclays Aggregate Bond Fund "HYD", #VanEck Vectors High-Yield Municipal ETF "VMBS", #Vanguard Mortgage-Backed Securities ETF "BKLN", #Invesco Senior Loan ETF "JNK", #SPDR Barclays High Yield Bond ETF "VT", ##Vanguard Total World Stock Index Fund "VWO", #Vanguard FTSE Emerging Markets ETF "TLT", #iShares 20+ Year Treasury Bond ETF ] self.assets_ACR = [ "VOE", "VDC", "XLP", "IJR", "TLT", "TIP", "DBC", "SHY", "XLB", #Materials "XLY", #Consumer Cyclical "XLF", #Financials "IYR", #ISHARES Real Estate "XLP", #Consumer Defensive "XLV", #Healthcare "XLU", #Utilities "XLE", #Energy "XLI", #Industrials "XLK", #Tech "SPY", ] self.assets_ZScore = [ "SPY", "TLT", "SHY", ] self.universe = self.assets_VAA + self.assets_ACR + self.assets_ZScore self.universe = (list(set(self.universe))) universe = [ Symbol.Create(symbol, SecurityType.Equity, Market.USA) for symbol in self.universe ] """ Framework Parameters """ #SET UNIVERSE MODEL self.SetUniverseSelection( ManualUniverseSelectionModel(universe) ) #SET ALPHA MODELS #self.SetAlpha( Alpha_VAA(self) ) #self.SetAlpha( Alpha_ACR(self) ) #self.SetAlpha( Alpha_ZScore(self) ) #self.SetAlpha( Alpha_Composite(self) ) alphas = [ Alpha_VAA(self), Alpha_ACR(self), Alpha_ZScore(self), ] self.num_alphas = len(alphas) self.SetAlpha( CompositeAlphaModel(*alphas)) ''' self.SetAlpha( CompositeAlphaModel( Alpha_VAA(self), Alpha_ACR(self), Alpha_ZScore(self), ) ) ''' #SET PORTFOLIO CONSTRUCTION MODEL self.SetPortfolioConstruction( Confidence_Weighted_Portfolio(Resolution.Daily, self.reserved, self.max_leverage, self.num_alphas, False) ) #SET EXECUTION MODEL #self.SetExecution( NullExecutionModel() ) self.SetExecution( Zero_Leverage_ExecutionModel(Resolution.Daily, self.Minimum_order_value) ) #SET RISK MANAGEMENT MODEL self.SetRiskManagement( NullRiskManagementModel() ) #self.SetRiskManagement( Drawdown_Risk_Model(self.max_drawdown, self.security_stop, self.multi_day_slide_stop, self.wait_days) ) """ Scheduled Functions """ #self.Schedule.On(self.DateRules.Every([DayOfWeek.Monday]), self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(9, 31), Action(self.VAA_Rebal)) """ Flags - Do not Change these! """ self.VAA_calculated = False def VAA_Rebal(self): pass #self.VAA_calculated = False
""" Misc/Utility Functions """ import numpy as np #class Utilities(object): class Utilities: def __init__(self, variables): self.var = variables def Variance(self,x,*args): #Variance Function for SLSQP p = np.squeeze(np.asarray(args)) Acov = np.cov(p.T) return np.dot(x,np.dot(Acov,x)) def Jac_Variance(self,x,*args): #jac_variance Function for SLSQP p = np.squeeze(np.asarray(args)) Acov = np.cov(p.T) return 2*np.dot(Acov,x) def Unique(self,seq,idfun=None): # order preserving if idfun is None: def idfun(x): return x seen = {} result = [] for item in seq: marker = idfun(item) if marker in seen: continue seen[marker] = 1 result.append(item) return result def my_record_vars(self): #self.var.Debug("my_record_vars") account_leverage = float(self.var.Portfolio.TotalHoldingsValue) / float(self.var.Portfolio.TotalPortfolioValue) self.var.Plot('Leverage', 'Leverage', account_leverage) portfolio = self.var.Portfolio positions = portfolio.Keys pos = 0 short = 0 for symbol in positions: if not self.var.Securities.ContainsKey(symbol): continue if portfolio[symbol].IsLong: pos += 1 if portfolio[symbol].IsShort: short += 1 self.var.Plot('Data Graph', 'Long', pos) self.var.Plot('Data Graph', 'Short', short) def cancel_open_orders(self): #self.var.Debug("cancel_open_orders") oo = [order.Symbol for order in self.var.Transactions.GetOpenOrders()] if len(oo) == 0: return oo = self.Unique(oo) for symbol in oo: if not self.var.Securities.ContainsKey(symbol): return self.var.Transactions.CancelOpenOrders(symbol) def cancel_open_order(self, symbol): #self.var.Debug("cancel_open_order") if not self.var.Securities.ContainsKey(symbol): return oo = self.var.Transactions.GetOpenOrders(symbol) if len(oo) == 0: return self.var.Transactions.CancelOpenOrders(symbol) def get_open_orders(self, symbol=None): #self.var.Debug("get_open_orders") orders = False if symbol == None: if len(self.var.Transactions.GetOpenOrders()) > 0: orders = True else: if not self.var.Securities.ContainsKey(symbol): return orders if len(self.var.Transactions.GetOpenOrders(symbol)) > 0: orders = True return orders
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 datetime, timedelta from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) import numpy as np class Alpha_VAA: def __init__(self, variables, *args, **kwargs): self.Name = "Alpha_VAA" """ VAA parameters """ self.periods = [42,21,63,126,252] self.weights = [0, 12, 6, 3, 1] self.resolution = Resolution.Daily self.rebalancingTime = UTCMIN self.days_predicted = 5 self.rebalancingPeriod = Extensions.ToTimeSpan(self.resolution)*self.days_predicted self.GrowthSymbols = [ "SPY", #SPDR S&P 500 Trust ETF "VEA", #Vanguard FTSE Developed Markets ETF "VTI", #Vanguard Total Stock Market ETF "VT", #Vanguard Total World Stock Index Fund "VWO", #Vanguard FTSE Emerging Markets ETF "AGG", #iShares Barclays Aggregate Bond ETF ] self.SafetySymbols = [ "HYD", #VanEck Vectors High-Yield Municipal ETF "VMBS", #Vanguard Mortgage-Backed Securities ETF "BKLN", #Invesco Senior Loan ETF "JNK", #SPDR Barclays High Yield Bond ETF "TLT", #iShares 20+ Year Treasury Bond ETF ] #Variable Definitions self.symbolDataBySymbol = {} self.var = variables self.VAA_calculated = False self.allocation = {} self.stocks = self.GrowthSymbols + self.SafetySymbols for symbol in self.stocks: self.allocation[symbol] = 0.0 def Update(self, algorithm, data): insights = [] if (algorithm.UtcTime <= self.rebalancingTime): return insights ##Using a weighted average, compute the score for each risky asset. growthdata = [] for symbol in self.GrowthSymbols: if not symbol in self.symbolDataBySymbol.keys(): continue temp_values = [] for i, period in enumerate(self.periods): if self.symbolDataBySymbol[symbol][i].CanEmit: temp_values.append(self.symbolDataBySymbol[symbol][i].Return) else: temp_values.append(0.0) score = sum([i*j for i,j in zip(temp_values,self.weights)]) growthdata.append([symbol, score, temp_values[0]]) orderedGrowthScores = sorted(growthdata, key=lambda x: x[1], reverse=True) #algorithm.Debug(orderedGrowthScores) ##Using a weighted average, compute the score for each risk-free asset. ##This approach overweights the front month momentum value and progressively underweights older momentum values safetydata = [] for symbol in self.SafetySymbols: #self.var.Debug(symbol) #self.var.Debug(self.symbolDataBySymbol.keys()) if not symbol in self.symbolDataBySymbol.keys(): continue #self.var.Debug("2") temp_values = [] for i, period in enumerate(self.periods): #self.var.Debug("3") if self.symbolDataBySymbol[symbol][i].CanEmit: #self.var.Debug("4") temp_values.append(self.symbolDataBySymbol[symbol][i].Return) else: temp_values.append(0.0) #self.var.Debug("5") #score = np.average(temp_values, weights = self.weights) score = sum([i*j for i,j in zip(temp_values,self.weights)]) safetydata.append([symbol, score, temp_values[0]]) orderedSafeScores = sorted(safetydata, key=lambda x: x[1], reverse=True) #algorithm.Debug(orderedSafeScores) ##Count the number of risky assets with negative momentum scores and store in N. If all four of the offensive assets exhibit positive momentum scores, ##select the offensive asset with the highest score and allocate 100% of the portfolio to that asset at the close count = 0 negative_flag = False for stock in orderedGrowthScores: if stock[1] <= 0.0: count += 1 if count > 0: negative_flag = True top_growth = orderedGrowthScores[0][0] second_growth = orderedGrowthScores[1][0] top_safe = orderedSafeScores[0][0] second_safe = orderedSafeScores[1][0] predictionInterval = Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), self.days_predicted) stock_data = {} for data in (orderedGrowthScores + orderedSafeScores): #stock_data[stock] = [score, momentum, magnitude, direction] score = data[1] if score == 0.0: #DATA ERROR continue momentum = data[2] magnitude = (data[2]/100*(self.days_predicted/self.periods[0])) if magnitude > -.005: direction = InsightDirection.Up #elif magnitude < -.005: #direction = InsightDirection.Down else: direction = InsightDirection.Flat stock_data[data[0]] = [score, momentum, magnitude, direction] for symbol in stock_data: weight = 0 if symbol == top_growth: if negative_flag: weight = 0.1 else: weight = 0.5 elif symbol == second_growth: if negative_flag: weight = 0.1 else: weight = 0.5 elif symbol == top_safe: if negative_flag: weight = 0.4 else: weight = 0.0 elif symbol == second_safe: if negative_flag: weight = 0.4 else: weight = 0.0 else: weight = 0 if self.allocation[symbol] != weight: insights.append(Insight(symbol, predictionInterval, InsightType.Price, stock_data[symbol][3], stock_data[symbol][2], weight, sourceModel="Test")) self.allocation[symbol] = weight self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod self.var.VAA_calculated = True #return insights return Insight.Group( insights ) def OnSecuritiesChanged(self, algorithm, changes): algorithm.Debug("Securities Changed. Added " + str(changes.AddedSecurities)) symbols = [ x.Symbol for x in changes.AddedSecurities if str(x.Symbol) in self.stocks ] history = algorithm.History(symbols, (np.max(self.periods)), Resolution.Daily) if history.empty: algorithm.Debug("History Error") return for removed in changes.RemovedSecurities: if removed.Symbol in self.stocks: symbolData_temp = self.symbolDataBySymbol.pop(removed.Symbol, None) if symbolData_temp is not None: symbolData_temp.RemoveConsolidators(algorithm) for stock in history.index.levels[0]: symbol = SymbolCache.GetSymbol(stock) algorithm.Debug("Getting Data for " + str(symbol)) if str(symbol) not in self.symbolDataBySymbol.keys(): algorithm.Debug("registering symboldata for " + str(symbol)) symbolData_temp = [] for period in self.periods: #algorithm.Debug("C") tempData = SymbolData(symbol, period) tempData.RegisterIndicators(algorithm, Resolution.Daily) tempData.WarmUpIndicators(history.loc[stock]) symbolData_temp.append(tempData) self.symbolDataBySymbol[str(symbol)] = symbolData_temp #algorithm.Debug(symbolData_temp) class SymbolData: def __init__(self, symbol, lookback): self.Symbol = symbol self.MOM = Momentum('{}.MOM({})'.format(symbol, lookback), lookback) self.Consolidator = None self.previous = 0 def RegisterIndicators(self, algorithm, resolution): #algorithm.Debug("Register Indicators. Alpha") self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution) algorithm.RegisterIndicator(self.Symbol, self.MOM, 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.MOM.Update(tuple.Index, tuple.close) @property def Return(self): return float(self.MOM.Current.Value) @property def CanEmit(self): if self.previous == self.MOM.Samples: return False self.previous = self.MOM.Samples return self.MOM.IsReady ''' def __str__(self, **kwargs): return '{}: {:.2%}'.format(self.MOM.Name, (1 + self.Return)**252 - 1) '''
from clr import AddReference AddReference("QuantConnect.Algorithm.Framework") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Common") import numpy as np import pandas as pd import math from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm.Framework.Alphas import * from datetime import datetime, timedelta from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) class Alpha_ZScore: def __init__(self, variables, *args, **kwargs): self.Name = "Alpha_ZScore" self.var = variables #zscore assets self.stocks = [ "SPY", "TLT", "SHY", ] """ zscore parameters """ self.fixed_wt_pct = 0.25 #.50 self.fixed_wt = { "SHY": 1.0 } self.vol_factor = 0.50 #TLT = .5, SPY = 1 self.ext_factor = 4.0 #move too extreme, leave asset self.lookback = 150 self.allocation = {} #Variable Definitions self.resolution = Resolution.Daily self.rebalancingTime = UTCMIN self.days_predicted = 5 self.rebalancingPeriod = Extensions.ToTimeSpan(self.resolution)*self.days_predicted for symbol in self.stocks: self.allocation[symbol] = 0.0 def Update(self, algorithm, data): insights = [] if (algorithm.UtcTime <= self.rebalancingTime): return insights safestock = "TLT" history = algorithm.History([safestock], self.lookback, Resolution.Daily) mean = history['close'].mean() sigma = history['close'].std() price = float(algorithm.Securities[safestock].Price) if sigma != 0.0: z = (price - mean) / sigma**self.vol_factor else: z = 0.0 if -self.ext_factor <= z <= self.ext_factor: tlt_target = 1.0/(1+math.exp(-1.2*z)) # Pure momentum adding 1 to the sin wave to prevent shorting else: tlt_target = 0.0 spy_target = (1.0-tlt_target) allocation_temp = {} magnitude = {} direction = {} for sid in self.stocks: allocation_temp[sid] = 0.0 if sid in self.fixed_wt: allocation_temp[sid] = self.fixed_wt[sid] * self.fixed_wt_pct allocation_temp[safestock] += tlt_target * (1.0 - self.fixed_wt_pct) allocation_temp["SPY"] += spy_target * (1.0 - self.fixed_wt_pct) predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.days_predicted) expected = algorithm.History(self.stocks, 5, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna().mean() + 1.0 for stock in self.stocks: magnitude[stock] = ((expected[stock]**self.days_predicted)-1.0)/2.0 if magnitude[stock] > -0.005: direction[stock] = InsightDirection.Up else: direction[stock] = InsightDirection.Flat for symbol in self.allocation: if (self.allocation[symbol] != allocation_temp[symbol]) or (self.allocation[symbol] != 0.0) or (allocation_temp[symbol] != 0.0): self.allocation[symbol] = allocation_temp[symbol] insights.append(Insight(symbol, predictionInterval, InsightType.Price, direction[symbol], magnitude[symbol], self.allocation[symbol], sourceModel="Test")) #algorithm.Debug(str(symbol) + " = " + str(self.allocation[symbol])) #insights = [] #Remove me #return insights self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod return Insight.Group( insights ) def OnSecuritiesChanged(self, algorithm, changes): pass
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 Utility_Functions import * from datetime import timedelta import numpy as np import pandas as pd import itertools from datetime import datetime, timedelta from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) class Alpha_ACR: def __init__(self, variables, *args, **kwargs): self.Name = "Alpha_ACR" self.ACR_assets = [ "VOE", "VDC", "XLP", "IJR", ] self.ACR_bonds = [ "TLT", "TIP", "DBC", "SHY", ] self.ACR_sectors = [ "XLB", #Materials "XLY", #Consumer Cyclical "XLF", #Financials "IYR", #ISHARES Real Estate "XLP", #Consumer Defensive "XLV", #Healthcare "XLU", #Utilities "XLE", #Energy "XLI", #Industrials "XLK", #Tech ] self.ACR_fixed = [ "SPY", ] #self.Hedge = ["VXX",] self.stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed# + self.Hedge """ ACR (Asset Class Rotation) parameters """ self.ACR_sector_step = 13 #12% step change = all bonds if 9 of 11 sectors down self.ACR_asset_step = 20 #20% step change self.allocation = {} self.ACR_fixed_weight = [ 0.0, #SPY ] self.stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed# + self.Hedge #Variable Definitions self.resolution = Resolution.Daily self.rebalancingTime = UTCMIN self.days_predicted = 5 self.rebalancingPeriod = Extensions.ToTimeSpan(self.resolution)*self.days_predicted self.var = variables #for stock in self.ACR_sectors: #self.var.AddEquity(stock) self.symbolData = {} for symbol in self.stocks: self.allocation[symbol] = 0.0 def Update(self, algorithm, data): #algorithm.Debug("Updating Alpha Model.") insights = [] if (algorithm.UtcTime <= self.rebalancingTime): return insights for sid in self.stocks: if not sid in self.allocation: self.allocation[sid] = 0.0 ACR_assets_weight = np.zeros(len(self.ACR_assets)) ACR_bonds_data = pd.DataFrame(0, columns=['Weight','Ratio','20Day','60Day'],index=self.ACR_bonds) ACR_sectors_data = pd.DataFrame(0, columns=['Ratio','20Day','200Day'],index=self.ACR_sectors) """ Determine sector trends and calculate weight to assets/bonds """ ACR_sectors_data.loc[:,'20Day'] = algorithm.History(self.ACR_sectors, 20, Resolution.Daily)["close"].unstack(level=0).mean() ACR_sectors_data.loc[:, '200Day'] = algorithm.History(self.ACR_sectors, 200, Resolution.Daily)["close"].unstack(level=0).mean() ACR_sectors_data['Ratio'] = ACR_sectors_data['20Day']/ACR_sectors_data['200Day'] - 1 ACR_bonds_weight = len(ACR_sectors_data[ACR_sectors_data['Ratio'] < 0]) * self.ACR_sector_step/100.0 if ACR_bonds_weight > 1.0: ACR_bonds_weight = 1.0 ACR_bonds_weight = ACR_bonds_weight * (1-sum(self.ACR_fixed_weight)) """ Determine bond trends and which duration to be in """ if ACR_bonds_weight > 0.0: ACR_bonds_data.loc[:,'20Day'] = algorithm.History(self.ACR_bonds, 20, Resolution.Daily)["close"].unstack(level=0).mean() ACR_bonds_data.loc[:, '60Day'] = algorithm.History(self.ACR_bonds, 60, Resolution.Daily)["close"].unstack(level=0).mean() ACR_bonds_data['Ratio'] = ACR_bonds_data['20Day']/ACR_bonds_data['60Day'] - 1 ACR_bonds_data['Weight'] = 0 ACR_bonds_data.loc[ACR_bonds_data['Ratio'].idxmax(), 'Weight'] = ACR_bonds_weight #log.info(self.ACR_bonds_data) returns = algorithm.History(self.ACR_assets, 126, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna() + 1.0 expected = algorithm.History(self.stocks, 20, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna().mean() + 1.0 """ Create portfolio combinations """ n = len(self.ACR_assets) steps = [x/100.0 for x in range(0,101,int(self.ACR_asset_step))] a = [steps for x in range(n)] b = list(itertools.product(*a)) x = [sum(i) for i in b] port = pd.DataFrame(b) port['Sum'] = x port = port[port.Sum == 1] del port['Sum'] """ Score and Weight portoflio """ port_returns = pd.DataFrame(np.dot(returns, port.T), index=returns.index) port_metrics = self.ACR_get_specs(port_returns) port_metrics = self.ACR_score(port_metrics) port_metrics['Z3'] = port_metrics.ZMean\ -port_metrics.ZSTD\ -port_metrics.ZDownSide\ +port_metrics.ZAnnualized\ -port_metrics.ZMax_Draw\ -port_metrics.ZSTD10\ +port_metrics.ZMean10\ +port_metrics.ZMinimum15 port_metrics = port_metrics.sort_values(by='Z3', ascending=False) portfolios = port portfolios.columns = list(returns.columns.values) best = pd.concat([pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]).T]) #log.info(best.loc[:, (best != 0).any(axis=0)].T) best = pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]) #log.info(best) ACR_assets_weight = [i[0]*(1-ACR_bonds_weight-sum(self.ACR_fixed_weight)) for i in best.values] predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.days_predicted) allocation_temp = {} magnitude = {} direction = {} risk_weight = 0 for x in range(n): allocation_temp[self.ACR_assets[x]] = ACR_assets_weight[x]#*.95 #expected_temp[self.ACR_assets[x]] = risk_weight += ACR_assets_weight[x] for stock in self.stocks: magnitude[stock] = ((expected[stock]**self.days_predicted)-1.0)/5.0 #magnitude[stock] = 0.001 #algorithm.Debug(str(stock) + " magnitude = " + str(magnitude[stock])) if magnitude[stock] > -0.01: direction[stock] = InsightDirection.Up else: direction[stock] = InsightDirection.Flat magnitude[stock] = 0.001 #self.allocation[self.Hedge[0]] = risk_weight * .05 for stock in self.ACR_bonds: allocation_temp[stock] = ACR_bonds_data.loc[stock, 'Weight'] for x in range(len(self.ACR_fixed)): allocation_temp[self.ACR_fixed[x]] = self.ACR_fixed_weight[x] for symbol in self.allocation: if (self.allocation[symbol] != allocation_temp[symbol]) or (self.allocation[symbol] != 0.0) or (allocation_temp[symbol] != 0.0): #if allocation_temp[symbol] > self.allocation[symbol]: self.allocation[symbol] = allocation_temp[symbol] insights.append(Insight(symbol, predictionInterval, InsightType.Price, InsightDirection.Up, magnitude[symbol], self.allocation[symbol], sourceModel="Test")) #algorithm.Debug(str(symbol) + " = " + str(self.allocation[symbol])) #insights = [] #Remove me #return insights self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod return Insight.Group( insights ) def ACR_drawdown(self, returns): mat = returns.cumprod().values [n, m] = np.shape(mat) maxes = np.maximum.accumulate(np.array(mat)) for i in range(0,n): for j in range(m): mat[i,j] = mat[i,j] / maxes[i,j] df = pd.DataFrame(mat) df[df > 1] = 1 return df def ACR_moving_returns(self, returns, w): mat = returns.values [n, m] = np.shape(mat) ret = np.zeros(shape = (n-w+1,m)) for i in range(w-1,n): for j in range(m): ret[i-w+1,j] = np.power(np.prod(mat[(i-w+1):i+1,j]),(1.0/w))- 1.0 return pd.DataFrame(ret) def ACR_get_specs(self, returns): metrics = pd.DataFrame((returns.mean()),columns=['Mean']) - 1.0 metrics['STD'] = pd.DataFrame((returns.std())) metrics['Annualized'] = np.power(returns.cumprod().values.tolist()[-1],1.0/len(returns))- 1.0 downside = returns.copy(deep=True) - 1 downside[downside > 0] = 0 downside = downside ** 2 metrics['DownSide'] = pd.DataFrame(downside.mean() ** 0.5) draw = self.ACR_drawdown(returns) metrics['Max_Draw'] = 1.0 - draw.min().values ret15 = self.ACR_moving_returns(returns,21) metrics['Minimum15'] = ret15.min().values ret10 = self.ACR_moving_returns(returns,21) metrics['Mean10'] = ret10.mean().values metrics['STD10'] = ret10.std().values return metrics def ACR_zscore(self, stocks, var, var_save): stocks[var_save] = (stocks[var] - stocks[var].mean())/stocks[var].std(ddof=0) return stocks def ACR_score(self, metrics): metrics = self.ACR_zscore(metrics, 'Mean', 'ZMean') metrics = self.ACR_zscore(metrics, 'STD', 'ZSTD') metrics = self.ACR_zscore(metrics, 'Annualized', 'ZAnnualized') metrics = self.ACR_zscore(metrics, 'DownSide', 'ZDownSide') metrics = self.ACR_zscore(metrics, 'Max_Draw', 'ZMax_Draw') metrics = self.ACR_zscore(metrics, 'Minimum15', 'ZMinimum15') metrics = self.ACR_zscore(metrics, 'STD10', 'ZSTD10') metrics = self.ACR_zscore(metrics, 'Mean10', 'ZMean10') return metrics def OnSecuritiesChanged(self, algorithm, changes): pass
from clr import AddReference AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm.Framework") from QuantConnect import Resolution, Extensions from QuantConnect.Algorithm.Framework.Alphas import InsightCollection, InsightDirection from QuantConnect.Algorithm.Framework.Portfolio import PortfolioConstructionModel, PortfolioTarget from itertools import groupby from datetime import datetime from pytz import utc UTCMIN = datetime.min.replace(tzinfo=utc) import numpy as np class Confidence_Weighted_Portfolio(PortfolioConstructionModel): def __init__(self, resolution = Resolution.Daily, reserve = 0, leverage = 1.0, num_alphas = 1.0, normalize = False): self.insightCollection = InsightCollection() self.removedSymbols = [] self.rebalancingTime = UTCMIN self.rebalancingPeriod = Extensions.ToTimeSpan(resolution) self.allocation = {} self.reserved = reserve self.max_leverage = leverage self.normalize = normalize self.num_alphas = num_alphas self.symbol_list = [] def CreateTargets(self, algorithm, insights): targets = [] if (algorithm.UtcTime <= self.rebalancingTime and len(insights) == 0 and self.removedSymbols is None): return targets self.insightCollection.AddRange(insights) # Create Sell target for each security that was removed from the universe if self.removedSymbols is not None: universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ] targets.extend(universeDeselectionTargets) self.removedSymbols = None if self.addedSymbols is not None: for symbol in self.addedSymbols: self.allocation[symbol] = {} self.addedSymbols = None # Get insight that haven't expired of each symbol that is still in the universe activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) # Get the last generated active insight for each symbol lastActiveInsights = [] for symbol, g in groupby(activeInsights, lambda x: x.Symbol): lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-1]) #Gets Adjusted Portfolio value port_value_adjusted = (float(algorithm.Portfolio.TotalPortfolioValue) - self.reserved) * self.max_leverage for insight in lastActiveInsights: symbol = insight.Symbol allocation = insight.Confidence alpha = insight.SourceModel #Calculate required rebalance amount for each allocation goal_value = (port_value_adjusted * allocation) #Adjusted % allocation self.allocation[symbol][alpha] = goal_value/float(algorithm.Portfolio.TotalPortfolioValue)/self.num_alphas combined_weight = {} for symbol in self.allocation.keys(): wt = sum(list(self.allocation[symbol].values())) combined_weight[symbol] = wt #option to normalize target_portfolio to fill all availible leverage if self.normalize: original_wt = sum(np.abs(list(combined_weight.values()))) if original_wt > 0.0: factor = self.max_leverage/original_wt else: factor = 0.0 for symbol in combined_weight.keys(): combined_weight[symbol] = combined_weight[symbol]*factor for symbol in combined_weight.keys(): target = PortfolioTarget.Percent(algorithm, symbol, combined_weight[symbol]) if target != None: targets.append(target) #Log final desired allocation #algorithm.Debug(str(symbol) + ". Allocation = " + str(round(self.allocation[symbol]*100,1)) + "% > " + str(int(target.Quantity))) else: algorithm.Debug("Target Error on " + str(symbol) + ". Should be " + str(round(combined_weight[symbol]*100,1)) + "%") self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod return targets def OnSecuritiesChanged(self, algorithm, changes): self.addedSymbols = [x.Symbol for x in changes.AddedSecurities] # Get removed symbol and invalidate them in the insight collection self.removedSymbols = [x.Symbol for x in changes.RemovedSecurities] self.insightCollection.Clear(self.removedSymbols)