The plan
Augen uses it to gain an edge in detecting where option volatility is mispriced, but I believe the concept is very useful and can be used in other areas. I originally thought it might be useful as a volatility filter by highlighting "abnormal" spikes in the VIX but I'm yet to have much success with that. Another idea was that it might be useful for swing trading in these mean-reverting times, which turned out to be more fruitful.
The basic signal is to go short when an upwards spike greater than 1 standard deviation occurs, and to go long when a downwards spike greater than 1 occurs (i.e spike values greater than 1 or less -1 respectively). For reference I compared it to trading daily mean reversion (going short after an up day, going long after a down day), and fading RSI(2) extremes of 90/10.
How'd it go?
Overall it performed well, with the caveat it was only effective in markets where mean reversion strategies in general are effective. Interestingly, using the default parameters of a 20 day standard deviation with a 1 day lookback has outperformed both Daily Mean Reversion and RSI(2) over the last few years, both of which have fallen a bit flat during the same period.
Scaling into positions based on the size of the spike also seemed to be effective in producing good returns in absolute terms.
Final thoughts
Like most MR strategies it has an abysmal backtest if you start from the 70's. I am still fascinated by the regime change that took place between follow through and mean reversion around 1999/2000, which Marketsci has covered in some depth. Maybe one day I will figure it out.
A comparison of the equity curves is below, along with the code. I think this is a very useful way of looking at price changes, and will continue to investigate ways it can be put to good use.
require(PerformanceAnalytics)
slideapply <- function(x, n, FUN=sd) {
v <- c(rep(NA, length(x)))
for (i in n:length(x) ) {
v[i] <- FUN(x[(i-n+1):i])
}
return(v)
}
augenSpike <- function(x, n=20, k=1) {
prchg <- c(rep(NA,k), diff(x, k))
lgchg <- c(rep(NA,k), diff(log(x), k))
stdevlgchg <- slideapply(lgchg, n, sd)
stdpr <- x * stdevlgchg
#shuffle things up one
stdpr <- c(NA, stdpr[-length(stdpr)])
spike <- prchg / stdpr
return(spike)
}
perf <- function(x) { print(maxDrawdown(x)); table.AnnualizedReturns(x) }
retsum <- function(x) { return(exp(cumsum(na.omit(x))))}
getSymbols("SPY", from="2000-01-01")
spy <- SPY["2000/2011"]
spy$up_day <- ifelse(ROC(Cl(spy)) >=0, 1, 0 )
sig <- Lag(ifelse(spy$up_day == 1, -1, 1))
ret <- ROC(Cl(spy)) * sig
dmr_eq <- retsum(ret)
plot(dmr_eq)
perf(ret)
#[1] 0.3217873
# SPY.Close
#Annualized Return 0.1131
#Annualized Std Dev 0.2203
#Annualized Sharpe (Rf=0%) 0.5137
spy$rsi2 <- RSI(Cl(spy), 2)
sig <- Lag(ifelse(spy$rsi2 < 10, 1, ifelse(spy$rsi2 > 90, -1, 0)))
ret <- ROC(Cl(spy)) * sig
rsi_eq <- retsum(ret)
plot(rsi_eq)
perf(ret)
#[1] 0.1819428
# SPY.Close
#Annualized Return 0.0963
#Annualized Std Dev 0.1198
#Annualized Sharpe (Rf=0%) 0.8038
aus <- augenSpike(as.vector(Cl(spy)), k=1)
spy$spike <- aus
sig <- Lag(ifelse(spy$spike > 1, -1, ifelse(spy$spike < -1, 1, 0)))
ret <- ROC(Cl(spy)) * sig
k1_eq <- retsum(ret)
plot(k1_eq)
perf(ret)
#[1] 0.1379868
# SPY.Close
#Annualized Return 0.1066
#Annualized Std Dev 0.1152
#Annualized Sharpe (Rf=0%) 0.9256
aus <- augenSpike(as.vector(Cl(spy)), k=2)
spy$spike <- aus
sig <- Lag(ifelse(spy$spike > 1, -1, ifelse(spy$spike < -1, 1, 0)))
ret <- ROC(Cl(spy)) * sig
k2_eq <- retsum(ret)
plot(k2_eq)
perf(ret)
#[1] 0.2134826
# SPY.Close
#Annualized Return 0.1091
#Annualized Std Dev 0.1433
#Annualized Sharpe (Rf=0%) 0.7608
aus <- augenSpike(as.vector(Cl(spy)), k=1)
spy$spike <- aus
sig <- Lag(ifelse(spy$spike > 1, (-1 * spy$spike), ifelse(spy$spike < -1, abs(spy$spike), 0)))
ret <- ROC(Cl(spy)) * sig
k1_scaled_eq <- retsum(ret)
plot(k1_scaled_eq)
perf(ret)
Very interesting, one question the k value used within the formula is the lookback period? I though in general it was a one day lookback but I see also the k=2 equation, if you can clarify please. Great work.
ReplyDeleteHey thanks. The k parameter is how many bars are used when calculating the spikes. From Augens book he suggested using 1,2 & 3 periods when making the spike data. I didnt really notice a material improvement when using 2 and 3 bar sized spikes
DeleteAlso from memory it backtested pretty nicely if you removed the constraint of spike size being > 1 (or < -1) and instead using the spike size as a position size. So a spike of 0.5 means you would take a half sized position. Hope thats clear
5y2vsatr
ReplyDeletecialis 20 mg satın al
https://shop.blognokta.com/urunler/ereksiyon-haplari/cialis/cialis-5-mg-28-tablet-eczane-fiyati-ve-orijinal-ilac-satisi/
cialis 100 mg satın al
viagra sipariş
kamagra 100 mg
glucotrust
sight care
gv1qu3p3
ReplyDeleteglucotrust official website
cialis 5 mg resmi satış sitesi
viagra sipariş
cialis 20 mg
sightcare
kamagra jel
cialis eczane