Overall Statistics |
Total Trades 237 Average Win 0.44% Average Loss -0.04% Compounding Annual Return 13.249% Drawdown 16.400% Expectancy 8.424 Net Profit 56.965% Sharpe Ratio 1.291 Probabilistic Sharpe Ratio 65.611% Loss Rate 23% Win Rate 77% Profit-Loss Ratio 11.19 Alpha 0.087 Beta 0.294 Annual Standard Deviation 0.108 Annual Variance 0.012 Information Ratio -0.211 Tracking Error 0.175 Treynor Ratio 0.474 Total Fees $291.83 Estimated Strategy Capacity $6600000.00 Lowest Capacity Asset IJH RV0PWMLXVHPH |
import scipy from pytz import timezone import numpy as np import scipy from scipy import optimize import pandas as pd class AlertLightBrownAlligator(QCAlgorithm): def Initialize(self): self.SetStartDate(2018, 1, 27) self.SetCash(100000) self.leverage = 1.0 self.ret = [] self.tickers = ["SPY", "TLT", "IJR", "IJH", "SVXY"] self.stocks = [] for ticker in self.tickers: symbol = self.AddEquity(ticker, Resolution.Minute).Symbol self.stocks.append(symbol) self.bullish = self.AddEquity("QQQ", Resolution.Minute).Symbol self.bearish = self.AddEquity("IEF", Resolution.Minute).Symbol self.volatility = self.AddEquity("VXX", Resolution.Minute).Symbol self.stability = self.AddEquity("SVXY", Resolution.Minute).Symbol self.Schedule.On(self.DateRules.MonthEnd("QQQ"), self.TimeRules.AfterMarketOpen(self.bullish, 15), self.allocVOL) self.Schedule.On(self.DateRules.MonthEnd("QQQ"), self.TimeRules.BeforeMarketClose(self.bullish, 15), self.allocSPY) self.n = 0 self.s = np.zeros_like(self.stocks) # Not needed self.x0 = np.zeros_like(self.stocks) # Not needed self.x1 = 1.0 * np.ones_like(self.stocks) / len(self.stocks) self.eps = 0.01 self.tol = 1.06-6 # assume convergence is 10 time SLSQP ftol of 1e-6 self.valid_constraint_count = 0 self.opt_pass_count = 0 self.run_count = 0 self.eps_vals = [] self.Schedule.On(self.DateRules.MonthEnd("QQQ"), self.TimeRules.AfterMarketOpen(self.bullish, 60), self.allocate) self.Schedule.On(self.DateRules.MonthEnd("QQQ", 1), self.TimeRules.AfterMarketOpen(self.bullish, 60), self.trade) #set_long_only() self.Schedule.On(self.DateRules.WeekStart("QQQ"), self.TimeRules.BeforeMarketClose(self.bullish, 0), self.every_day_on_end) self.Schedule.On(self.DateRules.WeekStart("QQQ"), self.TimeRules.BeforeMarketClose(self.bullish, 0), self.dynamic_leverage) self.data = None self.symbolDataBySymbol = {} self.symbolRsi = {} ''' This may cause some problems ''' self.leverage = 2 # rsi = self.RSI(symbol, 3, MovingAverageType.Simple, Resolution.Hour) # self.symbolRsi[symbol] = rsi def OnData(self, data): self.data = data ''' confused about this part ''' def dynamic_leverage(self): pass # win = 3 # rsi = 50 if len(self.ret) < win else talib.RSI(np.array(self.ret), timeperiod=win)[-1] # rsi = 50 if rsi != rsi else rsi # self.leverage = max(0, min(50/rsi, 2.0)) # record(rsi=rsi) # pass def every_day_on_end(self): pass # context.ret.append(context.portfolio.returns) # record(l1=context.leverage*100, l2=context.account.leverage * 100) def allocate(self): self.run_count += 1 tempPrices = self.History(self.stocks, 17*390, Resolution.Minute) # prices = data.history(context.stocks, 'price', 17 * 390, '1m') prices = {} for tuple in tempPrices.itertuples(): if tuple.Index[0] not in prices: prices[tuple.Index[0]] = [] prices[tuple.Index[0]].append(tuple.close) prices = pd.DataFrame.from_dict(prices) ret = prices.pct_change()[1:].as_matrix(self.stocks) ret_mean = prices.pct_change().mean() ret_std = prices.pct_change().std() ret_norm = ret_mean / ret_std ret_norm = ret_norm.as_matrix(self.stocks) ret_norm_max = np.max(ret_norm) eps_factor = 0.9 if ret_norm_max > 0 else 1.0 self.eps = eps_factor * ret_norm_max bnds = [] limits = (0, 1) for stock in self.stocks: bnds.append(limits) bnds = ((0, 1), (0, 1), (0, 1), (0, 1), (0, 1)) cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1.0}, {'type': 'ineq', 'fun': lambda x: np.dot(x, ret_norm) - self.eps}) ''' Cannot fix the scipy optimize ''' # res = scipy.optimize.minimize(self.variance, self.x1, args=ret, # jac=self.jac_variance, method='SLSQP', constraints=cons, bounds=bnds) # allocation = np.copy(self.x0) # if res.success: # if SLSQP declares success # self.opt_pass_count += 1 # weighted_ret_norm = np.dot(res.x, ret_norm) # w_ret_constraint = weighted_ret_norm - self.eps + self.tol # if(w_ret_constraint > 0): # and constraint is actually met # self.valid_constraint_count += 1 # allocation = res.x # allocation[allocation < 0] = 0 # denom = np.sum(allocation) # if denom > 0: # allocation = allocation / denom # # msg = "{0} runs, {1} SLSQP passes, {2} constraints passed".format( # # context.run_count, context.opt_pass_count, # # context.valid_constraint_count) # # if(self.run_count > 1000): # # log.info(msg) # # else: # # log.info("constraint fail, SLSQP status = {0}".format(res.status)) # # else: # # log.info("SLSQP fail, SLSQP status = {0}".format(res.status)) self.n += 1 ''' Need to fix this ''' self.s += 0.05 # self.s += allocation def trade(self): if self.n > 0: allocation = self.s / self.n else: return self.n = 0 self.s = np.zeros_like(self.stocks) self.x0 = allocation # if get_open_orders(): # return for i, stock in enumerate(self.stocks): p = allocation[i] * 0.6 * self.leverage if p < 0.05: self.Liquidate(stock) else: self.SetHoldings(stock, p) def allocVOL(self): vxx = self.volatility xiv = self.stability WFV_limit = 14 # (Kory used 14 but it becomes a bit too agressive) n = 28 vxx_prices = self.History(vxx, n+2, Resolution.Daily) if not(len(vxx_prices) > 0): vxx_prices = self.History(vxx, n+2, Resolution.Daily) tempVxxPrice = {} tempVxxLow = {} for tuple in vxx_prices.itertuples(): if tuple.Index[0] not in tempVxxPrice: tempVxxPrice[tuple.Index[0]] = [] tempVxxLow[tuple.Index[0]] = [] tempVxxPrice[tuple.Index[0]].append(tuple.close) tempVxxLow[tuple.Index[0]].append(tuple.low) vxx_prices = pd.DataFrame.from_dict(tempVxxPrice) vxx_lows = pd.DataFrame.from_dict(tempVxxLow) vxx_prices = vxx_prices[:-1] vxx_lows = vxx_lows[:-1] vxx_highest = vxx_prices.rolling(window=n, center=False).max() # William's VIX Fix indicator a.k.a. the Synthetic VIX WVF = ((vxx_highest - vxx_lows) / (vxx_highest)) * 100 # Sell position when WVF crosses under 14 if((float(WVF.iloc[-2]) > WFV_limit) and (float(WVF.iloc[-1]) <= WFV_limit)): self.Liquidate(xiv) def allocSPY(self): # Inputs Tab Criteria. _pd = 28 # "LookBack Period Standard Deviation High") bbl = 22 # "Bolinger Band Length") mult = 1.05 # "Bollinger Band Standard Devaition Up") lb = 22 # "Look Back Period Percentile High") ph = .90 # "Highest Percentile - 0.90=90%, 0.95=95%, 0.99=99%") # Criteria for Down Trend Definition for Filtered Pivots and Aggressive # Filtered Pivots ltLB = 40 # Long-Term Look Back Current Bar Has To Close Below This Value OR Medium Term--Default=40") mtLB = 14 # Medium-Term Look Back Current Bar Has To Close Below This Value OR Long Term--Default=14") Str = 3 # Entry Price Action Strength--Close > X Bars Back---Default=3") history = self.History(self.bullish, 2 * _pd + 2, Resolution.Daily) spy_close = {} spy_lows = {} spy_close[self.bullish] = [] spy_lows[self.bullish] = [] for tuple in history.loc[self.bullish].itertuples(): spy_close[self.bullish].append(tuple.close) spy_lows[self.bullish].append(tuple.low) spy_close = pd.DataFrame.from_dict(spy_close) spy_lows = pd.DataFrame.from_dict(spy_lows) spy_highest = spy_close.rolling(window=_pd).max() # Williams Vix Fix Formula wvf = ((spy_highest - spy_lows) / (spy_highest)) * 100 sDev = mult * np.std(wvf[-bbl:]) midLine = np.mean(wvf[-bbl:]) upperBand = midLine + sDev rangeHigh = (max(wvf.values[-lb:])) * ph spy_higher_then_Xdays_back = spy_close.values[-1] > spy_close.values[-Str] spy_lower_then_longterm = spy_close.values[-1] < spy_close.values[-ltLB] spy_lower_then_midterm = spy_close.values[-1] < spy_close.values[-mtLB] # Alerts Criteria alert = (wvf.values[-1][0] >= upperBand[0] and wvf.values[-1][0] >= rangeHigh[0]) and (wvf.values[-2][0] >= upperBand[0] and wvf.values[-2][0] >= rangeHigh[0]) # spy_higher_then_Xdays_back if (alert or spy_higher_then_Xdays_back) and (spy_lower_then_longterm or spy_lower_then_midterm): self.Liquidate(self.bearish) self.SetHoldings(self.bullish, 0.3 * self.leverage) else: self.Liquidate(self.bullish) self.SetHoldings(self.bearish, 0.4 * self.leverage) def variance(x, *args): p = np.squeeze(np.asarray(args)) Acov = np.cov(p.T) return np.dot(x, np.dot(Acov, x)) def jac_variance(x, *args): p = np.squeeze(np.asarray(args)) Acov = np.cov(p.T) return 2 * np.dot(Acov, x)