| 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)