Overall Statistics |
Total Trades 343 Average Win 6.49% Average Loss -2.48% Compounding Annual Return 16.856% Drawdown 45.300% Expectancy 1.199 Net Profit 6915.539% Sharpe Ratio 0.759 Probabilistic Sharpe Ratio 3.788% Loss Rate 39% Win Rate 61% Profit-Loss Ratio 2.62 Alpha 0.093 Beta 0.596 Annual Standard Deviation 0.171 Annual Variance 0.029 Information Ratio 0.425 Tracking Error 0.158 Treynor Ratio 0.218 Total Fees $625.00 Estimated Strategy Capacity $0 Lowest Capacity Asset QQQ.QQQ 2S |
# Import packages import numpy as np import pandas as pd import scipy as sc from scipy import stats class AccelDualMomentumInOut(QCAlgorithm): def Initialize(self): self.SetStartDate(1995, 1, 1) # Set Start Date #self.SetEndDate(2022,2,28) # Set End Date self.SetCash(10000) # Set Strategy Cash #self.aVFINX = self.AddData(VFINX, "VFINX", Resolution.Daily).Symbol self.aQQQ = self.AddData(QQQ, "QQQ", Resolution.Daily).Symbol self.aVINEX = self.AddData(VINEX, "VINEX", Resolution.Daily).Symbol self.aVUSTX = self.AddData(VUSTX, "VUSTX", Resolution.Daily).Symbol self.aVBILX = self.AddData(VBILX, "VBILX", Resolution.Daily).Symbol self.aRYURX = self.AddData(RYURX, "RYURX", Resolution.Daily).Symbol self.aLBMA = self.AddData(LBMA, "LBMA", Resolution.Daily).Symbol #self.aGLD = self.AddData(GLD, "GLD", Resolution.Daily).Symbol self.aVIPSX = self.AddData(VIPSX, "VIPSX", Resolution.Daily).Symbol self.aCASH = self.AddData(CASH, "CASH", Resolution.Daily).Symbol self.indicator = self.AddData(MOMENTUM, "MOMENTUM", Resolution.Daily).Symbol self.leverage = 1 # Set leverage | A value of 1 indicates no leverage | A value of 2 indicates 100% leverage #self.Securities["VFINX"].SetLeverage(self.leverage) self.Securities["QQQ"].SetLeverage(self.leverage) self.Securities["VINEX"].SetLeverage(self.leverage) self.Securities["VUSTX"].SetLeverage(self.leverage) self.Securities["LBMA"].SetLeverage(self.leverage) #self.Securities["GLD"].SetLeverage(self.leverage) self.Securities["VIPSX"].SetLeverage(self.leverage) self.Securities["CASH"].SetLeverage(self.leverage) self.Securities["VBILX"].SetLeverage(self.leverage) self.Securities["RYURX"].SetLeverage(self.leverage) self.basket_out = ['VUSTX', 'VBILX', 'RYURX'] # Set trading frequency self.monthly = 1 self.annual = 0 self.daily = 0 self.trading_fee = 5 # Fee per trade self.trading_day = 21 # Set trading day | Value = 21 is last trading day of month self.GetParameter("trading_day") def shiftAssets(self, target): if not (self.Portfolio[target].Invested): for symbol in self.Portfolio.Keys: self.Liquidate(symbol) if not self.Portfolio.Invested: self.SetHoldings(target, 1*self.leverage) def getMonthTradingDay(self): month_last_day = DateTime(self.Time.year, self.Time.month, DateTime.DaysInMonth(self.Time.year, self.Time.month)) tradingDays = self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, DateTime(self.Time.year, self.Time.month, 1), month_last_day) tradingDays = [day.Date.date() for day in tradingDays] return tradingDays[-22 + self.trading_day] def getYearLastTradingDay(self): year_last_day = DateTime(self.Time.year, 12, DateTime.DaysInMonth(self.Time.year, 12)) tradingDays = self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, DateTime(self.Time.year, 12, 1), year_last_day) tradingDays = [day.Date.date() for day in tradingDays] return tradingDays [-1] def OnData(self, data): if (self.daily ==1): if data.ContainsKey(self.indicator): ticker = data[self.indicator].GetProperty('Indicator') if (ticker =="VINEX"): #self.Securities["VINEX"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aVINEX) elif (ticker =="QQQ"): #self.Securities["QQQ"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aQQQ) elif (ticker =="VUSTX"): #self.Securities["VUSTX"].SetFeeModel(MonthlyCustomFeeModel()) self.Liquidate() dataframe = self.History(self.basket_out, 180, Resolution.Daily) df = dataframe['close'].unstack(level=0) self.adaptive_asset_allocation(df, 3, 40, 90, self.Portfolio.Cash, 1) #self.shiftAssets(self.aVUSTX) elif (ticker =="LBMA"): #self.Securities["LBMA"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aLBMA) elif (ticker =="VIPSX"): #self.Securities["VIPSX"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aVIPSX) elif (ticker =="CASH"): #self.Securities["CASH"].SetFeeModel(MonthlyCustomFeeModel()) self.Liquidate() dataframe = self.History(self.basket_out, 180, Resolution.Daily) df = dataframe['close'].unstack(level=0) self.adaptive_asset_allocation(df, 3, 40, 90, self.Portfolio.Cash, 1) #self.shiftAssets(self.aCASH) elif (ticker =="GLD"): #self.Securities["GLD"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aGLD) if (self.monthly ==1): if (self.Time.date() == self.getMonthTradingDay()): if data.ContainsKey(self.indicator): ticker = data[self.indicator].GetProperty('Indicator') if (ticker =="VINEX"): self.Securities["VINEX"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aVINEX) elif (ticker =="QQQ"): self.Securities["QQQ"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aQQQ) elif (ticker =="VUSTX"): #self.Securities["VUSTX"].SetFeeModel(MonthlyCustomFeeModel()) self.Liquidate() dataframe = self.History(self.basket_out, 180, Resolution.Daily) df = dataframe['close'].unstack(level=0) self.adaptive_asset_allocation(df, 3, 40, 90, self.Portfolio.Cash, 1) elif (ticker =="LBMA"): self.Securities["LBMA"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aLBMA) elif (ticker =="VIPSX"): self.Securities["VIPSX"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aVIPSX) elif (ticker =="CASH"): #self.Securities["CASH"].SetFeeModel(MonthlyCustomFeeModel()) self.Liquidate() dataframe = self.History(self.basket_out, 180, Resolution.Daily) df = dataframe['close'].unstack(level=0) self.adaptive_asset_allocation(df, 3, 40, 90, self.Portfolio.Cash, 1) elif (ticker =="GLD"): self.Securities["GLD"].SetFeeModel(MonthlyCustomFeeModel()) self.shiftAssets(self.aGLD) if (self.annual ==1): if (self.Time.date() == self.getYearLastTradingDay()): if data.ContainsKey(self.indicator): ticker = data[self.indicator].GetProperty('Indicator') if (ticker =="VINEX"): self.Securities["VINEX"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aVINEX) elif (ticker =="QQQ"): self.Securities["QQQ"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aQQQ) elif (ticker =="VUSTX"): self.Securities["VUSTX"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aVUSTX) elif (ticker =="LBMA"): self.Securities["LBMA"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aLBMA) elif (ticker =="VIPSX"): self.Securities["VIPSX"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aVIPSX) elif (ticker =="CASH"): self.Securities["CASH"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aCASH) elif (ticker =="GLD"): self.Securities["GLD"].FeeModel = ConstantFeeModel(self.trading_fee) self.shiftAssets(self.aGLD) # Charts self.Plot("Margin", "Remaining", self.Portfolio.MarginRemaining) self.Plot("Margin", "Used", self.Portfolio.TotalMarginUsed) self.Plot("Cash", "Remaining", self.Portfolio.Cash) self.Plot("Cash", "Remaining", self.Portfolio.TotalHoldingsValue) #self.Plot("VFINX", "Held", self.Portfolio["VFINX"].Quantity) self.Plot("VINEX", "Held", self.Portfolio["VINEX"].Quantity) self.Plot("VUSTX", "Held", self.Portfolio["VUSTX"].Quantity) self.Plot("VIPSX", "Held", self.Portfolio["VIPSX"].Quantity) #self.Plot("GLD", "Held", self.Portfolio["GLD"].Quantity) self.Plot("CASH", "Held", self.Portfolio["CASH"].Quantity) self.Plot("LBMA", "Held", self.Portfolio["LBMA"].Quantity) def adaptive_asset_allocation(self, df, nlargest, volatility_window, return_window, portfolio_value, leverage): window_returns = np.log(df.iloc[-1]) - np.log(df.iloc[0]) nlargest = list(window_returns.nlargest(nlargest).index) returns = df[nlargest].pct_change() returns_cov_normalized = returns[-volatility_window:].apply(lambda x: np.log(1+x)).cov() returns_corr_normalized = returns[-volatility_window:].apply(lambda x: np.log(1+x)).corr() returns_std = returns.apply(lambda x: np.log(1+x)).std() port_returns = [] port_volatility = [] port_weights = [] num_assets = len(returns.columns) num_portfolios = 100 individual_rets = window_returns[nlargest] for port in range(num_portfolios): weights = np.random.random(num_assets) weights = weights/np.sum(weights) port_weights.append(weights) rets = np.dot(weights, individual_rets) port_returns.append(rets) var = returns_cov_normalized.mul(weights, axis=0).mul(weights, axis=1).sum().sum() sd = np.sqrt(var) ann_sd = sd * np.sqrt(256) port_volatility.append(ann_sd) data = {'Returns': port_returns, 'Volatility': port_volatility} hover_data = [] for counter, symbol in enumerate(nlargest): data[symbol] = [w[counter] for w in port_weights] hover_data.append(symbol) portfolios_V1 = pd.DataFrame(data) min_var_portfolio = portfolios_V1.iloc[portfolios_V1['Volatility'].idxmin()] max_sharpe_portfolio = portfolios_V1.iloc[(portfolios_V1['Returns'] / portfolios_V1['Volatility']).idxmax()] proportions = min_var_portfolio[nlargest] index = 0 for proportion in proportions: self.SetHoldings(nlargest[index], proportion * leverage) self.Debug('{}% of portfolio in {}'.format(proportion * leverage, nlargest[index])) index += 1 class LBMA(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/81ixmlr8cx1uxgq/LBMA.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = LBMA() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[1] index["LBMA"] = float(data[1]) return index class VIPSX(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/51npkwxesct345x/VIPSX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = VIPSX() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[4] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) return index class VUSTX(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/hnv2swusm9wra5w/VUSTX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = VUSTX() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[4] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) return index class VFINX(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/zzh0ydo8t8l5ds4/VFINX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = VFINX() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[4] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) return index class VINEX(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/3otgob32pyl0hz8/VINEX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = VINEX() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[4] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) return index class GLD(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/c9asn799ugf8kja/GLD.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = GLD() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[4] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) return index class CASH(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/496wpuy5qrlq9za/CASH.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = CASH() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%d/%m/%Y") index.EndTime = index.Time + timedelta(days=1) index.Value = data[1] index["Close"] = float(data[1]) return index class QQQ(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/53tqrfh84h7h1ax/QQQ.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = QQQ() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%Y-%m-%d") index.EndTime = index.Time + timedelta(days=1) index.Value = data[1] index["Close"] = float(data[1]) return index class VBILX(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/sqlkx4q3w14dt90/VBILX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = VBILX() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%Y-%m-%d") index.EndTime = index.Time + timedelta(days=1) index.Value = data[5] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) index["Adj Close"] = float(data[5]) return index class RYURX(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/rtzx03fzejd36ok/RYURX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = RYURX() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%Y-%m-%d") index.EndTime = index.Time + timedelta(days=1) index.Value = data[5] index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) index["Adj Close"] = float(data[5]) return index class MOMENTUM(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://www.dropbox.com/s/t62ha5ynf3n4gge/Indicator_VFISX_FCYIX.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLive): if not (line.strip() and line[0].isdigit()): return None index = MOMENTUM() index.Symbol = config.Symbol data = line.split(',') index.Time = datetime.strptime(data[0], "%Y-%m-%d") index.EndTime = index.Time + timedelta(days=1) index.SetProperty("Indicator", str(data[1])) return index class MonthlyCustomFeeModel: def GetOrderFee(self, parameters): self.margin_rate = 0.015 #Set Margin Fee self.trading_fee = 5 #Set fee per trade fee = self.trading_fee + (parameters.Security.Leverage-1)*parameters.Security.Price*parameters.Order.AbsoluteQuantity*(self.margin_rate/12) return OrderFee(CashAmount(fee, 'USD'))