Another Failed Volatility Histeresis: Ehlers’s Own Idea

[This article was first published on QuantStrat TradeR » R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This week, I attempted to use Ehlers’s own idea from this presentation.

Essentially, the idea is that when an indicator is flat, line crossings can produce whipsaws, so add a fraction of the daily range to the lagged indicator, and see if the non-lagged indicator crosses the threshold. In this case, it’s an exponentially smoothed daily range that’s used to compute the bands. I ran this from 2012 through the present day at the time of this writing (July 14, 2014), as the original link goes through most of the 2000s. (Also, be sure you’re using my most up-to-date IKTrading package, as I updated the quandClean function to deal with some intraday messy data issues that had gone unnoticed before.)

The settings I used were John Ehlers’s original settings — that is, a 20 day analysis period, a 10 day exponential band smoothing (that is, the band is computed as .1*(high-low)+.9*band), entered upon the percent B (that is, the current FRAMA minus the low band over the difference of the bands), and the fraction is 1/10th of the daily range.

Here’s the indicator used:

FRAMAbands <- function(HLC, n=126, FC=1, SC=300, nBands=n/2, bandFrac=10, ...) {
  frama <- FRAMA(HLC, n=n, FC=FC, SC=SC, ...)
  band <- Hi(HLC) - Lo(HLC)
  band <- xts(filter(1/nBands*band, 1-1/nBands, method="recursive"), order.by=index(frama))
  bandUp <- frama$trigger + band/bandFrac
  bandDn <- frama$trigger - band/bandFrac
  pctB <- (frama$FRAMA-bandDn)/(bandUp-bandDn)
  out <- cbind(frama, pctB)
  colnames(out) <- c("FRAMA", "trigger", "pctB")
  return(out)
}

And here’s the strategy code:

source("futuresData.R")

#trade sizing and initial equity settings
tradeSize <- 100000
initEq <- tradeSize*length(symbols)

strategy.st <- portfolio.st <- account.st <- "FRAMA_BANDS_I"
rm.strat(portfolio.st)
rm.strat(strategy.st)
initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, currency='USD',initEq=initEq)
initOrders(portfolio.st, initDate=initDate)
strategy(strategy.st, store=TRUE)

#parameters
FC = 1
SC = 300
n = 20
triggerLag = 1
nBands = 10
bandFrac=10
entryThreshPctB=1
exitThreshPctB=.5

period=10
pctATR=.06

#indicators
add.indicator(strategy.st, name="FRAMAbands",
              arguments=list(HLC=quote(HLC(mktdata)), FC=FC, SC=SC, 
                             n=n, triggerLag=triggerLag, nBands=nBands,
                             bandFrac=bandFrac),
              label="Fbands")

add.indicator(strategy.st, name="lagATR", 
              arguments=list(HLC=quote(HLC(mktdata)), n=period), 
              label="atrX")

#signals
add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="pctB.Fbands", 
                          threshold=entryThreshPctB, 
                          relationship="gt", cross=TRUE),
           label="longEntry")

add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="pctB.Fbands", 
                          threshold=exitThreshPctB, 
                          relationship="lt", cross=TRUE),
           label="longExit")

#rules
add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="longEntry", sigval=TRUE, ordertype="market", 
                        orderside="long", replace=FALSE, prefer="Open", osFUN=osDollarATR,
                        tradeSize=tradeSize, pctATR=pctATR, atrMod="X"), 
         type="enter", path.dep=TRUE)

add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="longExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="long", replace=FALSE, prefer="Open"), 
         type="exit", path.dep=TRUE)

#apply strategy
t1 <- Sys.time()
out <- applyStrategy(strategy=strategy.st,portfolios=portfolio.st)
t2 <- Sys.time()
print(t2-t1)

#set up analytics
updatePortf(portfolio.st)
dateRange <- time(getPortfolio(portfolio.st)$summary)[-1]
updateAcct(portfolio.st,dateRange)
updateEndEq(account.st)

Here are the results:

> (aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
[1] 0.956477
> (aggCorrect <- mean(tStats$Percent.Positive))
[1] 36.39737
> (numTrades <- sum(tStats$Num.Trades))
[1] 1778
> (meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio[tStats$Avg.WinLoss.Ratio < Inf], na.rm=TRUE))
[1] 1.678421

> print(t(durStats))
      [,1]
Min      1
Q1       2
Med      6
Mean     9
Q3      14
Max     65
WMin     1
WQ1      3
WMed    13
WMean   13
WQ3     19
WMax    65
LMin     1
LQ1      2
LMed     4
LMean    6
LQ3      8
LMax    57

mean(corMeans)
[1] 0.08232023

> SharpeRatio.annualized(portfRets)
                                      [,1]
Annualized Sharpe Ratio (Rf=0%) -0.2476826
> Return.annualized(portfRets)
                         [,1]
Annualized Return -0.03485231
> maxDrawdown(portfRets)
[1] 0.2632001

In short, it’s a loser over the past three years. Here’s the equity curve:

Now while it may have worked in the past (or something similar to it, using Ehlers’s filter indicator), it doesn’t seem to do so going forward.

I’ll leave this here for now as a demonstration of how to do Ehlers bands.

Thanks for reading.


To leave a comment for the author, please follow the link and comment on their blog: QuantStrat TradeR » R.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)