Ehlers’s Autocorrelation Periodogram

This post will introduce John Ehlers’s Autocorrelation Periodogram mechanism–a mechanism designed to dynamically find a lookback period. That is, the most common parameter optimized in backtests is the lookback period.

Before beginning this post, I must give credit where it’s due, to one Mr. Fabrizio Maccallini, the head of structured derivatives at Nordea Markets in London. You can find the rest of the repository he did for Dr. John Ehlers’s Cycle Analytics for Traders on his github. I am grateful and honored that such intelligent and experienced individuals are helping to bring some of Dr. Ehlers’s methods into R.

The point of the Ehlers Autocorrelation Periodogram is to dynamically set a period between a minimum and a maximum period length. While I leave the exact explanation of the mechanic to Dr. Ehlers’s book, for all practical intents and purposes, in my opinion, the punchline of this method is to attempt to remove a massive source of overfitting from trading system creation–namely specifying a lookback period.

SMA of 50 days? 100 days? 200 days? Well, this algorithm takes that possibility of overfitting out of your hands. Simply, specify an upper and lower bound for your lookback, and it does the rest. How well it does it is a topic of discussion for those well-versed in the methodologies of electrical engineering (I’m not), so feel free to leave comments that discuss how well the algorithm does its job, and feel free to blog about it as well.

In any case, here’s the original algorithm code, courtesy of Mr. Maccallini:

AGC <- function(loCutoff = 10, hiCutoff = 48, slope = 1.5) {      accSlope = -slope # acceptableSlope = 1.5 dB   ratio = 10 ^ (accSlope / 20)   if ((hiCutoff - loCutoff) > 0)
    factor <-  ratio ^ (2 / (hiCutoff - loCutoff));
  return (factor)
}

autocorrPeriodogram <- function(x, period1 = 10, period2 = 48, avgLength = 3) {
  # high pass filter
  alpha1 <- (cos(sqrt(2) * pi / period2) + sin(sqrt(2) * pi / period2) - 1) / cos(sqrt(2) * pi / period2)
  hp <- (1 - alpha1 / 2) ^ 2 * (x - 2 * lag(x) + lag(x, 2))
  hp <- hp[-c(1, 2)]
  hp <- filter(hp, (1 - alpha1), method = "recursive")
  hp <- c(NA, NA, hp)
  hp <- xts(hp, order.by = index(x))
  # super smoother
  a1 <- exp(-sqrt(2) * pi / period1)
  b1 <- 2 * a1 * cos(sqrt(2) * pi / period1)
  c2 <- b1
  c3 <- -a1 * a1
  c1 <- 1 - c2 - c3
  filt <- c1 * (hp + lag(hp)) / 2
  leadNAs <- sum(is.na(filt))
  filt <- filt[-c(1: leadNAs)]
  filt <- filter(filt, c(c2, c3), method = "recursive")
  filt <- c(rep(NA, leadNAs), filt)
  filt <- xts(filt, order.by = index(x))
  # Pearson correlation for each value of lag
  autocorr <- matrix(0, period2, length(filt))
  for (lag in 2: period2) {
    # Set the average length as M
    if (avgLength == 0) M <- lag
    else M <- avgLength
    autocorr[lag, ] <- runCor(filt, lag(filt, lag), M)
  }
  autocorr[is.na(autocorr)] <- 0
  # Discrete Fourier transform
  # Correlate autocorrelation values with the cosine and sine of each period of interest
  # The sum of the squares of each value represents relative power at each period
  cosinePart <- sinePart <- sqSum <- R <- Pwr <- matrix(0, period2, length(filt))
  for (period in period1: period2) {
    for (N in 2: period2) {
      cosinePart[period, ] = cosinePart[period, ] + autocorr[N, ] * cos(2 * N * pi / period)
      sinePart[period, ] = sinePart[period, ] + autocorr[N, ] * sin(2 * N * pi / period)
    }
    sqSum[period, ] = cosinePart[period, ] ^ 2 + sinePart[period, ] ^ 2
    R[period, ] <- EMA(sqSum[period, ] ^ 2, ratio = 0.2)
  }
  R[is.na(R)] <- 0
  # Normalising Power
  K <- AGC(period1, period2, 1.5)
  maxPwr <- rep(0, length(filt))   for(period in period1: period2) {     for (i in 1: length(filt)) {       if (R[period, i] >= maxPwr[i]) maxPwr[i] <- R[period, i]
      else maxPwr[i] <- K * maxPwr[i]
    }
  }
  for(period in 2: period2) {
    Pwr[period, ] <- R[period, ] / maxPwr
  }
  # Compute the dominant cycle using the Center of Gravity of the spectrum
  Spx <- Sp <- rep(0, length(filter))
  for(period in period1: period2) {
    Spx <- Spx + period * Pwr[period, ] * (Pwr[period, ] >= 0.5)
    Sp <- Sp + Pwr[period, ] * (Pwr[period, ] >= 0.5)
  }
  dominantCycle <- Spx / Sp
  dominantCycle[is.nan(dominantCycle)] <- 0
  dominantCycle <- xts(dominantCycle, order.by=index(x))
  dominantCycle <- dominantCycle[dominantCycle > 0]
  return(dominantCycle)
  #heatmap(Pwr, Rowv = NA, Colv = NA, na.rm = TRUE, labCol = "", add.expr = lines(dominantCycle, col = 'blue'))
}

One thing I do notice is that this code uses a loop that says for(i in 1:length(filt)), which is an O(data points) loop, which I view as the plague in R. While I’ve used Rcpp before, it’s been for only the most basic of loops, so this is definitely a place where the algorithm can stand to be improved with Rcpp due to R’s inherent poor looping.

Those interested in the exact logic of the algorithm will, once again, find it in John Ehlers’s Cycle Analytics For Traders book (see link earlier in the post).

Of course, the first thing to do is to test how well the algorithm does what it purports to do, which is to dictate the lookback period of an algorithm.

Let’s run it on some data.

getSymbols('SPY', from = '1990-01-01')

t1 <- Sys.time()
out <- autocorrPeriodogram(Ad(SPY), period1 = 120, period2 = 252, avgLength = 3)
t2 <- Sys.time() print(t2-t1) 

And the result:

 > t1 <- Sys.time() > out <- autocorrPeriodogram(Ad(SPY), period1 = 120, period2 = 252, avgLength = 3) > t2 <- Sys.time() > print(t2-t1)
Time difference of 33.25429 secs

Now, what does the algorithm-set lookback period look like?

plot(out)

Let’s zoom in on 2001 through 2003, when the markets went through some upheaval.

plot(out['2001::2003']

In this zoomed-in image, we can see that the algorithm’s estimates seem fairly jumpy.

Here’s some code to feed the algorithm’s estimates of n into an indicator to compute an indicator with a dynamic lookback period as set by Ehlers’s autocorrelation periodogram.

acpIndicator <- function(x, minPeriod, maxPeriod, indicatorFun = EMA, ...) {
  acpOut <- autocorrPeriodogram(x = x, period1 = minPeriod, period2 = maxPeriod)
  roundedAcpNs <- round(acpOut, 0) # round to the nearest integer
  uniqueVals <- unique(roundedAcpNs) # unique integer values
  out <- xts(rep(NA, length(roundedAcpNs)), order.by=index(roundedAcpNs))

  for(i in 1:length(uniqueVals)) { # loop through unique values, compute indicator
    tmp <- indicatorFun(x, n = uniqueVals[i], ...)
    out[roundedAcpNs==uniqueVals[i]] <- tmp[roundedAcpNs==uniqueVals[i]]
  }
  return(out)
}

And here is the function applied with an SMA, to tune between 120 and 252 days.

ehlersSMA <- acpIndicator(Ad(SPY), 120, 252, indicatorFun = SMA)

plot(Ad(SPY)['2008::2010'])
lines(ehlersSMA['2008::2010'], col = 'red')

And the result:

As seen, this algorithm is less consistent than I would like, at least when it comes to using a simple moving average.

For now, I’m going to leave this code here, and let people experiment with it. I hope that someone will find that this indicator is helpful to them.

Thanks for reading.

NOTES: I am always interested in networking/meet-ups in the northeast (Philadelphia/NYC). Furthermore, if you believe your firm will benefit from my skills, please do not hesitate to reach out to me. My linkedin profile can be found here.

Lastly, I am volunteering to curate the R section for books on quantocracy. If you have a book about R that can apply to finance, be sure to let me know about it, so that I can review it and possibly recommend it. Thakn you.

A John Ehlers oscillator — Cycle RSI(2)

Since I’ve hit a rut in trend following (how do you quantify rising/falling/flat? What even defines those three terms in precise, machine definition? How do you avoid buying tops while not getting chopped by whipsaws?), I decided to look the other way, with oscillators. Certainly, I’m not ready to give up on Dr. Ehlers just yet. So, in this post, I’ll introduce a recent innovation of the RSI by Dr. John Ehlers.

The indicator is Dr. Ehlers’s modified RSI from Chapter 7 of Cycle Analytics for Traders.

For starters, here’s how the Ehlers RSI is different than the usual ones: it gets filtered with a high-pass filter and then smoothed with a supersmoother filter. While Michael Kapler also touched on this topic a while back, I suppose it can’t hurt if I attempted to touch on it myself.

Here is the high pass filter and the super smoother, from the utility.R file in DSTrading. They’re not exported since as of the moment, they’re simply components of other indicators.

highPassFilter <- function(x) {
  alpha1 <- (cos(.707*2*pi/48)+sin(.707*2*pi/48)-1)/cos(.707*2*pi/48)
  HP <- (1-alpha1/2)*(1-alpha1/2)*(x-2*lag(x)+lag(x,2))
  HP <- HP[-c(1,2)]
  HP <- filter(HP, c(2*(1-alpha1), -1*(1-alpha1)*(1-alpha1)), method="recursive")
  HP <- c(NA, NA, HP)
  HP <- xts(HP, order.by=index(x))
  return(HP)
}

superSmoother <- function(x) {
  a1 <- exp(-1.414*pi/10)
  b1 <- 2*a1*cos(1.414*pi/10)
  c2 <- b1
  c3 <- -a1*a1
  c1 <- 1-c2-c3
  filt <- c1*(x+lag(x))/2
  leadNAs <- sum(is.na(filt))
  filt <- filt[-c(1:leadNAs)]
  filt <- filter(filt, c(c2, c3), method="recursive")
  filt <- c(rep(NA,leadNAs), filt)
  filt <- xts(filt, order.by=index(x))
}

In a nutshell, both of these functions serve to do an exponential smoothing on the data using some statically computed trigonometric quantities, the rationale of which I will simply defer to Dr. Ehlers’s book (link here).

Here’s the modified ehlers RSI, which I call CycleRSI, from the book in which it’s defined:

"CycleRSI" <- function(x, n=20) {
  filt <- superSmoother(highPassFilter(x))
  diffFilt <- diff(filt)
  posDiff <- negDiff <- diffFilt
  posDiff[posDiff < 0] <- 0
  negDiff[negDiff > 0] <- 0
  negDiff <- negDiff*-1
  posSum <- runSum(posDiff, n)
  negSum <- runSum(negDiff, n)
  denom <- posSum+negSum
  rsi <- posSum/denom
  rsi <- superSmoother(rsi)*100
  colnames(rsi) <- "CycleRSI"
  return(rsi)
}

Here’s a picture comparing four separate RSIs.

The first is the RSI featured in this post (cycle RSI) in blue. The next is the basic RSI(2) in red. The one after that is Larry Connors’s Connors RSI , which may be touched on in the future, and the last one, in purple, is the generalized Laguerre RSI, which is yet another Dr. Ehlers creation (which I’ll have to test sometime in the future).

To start things off with the Cycle RSI, I decided to throw a simple strategy around it:

Buy when the CycleRSI(2) crosses under 10 when the close is above the SMA200, which is in the vein of a Larry Connors trading strategy from “Short Term ETF Trading Strategies That Work” (whether they work or not remains debatable), and sell when the CycleRSI(2) crosses above 70, or when the close falls below the SMA200 so that the strategy doesn’t get caught in a runaway downtrend.

Since the strategy comes from an ETF Trading book, I decided to use my old ETF data set, from 2003 through 2010.

Here’s the strategy code, as usual:

require(DSTrading)
require(IKTrading)
require(quantstrat)
require(PerformanceAnalytics)

initDate="1990-01-01"
from="2003-01-01"
to="2010-12-31"
options(width=70)
verbose=TRUE

source("demoData.R")

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

strategy.st <- portfolio.st <- account.st <- "Cycle_RSI_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
nRSI=2
RSIentry=10
RSIexit=70

nSMA=200

period=10
pctATR=.04

#indicators
add.indicator(strategy.st, name="lagATR", 
              arguments=list(HLC=quote(HLC(mktdata)), n=period), 
              label="atrX")
add.indicator(strategy.st, name="SMA",
              arguments=list(x=quote(Cl(mktdata)), n=nSMA),
              label="SMA")
add.indicator(strategy.st, name="CycleRSI",
              arguments=list(x=quote(Cl(mktdata)), n=nRSI),
              label="RSI")

#signals
add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("Close", "SMA"), relationship="gt"),
           label="ClGtSMA")

add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="CycleRSI.RSI", threshold=RSIentry, 
                          relationship="lt", cross=FALSE),
           label="RSIltEntryThresh")

add.signal(strategy.st, name="sigAND",
           arguments=list(columns=c("ClGtSMA", "RSIltEntryThresh"), 
                          cross=TRUE),
           label="longEntry")

add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "SMA"), relationship="lt"),
           label="exitSMA")

add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="CycleRSI.RSI", threshold=RSIexit,
                          relationship="gt", cross=TRUE),
           label="longExit")

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

add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="exitSMA", 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)

And here are the results:

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

> print(t(durStats))
      [,1]
Min      1
Q1       6
Med      9
Mean    11
Q3      14
Max     43
WMin     1
WQ1      7
WMed     9
WMean   11
WQ3     13
WMax    40
LMin     1
LQ1      6
LMed    11
LMean   12
LQ3     15
LMax    43

> print(mean(as.numeric(as.character(mktExposure$MktExposure))))
[1] 0.2806

> mean(corMeans)
[1] 0.2763

> SharpeRatio.annualized(portfRets)
                                    [,1]
Annualized Sharpe Ratio (Rf=0%) 1.215391
> Return.annualized(portfRets)
                       [,1]
Annualized Return 0.1634448
> maxDrawdown(portfRets)
[1] 0.1694307

Overall, the statistics don’t look bad. However, the 1:1 annualized returns to max drawdown isn’t particularly pleasing, as it means that this strategy can’t be leveraged effectively to continue getting outsized returns in this state. Quite irritating. Here’s the equity curve.

In short, as with other mean reverters, when drawdowns happen, they happen relatively quickly and brutally.

Here’s an individual instrument position chart.

By the looks of things, the strategy does best in a market that grinds upwards, rather than a completely choppy sideways market.

Finally, here’s some code for charting all of the different trades.

agg.chart.ME <- function(Portfolio, Symbols, type=c("MAE", "MFE"), scale=c("cash", "percent", "tick")) {
  type=type[1]
  scale=scale[1]
  trades <- list()
  length(trades) <- length(Symbols)
  for(Symbol in Symbols) {
    trades[[Symbol]] <- pts <- perTradeStats(Portfolio=Portfolio, Symbol=Symbol, includeOpenTrade=FALSE)
  }
  trades <- do.call(rbind, trades)
  trades$Pct.Net.Trading.PL <- 100 * trades$Pct.Net.Trading.PL
  trades$Pct.MAE <- 100 * trades$Pct.MAE
  trades$Pct.MFE <- 100 * trades$Pct.MFE
  profitable <- (trades$Net.Trading.PL > 0)
  switch(scale, cash = {
    .ylab <- "Profit/Loss (cash)"
    if (type == "MAE") {
      .cols <- c("MAE", "Net.Trading.PL")
      .xlab <- "Drawdown (cash)"
      .main <- "Maximum Adverse Excursion (MAE)"
    } else {
      .cols <- c("MFE", "Net.Trading.PL")
      .xlab <- "Run Up (cash)"
      .main <- "Maximum Favourable Excursion (MFE)"
    }
  }, percent = {
    .ylab <- "Profit/Loss (%)"
    if (type == "MAE") {
      .cols <- c("Pct.MAE", "Pct.Net.Trading.PL")
      .xlab <- "Drawdown (%)"
      .main <- "Maximum Adverse Excursion (MAE)"
    } else {
      .cols <- c("Pct.MFE", "Pct.Net.Trading.PL")
      .xlab <- "Run Up (%)"
      .main <- "Maximum Favourable Excursion (MFE)"
    }
  }, tick = {
    .ylab <- "Profit/Loss (ticks)"
    if (type == "MAE") {
      .cols <- c("tick.MAE", "tick.Net.Trading.PL")
      .xlab <- "Drawdown (ticks)"
      .main <- "Maximum Adverse Excursion (MAE)"
    } else {
      .cols <- c("tick.MFE", "tick.Net.Trading.PL")
      .xlab <- "Run Up (ticks)"
      .main <- "Maximum Favourable Excursion (MFE)"
    }
  })
  .main <- paste("All trades", .main)
  plot(abs(trades[, .cols]), type = "n", xlab = .xlab, ylab = .ylab, 
       main = .main)
  grid()
  points(abs(trades[profitable, .cols]), pch = 24, col = "green", 
         bg = "green", cex = 0.6)
  points(abs(trades[!profitable, .cols]), pch = 25, col = "red", 
         bg = "red", cex = 0.6)
  abline(a = 0, b = 1, lty = "dashed", col = "darkgrey")
  legend(x = "bottomright", inset = 0.1, legend = c("Profitable Trade", 
                                                    "Losing Trade"), pch = c(24, 25), col = c("green", "red"), 
         pt.bg = c("green", "red"))
}

And the resulting plot:

One last thing to note…that $50,000 trade in the upper left hand corner? That was a yahoo data issue and is a false print. Beyond that, once again, this seems like standard fare for a mean reverter–when trades go bad, they’re *really* bad, but the puzzle of where to put a stop is a completely separate issue, as it usually means locking in plenty of losses that decrease in magnitude, along with possibly turning winners into losers. On the flip side, here’s the maximum favorable excursion plot.

In short, there are definitely trades that could have been stopped for a profit that turned into losers.

In conclusion, while the initial trading system seems to be a good start, it’s far from complete.

Thanks for reading.

Another Failed Volatility Histeresis: Ehlers’s Own Idea

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.

Volatility Histeresis: A First Attempt

So the last time that a FRAMA strategy was tried with price crossovers, the problem was that due to counter-trending failures, the filter that was added missed a lot of good trades, and wound up losing a lot of money during flat markets that passed the arbitrary filter.

This trading system tries to rectify those issues by trading a rising FRAMA filtered on a 5-day standard deviation ratio.

The hypothesis is this: the FRAMA rises in legitimately trending markets, and stays flat in choppy markets. Therefore, the ratio of standard deviations (that is, a running standard deviation of the FRAMA over the standard deviation of the market close) should be higher during trending markets, and lower during choppy markets. Additionally, as this ratio bottoms out at zero and usually tops out at 1 (rarely gets higher), it can be used as an indicator across instruments of vastly different properties.

The data that will be used will be the quandl futures data file (without federal funds, coffee, or sugar, because of data issues).

Here’s the data file:

require(IKTrading)


currency('USD')
Sys.setenv(TZ="UTC")


t1 <- Sys.time()
if(!"CME_CL" %in% ls()) {
  #Energies
  CME_CL <- quandClean("CHRIS/CME_CL", start_date=from, end_date=to, verbose=verbose) #Crude
  CME_NG <- quandClean("CHRIS/CME_NG", start_date=from, end_date=to, verbose=verbose) #NatGas
  CME_HO <- quandClean("CHRIS/CME_HO", start_date=from, end_date=to, verbose=verbose) #HeatingOil
  CME_RB <- quandClean("CHRIS/CME_RB", start_date=from, end_date=to, verbose=verbose) #Gasoline
  ICE_B <- quandClean("CHRIS/ICE_B", start_date=from, end_date=to, verbose=verbose) #Brent
  ICE_G <- quandClean("CHRIS/ICE_G", start_date=from, end_date=to, verbose=verbose) #Gasoil
  
  #Grains
  CME_C <- quandClean("CHRIS/CME_C", start_date=from, end_date=to, verbose=verbose) #Chicago Corn
  CME_S <- quandClean("CHRIS/CME_S", start_date=from, end_date=to, verbose=verbose) #Chicago Soybeans
  CME_W <- quandClean("CHRIS/CME_W", start_date=from, end_date=to, verbose=verbose) #Chicago Wheat
  CME_SM <- quandClean("CHRIS/CME_SM", start_date=from, end_date=to, verbose=verbose) #Chicago Soybean Meal
  CME_KW <- quandClean("CHRIS/CME_KW", start_date=from, end_date=to, verbose=verbose) #Kansas City Wheat
  CME_BO <- quandClean("CHRIS/CME_BO", start_date=from, end_date=to, verbose=verbose) #Chicago Soybean Oil
  
  #Softs
  #ICE_SB <- quandClean("CHRIS/ICE_SB", start_date=from, end_date=to, verbose=verbose) #Sugar
  #Sugar 2007-03-26 is wrong
  #ICE_KC <- quandClean("CHRIS/ICE_KC", start_date=from, end_date=to, verbose=verbose) #Coffee
  #Coffee January of 08 is FUBAR'd
  ICE_CC <- quandClean("CHRIS/ICE_CC", start_date=from, end_date=to, verbose=verbose) #Cocoa
  ICE_CT <- quandClean("CHRIS/ICE_CT", start_date=from, end_date=to, verbose=verbose) #Cotton
  
  #Other Ags
  CME_LC <- quandClean("CHRIS/CME_LC", start_date=from, end_date=to, verbose=verbose) #Live Cattle
  CME_LN <- quandClean("CHRIS/CME_LN", start_date=from, end_date=to, verbose=verbose) #Lean Hogs
  
  #Precious Metals
  CME_GC <- quandClean("CHRIS/CME_GC", start_date=from, end_date=to, verbose=verbose) #Gold
  CME_SI <- quandClean("CHRIS/CME_SI", start_date=from, end_date=to, verbose=verbose) #Silver
  CME_PL <- quandClean("CHRIS/CME_PL", start_date=from, end_date=to, verbose=verbose) #Platinum
  CME_PA <- quandClean("CHRIS/CME_PA", start_date=from, end_date=to, verbose=verbose) #Palladium
  
  #Base
  CME_HG <- quandClean("CHRIS/CME_HG", start_date=from, end_date=to, verbose=verbose) #Copper
  
  #Currencies
  CME_AD <- quandClean("CHRIS/CME_AD", start_date=from, end_date=to, verbose=verbose) #Ozzie
  CME_CD <- quandClean("CHRIS/CME_CD", start_date=from, end_date=to, verbose=verbose) #Loonie
  CME_SF <- quandClean("CHRIS/CME_SF", start_date=from, end_date=to, verbose=verbose) #Franc
  CME_EC <- quandClean("CHRIS/CME_EC", start_date=from, end_date=to, verbose=verbose) #Euro
  CME_BP <- quandClean("CHRIS/CME_BP", start_date=from, end_date=to, verbose=verbose) #Cable
  CME_JY <- quandClean("CHRIS/CME_JY", start_date=from, end_date=to, verbose=verbose) #Yen
  CME_NE <- quandClean("CHRIS/CME_NE", start_date=from, end_date=to, verbose=verbose) #Kiwi
  
  #Equities
  CME_ES <- quandClean("CHRIS/CME_ES", start_date=from, end_date=to, verbose=verbose) #Emini
  CME_MD <- quandClean("CHRIS/CME_MD", start_date=from, end_date=to, verbose=verbose) #Midcap 400
  CME_NQ <- quandClean("CHRIS/CME_NQ", start_date=from, end_date=to, verbose=verbose) #Nasdaq 100
  CME_TF <- quandClean("CHRIS/CME_TF", start_date=from, end_date=to, verbose=verbose) #Russell Smallcap
  CME_NK <- quandClean("CHRIS/CME_NK", start_date=from, end_date=to, verbose=verbose) #Nikkei
  
  #Dollar Index and Bonds/Rates
  ICE_DX  <- quandClean("CHRIS/CME_DX", start_date=from, end_date=to, verbose=verbose) #Dixie
  #CME_FF  <- quandClean("CHRIS/CME_FF", start_date=from, end_date=to, verbose=verbose) #30-day fed funds
  CME_ED  <- quandClean("CHRIS/CME_ED", start_date=from, end_date=to, verbose=verbose) #3 Mo. Eurodollar/TED Spread
  CME_FV  <- quandClean("CHRIS/CME_FV", start_date=from, end_date=to, verbose=verbose) #Five Year TNote
  CME_TY  <- quandClean("CHRIS/CME_TY", start_date=from, end_date=to, verbose=verbose) #Ten Year Note
  CME_US  <- quandClean("CHRIS/CME_US", start_date=from, end_date=to, verbose=verbose) #30 year bond
}

CMEinsts <- c("CL", "NG", "HO", "RB", "C", "S", "W", "SM", "KW", "BO", "LC", "LN", "GC", "SI", "PL", 
              "PA", "HG", "AD", "CD", "SF", "EC", "BP", "JY", "NE", "ES", "MD", "NQ", "TF", "NK", #"FF",
              "ED", "FV", "TY", "US")

ICEinsts <- c("B", "G", #"SB", #"KC", 
              "CC", "CT", "DX")
CME <- paste("CME", CMEinsts, sep="_")
ICE <- paste("ICE", ICEinsts, sep="_")
symbols <- c(CME, ICE)
stock(symbols, currency="USD", multiplier=1)
t2 <- Sys.time()
print(t2-t1)

Here’s the strategy:

require(DSTrading)
require(IKTrading)
require(quantstrat)
require(PerformanceAnalytics)

initDate="1990-01-01"
from="2000-03-01"
to="2011-12-31"
options(width=70)
verose=TRUE

FRAMAsdr <- function(HLC, n, FC, SC, nSD, ...) {
  frama <- FRAMA(HLC, n=n, FC=FC, SC=SC, ...)
  sdr <- runSD(frama$FRAMA, n=nSD)/runSD(Cl(HLC), n=nSD)
  sdr[sdr > 2]  <- 2
  out <- cbind(FRAMA=frama$FRAMA, trigger=frama$trigger, sdr=sdr)
  out
}

source("futuresData.R")

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

strategy.st <- portfolio.st <- account.st <- "FRAMA_SDR_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 = 126
triggerLag = 1
nSD = 5
sdThresh <- .3

period=10
pctATR=.02

#indicators
add.indicator(strategy.st, name="FRAMAsdr",
              arguments=list(HLC=quote(HLC(mktdata)), FC=FC, SC=SC, 
                             n=n, triggerLag=triggerLag, nSD=nSD),
              label="SDR")

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

#signals
add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("FRAMA.SDR", "trigger.SDR"), relationship="gt"),
           label="FRAMAup")

add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="sdr.SDR", threshold=sdThresh, 
                          relationship="gt",cross=FALSE),
           label="SDRgtThresh")

add.signal(strategy.st, name="sigAND",
           arguments=list(columns=c("FRAMAup", "SDRgtThresh"), cross=TRUE),
           label="longEntry")

add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("FRAMA.SDR", "trigger.SDR"), relationship="lt"),
           label="FRAMAdnExit")

#add.signal(strategy.st, name="sigThreshold",
#           arguments=list(column="sdr.SDR", threshold=sdThresh, relationship="lt", cross=TRUE),
#           label="SDRexit")

#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="FRAMAdnExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="long", replace=FALSE, prefer="Open"), 
         type="exit", path.dep=TRUE)

#add.rule(strategy.st, name="ruleSignal", 
#         arguments=list(sigcol="SDRexit", 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)

Notice that the exit due to the volatility filter had to have been commented out (as it caused the strategy to lose all its edge). In any case, the FRAMA is the usual 126 day FRAMA, and the running standard deviation is 5 days, in order to try and reduce lag. The standard deviation ratio threshold will be .2 or higher. Here are the results:

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

In other words, typical trend follower results. 40/60 wrong to right, with a 2:1 win to loss ratio. Far from spectacular.

Duration statistics:

print(t(durStats))
      [,1]
Min      1
Q1       2
Med      5
Mean    11
Q3      11
Max    158
WMin     1
WQ1      5
WMed    10
WMean   18
WQ3     21
WMax   158
LMin     1
LQ1      1
LMed     3
LMean    6
LQ3      6
LMax    93

In short, winners last longer than losers, which makes sense given that there are a lot of whipsaws, and that this is a trend-following strategy.

Market exposure:

> print(mean(as.numeric(as.character(mktExposure$MktExposure))))
[1] 0.3820789

38%. So how did it perform?

Like this. Not particularly great, considering it’s a 60% gain over 11 years. Here are the particular statistics:

> SharpeRatio.annualized(portfRets)
                                     [,1]
Annualized Sharpe Ratio (Rf=0%) 0.8124137
> Return.annualized(portfRets)
                        [,1]
Annualized Return 0.04229355
> maxDrawdown(portfRets)
[1] 0.07784351

In other words, about 10 basis points of returns per percent of market exposure, or a 10% annualized return. The problem being? The drawdown is much higher than the annualized return, meaning that leverage will only make things worse. Basically, for the low return on exposure and high drawdown to annualized return, this strategy is a failure. While the steadily ascending equity curve is good, it is meaningless when the worst losses take more than a year to recover from.

In any case, here’s a look at some individual instruments.

Here’s the equity curve for the E-minis.

So first off, we can see one little feature of this strategy–due to the entry and exit not being symmetric (that is, it takes two conditions to enter–a rising FRAMA and a standard deviation ratio above .2–and only exits on one of them (falling FRAMA), price action that exhibits a steady grind upwards, due to the rapid change in ATR (it’s a 10-day figure) can actually slightly pyramid from time to time. This is a good feature in my opinion, since it can add onto a winning position. However, in times of extreme volatility, when even an adaptive indicator can get bounced around chasing “mini-trends”, we can see losses pile on.

Next, let’s look at a much worse situation. Here’s the equity curve for the Eurodollar/TED spread.

In this case, it’s clearly visible that the strategy has countertrend issues, as well as the fact that the 5-day standard deviation ratio can be relatively myopic when it comes to instruments that have protracted periods of complete inactivity–that is, the market is not even choppy so much as just still.

I’ll leave this here, and move onto other attempts at getting around this sort of roadblock next.

Thanks for reading.

FRAMA Part V: Wrap-Up on Confirmatory Indicator/Test Sample

So, it is possible to create a trading system that can correctly isolate severe and protracted downturns, without taking (too many) false signals.

Here are the rules:

126 day FRAMA, FC=4, SC=300 (we’re still modifying the original ETFHQ strategy).
A running median (somewhere between 150 days and 252 days–seemingly, all these configurations work).

Both the FRAMA and the confirmatory median must be moving in the same direction (up for a long trade, down for a short trade–the median rising is the new rule here), and the price must cross the FRAMA in that direction to enter into a trade, while exiting when the price crosses below the FRAMA. Presented as a set of quantstrat rules, it gets rather lengthy, since a rule needs to specify the three setup conditions, a rule to bind them together, and an exit rule (5 rules each side).

The strategy works on both long and short ends, though the short version seems more of an insurance strategy than anything else. Here’s the equity curve for a 150 day median:

Basically, it makes money in terrible periods, but gives some of it back during just about any other time. It’s there just to put it out there as something that can finally try and isolate the truly despicable conditions and give you a pop in those times. Other than that? Using it would depend on how often someone believes those sorts of drawdown conditions would occur–that is, a descending adaptive indicator, a descending 7-12 month median.

Here are the trade and portfolio statistics:

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

                                      [,1]
Annualized Sharpe Ratio (Rf=0%) 0.07606299
> Return.annualized(portfRets)
                         [,1]
Annualized Return 0.004247807
> maxDrawdown(portfRets)
[1] 0.09845553

In other words, it’s absolutely not a standalone strategy, but more of a little something to give a long-only strategy a boost during bad times. It’s certainly not as spectacular as it gets. For instance, here’s the equity curve for XLK in late 2008-2009. Mainly, the problem with the ETFHQ strategy (I’m still on that, yes) is that it does not at all take into account the magnitude of the direction of the indicator. This means that in a reverting market, this strategy has a potential to lose a great deal of money unnecessarily.

Basically, this strategy is highly conservative, meaning that it has a tendency to miss good trades, take unnecessary ones, and is generally flawed because it has no way of really estimating the slope of the FRAMA.

As the possible solution to this involves a strategy by John Ehlers, I think I’ll leave this strategy here for now.

So, to send off this original ETFHQ price cross strategy off, I’ll test it out of sample using a 200-day median, using both long and short sides (from 2010-03-01 to get the 200 day median burned in, to the current date as of the time of this writing, 2014-06-20).

Here are the trade stats and portfolio stats:

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

> SharpeRatio.annualized(portfRets)
                                     [,1]
Annualized Sharpe Ratio (Rf=0%) 0.3290298
> Return.annualized(portfRets)
                        [,1]
Annualized Return 0.02467234
> maxDrawdown(portfRets)
[1] 0.1354166

With the corresponding equity curve:

In short, definitely not good. Why?

Here’s a good symptom as to why:

This is the out-of-sample equity curve of SHY–that is, the ETF of short term bonds. The trend had ended, but the trading system didn’t pick up on that.

In this case, you can see that the magnitude of the trend makes no difference to the strategy–which is a major problem. Although the counter-trend trading was eliminated, forcing action was not, and trying to remain loyal to the price crossing the indicator strategy while sticking to more conventional methods (a confirming indicator) turns out to be flawed. Here is another side symptom of a flawed system:

In this instance, using such a conservative confirmatory indicator for the short trade and simply using that same indicator for the long side indicates that there may very well have been overfitting on the system. On a more general note, however, this picture makes one wonder whether a confirmatory indicator was even necessary. For instance, there were certainly protracted periods during which there was a long trend that were cut off due to the running median being slightly negative. There were both long and short opportunities missed.

In my opinion, I think this puts the kibosh on something as ham-handed as a long-running confirmatory indicator. Why? Because I think that it over-corrects for a flawed order logic system that doesn’t take into account the magnitude of the slope of the indicator. Obviously, trading in a countertrend (descending indicator) is a terrible idea. But what about a slight change of directional sign as part of a greater counter-trend? Suddenly, a robust, deliberately lagging confirmatory indicator no longer seems like such a bad idea. However, as you can see, the downside of a lagging indicator is that it may very well lag your primary indicator in a good portion of cases. And it does nothing to eliminate sideways trading.

Surely, a more elegant solution exists that attempts to quantify the fact that sometimes, the smooth-yet-adaptive FRAMA can trend rapidly (and such trades should be taken posthaste), and can also go flat. Ultimately, I think that while the indicator settings from ETFHQ have some merit, the simplistic order logic on its own can certainly hurt–and coupled with an order-sizing function that downsizes orders in times of trending while magnifying them in times of calm (a side-effect of ATR, which was created to equalize risk across instruments, but with the unintended consequence of very much not equalizing risk across market conditions) can cause problems.

The next strategy will attempt to rectify these issues.

Thanks for reading.

FRAMA Part IV: Continuing the Long/Short Filter Search

This post examines an n-day median filter for two desirable properties: robustness to outliers and an inherent trend-confirming lag. While this is an incomplete filter (or maybe even inferior), it offers some key insights into improving the trading system.

The strategy will be thus:

First and foremost, this will be a short-only strategy, due to the long bias within the sample period, so the stress-test of the system will be to attempt to capture the non-dominant trend (and only when appropriate).

Here’s the strategy: we will continue to use the same 126 day FRAMA with the fast constant set at 4, and a slow constant at 300 (that is, it can oscillate anywhere between an EMA4 and EMA300). We will only enter into a short position when this indicator is descending, below the 126-day median of the price action, and when the price action is lower than this indicator (usually this means a cross, not in all cases though). We will exit when the price action rises back above the indicator.

Here’s the strategy in R code:

require(DSTrading)
require(IKTrading)
require(quantstrat)
require(PerformanceAnalytics)

initDate="1990-01-01"
from="2003-01-01"
to="2010-12-31"
options(width=70)

#to rerun the strategy, rerun everything below this line
source("demoData.R") #contains all of the data-related boilerplate.

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

strategy.st <- portfolio.st <- account.st <- "FRAMA_III"
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=4
SC=300
n=126
triggerLag=1

period=10
pctATR=.02

#indicators 

add.indicator(strategy.st, name="FRAMA",
              arguments=list(HLC=quote(HLC(mktdata)), n=n, 
                             SC=SC, FC=FC, triggerLag=triggerLag),
              label="primary")

add.indicator(strategy.st, name="runMedian",
              arguments=list(x=quote(Cl(mktdata)), n=n),
              label="confirmatory")

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

# #long signals
# 
# add.signal(strategy.st, name="sigComparison",
#            arguments=list(columns=c("FRAMA.primary", "X1.confirmatory"), 
#                           relationship="gte"),
#            label="FRAMAgteMedian")
# 
# add.signal(strategy.st, name="sigComparison",
#            arguments=list(columns=c("FRAMA.primary", "trigger.primary"), 
#                           relationship="gte"),
#            label="FRAMArising")
# 
# add.signal(strategy.st, name="sigComparison",
#            arguments=list(columns=c("Close", "FRAMA.primary"), 
#                           relationship="gte"),
#            label="ClGtFRAMA")
# 
# add.signal(strategy.st, name="sigAND",
#            arguments=list(columns=c("FRAMAgteMedian", 
#                                     "FRAMArising", "ClGtFRAMA"), 
#                           cross=TRUE),
#            label="longEntry")
# 
# add.signal(strategy.st, name="sigCrossover",
#            arguments=list(columns=c("Close", "FRAMA.primary"), 
#                           relationship="lt"),
#            label="longExit")
# 
# #long 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)

#short signals

add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("FRAMA.primary", "X1.confirmatory"), 
                          relationship="lt"),
           label="FRAMAltMedian")

add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("FRAMA.primary", "trigger.primary"), 
                          relationship="lt"),
           label="FRAMAfalling")

add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("Close", "FRAMA.primary"), 
                          relationship="lt"),
           label="ClLtFRAMA")

add.signal(strategy.st, name="sigAND",
           arguments=list(columns=c("FRAMAltMedian", 
                                    "FRAMAfalling", "ClLtFRAMA"), 
                          cross=TRUE),
           label="shortEntry")

add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "FRAMA.primary"), 
                          relationship="gt"),
           label="shortExit")

#short rules

add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="shortEntry", sigval=TRUE, ordertype="market", 
                        orderside="short", 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="shortExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="short", 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)

The results aren’t pretty, meaning that the filter is still incomplete. Here are the trade stats:

                        EFA      EPP      EWA      EWC      EWG
Num.Txns              90.00    68.00    85.00    66.00    90.00
Num.Trades            44.00    34.00    41.00    33.00    45.00
Net.Trading.PL     -2030.68   -25.54 -1485.82 -2283.03  -356.83
Avg.Trade.PL         -46.15    -0.75   -36.24   -69.18    -7.93
Med.Trade.PL        -103.99   -44.95   -77.27  -100.40   -69.91
Largest.Winner      1238.56  1656.56  1106.59  2195.51  3197.29
Largest.Loser       -661.04  -786.27  -548.06  -823.55  -783.65
Gross.Profits       4336.92  4455.45  3246.48  3566.35  5948.76
Gross.Losses       -6367.61 -4480.99 -4732.30 -5849.38 -6305.59
Std.Dev.Trade.PL     364.75   419.42   288.36   487.05   557.98
Percent.Positive      29.55    32.35    43.90    24.24    37.78
Percent.Negative      70.45    67.65    56.10    75.76    62.22
Profit.Factor          0.68     0.99     0.69     0.61     0.94
Avg.Win.Trade        333.61   405.04   180.36   445.79   349.93
Med.Win.Trade         57.09   238.60    66.34   124.31   101.88
Avg.Losing.Trade    -205.41  -194.83  -205.75  -233.98  -225.20
Med.Losing.Trade    -156.80  -122.45  -170.49  -166.84  -184.70
Avg.Daily.PL         -46.15    -0.75   -36.24   -69.18    -7.93
Med.Daily.PL        -103.99   -44.95   -77.27  -100.40   -69.91
Std.Dev.Daily.PL     364.75   419.42   288.36   487.05   557.98
Ann.Sharpe            -2.01    -0.03    -2.00    -2.25    -0.23
Max.Drawdown       -5089.55 -3095.58 -3609.64 -4915.76 -4222.60
Profit.To.Max.Draw    -0.40    -0.01    -0.41    -0.46    -0.08
Avg.WinLoss.Ratio      1.62     2.08     0.88     1.91     1.55
Med.WinLoss.Ratio      0.36     1.95     0.39     0.75     0.55
Max.Equity           146.74  1473.19   834.54    78.43  1467.12
Min.Equity         -4942.81 -2562.72 -2775.10 -4837.32 -3960.49
End.Equity         -2030.68   -25.54 -1485.82 -2283.03  -356.83

                        EWH      EWJ      EWS      EWT      EWU
Num.Txns              74.00   102.00    72.00    66.00    72.00
Num.Trades            36.00    51.00    35.00    33.00    36.00
Net.Trading.PL       596.16 -1493.76   982.93  1354.40   439.00
Avg.Trade.PL          16.56   -29.29    28.08    41.04    12.19
Med.Trade.PL         -54.45   -89.63   -52.85   -40.22   -56.50
Largest.Winner      3436.43  1076.25  1165.10  1980.68  1680.33
Largest.Loser       -544.78  -781.15  -429.64  -441.47  -468.28
Gross.Profits       4519.53  5681.81  4763.28  4317.26  5105.73
Gross.Losses       -3923.37 -7175.57 -3780.35 -2962.87 -4666.73
Std.Dev.Trade.PL     610.29   368.61   353.59   408.77   415.23
Percent.Positive      38.89    37.25    37.14    42.42    33.33
Percent.Negative      61.11    62.75    62.86    57.58    66.67
Profit.Factor          1.15     0.79     1.26     1.46     1.09
Avg.Win.Trade        322.82   299.04   366.41   308.38   425.48
Med.Win.Trade         79.88    99.19   267.75   115.47   399.34
Avg.Losing.Trade    -178.33  -224.24  -171.83  -155.94  -194.45
Med.Losing.Trade    -137.71  -175.69  -141.06   -95.88  -180.13
Avg.Daily.PL          16.56   -29.29    28.08    41.04    12.19
Med.Daily.PL         -54.45   -89.63   -52.85   -40.22   -56.50
Std.Dev.Daily.PL     610.29   368.61   353.59   408.77   415.23
Ann.Sharpe             0.43    -1.26     1.26     1.59     0.47
Max.Drawdown       -2390.89 -2994.16 -1689.63 -2113.93 -3192.52
Profit.To.Max.Draw     0.25    -0.50     0.58     0.64     0.14
Avg.WinLoss.Ratio      1.81     1.33     2.13     1.98     2.19
Med.WinLoss.Ratio      0.58     0.56     1.90     1.20     2.22
Max.Equity          2534.61  1380.90  1938.04  2065.70  1845.45
Min.Equity         -2131.17 -2540.03 -1265.23 -1501.15 -3192.52
End.Equity           596.16 -1493.76   982.93  1354.40   439.00

                        EWY      EWZ      EZU      IEF      IGE
Num.Txns              68.00    80.00    96.00    63.00    56.00
Num.Trades            34.00    40.00    48.00    32.00    28.00
Net.Trading.PL      1359.59 -2763.77  -178.24 -5286.17  -588.44
Avg.Trade.PL          39.99   -69.09    -3.71  -165.19   -21.02
Med.Trade.PL          19.52  -103.71   -73.53  -253.57   -87.68
Largest.Winner      1799.34  2495.03  1423.73   908.54  2146.42
Largest.Loser       -467.07  -496.73  -847.67  -758.78  -466.57
Gross.Profits       4729.27  2790.33  5960.68  2309.18  2757.36
Gross.Losses       -3369.68 -5554.10 -6138.92 -7595.36 -3345.80
Std.Dev.Trade.PL     414.55   440.16   402.00   349.52   456.89
Percent.Positive      55.88    15.00    33.33    25.00    25.00
Percent.Negative      44.12    85.00    66.67    75.00    75.00
Profit.Factor          1.40     0.50     0.97     0.30     0.82
Avg.Win.Trade        248.91   465.05   372.54   288.65   393.91
Med.Win.Trade         58.75    43.02    67.59   156.68    43.03
Avg.Losing.Trade    -224.65  -163.36  -191.84  -316.47  -159.32
Med.Losing.Trade    -217.23  -110.98  -139.29  -284.87  -115.98
Avg.Daily.PL          39.99   -69.09    -3.71  -192.64   -21.02
Med.Daily.PL          19.52  -103.71   -73.53  -260.53   -87.68
Std.Dev.Daily.PL     414.55   440.16   402.00   318.33   456.89
Ann.Sharpe             1.53    -2.49    -0.15    -9.61    -0.73
Max.Drawdown       -2237.74 -3903.71 -3510.08 -6682.82 -2836.80
Profit.To.Max.Draw     0.61    -0.71    -0.05    -0.79    -0.21
Avg.WinLoss.Ratio      1.11     2.85     1.94     0.91     2.47
Med.WinLoss.Ratio      0.27     0.39     0.49     0.55     0.37
Max.Equity          3532.28   836.88  1270.40   669.24   709.44
Min.Equity          -790.83 -3066.84 -3222.22 -6013.57 -2127.36
End.Equity          1359.59 -2763.77  -178.24 -5286.17  -588.44

                        IYR      IYZ      LQD      RWR      SHY
Num.Txns              96.00   108.00    63.00    98.00    51.00
Num.Trades            48.00    54.00    31.00    49.00    25.00
Net.Trading.PL     -3444.89 -2032.70  1532.27 -3740.29 -4049.16
Avg.Trade.PL         -71.77   -37.64    49.43   -76.33  -161.97
Med.Trade.PL        -129.99   -83.00   -84.44  -114.84  -141.20
Largest.Winner      1714.13  2673.04  2693.04  1455.78    86.02
Largest.Loser       -745.50  -463.08  -480.73  -578.29  -644.17
Gross.Profits       4652.33  4978.26  5114.62  3534.95   365.46
Gross.Losses       -8097.22 -7010.97 -3582.35 -7275.24 -4414.63
Std.Dev.Trade.PL     405.93   479.46   604.30   341.37   195.72
Percent.Positive      22.92    22.22    35.48    16.33    28.00
Percent.Negative      77.08    77.78    64.52    83.67    72.00
Profit.Factor          0.57     0.71     1.43     0.49     0.08
Avg.Win.Trade        422.94   414.86   464.97   441.87    52.21
Med.Win.Trade        110.81    29.28   139.76   188.82    44.33
Avg.Losing.Trade    -218.84  -166.93  -179.12  -177.44  -245.26
Med.Losing.Trade    -182.73  -134.02  -129.79  -138.78  -232.61
Avg.Daily.PL         -71.77   -37.64    45.47   -76.33  -162.83
Med.Daily.PL        -129.99   -83.00   -86.18  -114.84  -144.12
Std.Dev.Daily.PL     405.93   479.46   614.22   341.37   199.88
Ann.Sharpe            -2.81    -1.25     1.18    -3.55   -12.93
Max.Drawdown       -3857.85 -5575.45 -2876.51 -4695.60 -4049.16
Profit.To.Max.Draw    -0.89    -0.36     0.53    -0.80    -1.00
Avg.WinLoss.Ratio      1.93     2.49     2.60     2.49     0.21
Med.WinLoss.Ratio      0.61     0.22     1.08     1.36     0.19
Max.Equity           260.07   118.92  3375.06   302.96     0.00
Min.Equity         -3597.77 -5456.52 -2138.62 -4392.65 -4049.16
End.Equity         -3444.89 -2032.70  1532.27 -3740.29 -4049.16

                        TLT      XLB      XLE      XLF      XLI
Num.Txns              85.00   104.00    50.00   120.00    92.00
Num.Trades            43.00    51.00    25.00    60.00    46.00
Net.Trading.PL     -4037.97 -5591.16  -308.15 -3036.79 -2136.85
Avg.Trade.PL         -93.91  -109.63   -12.33   -50.61   -46.45
Med.Trade.PL        -133.03  -138.47   -96.47   -79.48  -108.98
Largest.Winner      1425.91  1831.45  1828.51  1058.03  1218.87
Largest.Loser       -543.28  -707.20  -430.13  -711.69  -632.77
Gross.Profits       3355.40  3130.31  2472.08  5282.27  4597.82
Gross.Losses       -7393.37 -8721.48 -2780.24 -8319.05 -6734.68
Std.Dev.Trade.PL     338.88   345.20   420.71   309.84   342.80
Percent.Positive      25.58    25.49    20.00    30.00    23.91
Percent.Negative      74.42    74.51    80.00    70.00    76.09
Profit.Factor          0.45     0.36     0.89     0.63     0.68
Avg.Win.Trade        305.04   240.79   494.42   293.46   417.98
Med.Win.Trade        168.50    83.98    33.87   135.46   294.74
Avg.Losing.Trade    -231.04  -229.51  -139.01  -198.07  -192.42
Med.Losing.Trade    -197.38  -207.82  -120.99  -171.67  -149.06
Avg.Daily.PL        -101.44  -109.63   -12.33   -50.61   -46.45
Med.Daily.PL        -140.48  -138.47   -96.47   -79.48  -108.98
Std.Dev.Daily.PL     339.33   345.20   420.71   309.84   342.80
Ann.Sharpe            -4.75    -5.04    -0.47    -2.59    -2.15
Max.Drawdown       -4926.34 -6711.79 -1938.05 -3451.10 -4068.90
Profit.To.Max.Draw    -0.82    -0.83    -0.16    -0.88    -0.53
Avg.WinLoss.Ratio      1.32     1.05     3.56     1.48     2.17
Med.WinLoss.Ratio      0.85     0.40     0.28     0.79     1.98
Max.Equity           459.78     0.00  1298.51   414.31     0.00
Min.Equity         -4466.56 -6711.79 -1329.01 -3036.79 -4068.90
End.Equity         -4037.97 -5591.16  -308.15 -3036.79 -2136.85

                        XLK      XLP      XLU      XLV      XLY
Num.Txns              86.00    92.00    82.00    94.00    82.00
Num.Trades            43.00    45.00    40.00    47.00    40.00
Net.Trading.PL     -1205.62 -4427.34 -3490.76 -4291.56  -230.80
Avg.Trade.PL         -28.04   -98.39   -87.27   -91.31    -5.77
Med.Trade.PL        -101.30  -153.01   -93.87   -98.69  -140.01
Largest.Winner      2403.16  1008.09  1805.03   842.35  2090.68
Largest.Loser       -806.29  -460.10  -462.68  -554.57  -698.45
Gross.Profits       4984.72  2839.42  2493.63  2959.31  6253.33
Gross.Losses       -6190.34 -7266.76 -5984.39 -7250.87 -6484.14
Std.Dev.Trade.PL     464.22   294.41   350.37   280.87   495.21
Percent.Positive      30.23    15.56    20.00    19.15    30.00
Percent.Negative      69.77    84.44    80.00    80.85    70.00
Profit.Factor          0.81     0.39     0.42     0.41     0.96
Avg.Win.Trade        383.44   405.63   311.70   328.81   521.11
Med.Win.Trade        191.31   116.12    61.87   266.16   307.13
Avg.Losing.Trade    -206.34  -191.23  -187.01  -190.81  -231.58
Med.Losing.Trade    -188.49  -191.04  -156.33  -161.51  -171.21
Avg.Daily.PL         -28.04   -98.39   -87.27   -91.31    -5.77
Med.Daily.PL        -101.30  -153.01   -93.87   -98.69  -140.01
Std.Dev.Daily.PL     464.22   294.41   350.37   280.87   495.21
Ann.Sharpe            -0.96    -5.30    -3.95    -5.16    -0.18
Max.Drawdown       -3448.99 -5384.93 -3540.13 -5186.60 -3964.07
Profit.To.Max.Draw    -0.35    -0.82    -0.99    -0.83    -0.06
Avg.WinLoss.Ratio      1.86     2.12     1.67     1.72     2.25
Med.WinLoss.Ratio      1.01     0.61     0.40     1.65     1.79
Max.Equity           646.11   651.59     0.00   895.04  2960.96
Min.Equity         -3003.57 -4733.34 -3540.13 -4291.56 -1003.11
End.Equity         -1205.62 -4427.34 -3490.76 -4291.56  -230.80

At this point, for the sake of brevity, I’ll leave off the equity curves and portfolio statistics (they’ll obviously be bad). However, let’s look at some images of what exactly is going on with individual trades.

Here is the full-backtest equity curve and corresponding indicators for XLP. The FRAMA is in purple, with the 126-day median in orange, along with the 10-day ATR (lagged by a day) on the bottom.

And here we can immediately see certain properties:

1) ATR order-sizing is not a be-all, end-all type of order. It was created for one purpose, which is to equalize risk across instruments (the original idea of which, I defer to Andreas Clenow’s article). However, that is only a base from which to begin, using other scaled order-sizing procedures which can attempt to quantify the confidence in any particular trade. As it currently stands, for short strategies in equities, the best opportunities happen in the depths of rapid falling price action, during which ATR will rise. One may consider augmenting the ATR order sizing function in order to accomplish this task (or merely apply leverage at the proper time, through modifying the pctATR parameter).

2) While the running median certainly has value as a filter to keep out obviously brainless trades (E.G. in the middle of an uptrend), once the FRAMA crosses the median, anything can happen, as the only logic is that the current FRAMA is just slightly lower than the previous day’s. This may mean that the running median itself is still rising, or that the FRAMA is effectively flat, and what is being traded on is purely noise. And furthermore, with ATR order sizing amplifying the consequences of that noise, this edge case can have disastrous consequences on an equity curve.

Here’s a zoom in on 2005, where we see a pretty severe drawdown (chart time series recolored for clarity).

As can be seen, even though the FRAMA seems to be slightly rising, a price crossing when the FRAMA is lower than the previous day by even an invisibly small amount (compare the purple–the FRAMA, to the red–the same quantity lagged a day) is enough to trigger a trade that will buy a sizable number of shares, even when the volatility is too small to justify such a trade. Essentially, most of the losses in this trading system arise as a result of trading during these flat periods during which the system attempts to force action.

This pattern repeats itself. Here is the equity curve for XLB.

Again, aside from maybe a bad trade in the end thanks to any trade being taken once all three conditions line up (decreasing FRAMA, FRAMA lower than median, price lower than FRAMA) too late due to a flat FRAMA/median relationship, most of the losers seem to be trades made during very flat and calm market action, even when the running median may be going in the opposite direction of the FRAMA, during which the ATR order-sizing function tried to force action. A second filter that serves to catch these edge-case situations (or maybe a filter that replaces the running median entirely) will be investigated in the future.

So, to recap this post:

The running median filter is an intrinsically lagging but robust indicator, chosen deliberately for these two properties. It is able to filter out trades that obviously go against the trend. However, due to some edge cases, there were still a great deal of losses that were incurred, which drown out the one good shorting opportunity over this sample period. This is an issue that needs addressing.

Thanks for reading.

FRAMA Part III: Avoiding Countertrend Trading — A First Attempt

This post will begin to experiment with long-term directional detection using relationships between two FRAMA indicators. By observing the relationship between two differently parametrized FRAMAs and the relationship by virtue of the ATR, it will be possible to avoid counter-trend trading on both sides. We will see this example later:

As with TVI, when the signals and rules were swapped for the short end, the equity curve was an unmitigated disaster. Unlike the flat-during-bull-runs-and-permanently-popping-up equity curve of ETFHQ, this equity curve was a disaster. For those that read the final TVI post, the equity curve looked almost identical to that–just a monotonous drawdown until the crisis, at which point the gains aren’t made up, and then the losses continue. In short, there’s no need to go into depth of those statistics.

As the link to ETFHQ suggests, we will use a longer-term FRAMA (the n=252, FC=40, SC=252 version). The market will be in an uptrend when the fast FRAMA (the FRAMA from the previous post) is above this slower FRAMA, and vice versa. Furthermore, in order to avoid some whipsaws, the fast FRAMA will have to be ascending (or descending, during a downtrend), and the entry signal will be when the price crosses over (under) the faster FRAMA, with the exit being the reverse.

In the interest of brevity, since the sample period was an uptrend, then a great deal of strategies will look good on the upside. The question is whether or not the strategy does well on the short side, as the litmus test in testing a confirming indicator is whether or not it can create a positive expectation in a strategy that is counter-trend to the dominant trend in the sample data. As this is a replication of an implied idea by ETFHQ (rather than my own particular idea), let’s look at the code for the strategy. In this instance, both the long and short end of this symmetric strategy are included, and in RStudio, commenting or uncommenting one half or the other is as simple as highlight+ctrl+shift+C.

Here’s the code.

require(DSTrading)
require(IKTrading)
require(quantstrat)
require(PerformanceAnalytics)

initDate="1990-01-01"
from="2003-01-01"
to="2010-12-31"
options(width=70)

#to rerun the strategy, rerun everything below this line
source("demoData.R") #contains all of the data-related boilerplate.

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

strategy.st <- portfolio.st <- account.st <- "FRAMA_II"
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

FCfast=4
SCfast=300
nFast=126
fastTriggerLag=1

FCslow=40
SCslow=252
nSlow=252
slowTriggerLag=1

period=10
pctATR=.02

#indicators
#Have to add in this function first, since the word 'slow' gets picked up
#by the HLC function as "low", which causes issues.
add.indicator(strategy.st, name="lagATR", 
              arguments=list(HLC=quote(HLC(mktdata)), n=period), 
              label="atrX")


add.indicator(strategy.st, name="FRAMA", 
              arguments=list(HLC=quote(HLC(mktdata)), n=nFast, FC=FCfast, 
                             SC=SCfast, triggerLag=fastTriggerLag),
              label="fast")


add.indicator(strategy.st, name="FRAMA", 
              arguments=list(HLC=quote(HLC(mktdata)), n=nSlow, FC=FCslow, 
                             SC=SCslow, triggerLag=slowTriggerLag),
              label="slow")

# #long signals
# 
# #condition 1: our fast FRAMA is above our slow FRAMA
# add.signal(strategy.st, name="sigComparison",
#            arguments=list(columns=c("FRAMA.fast", "FRAMA.slow"), relationship="gt"),
#            label="fastFRAMAaboveSlow")
# 
# #condition 2: our fast FRAMA is rising
# add.signal(strategy.st, name="sigComparison",
#            arguments=list(columns=c("FRAMA.fast", "trigger.fast"), relationship="gt"),
#            label="fastFRAMArising")
# 
# #setup: price crosses above the fast FRAMA
# add.signal(strategy.st, name="sigComparison",
#            arguments=list(columns=c("Close", "FRAMA.fast"), relationship="gte"),
#            label="CloseGteFastFRAMA")
# 
# #wrap our conditions and our setup into one entry signal
# add.signal(strategy.st, name="sigAND",
#            arguments=list(columns=c("fastFRAMAaboveSlow", "fastFRAMArising", "CloseGteFastFRAMA"), cross=TRUE),
#            label="longEntry")
# 
# #our old exit signal
# add.signal(strategy.st, name="sigCrossover",
#            arguments=list(columns=c("Close", "FRAMA.fast"), relationship="lt"),
#            label="longExit")

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



#short signals
add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("FRAMA.fast", "FRAMA.slow"), relationship="lt"),
           label="fastFRAMAbelowSlow")

#condition 2: our fast FRAMA is falling
add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("FRAMA.fast", "trigger.fast"), relationship="lt"),
           label="fastFRAMAfalling")


#setup: price crosses below the fast FRAMA
add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "FRAMA.fast"), relationship="lt"),
           label="CloseLtFastFRAMA")

#wrap our conditions and our setup into one entry signal
add.signal(strategy.st, name="sigAND",
           arguments=list(columns=c("fastFRAMAbelowSlow", "fastFRAMAfalling", "CloseLtFastFRAMA"), cross=TRUE),
           label="shortEntry")

#our old exit signal
add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "FRAMA.fast"), relationship="gt"),
           label="shortExit")

#short rules
add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="shortEntry", sigval=TRUE, ordertype="market", 
                        orderside="short", 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="shortExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="short", 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)

First, a quick little side-note: since many indicators look for the word “low”, my usage of the label “slow” would cause a bug if I added the lagATR indicator after that point. So try to avoid labels such as “high”, “low”, “open”, and “close” in your indicators, or at least declare all indicators that would look for the word “low” before declaring the indicator you label in part as “slow”, if you must go this route. Low is probably the easiest one for which to overlook this, since “slow” has so many applications to confirmatory indicators. (EG: SMA fast vs. SMA slow, etc.)

Again, to reiterate, the system will take a long position when price crosses over (under) a rising (falling) fast FRAMA that’s higher (lower) than the slow FRAMA, and exit that position when the price crosses back under (over) the fast FRAMA. The cross must happen when the other two conditions are intact, as opposed to a trade being entered when all three conditions come together, which may be in the middle of a trend.

As the majority of the sample data was in an uptrend (and the fact that the Andreas Clenow-inspired ATR order-sizing function pays enormous dividends in protecting for a limited time in a counter-trend), I decided to put the (slightly modified–with the one condition of rising in an uptrend or falling in a downtrend) system to the test by testing it on the non-dominant trend in the sample–that is, to see if the system can stay out at all points aside from the crisis.

Here are the (not-so-flattering) results:

Trade statistics:

                        EFA      EPP      EWA      EWC      EWG
Num.Txns              88.00    80.00    84.00    60.00    70.00
Num.Trades            43.00    40.00    40.00    30.00    35.00
Net.Trading.PL     -1393.99 -1585.91   981.89   875.47  -810.83
Avg.Trade.PL         -32.42   -39.65    24.55    29.18   -23.17
Med.Trade.PL        -106.64   -70.14     1.79   -74.83   -46.21
Largest.Winner      1238.56   775.14  1106.59  2195.51   449.23
Largest.Loser       -661.04  -466.95  -552.10  -565.80  -547.09
Gross.Profits       4211.42  3301.63  3677.16  3856.57  2719.33
Gross.Losses       -5605.42 -4887.54 -2695.27 -2981.10 -3530.16
Std.Dev.Trade.PL     344.67   284.29   263.92   462.93   226.75
Percent.Positive      32.56    30.00    50.00    26.67    48.57
Percent.Negative      67.44    70.00    50.00    73.33    51.43
Profit.Factor          0.75     0.68     1.36     1.29     0.77
Avg.Win.Trade        300.82   275.14   183.86   482.07   159.96
Med.Win.Trade         73.89   172.89    56.79   245.71   121.09
Avg.Losing.Trade    -193.29  -174.55  -134.76  -135.50  -196.12
Med.Losing.Trade    -156.80  -141.04   -88.56  -108.19  -187.47
Avg.Daily.PL         -33.19   -39.65    24.55    29.18   -23.17
Med.Daily.PL        -103.99   -70.14     1.79   -74.83   -46.21
Std.Dev.Daily.PL     337.33   284.29   263.92   462.93   226.75
Ann.Sharpe            -1.56    -2.21     1.48     1.00    -1.62
Max.Drawdown       -4696.67 -3090.61 -2193.20 -2032.07 -1990.83
Profit.To.Max.Draw    -0.30    -0.51     0.45     0.43    -0.41
Avg.WinLoss.Ratio      1.56     1.58     1.36     3.56     0.82
Med.WinLoss.Ratio      0.47     1.23     0.64     2.27     0.65
Max.Equity           560.75   355.13  2236.74  1567.71  1030.36
Min.Equity         -4135.91 -2881.48 -1702.76 -2019.64  -960.47
End.Equity         -1393.99 -1585.91   981.89   875.47  -810.83

                        EWH      EWJ      EWS      EWT      EWU
Num.Txns              48.00   102.00    74.00    56.00    82.00
Num.Trades            23.00    51.00    35.00    27.00    41.00
Net.Trading.PL      -420.23  -951.54  1424.73   292.07 -1756.36
Avg.Trade.PL         -18.27   -18.66    40.71    10.82   -42.84
Med.Trade.PL         -42.63   -54.18   -15.44     3.83   -46.42
Largest.Winner       309.93  1704.54  1165.10   437.42   664.06
Largest.Loser       -341.09  -460.39  -424.29  -367.22  -367.18
Gross.Profits        996.32  5137.50  4424.92  2072.64  2461.62
Gross.Losses       -1416.55 -6089.05 -3000.19 -1780.57 -4217.98
Std.Dev.Trade.PL     135.68   358.27   328.50   180.94   227.57
Percent.Positive      39.13    39.22    42.86    51.85    34.15
Percent.Negative      60.87    60.78    57.14    48.15    65.85
Profit.Factor          0.70     0.84     1.47     1.16     0.58
Avg.Win.Trade        110.70   256.88   294.99   148.05   175.83
Med.Win.Trade         91.25    80.40    84.30   100.73    66.50
Avg.Losing.Trade    -101.18  -196.42  -150.01  -136.97  -156.22
Med.Losing.Trade     -92.50  -173.06  -141.06  -144.50  -146.70
Avg.Daily.PL         -18.27   -18.66    40.71    10.82   -42.84
Med.Daily.PL         -42.63   -54.18   -15.44     3.83   -46.42
Std.Dev.Daily.PL     135.68   358.27   328.50   180.94   227.57
Ann.Sharpe            -2.14    -0.83     1.97     0.95    -2.99
Max.Drawdown       -1330.17 -3187.00 -1855.03 -1440.36 -3674.50
Profit.To.Max.Draw    -0.32    -0.30     0.77     0.20    -0.48
Avg.WinLoss.Ratio      1.09     1.31     1.97     1.08     1.13
Med.WinLoss.Ratio      0.99     0.46     0.60     0.70     0.45
Max.Equity           791.29  2235.46  2100.37   919.22   280.39
Min.Equity          -974.85 -2116.00 -1230.08  -885.33 -3394.12
End.Equity          -420.23  -951.54  1424.73   292.07 -1756.36

                        EWY      EWZ      EZU      IEF      IGE
Num.Txns              82.00    58.00    90.00    47.00    96.00
Num.Trades            41.00    29.00    45.00    24.00    48.00
Net.Trading.PL      2644.53   434.29 -1639.77 -1071.52 -1826.08
Avg.Trade.PL          64.50    14.98   -36.44   -44.65   -38.04
Med.Trade.PL         -36.18   -89.69   -70.13   -56.73   -79.30
Largest.Winner      2447.28  2495.03  1222.50   908.54  2146.42
Largest.Loser       -392.38  -382.20  -455.60  -717.52  -297.67
Gross.Profits       5519.58  3649.15  3231.22  2332.00  3162.60
Gross.Losses       -2875.04 -3214.86 -4870.99 -3403.52 -4988.68
Std.Dev.Trade.PL     441.24   521.49   282.42   339.58   349.80
Percent.Positive      48.78    24.14    37.78    41.67    20.83
Percent.Negative      51.22    75.86    62.22    58.33    79.17
Profit.Factor          1.92     1.14     0.66     0.69     0.63
Avg.Win.Trade        275.98   521.31   190.07   233.20   316.26
Med.Win.Trade        119.21    91.01    54.82    81.98    82.87
Avg.Losing.Trade    -136.91  -146.13  -173.96  -243.11  -131.28
Med.Losing.Trade     -97.92  -109.64  -155.51  -230.79  -113.58
Avg.Daily.PL          64.50    14.98   -36.44   -74.70   -38.04
Med.Daily.PL         -36.18   -89.69   -70.13   -71.76   -79.30
Std.Dev.Daily.PL     441.24   521.49   282.42   312.88   349.80
Ann.Sharpe             2.32     0.46    -2.05    -3.79    -1.73
Max.Drawdown       -1779.21 -3253.19 -3402.61 -3204.56 -3455.82
Profit.To.Max.Draw     1.49     0.13    -0.48    -0.33    -0.53
Avg.WinLoss.Ratio      2.02     3.57     1.09     0.96     2.41
Med.WinLoss.Ratio      1.22     0.83     0.35     0.36     0.73
Max.Equity          3319.81  2235.92   291.74  1170.92   255.57
Min.Equity         -1779.21 -1280.22 -3110.88 -2033.64 -3200.25
End.Equity          2644.53   434.29 -1639.77 -1071.52 -1826.08

                        IYR      IYZ      LQD      RWR      SHY
Num.Txns             106.00   108.00    43.00   114.00    33.00
Num.Trades            53.00    54.00    21.00    56.00    17.00
Net.Trading.PL     -3809.10 -3010.91  1863.94 -3690.62 -3715.43
Avg.Trade.PL         -71.87   -55.76    88.76   -65.90  -218.55
Med.Trade.PL        -107.50   -94.91   -14.30   -95.48  -165.13
Largest.Winner      1714.13  2673.04  1618.71  1455.78    23.93
Largest.Loser       -745.50  -463.08  -236.13  -476.51  -870.37
Gross.Profits       3465.76  3941.01  3050.21  2877.22    44.39
Gross.Losses       -7274.86 -6951.92 -1186.26 -6567.84 -3759.82
Std.Dev.Trade.PL     316.10   412.75   387.67   256.76   226.14
Percent.Positive      22.64    22.22    47.62    19.64    11.76
Percent.Negative      77.36    77.78    52.38    80.36    88.24
Profit.Factor          0.48     0.57     2.57     0.44     0.01
Avg.Win.Trade        288.81   328.42   305.02   261.57    22.19
Med.Win.Trade        125.96    52.77   178.87   144.25    22.19
Avg.Losing.Trade    -177.44  -165.52  -107.84  -145.95  -250.65
Med.Losing.Trade    -151.10  -150.12   -97.91  -134.83  -210.93
Avg.Daily.PL         -71.87   -55.76    80.29   -65.90  -223.39
Med.Daily.PL        -107.50   -94.91   -33.90   -95.48  -188.03
Std.Dev.Daily.PL     316.10   412.75   395.74   256.76   232.64
Ann.Sharpe            -3.61    -2.14     3.22    -4.07   -15.24
Max.Drawdown       -4518.57 -4628.68 -1075.56 -4511.52 -4429.88
Profit.To.Max.Draw    -0.84    -0.65     1.73    -0.82    -0.84
Avg.WinLoss.Ratio      1.63     1.98     2.83     1.79     0.09
Med.WinLoss.Ratio      0.83     0.35     1.83     1.07     0.11
Max.Equity           709.48   561.64  2649.32   820.90   714.45
Min.Equity         -3809.10 -4067.04  -318.88 -3690.62 -3715.43
End.Equity         -3809.10 -3010.91  1863.94 -3690.62 -3715.43

                        TLT      XLB      XLE      XLF      XLI
Num.Txns              73.00    72.00    82.00   104.00   106.00
Num.Trades            37.00    36.00    41.00    52.00    53.00
Net.Trading.PL     -2881.18    75.64  -738.57  -705.52 -1281.19
Avg.Trade.PL         -77.87     2.10   -18.01   -13.57   -24.17
Med.Trade.PL        -147.94   -45.01   -94.63   -71.06   -77.50
Largest.Winner      1425.91  1831.45  2087.67  1058.03  1218.87
Largest.Loser       -486.72  -423.07  -299.82  -711.69  -480.88
Gross.Profits       3086.09  3723.24  3173.04  5277.71  4948.54
Gross.Losses       -5967.27 -3647.61 -3911.61 -5983.23 -6229.73
Std.Dev.Trade.PL     338.67   369.57   371.02   313.49   307.17
Percent.Positive      24.32    36.11    14.63    34.62    30.19
Percent.Negative      75.68    63.89    85.37    65.38    69.81
Profit.Factor          0.52     1.02     0.81     0.88     0.79
Avg.Win.Trade        342.90   286.40   528.84   293.21   309.28
Med.Win.Trade        151.67   158.38   237.83   139.29   204.28
Avg.Losing.Trade    -213.12  -158.59  -111.76  -175.98  -168.37
Med.Losing.Trade    -195.66  -128.42   -96.70  -144.43  -147.64
Avg.Daily.PL         -86.21     2.10   -18.01   -13.57   -24.17
Med.Daily.PL        -149.61   -45.01   -94.63   -71.06   -77.50
Std.Dev.Daily.PL     339.60   369.57   371.02   313.49   307.17
Ann.Sharpe            -4.03     0.09    -0.77    -0.69    -1.25
Max.Drawdown       -3946.88 -2772.07 -2742.16 -2243.85 -2727.83
Profit.To.Max.Draw    -0.73     0.03    -0.27    -0.31    -0.47
Avg.WinLoss.Ratio      1.61     1.81     4.73     1.67     1.84
Med.WinLoss.Ratio      0.78     1.23     2.46     0.96     1.38
Max.Equity           139.90  1411.59   335.20  1066.45   848.83
Min.Equity         -3806.97 -1978.82 -2742.16 -1573.88 -2028.21
End.Equity         -2881.18    75.64  -738.57  -705.52 -1281.19

                        XLK      XLP      XLU      XLV      XLY
Num.Txns              94.00    84.00    86.00    62.00    66.00
Num.Trades            47.00    41.00    42.00    31.00    33.00
Net.Trading.PL     -1651.16 -3264.51 -4665.83 -2093.02   507.06
Avg.Trade.PL         -35.13   -79.62  -111.09   -67.52    15.37
Med.Trade.PL         -99.55  -129.52   -90.84   -80.14  -100.97
Largest.Winner      2403.16  1008.09   174.64  1447.68  2090.68
Largest.Loser       -526.85  -460.10  -419.92  -533.05  -352.17
Gross.Profits       4484.79  2660.58   600.44  2419.62  4101.12
Gross.Losses       -6135.95 -5925.09 -5266.27 -4512.64 -3594.06
Std.Dev.Trade.PL     419.01   287.43   134.66   364.01   430.90
Percent.Positive      31.91    19.51    21.43    16.13    30.30
Percent.Negative      68.09    80.49    78.57    83.87    69.70
Profit.Factor          0.73     0.45     0.11     0.54     1.14
Avg.Win.Trade        298.99   332.57    66.72   483.92   410.11
Med.Win.Trade        106.63    84.26    46.23    86.14   202.57
Avg.Losing.Trade    -191.75  -179.55  -159.58  -173.56  -156.26
Med.Losing.Trade    -168.10  -161.95  -152.33  -128.20  -151.78
Avg.Daily.PL         -35.13   -79.62  -111.09   -67.52    15.37
Med.Daily.PL         -99.55  -129.52   -90.84   -80.14  -100.97
Std.Dev.Daily.PL     419.01   287.43   134.66   364.01   430.90
Ann.Sharpe            -1.33    -4.40   -13.10    -2.94     0.57
Max.Drawdown       -4435.53 -5189.24 -4665.83 -3779.96 -2264.20
Profit.To.Max.Draw    -0.37    -0.63    -1.00    -0.55     0.22
Avg.WinLoss.Ratio      1.56     1.85     0.42     2.79     2.62
Med.WinLoss.Ratio      0.63     0.52     0.30     0.67     1.33
Max.Equity           861.83  1156.76     0.00  1686.94  2771.26
Min.Equity         -3573.71 -4032.47 -4665.83 -2093.02  -613.72
End.Equity         -1651.16 -3264.51 -4665.83 -2093.02   507.06
> (aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
[1] 0.7443694
> (aggCorrect <- mean(tStats$Percent.Positive))
[1] 31.708
> (numTrades <- sum(tStats$Num.Trades))
[1] 1166
> (meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio))
[1] 1.824333

In other words, we can already see that the proposed confirmatory indicator is a dud. To display the raw instrument daily stats at this point would also be uninteresting, so we’ll move past that.

Duration statistics:

durStats <- durationStatistics(Portfolio=portfolio.st, Symbols=sort(symbols))
print(t(durStats))
      EFA EPP EWA EWC EWG EWH EWJ EWS EWT EWU EWY EWZ EZU IEF IGE IYR
Min     1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
Q1      2   1   1   4   1   2   1   3   1   1   1   1   2   2   1   1
Med     4   4   4   8   5   4   4   5   5   4   3   5   4   5   5   3
Mean   10   8   9  13  10   7   9  11  13   8  10  11   8  10   9   7
Q3      7   8   7  16  10   6   8  10  10  10   7   8   7   8   8   7
Max    83  72  79  74  77  55  98  76  79  72 105  71  83  66  90  59
WMin    1   1   1   7   1   1   1   1   1   1   1   1   1   1   6   1
WQ1     3   2   1  14   1   3   1   4   2   1   1   4   2   7   8   6
WMed   10   6   6  23   5   6   2  10  10   4   3   8   4   8  18   9
WMean  21  14  13  26  13  11  13  18  22  13  16  24  11  20  26  15
WQ3    26  14   9  29  13   7   9  26  21  15  14  41   7  39  30  17
WMax   83  72  79  74  77  55  98  76  79  72 105  71  83  66  90  59
LMin    1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
LQ1     2   1   1   4   2   2   2   3   1   2   1   1   2   1   1   1
LMed    4   3   4   6   4   3   4   4   4   4   1   4   5   2   3   3
LMean   4   5   6   9   6   4   6   5   4   6   3   7   6   4   4   4
LQ3     6   6   6  12   8   6   8   5   5   7   3   7   7   6   6   5
LMax   21  23  22  33  21  14  25  32   8  29  12  68  26  10  14  36

      IYZ LQD RWR SHY TLT XLB XLE XLF XLI XLK XLP XLU XLV XLY
Min     1   1   1   1   1   1   1   1   1   1   1   1   1   1
Q1      1   1   1   4   2   2   1   2   2   2   2   2   2   2
Med     5   3   3   6   4   6   4   5   4   3   4   4   5   5
Mean    9   9   6   9   8  12  10  13   9   9   8   6   8  14
Q3      7   5   6  12   7  14   7  18  11  10   7   6   8  11
Max   105  91  67  29  55  82  97  91  55  67  67  34  35 100
WMin    1   1   1   3   1   2   7   1   1   1   1   1   3   2
WQ1     3   3   8   4   2   7   9   3   3   2   4   1   7   7
WMed    8   5  10   6   6  17  22  20  10  15  15   4  17  26
WMean  21  17  18   6  18  23  40  26  17  20  22   9  19  35
WQ3    28  18  24   7  32  22  69  34  26  26  32  13  35  56
WMax  105  91  67   8  55  82  97  91  55  67  67  34  35 100
LMin    1   1   1   1   1   1   1   1   1   1   1   1   1   1
LQ1     1   1   1   4   2   2   1   2   2   1   1   2   1   1
LMed    4   1   2   6   4   3   3   4   4   3   3   4   4   4
LMean   5   2   3   9   5   6   5   6   6   4   4   4   6   4
LQ3     7   3   5  12   7   8   6   8  10   5   6   5   7   6
LMax   29   7  15  29  19  29  40  21  19  21  20  16  30  13

Basically, we can see that there are some really long trades that win, but between this table and the previous trade stats output, that is more than swamped by the legions of losers. Here is the market exposure:


#market exposure
tmp <- list()
length(tmp) <- length(symbols)
for(i in 1:nrow(dStats)) {
  totalDays <- nrow(get(rownames(dStats)[i]))
  mktExposure <- dStats$Total.Days[i]/totalDays
  tmp[[i]] <- c(rownames(dStats)[i], round(mktExposure, 3))
}
mktExposure <- data.frame(do.call(rbind, tmp))
colnames(mktExposure) <- c("Symbol","MktExposure")
print(mktExposure)

   Symbol MktExposure
1     EFA       0.168
2     EPP       0.125
3     EWA       0.149
4     EWC        0.15
5     EWG       0.133
6     EWH       0.063
7     EWJ       0.176
8     EWS       0.145
9     EWT       0.133
10    EWU       0.135
11    EWY       0.152
12    EWZ       0.126
13    EZU       0.147
14    IEF       0.102
15    IGE       0.168
16    IYR       0.152
17    IYZ       0.186
18    LQD       0.083
19    RWR       0.149
20    SHY       0.052
21    TLT       0.126
22    XLB       0.168
23    XLE        0.16
24    XLF       0.249
25    XLI       0.196
26    XLK       0.168
27    XLP       0.129
28    XLU       0.101
29    XLV         0.1
30    XLY       0.168

In other words, even though the market exposure is rather small, the system still manages to hemorrhage a great deal during those small exposures, which does not sing many praises for the proposed system.

Here is the code for a cash sharpe and the equity curve comparisons:

#portfolio cash PL
portString <- paste0("portfolio.", portfolio.st)
portPL <- .blotter[[portString]]$summary$Net.Trading.PL

#Cash Sharpe
(SharpeRatio.annualized(portPL, geometric=FALSE))

#Portfolio comparisons to SPY
instRets <- PortfReturns(account.st)

#Correlations
instCors <- cor(instRets)
diag(instRets) <- NA
corMeans <- rowMeans(instCors, na.rm=TRUE)
names(corMeans) <- gsub(".DailyEndEq", "", names(corMeans))
print(round(corMeans,3))
mean(corMeans)

portfRets <- xts(rowMeans(instRets)*ncol(instRets), order.by=index(instRets))
portfRets <- portfRets[!is.na(portfRets)]
cumPortfRets <- cumprod(1+portfRets)
firstNonZeroDay <- as.character(index(portfRets)[min(which(portfRets!=0))])
getSymbols("SPY", from=firstNonZeroDay, to=to)
SPYrets <- diff(log(Cl(SPY)))[-1]
cumSPYrets <- cumprod(1+SPYrets)
comparison <- cbind(cumPortfRets, cumSPYrets)
colnames(comparison)  <- c("strategy", "SPY")
chart.TimeSeries(comparison, legend.loc = "topleft",
                 colors=c("green","red"))

Which gives us the following results:

(SharpeRatio.annualized(portPL, geometric=FALSE))
                                Net.Trading.PL
Annualized Sharpe Ratio (Rf=0%)     -0.2687879

In short, the idea of using a “slower” FRAMA does not seem to hold much water. And here are the portfolio statistics to confirm it:

> SharpeRatio.annualized(portfRets)
                                      [,1]
Annualized Sharpe Ratio (Rf=0%) -0.2950648
> Return.annualized(portfRets)
                         [,1]
Annualized Return -0.01560975
> maxDrawdown(portfRets)
[1] 0.1551006

But why?

For that, we’ll look at a picture of the equity curve of an individual instrument, complete with overlaid indicators.

chart.Posn(portfolio.st, "XLF")
tmp <- FRAMA(HLC(XLF), n=nFast, FC=FCfast, SC=SCfast, triggerLag=fastTriggerLag)
add_TA(tmp$FRAMA, on=1, col="purple", lwd=3)
add_TA(tmp$trigger, on=1, col="blue", lwd=0.5)
tmp2 <- FRAMA(HLC(XLF), n=nSlow, FC=FCslow, SC=SCslow, triggerLag=slowTriggerLag)
add_TA(tmp2$FRAMA, on=1, col="orange", lwd=3)
tmp2 <- lagATR(HLC=HLC(XLF), n=period)
add_TA(tmp2$atr, col="purple", lwd=2)

Which produces the following plot:

The primary indicator is in purple, while the confirmatory indicator is in orange. And now we see the reason why: because although the FRAMA (n=252, FC=40, SC=252) is a seemingly fine parametrization in and of itself, as a “big-picture/long-term-trend/greater smoothing” indicator, it does not seem like the best choice, at least in the conventional sense as using something such as the SMA200, ROC200 > 0, or RS Rank (see this post from SystemTraderSuccess).

Why? Because from my intuition, adaptive moving average indicators all aim to do the same thing–they aim to be a more accurate way of aggregating lots of data in order to tell you what is happening to as close as current time as they can get. That is, if you look at the presentation by Dr. John Ehlers (see this link), you’ll notice how similar all of the indicators are. All of them effectively aim to maximize near-term smoothness and eliminate as much lag as possible. That is, if you’re looking to make short-term momentum trades that last five days, if your indicator has a five-day lag (EG a 10-day running median), well, your indicator isn’t of much use in that case, because by the time you receive the signal, the opportunity is over!

However, while eliminating lag is usually desirable, in one case, it isn’t. To go off on a tangent, the Japanese trading system called Ichimoku Kinko Hyo (which may be investigated in the future), created by Goichi Hosoda, deliberately makes use of lagging current price action to create a cloud. That is, if you want a confirmatory indicator, you want something robust (especially to the heightened volatility during corrections, bear markets, downtrends, etc.), and something that *has* a bit of lag to it, to confirm the relationship between the more up-to-date indicator (E.G. an adaptive moving average, a short-term oscillator such as RSI2, etc.), and the overarching, long-term trend.

The failure to do so in this case results in problematic counter-trend trades before the financial crisis. While the trading during the financial crisis had a very choppy equity curve during the height of the crisis itself, this was for an individual instrument, and note, that by the end of the crisis, the strategy had indeed made money. The greater problem was that due to the similarities in kind of the confirmatory indicator with the one used for entries and exits, then occasionally, the confirmatory indicator would overtake the indicator it was supposed to confirm, even in a sideways or upwards market, which resulted in several disastrous trades.

And while the indicator used for entries and exits should be as up-to-date as possible so as to get in and out in as timely a fashion as possible, a confirmatory indicator, first and foremost, should not reverse the entire system’s understanding of the market mode on a whim, and secondly, should try to be more backward looking, so as to better do its job of confirmation. Thus, in my opinion, the recommendation of this “slower” FRAMA to be used as a confirmatory indicator by ETFHQ was rather ill-advised. Thus, the investigation will continue into finding a more suitable confirmatory indicator.

Thanks for reading.

FRAMA Part II: Replicating A Simple Strategy

This post will begin the investigation into FRAMA strategies, with the aim of ultimately finding a FRAMA trading strategy with less market exposure, fewer whipsaw trades, and fewer counter-trend trades. This post will also introduce new analytics regarding trade duration.

To begin the investigation into developing strategies based on the previously-introduced FRAMA, I’m going to replicate the simple strategy from ETFHQ — use a 126 day FRAMA with a fast constant of 4 (that is, an EMA that goes as fast as a 4-day EMA), and all the way up to a slow constant of 300. For my ATR order-sizing, which, once again, was inspired by Andreas Clenow in the post on leverage being pointless, I’m going to use 2 percent of notional capital, with a 10 day ATR for my order sizing (ATR 20 and 30 display slightly weaker results, but nevertheless, are very close in performance).

Once again, let’s start by looking at the strategy, using our same 30 instruments as with our TVI demos (I thought about testing on mutual funds, but due to the obnoxious fees that mutual funds charge for trying to trade with them, I feel that I’d have to employ too much magical thinking to neglect their obscene trading transaction costs):

require(DSTrading)
require(IKTrading)
require(quantstrat)

initDate="1990-01-01"
from="2003-01-01"
to="2010-12-31"
options(width=70)

#to rerun the strategy, rerun everything below this line
source("demoData.R") #contains all of the data-related boilerplate.

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

strategy.st <- portfolio.st <- account.st <- "FRAMA_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=4
SC=300
n=126
triggerLag=1
pctATR=.02
period=10

#indicators

add.indicator(strategy.st, name="FRAMA", 
              arguments=list(HLC=quote(HLC(mktdata)),n=n, 
                             FC=FC, SC=SC, triggerLag=triggerLag),
              label="frama")

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


#signals

add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "FRAMA.frama"), relationship="gte"),
           label="longEntry")

add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "FRAMA.frama"), relationship="lt"),
           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)

It’s a fairly simple strategy–buy the next day’s open when the price crosses above the indicator, and vice versa. In other words, it’s about as simple a strategy as you can get as its sole purpose was to demonstrate the effectiveness of the indicator. And while someone interested can peruse through ETFHQ to find all of the indicator relative tests, the general gist is that adaptive moving averages work very well, and the FRAMA (the fractal adaptive moving average) works slightly better than the rest. Ultimately though, if one believes ETFHQ’s analysis (and I do), then even one good trend-following indicator will be sufficient.

Here are the trade statistics:

                         EFA       EPP       EWA      EWC       EWG
Num.Txns              289.00    245.00    281.00   219.00    248.00
Num.Trades            145.00    123.00    140.00   109.00    121.00
Net.Trading.PL       9233.11  17020.32  14917.84 14298.85  13603.78
Avg.Trade.PL           63.68    138.38    106.56   131.18    112.43
Med.Trade.PL          -46.29    -26.79    -24.81   -21.06    -28.14
Largest.Winner       2454.58   4499.10   3301.82  4031.95   3582.43
Largest.Loser        -523.15   -847.71   -607.41  -613.76   -531.39
Gross.Profits       23874.49  29202.05  28060.29 23541.10  24820.41
Gross.Losses       -14641.38 -12181.73 -13142.45 -9242.26 -11216.63
Std.Dev.Trade.PL      420.81    624.11    518.71   565.75    565.31
Percent.Positive       42.07     46.34     46.43    46.79     46.28
Percent.Negative       57.93     53.66     53.57    53.21     53.72
Profit.Factor           1.63      2.40      2.14     2.55      2.21
Avg.Win.Trade         391.39    512.32    431.70   461.59    443.22
Med.Win.Trade         199.18    238.18    220.98   236.81    179.68
Avg.Losing.Trade     -174.30   -184.57   -175.23  -159.35   -172.56
Med.Losing.Trade     -152.31   -121.13   -159.74  -133.38   -128.70
Avg.Daily.PL           63.07    137.42    105.72   128.59    112.43
Med.Daily.PL          -49.26    -29.12    -27.68   -22.15    -28.14
Std.Dev.Daily.PL      422.22    626.59    520.49   567.74    565.31
Ann.Sharpe              2.37      3.48      3.22     3.60      3.16
Max.Drawdown        -2338.49  -1945.85  -3026.93 -2001.41  -2047.83
Profit.To.Max.Draw      3.95      8.75      4.93     7.14      6.64
Avg.WinLoss.Ratio       2.25      2.78      2.46     2.90      2.57
Med.WinLoss.Ratio       1.31      1.97      1.38     1.78      1.40
Max.Equity           9902.70  17058.92  14996.93 14298.85  14604.43
Min.Equity           -136.30   -189.12   -159.59  -233.07   -315.54
End.Equity           9233.11  17020.32  14917.84 14298.85  13603.78

                         EWH       EWJ       EWS      EWT      EWU
Num.Txns              271.00    243.00    275.00   199.00   227.00
Num.Trades            135.00    119.00    133.00    96.00   111.00
Net.Trading.PL      11548.79   5965.14  12676.51 12221.36 12044.63
Avg.Trade.PL           85.55     50.13     95.31   127.31   108.51
Med.Trade.PL          -27.05    -26.14    -12.78    14.04    -7.08
Largest.Winner       2668.92   1872.67   2773.40  2749.00  2521.64
Largest.Loser        -572.88   -521.96   -593.95  -537.06  -603.96
Gross.Profits       23173.00  17577.09  23801.66 19413.97 21927.72
Gross.Losses       -11624.21 -11611.96 -11125.16 -7192.61 -9883.09
Std.Dev.Trade.PL      501.77    395.06    470.11   506.25   512.20
Percent.Positive       44.44     43.70     46.62    53.12    49.55
Percent.Negative       55.56     56.30     53.38    46.88    50.45
Profit.Factor           1.99      1.51      2.14     2.70     2.22
Avg.Win.Trade         386.22    338.02    383.90   380.67   398.69
Med.Win.Trade         125.17    140.16    212.49   172.84   195.04
Avg.Losing.Trade     -154.99   -173.31   -156.69  -159.84  -176.48
Med.Losing.Trade     -111.58   -146.64   -115.28  -138.75  -158.23
Avg.Daily.PL           86.78     38.81     94.45   108.42   108.20
Med.Daily.PL          -25.57    -32.55    -17.73    12.24    -7.80
Std.Dev.Daily.PL      503.45    376.87    471.79   473.73   514.53
Ann.Sharpe              2.74      1.63      3.18     3.63     3.34
Max.Drawdown        -2298.32  -3445.56  -2017.06 -2764.34 -2071.61
Profit.To.Max.Draw      5.02      1.73      6.28     4.42     5.81
Avg.WinLoss.Ratio       2.49      1.95      2.45     2.38     2.26
Med.WinLoss.Ratio       1.12      0.96      1.84     1.25     1.23
Max.Equity          12708.66   6336.41  13177.76 12221.36 12640.57
Min.Equity           -272.68   -307.36   -331.04     0.00   -57.47
End.Equity          11548.79   5965.14  12676.51 12221.36 12044.63

                         EWY       EWZ       EZU      IEF       IGE
Num.Txns              261.00    265.00    270.00   216.00    249.00
Num.Trades            130.00    133.00    134.00   107.00    125.00
Net.Trading.PL      10513.27  14496.80  11233.90 13370.63  12428.65
Avg.Trade.PL           80.87    109.00     83.84   124.96     99.43
Med.Trade.PL           21.32    -42.35    -34.59   -41.49    -93.07
Largest.Winner       1550.67   2633.38   3163.76  2799.11   3710.24
Largest.Loser        -614.98   -682.75   -553.54  -542.02   -576.53
Gross.Profits       23615.06  28466.25  25227.96 22877.10  26829.04
Gross.Losses       -13101.78 -13969.45 -13994.06 -9506.47 -14400.39
Std.Dev.Trade.PL      417.96    548.34    538.74   546.44    660.65
Percent.Positive       50.77     43.61     42.54    42.06     32.80
Percent.Negative       49.23     56.39     57.46    57.94     67.20
Profit.Factor           1.80      2.04      1.80     2.41      1.86
Avg.Win.Trade         357.80    490.80    442.60   508.38    654.37
Med.Win.Trade         207.28    210.90    194.52   221.16    367.42
Avg.Losing.Trade     -204.72   -186.26   -181.74  -153.33   -171.43
Med.Losing.Trade     -187.43   -163.60   -144.77  -129.05   -154.06
Avg.Daily.PL           77.86    105.49     83.84   124.96     77.08
Med.Daily.PL           19.08    -44.57    -34.59   -41.49    -93.37
Std.Dev.Daily.PL      418.17    548.93    538.74   546.44    614.05
Ann.Sharpe              2.96      3.05      2.47     3.63      1.99
Max.Drawdown        -2462.61  -2188.41  -2310.88 -2650.59  -3045.12
Profit.To.Max.Draw      4.27      6.62      4.86     5.04      4.08
Avg.WinLoss.Ratio       1.75      2.64      2.44     3.32      3.82
Med.WinLoss.Ratio       1.11      1.29      1.34     1.71      2.38
Max.Equity          10513.27  14760.11  12629.07 14339.37  12428.65
Min.Equity           -339.01   -214.04   -203.63  -278.08   -268.60
End.Equity          10513.27  14496.80  11233.90 13370.63  12428.65

                         IYR       IYZ      LQD       RWR       SHY
Num.Txns              253.00    257.00   222.00    267.00    300.00
Num.Trades            125.00    127.00   108.00    133.00    143.00
Net.Trading.PL       9702.92   8599.09 13277.24  12090.68  15021.96
Avg.Trade.PL           77.62     67.71   122.94     90.91    105.05
Med.Trade.PL          -60.97    -47.00   -17.31    -63.38    -41.73
Largest.Winner       2602.17   2511.55  2526.23   2810.42   1987.06
Largest.Loser        -864.63   -619.97  -339.49   -758.94   -971.00
Gross.Profits       22264.43  20356.47 20777.57  25087.09  30006.10
Gross.Losses       -12561.51 -11757.38 -7500.32 -12996.41 -14984.14
Std.Dev.Trade.PL      488.33    426.35   486.90    528.23    499.11
Percent.Positive       36.80     43.31    46.30     39.10     44.06
Percent.Negative       63.20     56.69    53.70     60.90     55.94
Profit.Factor           1.77      1.73     2.77      1.93      2.00
Avg.Win.Trade         484.01    370.12   415.55    482.44    476.29
Med.Win.Trade         313.81    182.80   139.12    228.24    229.73
Avg.Losing.Trade     -159.01   -163.30  -129.32   -160.45   -187.30
Med.Losing.Trade     -126.56   -112.84  -103.66   -147.59   -145.26
Avg.Daily.PL           75.21     66.83   122.94     88.59    105.05
Med.Daily.PL          -61.69    -50.50   -17.31    -65.60    -41.73
Std.Dev.Daily.PL      489.56    427.93   486.90    529.57    499.11
Ann.Sharpe              2.44      2.48     4.01      2.66      3.34
Max.Drawdown        -5488.96  -2253.39 -1261.10  -5062.88  -2852.91
Profit.To.Max.Draw      1.77      3.82    10.53      2.39      5.27
Avg.WinLoss.Ratio       3.04      2.27     3.21      3.01      2.54
Med.WinLoss.Ratio       2.48      1.62     1.34      1.55      1.58
Max.Equity          13313.44   9225.39 14084.98  15192.88  16570.24
Min.Equity           -152.91   -522.27  -347.48   -394.76  -1036.81
End.Equity           9702.92   8599.09 13277.24  12090.68  15021.96

                         TLT       XLB       XLE       XLF       XLI
Num.Txns              238.00    247.00    229.00    245.00    261.00
Num.Trades            118.00    122.00    115.00    121.00    130.00
Net.Trading.PL       7117.62   6227.48  10335.40     -8.45   6351.34
Avg.Trade.PL           60.32     51.04     89.87     -0.07     48.86
Med.Trade.PL          -71.33    -62.80    -65.87    -56.76    -45.18
Largest.Winner       2548.77   2083.24   2407.81   1895.31   1571.81
Largest.Loser        -509.75   -544.43   -484.90   -626.99   -465.20
Gross.Profits       20099.36  19500.08  21827.84  12869.00  17364.07
Gross.Losses       -12981.73 -13272.60 -11492.44 -12877.45 -11012.73
Std.Dev.Trade.PL      506.64    421.98    566.48    345.43    379.26
Percent.Positive       31.36     36.89     34.78     35.54     41.54
Percent.Negative       68.64     63.11     65.22     64.46     58.46
Profit.Factor           1.55      1.47      1.90      1.00      1.58
Avg.Win.Trade         543.23    433.34    545.70    299.28    321.56
Med.Win.Trade         230.04    248.59    241.31    153.68    129.61
Avg.Losing.Trade     -160.27   -172.37   -153.23   -165.10   -144.90
Med.Losing.Trade     -130.27   -159.19   -148.44   -134.74   -131.29
Avg.Daily.PL           60.32     47.96     68.35     -8.00     34.56
Med.Daily.PL          -71.33    -65.49    -66.25    -58.07    -46.28
Std.Dev.Daily.PL      506.64    422.35    519.59    335.64    343.76
Ann.Sharpe              1.89      1.80      2.09     -0.38      1.60
Max.Drawdown        -4938.54  -3692.51  -2780.65  -5704.14  -3013.95
Profit.To.Max.Draw      1.44      1.69      3.72      0.00      2.11
Avg.WinLoss.Ratio       3.39      2.51      3.56      1.81      2.22
Med.WinLoss.Ratio       1.77      1.56      1.63      1.14      0.99
Max.Equity           9318.18   6780.72  10335.40   4191.68   6357.94
Min.Equity           -693.51   -213.25   -557.67  -1512.46   -795.23
End.Equity           7117.62   6227.48  10335.40     -8.45   6351.34

                         XLK       XLP       XLU       XLV       XLY
Num.Txns              254.00    280.00    241.00    218.00    220.00
Num.Trades            127.00    137.00    121.00    107.00    109.00
Net.Trading.PL       2940.29   2543.61   7904.42   3189.25   6727.03
Avg.Trade.PL           23.15     18.57     65.33     29.81     61.72
Med.Trade.PL          -72.35    -85.92    -38.84    -88.65    -71.80
Largest.Winner       2050.23   1782.84   1240.83   1927.28   2667.81
Largest.Loser        -749.16   -679.39   -598.65   -662.12   -559.61
Gross.Profits       16321.02  17361.88  18269.97  14178.93  18237.47
Gross.Losses       -13380.73 -14818.27 -10365.55 -10989.67 -11510.44
Std.Dev.Trade.PL      389.23    363.63    363.21    418.10    525.74
Percent.Positive       35.43     32.12     43.80     28.97     39.45
Percent.Negative       64.57     67.88     56.20     71.03     60.55
Profit.Factor           1.22      1.17      1.76      1.29      1.58
Avg.Win.Trade         362.69    394.59    344.72    457.38    424.13
Med.Win.Trade         158.36    236.63    180.99    238.62    159.38
Avg.Losing.Trade     -163.18   -159.34   -152.43   -144.60   -174.40
Med.Losing.Trade     -135.65   -131.38   -124.59   -122.90   -146.41
Avg.Daily.PL           23.15     18.57     63.70     29.81     61.72
Med.Daily.PL          -72.35    -85.92    -41.32    -88.65    -71.80
Std.Dev.Daily.PL      389.23    363.63    364.29    418.10    525.74
Ann.Sharpe              0.94      0.81      2.78      1.13      1.86
Max.Drawdown        -3074.10  -4531.59  -2545.84  -4623.78  -4041.34
Profit.To.Max.Draw      0.96      0.56      3.10      0.69      1.66
Avg.WinLoss.Ratio       2.22      2.48      2.26      3.16      2.43
Med.WinLoss.Ratio       1.17      1.80      1.45      1.94      1.09
Max.Equity           2981.10   2993.23   8632.88   3902.68   6838.89
Min.Equity          -1716.68  -1538.36   -165.00  -1213.50   -310.63
End.Equity           2940.29   2543.61   7904.42   3189.25   6727.03

And the aggregate trade statistics:

> (aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
[1] 1.828178
> (aggCorrect <- mean(tStats$Percent.Positive))
[1] 41.55233
> (numTrades <- sum(tStats$Num.Trades))
[1] 3704
> (meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio))
[1] 2.619

Far from spectacular. Less than 50% hit rate, the profit factor definitely indicates that there is massive room for improvement as well.

Here are the daily statistics:

                         EFA       EPP       EWA       EWC       EWG
Total.Net.Profit     9233.11  17020.32  14917.84  14298.85  13603.78
Total.Days           1280.00   1330.00   1330.00   1296.00   1264.00
Winning.Days          690.00    729.00    725.00    719.00    702.00
Losing.Days           590.00    601.00    605.00    577.00    562.00
Avg.Day.PL              7.21     12.80     11.22     11.03     10.76
Med.Day.PL             12.85     16.84     17.06     18.59     21.33
Largest.Winner        725.53    521.47    428.25    648.73    510.14
Largest.Loser        -973.81  -1162.04   -722.38   -505.11   -841.33
Gross.Profits       72427.12  85143.30  80490.48  70817.88  75036.35
Gross.Losses       -63194.01 -68122.98 -65572.64 -56519.03 -61432.57
Std.Dev.Daily.PL      139.51    153.35    140.28    125.46    141.11
Percent.Positive       53.91     54.81     54.51     55.48     55.54
Percent.Negative       46.09     45.19     45.49     44.52     44.46
Profit.Factor           1.15      1.25      1.23      1.25      1.22
Avg.Win.Day           104.97    116.79    111.02     98.49    106.89
Med.Win.Day            83.48     95.05     90.88     83.65     84.31
Avg.Losing.Day       -107.11   -113.35   -108.38    -97.95   -109.31
Med.Losing.Day        -81.61    -83.94    -84.18    -72.76    -84.31
Avg.Daily.PL            7.21     12.80     11.22     11.03     10.76
Med.Daily.PL           12.85     16.84     17.06     18.59     21.33
Std.Dev.Daily.PL.1    139.51    153.35    140.28    125.46    141.11
Ann.Sharpe              0.82      1.32      1.27      1.40      1.21
Max.Drawdown        -2338.49  -1945.85  -3026.93  -2001.41  -2047.83
Profit.To.Max.Draw      3.95      8.75      4.93      7.14      6.64
Avg.WinLoss.Ratio       0.98      1.03      1.02      1.01      0.98
Med.WinLoss.Ratio       1.02      1.13      1.08      1.15      1.00
Max.Equity           9902.70  17058.92  14996.93  14298.85  14604.43
Min.Equity           -136.30   -189.12   -159.59   -233.07   -315.54
End.Equity           9233.11  17020.32  14917.84  14298.85  13603.78

                         EWH       EWJ       EWS       EWT       EWU
Total.Net.Profit    11548.79   5965.14  12676.51  12221.36  12044.63
Total.Days           1221.00   1130.00   1300.00   1138.00   1252.00
Winning.Days          644.00    588.00    712.00    610.00    689.00
Losing.Days           577.00    542.00    588.00    528.00    563.00
Avg.Day.PL              9.46      5.28      9.75     10.74      9.62
Med.Day.PL             11.07     13.74     17.01     18.92     18.71
Largest.Winner        456.34    640.14    578.24    780.22    596.56
Largest.Loser        -495.32   -496.23  -1058.32   -835.42   -812.47
Gross.Profits       71689.04  67809.75  70413.47  71565.95  71295.50
Gross.Losses       -60140.25 -61844.61 -57736.96 -59344.59 -59250.87
Std.Dev.Daily.PL      138.92    148.13    130.19    150.04    132.82
Percent.Positive       52.74     52.04     54.77     53.60     55.03
Percent.Negative       47.26     47.96     45.23     46.40     44.97
Profit.Factor           1.19      1.10      1.22      1.21      1.20
Avg.Win.Day           111.32    115.32     98.90    117.32    103.48
Med.Win.Day            89.52     94.38     79.32     95.13     87.31
Avg.Losing.Day       -104.23   -114.10    -98.19   -112.40   -105.24
Med.Losing.Day        -81.34    -92.34    -75.96    -85.69    -82.84
Avg.Daily.PL            9.46      5.28      9.75     10.74      9.62
Med.Daily.PL           11.07     13.74     17.01     18.92     18.71
Std.Dev.Daily.PL.1    138.92    148.13    130.19    150.04    132.82
Ann.Sharpe              1.08      0.57      1.19      1.14      1.15
Max.Drawdown        -2298.32  -3445.56  -2017.06  -2764.34  -2071.61
Profit.To.Max.Draw      5.02      1.73      6.28      4.42      5.81
Avg.WinLoss.Ratio       1.07      1.01      1.01      1.04      0.98
Med.WinLoss.Ratio       1.10      1.02      1.04      1.11      1.05
Max.Equity          12708.66   6336.41  13177.76  12221.36  12640.57
Min.Equity           -272.68   -307.36   -331.04      0.00    -57.47
End.Equity          11548.79   5965.14  12676.51  12221.36  12044.63

                         EWY       EWZ       EZU       IEF       IGE
Total.Net.Profit    10513.27  14496.80  11233.90  13370.63  12428.65
Total.Days           1297.00   1351.00   1282.00   1186.00   1362.00
Winning.Days          714.00    743.00    705.00    628.00    730.00
Losing.Days           583.00    608.00    577.00    558.00    632.00
Avg.Day.PL              8.11     10.73      8.76     11.27      9.13
Med.Day.PL             15.39     16.83     16.09     13.04     12.81
Largest.Winner        461.13    661.59    726.99    660.63    666.40
Largest.Loser        -819.21   -837.40  -1254.82   -555.37   -643.72
Gross.Profits       77069.26  77329.78  74286.39  79557.53  81524.20
Gross.Losses       -66555.99 -62832.98 -63052.49 -66186.90 -69095.55
Std.Dev.Daily.PL      146.05    135.63    142.76    158.58    142.55
Percent.Positive       55.05     55.00     54.99     52.95     53.60
Percent.Negative       44.95     45.00     45.01     47.05     46.40
Profit.Factor           1.16      1.23      1.18      1.20      1.18
Avg.Win.Day           107.94    104.08    105.37    126.68    111.68
Med.Win.Day            88.89     81.86     84.23     99.02     92.92
Avg.Losing.Day       -114.16   -103.34   -109.28   -118.61   -109.33
Med.Losing.Day        -80.09    -78.39    -82.94    -94.81    -90.22
Avg.Daily.PL            8.11     10.73      8.76     11.27      9.13
Med.Daily.PL           15.39     16.83     16.09     13.04     12.81
Std.Dev.Daily.PL.1    146.05    135.63    142.76    158.58    142.55
Ann.Sharpe              0.88      1.26      0.97      1.13      1.02
Max.Drawdown        -2462.61  -2188.41  -2310.88  -2650.59  -3045.12
Profit.To.Max.Draw      4.27      6.62      4.86      5.04      4.08
Avg.WinLoss.Ratio       0.95      1.01      0.96      1.07      1.02
Med.WinLoss.Ratio       1.11      1.04      1.02      1.04      1.03
Max.Equity          10513.27  14760.11  12629.07  14339.37  12428.65
Min.Equity           -339.01   -214.04   -203.63   -278.08   -268.60
End.Equity          10513.27  14496.80  11233.90  13370.63  12428.65

                         IYR       IYZ       LQD       RWR       SHY
Total.Net.Profit     9702.92   8599.09  13277.24  12090.68  15021.96
Total.Days           1272.00   1232.00   1231.00   1306.00   1350.00
Winning.Days          689.00    656.00    687.00    708.00    738.00
Losing.Days           583.00    576.00    544.00    598.00    612.00
Avg.Day.PL              7.63      6.98     10.79      9.26     11.13
Med.Day.PL             12.50     11.72     14.81     11.46     20.75
Largest.Winner        550.83    601.69   1475.28    648.22    591.25
Largest.Loser        -749.58   -803.69   -724.79   -960.12   -988.98
Gross.Profits       68544.80  62158.11  60933.53  74573.82  80464.45
Gross.Losses       -58841.88 -53559.03 -47656.28 -62483.14 -65442.49
Std.Dev.Daily.PL      131.85    123.46    119.48    139.10    140.36
Percent.Positive       54.17     53.25     55.81     54.21     54.67
Percent.Negative       45.83     46.75     44.19     45.79     45.33
Profit.Factor           1.16      1.16      1.28      1.19      1.23
Avg.Win.Day            99.48     94.75     88.70    105.33    109.03
Med.Win.Day            82.65     72.40     70.23     83.85     88.76
Avg.Losing.Day       -100.93    -92.98    -87.60   -104.49   -106.93
Med.Losing.Day        -72.35    -70.97    -69.84    -78.11    -88.06
Avg.Daily.PL            7.63      6.98     10.79      9.26     11.13
Med.Daily.PL           12.50     11.72     14.81     11.46     20.75
Std.Dev.Daily.PL.1    131.85    123.46    119.48    139.10    140.36
Ann.Sharpe              0.92      0.90      1.43      1.06      1.26
Max.Drawdown        -5488.96  -2253.39  -1261.10  -5062.88  -2852.91
Profit.To.Max.Draw      1.77      3.82     10.53      2.39      5.27
Avg.WinLoss.Ratio       0.99      1.02      1.01      1.01      1.02
Med.WinLoss.Ratio       1.14      1.02      1.01      1.07      1.01
Max.Equity          13313.44   9225.39  14084.98  15192.88  16570.24
Min.Equity           -152.91   -522.27   -347.48   -394.76  -1036.81
End.Equity           9702.92   8599.09  13277.24  12090.68  15021.96

                         TLT       XLB       XLE       XLF       XLI
Total.Net.Profit     7117.62   6227.48  10335.40     -8.45   6351.34
Total.Days           1126.00   1252.00   1336.00   1208.00   1291.00
Winning.Days          576.00    679.00    732.00    626.00    694.00
Losing.Days           550.00    573.00    604.00    582.00    597.00
Avg.Day.PL              6.32      4.97      7.74     -0.01      4.92
Med.Day.PL              7.01     12.34     15.29      6.50     11.06
Largest.Winner        706.10    586.14    525.30    425.95    479.01
Largest.Loser        -609.88   -815.74   -501.16   -446.61   -384.75
Gross.Profits       70845.43  64778.65  73739.30  55919.96  61941.72
Gross.Losses       -63727.80 -58551.17 -63403.90 -55928.41 -55590.37
Std.Dev.Daily.PL      158.57    130.49    131.97    122.51    116.99
Percent.Positive       51.15     54.23     54.79     51.82     53.76
Percent.Negative       48.85     45.77     45.21     48.18     46.24
Profit.Factor           1.11      1.11      1.16      1.00      1.11
Avg.Win.Day           123.00     95.40    100.74     89.33     89.25
Med.Win.Day            93.07     72.97     79.85     66.21     73.41
Avg.Losing.Day       -115.87   -102.18   -104.97    -96.10    -93.12
Med.Losing.Day        -89.17    -77.13    -79.13    -69.41    -72.87
Avg.Daily.PL            6.32      4.97      7.74     -0.01      4.92
Med.Daily.PL            7.01     12.34     15.29      6.50     11.06
Std.Dev.Daily.PL.1    158.57    130.49    131.97    122.51    116.99
Ann.Sharpe              0.63      0.61      0.93      0.00      0.67
Max.Drawdown        -4938.54  -3692.51  -2780.65  -5704.14  -3013.95
Profit.To.Max.Draw      1.44      1.69      3.72      0.00      2.11
Avg.WinLoss.Ratio       1.06      0.93      0.96      0.93      0.96
Med.WinLoss.Ratio       1.04      0.95      1.01      0.95      1.01
Max.Equity           9318.18   6780.72  10335.40   4191.68   6357.94
Min.Equity           -693.51   -213.25   -557.67  -1512.46   -795.23
End.Equity           7117.62   6227.48  10335.40     -8.45   6351.34

                         XLK       XLP       XLU       XLV       XLY
Total.Net.Profit     2940.29   2543.61   7904.42   3189.25   6727.03
Total.Days           1210.00   1326.00   1322.00   1153.00   1179.00
Winning.Days          664.00    704.00    710.00    593.00    611.00
Losing.Days           546.00    622.00    612.00    560.00    568.00
Avg.Day.PL              2.43      1.92      5.98      2.77      5.71
Med.Day.PL             14.64     11.03     12.12      5.31      8.48
Largest.Winner        437.09    537.52    546.32    453.96    450.42
Largest.Loser        -765.80   -750.98   -855.10   -726.49   -447.69
Gross.Profits       57610.32  62334.98  61795.49  53165.92  58852.52
Gross.Losses       -54670.02 -59791.38 -53891.07 -49976.67 -52125.49
Std.Dev.Daily.PL      122.10    118.49    115.44    117.37    120.29
Percent.Positive       54.88     53.09     53.71     51.43     51.82
Percent.Negative       45.12     46.91     46.29     48.57     48.18
Profit.Factor           1.05      1.04      1.15      1.06      1.13
Avg.Win.Day            86.76     88.54     87.04     89.66     96.32
Med.Win.Day            71.15     73.84     70.10     72.61     78.75
Avg.Losing.Day       -100.13    -96.13    -88.06    -89.24    -91.77
Med.Losing.Day        -72.57    -74.82    -65.52    -71.56    -70.88
Avg.Daily.PL            2.43      1.92      5.98      2.77      5.71
Med.Daily.PL           14.64     11.03     12.12      5.31      8.48
Std.Dev.Daily.PL.1    122.10    118.49    115.44    117.37    120.29
Ann.Sharpe              0.32      0.26      0.82      0.37      0.75
Max.Drawdown        -3074.10  -4531.59  -2545.84  -4623.78  -4041.34
Profit.To.Max.Draw      0.96      0.56      3.10      0.69      1.66
Avg.WinLoss.Ratio       0.87      0.92      0.99      1.00      1.05
Med.WinLoss.Ratio       0.98      0.99      1.07      1.01      1.11
Max.Equity           2981.10   2993.23   8632.88   3902.68   6838.89
Min.Equity          -1716.68  -1538.36   -165.00  -1213.50   -310.63
End.Equity           2940.29   2543.61   7904.42   3189.25   6727.03

Now, I’d like to introduce some new analytics found in my IKTrading package–namely, trade duration statistics. This is a simple little function that breaks down the trades by duration, in aggregate, winners, and losers, using the usual five-number summary and the mean time. It’s programmed under the assumption that the units are days. Once you start drilling into more frequent trading, one would likely need a bit more refined analysis. However, as most freely available data occurs at the daily frequency, this function should be sufficient for most analyses.

Here is the input and output for this strategy:

durStats <- durationStatistics(Portfolio=portfolio.st, Symbols=sort(symbols))
print(t(durStats))
      EFA EPP EWA EWC EWG EWH EWJ EWS EWT EWU EWY EWZ EZU IEF IGE IYR
Min     1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
Q1      2   2   2   3   2   2   2   2   2   2   2   3   2   2   2   3
Med     6   6   5   7   6   4   5   6   6   6   6   6   6   4   6   6
Mean   12  14  13  16  14  12  12  13  16  15  13  13  12  15  14  13
Q3     14  15  14  17  13  12  11  14  18  14  15  14  13  17  14  12
Max   119 132 135 122 126 185 130 139 106 132 105  95 131 144 125 128
WMin    1   1   1   1   1   1   1   1   1   1   1   1   1   1   3   1
WQ1     9   7   3   8   3   2   2   6   3   6   5   7   5   6   9   7
WMed   14  14  14  18  12  11   8  13  14  14  13  14  13  17  19  15
WMean  22  25  22  28  24  21  20  23  24  27  21  24  23  28  30  28
WQ3    20  28  26  34  25  20  25  23  38  28  27  37  22  38  42  33
WMax  119 132 135 122 126 185 130 139 106 132 105  95 131 144 125 128
LMin    1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
LQ1     1   1   1   2   1   2   2   1   1   1   1   1   1   1   1   2
LMed    3   4   3   4   3   3   3   3   3   3   3   3   3   3   3   4
LMean   4   5   5   6   5   5   6   5   7   4   5   5   5   5   6   5
LQ3     5   7   6   7   7   6   8   6   6   6   6   6   6   5   6   6
LMax   30  22  22  34  28  30  25  41  64  17  28  43  22  40  62  40

      IYZ LQD RWR SHY TLT XLB XLE XLF XLI XLK XLP XLU XLV XLY
Min     1   1   1   1   1   1   1   1   1   1   1   1   1   1
Q1      3   1   2   2   2   3   2   2   2   2   2   3   2   3
Med     6   4   5   6   5   7   6   5   7   6   4   7   5   8
Mean   13  15  13  12  12  14  15  13  12  13  13  15  14  15
Q3     13  14  12  14  12  15  16  15  13  13  13  14  13  14
Max   115 151 128 109  90 105 113 130  99 116 117 121 132 123
WMin    1   1   1   1   1   6   1   1   1   1   1   1   1   1
WQ1     6   3   7   6   9  12   9   9   6   6   5   7   9  11
WMed   13  13  14  14  21  16  19  17  12  14  17  16  15  14
WMean  23  27  27  22  29  28  31  27  21  27  29  27  35  28
WQ3    34  28  27  28  43  34  48  32  20  38  45  40  44  28
WMax  115 151 128 109  90 105 113 130  99 116 117 121 132 123
LMin    1   1   1   1   1   1   1   1   1   1   1   1   1   1
LQ1     2   1   1   2   1   1   1   2   1   1   1   1   1   2
LMed    4   2   3   4   3   3   3   3   4   4   4   4   4   4
LMean   5   5   4   5   5   6   6   6   7   5   6   5   6   6
LQ3     6   5   6   6   6   7   7   7   8   7   7   7   7   8
LMax   33  25  32  42  31  36  38  41  36  19  41  39  69  35

From top to bottom in this transposed table (or left to right in the original), we have aggregate trade duration statistics, the same statistics on winners, and finally, losers. This paints a picture of this current strategy as having a profile of a classic trend follower: let your winners run, and cut your losses. Or, to set it to a higher standard, the occasional winner at the price of plenty of small whipsaws, with the occasional long-duration loser. Note that this loser may not have been a loser the entire time–it could very well have been a trade that was going sideways for a long time and finally sunk into negative territory near the end, but it seems that every instrument has had at least one somewhat long-running trade that wound up losing at least a penny.

On the winning side of the trade, the winning trades stay in the market for long berths, with the best ones riding waves that last for around half a year. Ultimately, this is the appeal of trend following–the idea of just getting in once, and just having the market pay you. The goal of investigating the FRAMA (and potentially other trend indicators), is to try and locate those long profit waves while avoiding the whipsaws, countertrends, and so on.

One other interesting piece of analytics I’m incorporating into this demo is the idea of market exposure–or what percentage of the time that the strategy is actually *in* the market. Here is the code and the results:

#market exposure
tmp <- list()
length(tmp) <- length(symbols)
for(i in 1:nrow(dStats)) {
  totalDays <- nrow(get(rownames(dStats)[i]))
  mktExposure <- dStats$Total.Days[i]/totalDays
  tmp[[i]] <- c(rownames(dStats)[i], round(mktExposure, 3))
}
mktExposure <- data.frame(do.call(rbind, tmp))
colnames(mktExposure) <- c("Symbol","MktExposure")
print(mktExposure)

Essentially, this little piece of code takes advantage of the daily statistics output to compute market exposure. Here’s the output:

   Symbol MktExposure
1     EFA       0.635
2     EPP        0.66
3     EWA        0.66
4     EWC       0.643
5     EWG       0.627
6     EWH       0.606
7     EWJ       0.561
8     EWS       0.645
9     EWT       0.565
10    EWU       0.621
11    EWY       0.644
12    EWZ        0.67
13    EZU       0.636
14    IEF       0.589
15    IGE       0.676
16    IYR       0.631
17    IYZ       0.611
18    LQD       0.611
19    RWR       0.648
20    SHY        0.67
21    TLT       0.559
22    XLB       0.621
23    XLE       0.663
24    XLF         0.6
25    XLI       0.641
26    XLK         0.6
27    XLP       0.658
28    XLU       0.656
29    XLV       0.572
30    XLY       0.585

So basically, around 60-66% of the time spent in the market, most of which are short, sporadic, losing trades, with the occasional long winner.

Here’s what the equity curve looks like:

As can be seen during the crisis, this baseline strategy is taking lots of trades…for no reason at all.

And here are the three aggregate portfolio statistics:

> SharpeRatio.annualized(portfRets)
                                    [,1]
Annualized Sharpe Ratio (Rf=0%) 1.334861
> Return.annualized(portfRets)
                       [,1]
Annualized Return 0.1288973
> maxDrawdown(portfRets)
[1] 0.1562275

The largest drawdown occurs during the crisis, but beyond that, the annualized returns are solid. What I find most impressive is that the annualized Sharpe Ratio over the course of this backtest, even with what seems to be a period of drawdowns that can be removed with what seems to be relative ease (most market timing trend-followers seem to do a decent job of avoiding most of the brunt of the crisis).

And finally, to demonstrate the indicator and investigate areas for improvement, here’s the equity curve of XLF (not XLB this time), since XLF lost eight dollars over the course of the backtest.

One advantage that I think the FRAMA has over the Trend Vigor is that as it is an indicator that’s superimposed directly on the price action, it’s easier to understand visually. (Also, the math is definitely more intuitive.) As we can see from the XLF equity curve, the base strategy presented by ETFHQ could certainly use a confirmatory, slower-moving indicator to keep out counter-trend trading. After all, while the tiny little whipsaw trades are undoubtedly a nuisance, correcting counter-trend trading is a lower-hanging fruit, and would seem to be more profitable in addressing.

Thanks for reading.

The Continuing Search For Robust Momentum Indicators: the Fractal Adaptive Moving Average

Following from the last post and setting aside the not-working-as-advertised Trend Vigor indicator, we will turn our attention to the world of adaptive moving averages. In this case, I will be working with the FRAMA–the FRactal Adaptive Moving Average. The reason I am starting off with this one is that according to ETFHQ in this post, FRAMA is an indicator that seems to have very strong performance, even using what definitely seems to be a very simple strategy (long if the price crosses over the indicator, exit vice versa), which would most likely leave one open to whipsaws.

But before then, I’d like to make an introduction to the FRAMA, by linking to the original Dr. John Ehlers paper, here.

While I won’t attempt to give a better formal explanation than the man that created the indicator (which is why the paper is there), the way I intuitively think about the FRAMA (or the adaptive moving average family of indicators, found here) is that they are improvements of the exponential moving average that attempt to smooth the indicator during cyclical market periods to avoid whipsaws, and to have a faster response during periods of strong trends, so as to minimize the damage done due to an ending trend.

The FRAMA itself compares two periods of n/2 days (the last n/2 days, and the last n/2 days before those last n/2 days) to the total period (n days). Intuitively, if there is a straight trend upwards, then the expression (log(N1+N2)-log(N3))/log(2), where N1 is the difference of highest high and lowest low over the last n/2 days and N2 is identical except for the previous n/2 days before the last n/2 days, and N3 is the same quantity over all n days, will be equal to zero, and thus, the exponent of that would be equal to 1, which is analogous to an EMA of 1 day. Similarly, when there is a great deal of congestion, then the expression log(N1+N2) will be greater than log(N3), and so the exponent (that is, the fractal dimension) of the exponent would be closer to 2 (or greater, since I implemented the modified FRAMA).

Let’s look at the code:

"FRAMA" <- function(HLC, n=20, FC=1, SC=200, triggerLag=1, ...) {
  price <- ehlersPriceMethod(HLC, ...)
  if (n%%2==1) n=n-1 #n must be even
  N3 <- (runMax(Hi(HLC), n)-runMin(Lo(HLC), n))/n
  N1 <- (runMax(Hi(HLC), n/2)-runMin(Lo(HLC), n/2))/(n/2)
  lagSeries <- lag(HLC, n/2)
  N2 <- (runMax(Hi(lagSeries), n/2)-runMin(Lo(lagSeries), n/2))/(n/2)
  dimen <- (log(N1+N2)-log(N3))/log(2)
  w <- log(2/(SC+1))
  oldAlpha <- exp(w*(dimen-1))
  oldN <- (2-oldAlpha)/oldAlpha
  newN <- ((SC-FC)*(oldN-1)/(SC-1))+FC
  alpha <- 2/(newN+1)
  alpha[which(alpha > 1)] <- 1
  alpha[which(alpha < w)] <- w
  alphaComplement <- 1-alpha
  initializationIndex <- index(alpha[is.na(alpha)])
  alpha[is.na(alpha)] <- 1; alphaComplement[is.na(alphaComplement)] <- 0
  FRAMA <- rep(0, length(price))
  FRAMA[1] <- price[1]
  FRAMA <- computeFRAMA(alpha, alphaComplement, FRAMA, price)
  FRAMA <- xts(FRAMA, order.by=index(price))
  FRAMA[initializationIndex] <- alpha[initializationIndex] <- NA
  trigger <- lag(FRAMA, triggerLag)
  out <- cbind(FRAMA=FRAMA, trigger=trigger)
  return(out)
}
NumericVector computeFRAMA(NumericVector alpha, NumericVector alphaComplement, NumericVector FRAMA, NumericVector price) {
  int n = price.size();
  for(int i=1; i<n; i++) {
    FRAMA[i] = alpha[i]*price[i] + alphaComplement[i]*FRAMA[i-1];
  }
  return FRAMA;
}

Essentially, from the second chunk of code, this is an advanced form of the exponential moving average that takes into account the amount of movement over a greater time period relative to the swing at two finer intervals in the two halves of that period. The methodology for the modified FRAMA is thanks to ETFHQ (once again), found here.

And while words can make for a bit of explanation, in this case, a picture (or several) is worth far more. Here is some code I wrote to plot an EMA, and three separate FRAMA computations (the default John Ehlers settings, the best ETFHQ settings, and the slower ETFHQ settings) on XLB from 2003 through 2010 (yes, the same XLB from our Trend Vigor backtest, because it was the go-to instrument for all our individual equity curves).

from="2003-01-01"
to="2010-12-31"
source("demoData.R")

tmp <- FRAMA(HLC(XLB))
tmp2 <- FRAMA(HLC(XLB), FC=4, n=126, SC=300)
tmp3 <- FRAMA(HLC(XLB), FC=40, n=252, SC=252)
ema <- EMA(x=Cl(XLB),n=126)

myTheme<-chart_theme()
myTheme$col$dn.col<-'gray2'
myTheme$col$dn.border <-'gray2'
myTheme$col$up.border <-'gray2'

chart_Series(XLB,
             TA="add_TA(tmp$FRAMA, col='blue', on=1, lwd=3);
              add_TA(tmp2$FRAMA, col='green', on=1, lwd=3);
              add_TA(tmp3$FRAMA, col='red', on=1, lwd=3);
              add_TA(ema$EMA, col='orange', on=1, lwd=3)", 
             theme=myTheme)

legend(x=5, y=40, legend=c("FRAMA FC=1, n=20, SC=200", 
                           "FRAMA FC=4, n=126, SC=300", 
                           "FRAMA FC=40, n=252, SC=252",
                           "EMA n=126"),
       fill=c("blue", "green", "red", "orange"), bty="n")

This produces the following plot:

From this perspective, the improvements are clear. Essentially, the long-term FRAMA (FC 40, n 252, SC 252) possesses much of the smoothness of the 126 day EMA, while being far more responsive to the turns in price action to keep open equity at the end of a trend. The two faster FRAMAs, on the other hand, hug the price action more closely, yet still retain a degree of smoothness.

Here’s the code to zoom in on 2007-2008.

chart_Series(XLB,
             TA="add_TA(tmp$FRAMA, col='blue', on=1, lwd=3);
              add_TA(tmp2$FRAMA, col='green', on=1, lwd=3);
              add_TA(tmp3$FRAMA, col='red', on=1, lwd=3);
              add_TA(ema$EMA, col='orange', on=1, lwd=3)", 
             theme=myTheme, subset="2007::2008")

legend(x=5, y=30, legend=c("FRAMA FC=1, n=20, SC=200", 
                           "FRAMA FC=4, n=126, SC=300", 
                           "FRAMA FC=40, n=252, SC=252",
                           "EMA n=126"),
       fill=c("blue", "green", "red", "orange"), bty="n")

And, the corresponding plot.

Here, we can see some more properties. While the default John Ehlers settings (blue) seemingly tracks price action very closely, the indicator usually finds itself right in the middle of the price action, but still has the occasional trend following property when price action breaks through it at the start of the financial crisis. In other words, it seems that it can hurt you both as a trend follower (whipsaws), and as a mean reverting indicator (as seen when XLB starts falling in the crisis), so this gives rise to the idea that an indicator can track the price too well.

On the other hand, the 126 day FRAMA (the ETFHQ settings, in green) seems to look like a dynamic support and resistance indicator that the talking heads go on and on about (yet give very little advice on how to actually objectively compute), in that the price action seems to touch it every so often, but not oscillate around it. It breaks in one direction and manages to stay in that direction, until it breaks in the other direction, and sustain a move to that direction. This seems like a foundation of a future trading strategy.

Finally, the 252 day FRAMA (the ETFHQ settings for the long-term FRAMA indicator, in red) looks like a confirmatory indicator or filter.

Notice that by comparison, the 126 day EMA seems to lag as much if not more than the 252 day FRAMA, and from this vantage point, it seems that the results are not as good for the same amount of data processed.

Overall, it seems that by trading off smoothness and responsiveness, one can see the foundations of a possible system.

The potential trading systems here will be explored in the future.

Thanks for reading.

Trend Vigor Part IV: Shorting and Walk Forward Test

While Trend Vigor has potential on the long end (as seen in part III of this investigation here), as Andreas Clenow has stated in his article “Trend Following Does Not Work On Stocks”, the short side of trend following gets killed in equities. And, since by far and away most of the securities in this backtest were equity-based ETFs (most of the ones available before 2003 were equity index ETFs), the results can basically be summed up as “buying insurance for Black Swans”. On the plus side, Trend Vigor at very high period settings (longer than 120) is able to capture some of the upside from being short in the depths of the financial crisis, but unfortunately gives most of it back.

To begin, here’s the code for this strategy, starting off at the previous settings of a period of 20 and delta 0 with ATR order sizing. The demo data has slightly changed in that the initialization, from, and to parameters are now found in the demo scripts, rather than the file itself, which, as a result, is no longer a standalone file.

require(DSTrading)
require(IKTrading)
require(quantstrat)

initDate="1990-01-01"
from="2003-01-01"
to="2010-12-31"

#to rerun the strategy, rerun everything below this line
source("demoData.R") #contains all of the data-related boilerplate.

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

strategy.st <- portfolio.st <- account.st <- "TVI_short"
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 (trigger lag unchanged, defaulted at 1)
delta=0
period=20
pctATR=.02 #control risk with this parameter

#indicators
add.indicator(strategy.st, name="TVI", arguments=list(x=quote(Cl(mktdata)), period=period, delta=delta), label="TVI")
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(threshold=-1, column="vigor.TVI", relationship="lte", cross=FALSE),
           label="TVIltThresh")
add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("vigor.TVI","trigger.TVI"), relationship="lt"),
           label="TVIltLag")
add.signal(strategy.st, name="sigAND",
           arguments=list(columns=c("TVIltThresh","TVIltLag"), cross=TRUE),
           label="shortEntry")
add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("vigor.TVI","trigger.TVI"), relationship="gt"),
           label="shortExit")

#rules
add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="shortEntry", sigval=TRUE, ordertype="market", 
                        orderside="short", 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="shortExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="short", 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)

And here is the output:

> (aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
[1] 0.7088293
> (numTrades <- sum(tStats$Num.Trades))
[1] 947
> (aggCorrect <- sum(tStats$Num.Trades*tStats$Percent.Positive/100)/numTrades)
[1] 0.2734925
> (meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio))
[1] 2.073667

So, already, we can see that it’s pretty much a bloodbath–namely because throughout 2003 through 2010, the general trend was upwards, and also, because of the use of constant ATR order sizing, when the moves are largest (during the heightened volatility), our position size is smallest, relative to it. Which isn’t to say that this is a particularly bad idea considering that a short position can get whipsawed badly, but that such a short-term detection system has no sense of the overall larger trend.

Here are some more portfolio statistics.

> SharpeRatio.annualized(portfRets)
                                      [,1]
Annualized Sharpe Ratio (Rf=0%) -0.3647753
> Return.annualized(portfRets)
                         [,1]
Annualized Return -0.04075671
> maxDrawdown(portfRets)
[1] 0.3464871
> 

In other words, this strategy simply costs you an annualized 4% a year, just to get the pop-up during the financial crisis, and then proceed to give that all back to the market anyway. In other words, it’s a really silly form of insurance, especially considering that due to the heightened volatility in times of extreme crisis, the ATR order sizing function *already* functions as a massive hedge. For the sake of completeness, here’s the equity curve of this strategy.

Equity Curve short

And here’s the usual equity curve of XLB.

Note that just when all the blood is in the water, the ATR order-sizing function actually works *against* the strategy. This is due to the fact that with equities, that are bought in the expectation of price appreciation as an investment (rather than, say, a commodity, whose prices need to fluctuate, and whose price appreciation perhaps means adverse consequences outside of the investment universe, or an exchange rate, which is by nature long in one instrument, short another, so may not be marked by severe volatility to the downside), and thus, trends to the upside are more sustained, while moves to the downside happen much more quickly, and by the time there is “sufficient evidence” (at any level of evidence) of a move to the downside, the largest part of the move already happened, and it’s much more likely that a short trade would get whipsawed for a loss.

If the other extreme is tried–that is, a more conservative delta parameter (.1), and a much larger period (greater than 120 days, to attempt to implement this more as a filter, in this case, 140), these are the results:

> (aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
[1] 1.157702
> (numTrades <- sum(tStats$Num.Trades))
[1] 77
> (aggCorrect <- sum(tStats$Num.Trades*tStats$Percent.Positive/100)/numTrades)
[1] 0.4155857
> (meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio))
[1] NaN

The NaN means that one or more of the instruments had so few trades that there were no losers (most likely 1 or 2 trades that just so happened to get lucky), E.G. XLU had 1 trade.

Again, nothing worth writing home over. Even after I tried several different settings (for instance, if the delta parameter is too high, most of the move to the downside in the crisis will be missed, and any profit will get consumed by the recovery, and then some), when the best result barely breaks even in a once-in-a-lifetime crisis, and so many other configurations either bite too easily on minor dips in an uptrend, or the converse, are too afraid to enter even in a once-in-a-lifetime opportunity, it’s best to not even try to fit this square peg into a round hole, and just not even think about trying Trend Vigor on the short end in equities.

For the record, here’s the equity curve.

So even when the indicator gets the once-in-a-lifetime black swan opportunity, it still manages to do little noteworthy. To put it into perspective, let’s once again look at XLB.

Basically, enter late, exit late (meaning there’s a lot of lag to this computation) on the filter end, and bite too easily on the short-term trading end.

In other words, as a standalone indicator, I’d be careful with it. Originally, this indicator was meant to be a predictor of trending vs. cycling. And essentially, at least when it comes to equities, it fails miserably, taking many trades it shouldn’t in an environment that steadily grinds upwards. This makes me believe that in a trend that grinds downwards, that a long strategy would be similarly disastrous. In short, in my opinion, the Trend Vigor indicator fails its original stated purpose, which is to partition markets into trending/cycling/trending downwards.

This all in mind, let’s see what happened to our holdout data–that is, from 2011 onwards using the long-only ATR position-sizing strategy through 2011 to present day (that is, the current date as of the time of this writing, which is June 9th, 2014, or “2014-06-09”).

Nothing spectacular, that’s for certain. Here are the results:

Trade stats:

                        EFA      EPP      EWA      EWC      EWG
Num.Txns              31.00    39.00    37.00    27.00    29.00
Num.Trades            16.00    20.00    19.00    14.00    15.00
Net.Trading.PL      1006.37 -1067.24  -777.68  -239.11  1820.57
Avg.Trade.PL          62.90   -53.36   -40.93   -17.08   121.37
Med.Trade.PL        -132.88   -71.38   -98.26  -332.33  -151.21
Largest.Winner      1434.98  1085.83  1678.21  1057.21  1517.68
Largest.Loser      -1380.12 -1025.44 -1268.16  -953.35  -834.48
Gross.Profits       4939.32  4273.32  4932.19  4821.37  5546.32
Gross.Losses       -3932.95 -5340.56 -5709.87 -5060.48 -3725.75
Std.Dev.Trade.PL     702.30   626.78   735.31   804.69   761.01
Percent.Positive      43.75    40.00    47.37    42.86    40.00
Percent.Negative      56.25    60.00    52.63    57.14    60.00
Profit.Factor          1.26     0.80     0.86     0.95     1.49
Avg.Win.Trade        705.62   534.16   548.02   803.56   924.39
Med.Win.Trade        720.56   533.59   363.94   874.10   889.67
Avg.Losing.Trade    -436.99  -445.05  -570.99  -632.56  -413.97
Med.Losing.Trade    -319.93  -297.21  -514.73  -625.85  -394.68
Avg.Daily.PL          13.16   -59.45   -50.63  -121.83    79.13
Med.Daily.PL        -140.36   -80.61  -131.59  -429.16  -187.01
Std.Dev.Daily.PL     697.17   643.35   755.38   731.48   771.27
Ann.Sharpe             0.30    -1.47    -1.06    -2.64     1.63
Max.Drawdown       -2826.18 -3230.34 -3731.81 -3230.98 -2594.95
Profit.To.Max.Draw     0.36    -0.33    -0.21    -0.07     0.70
Avg.WinLoss.Ratio      1.61     1.20     0.96     1.27     2.23
Med.WinLoss.Ratio      2.25     1.80     0.71     1.40     2.25
Max.Equity          2628.13   352.15   229.19  1555.99  3032.24
Min.Equity         -1639.66 -2878.19 -3507.75 -1674.99 -1430.39
End.Equity          1006.37 -1067.24  -777.68  -239.11  1820.57

                        EWH      EWJ      EWS      EWT      EWU
Num.Txns              31.00    33.00    33.00    31.00    33.00
Num.Trades            15.00    17.00    17.00    16.00    17.00
Net.Trading.PL      -230.79 -1209.81  -348.15 -2362.66 -2162.86
Avg.Trade.PL         -15.39   -71.17   -20.48  -147.67  -127.23
Med.Trade.PL        -178.21  -319.55  -199.30  -150.33   -76.03
Largest.Winner      2303.00  2887.37  1185.85  1179.33  1032.23
Largest.Loser      -1069.43 -2169.74 -1076.99 -1795.83 -1195.97
Gross.Profits       5306.60  5403.59  4539.11  3425.73  3320.74
Gross.Losses       -5537.39 -6613.40 -4887.26 -5788.39 -5483.60
Std.Dev.Trade.PL     963.23  1029.16   698.54   728.75   672.01
Percent.Positive      33.33    35.29    41.18    37.50    41.18
Percent.Negative      66.67    64.71    58.82    62.50    58.82
Profit.Factor          0.96     0.82     0.93     0.59     0.61
Avg.Win.Trade       1061.32   900.60   648.44   570.96   474.39
Med.Win.Trade        975.54   597.00   578.61   491.34   371.68
Avg.Losing.Trade    -553.74  -601.22  -488.73  -578.84  -548.36
Med.Losing.Trade    -491.03  -487.30  -489.20  -575.76  -426.36
Avg.Daily.PL         -23.37  -130.20   -89.64  -179.67  -199.37
Med.Daily.PL        -291.79  -321.44  -212.21  -153.16  -133.24
Std.Dev.Daily.PL     999.07  1032.75   658.59   742.60   622.35
Ann.Sharpe            -0.37    -2.00    -2.16    -3.84    -5.09
Max.Drawdown       -2594.56 -4926.69 -3641.30 -4719.71 -4105.55
Profit.To.Max.Draw    -0.09    -0.25    -0.10    -0.50    -0.53
Avg.WinLoss.Ratio      1.92     1.50     1.33     0.99     0.87
Med.WinLoss.Ratio      1.99     1.23     1.18     0.85     0.87
Max.Equity          2143.41  2447.92  1925.63   928.36   915.55
Min.Equity         -2540.25 -3809.54 -1715.67 -3791.36 -3190.00
End.Equity          -230.79 -1209.81  -348.15 -2362.66 -2162.86

                        EWY      EWZ      EZU      IEF      IGE
Num.Txns              35.00    38.00    41.00    31.00    33.00
Num.Trades            18.00    19.00    21.00    16.00    17.00
Net.Trading.PL     -2694.52 -2396.04   859.85  3933.39  1868.20
Avg.Trade.PL        -149.70  -126.11    40.95   245.84   109.89
Med.Trade.PL          12.78  -286.33    14.46    51.79    40.66
Largest.Winner       583.36   809.58  1157.57  2216.26  1422.14
Largest.Loser      -1506.45  -791.25  -658.35  -496.69 -1458.75
Gross.Profits       2579.07  2727.04  4061.44  6119.33  6139.07
Gross.Losses       -5273.59 -5123.08 -3201.60 -2185.94 -4270.87
Std.Dev.Trade.PL     562.04   467.07   465.97   740.00   814.68
Percent.Positive      50.00    31.58    52.38    50.00    52.94
Percent.Negative      50.00    68.42    47.62    50.00    47.06
Profit.Factor          0.49     0.53     1.27     2.80     1.44
Avg.Win.Trade        286.56   454.51   369.22   764.92   682.12
Med.Win.Trade        262.99   453.56   221.78   549.08   426.84
Avg.Losing.Trade    -585.95  -394.08  -320.16  -273.24  -533.86
Med.Losing.Trade    -466.95  -354.64  -304.80  -286.43  -399.31
Avg.Daily.PL        -164.86  -126.11    14.04   239.13     3.55
Med.Daily.PL         -45.85  -286.33   -17.84    -3.45   -36.71
Std.Dev.Daily.PL     575.53   467.07   461.04   765.47   709.12
Ann.Sharpe            -4.55    -4.29     0.48     4.96     0.08
Max.Drawdown       -3796.28 -3435.45 -2154.32 -2166.31 -3368.63
Profit.To.Max.Draw    -0.71    -0.70     0.40     1.82     0.55
Avg.WinLoss.Ratio      0.49     1.15     1.15     2.80     1.28
Med.WinLoss.Ratio      0.56     1.28     0.73     1.92     1.07
Max.Equity           738.72   180.61  1947.51  4512.77  1868.20
Min.Equity         -3057.56 -3254.84 -1321.26  -576.85 -1540.72
End.Equity         -2694.52 -2396.04   859.85  3933.39  1868.20

                        IYR      IYZ      LQD      RWR      SHY
Num.Txns              27.00    29.00    35.00    27.00    31.00
Num.Trades            14.00    15.00    17.00    14.00    16.00
Net.Trading.PL      3676.71  1141.84  3885.88  2503.47  2087.28
Avg.Trade.PL         262.62    76.12   228.58   178.82   130.46
Med.Trade.PL         216.71    59.28   140.49   167.95    83.51
Largest.Winner      1597.44  2165.96   813.01  1725.22  2018.03
Largest.Loser      -1205.43 -1087.93  -880.30 -1435.08  -564.72
Gross.Profits       6179.06  5208.19  7516.04  6097.92  4069.39
Gross.Losses       -2502.35 -4066.35 -3630.16 -3594.45 -1982.11
Std.Dev.Trade.PL     752.85   894.64   896.25   886.91   592.27
Percent.Positive      57.14    60.00    52.94    57.14    56.25
Percent.Negative      42.86    40.00    47.06    42.86    43.75
Profit.Factor          2.47     1.28     2.07     1.70     2.05
Avg.Win.Trade        772.38   578.69   835.12   762.24   452.15
Med.Win.Trade        694.95   319.46   673.49   454.00   281.30
Avg.Losing.Trade    -417.06  -677.72  -453.77  -599.08  -283.16
Med.Losing.Trade    -291.44  -694.92  -444.53  -435.46  -235.46
Avg.Daily.PL         198.73    77.33    59.12    96.11   111.87
Med.Daily.PL          94.41    32.47   -10.69    54.71    71.05
Std.Dev.Daily.PL     743.03   928.40   579.72   865.11   608.21
Ann.Sharpe             4.25     1.32     1.62     1.76     2.92
Max.Drawdown       -3147.23 -3389.11 -2718.68 -3029.80 -1495.18
Profit.To.Max.Draw     1.17     0.34     1.43     0.83     1.40
Avg.WinLoss.Ratio      1.85     0.85     1.84     1.27     1.60
Med.WinLoss.Ratio      2.38     0.46     1.52     1.04     1.19
Max.Equity          4203.56  2147.66  4370.31  2604.67  2311.64
Min.Equity          -651.36 -3389.11  -594.50 -1612.16  -776.36
End.Equity          3676.71  1141.84  3885.88  2503.47  2087.28

                        TLT      XLB      XLE      XLF      XLI
Num.Txns              27.00    35.00    25.00    27.00    31.00
Num.Trades            14.00    18.00    13.00    14.00    16.00
Net.Trading.PL      4188.91  1525.85  3015.76  3312.93  5243.37
Avg.Trade.PL         299.21    84.77   231.98   236.64   327.71
Med.Trade.PL         -29.23    99.39   -25.64    47.93   139.79
Largest.Winner      3310.84   909.91  2265.85  1907.25  2116.29
Largest.Loser       -829.51  -894.26 -1356.04  -652.95  -873.32
Gross.Profits       6548.35  4078.77  6369.44  5587.83  7236.98
Gross.Losses       -2359.44 -2552.93 -3353.68 -2274.90 -1993.61
Std.Dev.Trade.PL    1025.04   460.16  1021.63   735.76   767.98
Percent.Positive      50.00    55.56    46.15    57.14    62.50
Percent.Negative      50.00    44.44    53.85    42.86    37.50
Profit.Factor          2.78     1.60     1.90     2.46     3.63
Avg.Win.Trade        935.48   407.88  1061.57   698.48   723.70
Med.Win.Trade        436.88   334.09  1028.27   607.54   565.35
Avg.Losing.Trade    -337.06  -319.12  -479.10  -379.15  -332.27
Med.Losing.Trade    -296.04  -262.59  -335.76  -367.86  -203.93
Avg.Daily.PL         257.88    43.28   108.37   212.17   300.65
Med.Daily.PL         -77.71    94.92   -79.46     8.01    50.98
Std.Dev.Daily.PL    1054.69   438.26   960.17   759.85   787.00
Ann.Sharpe             3.88     1.57     1.79     4.43     6.06
Max.Drawdown       -3113.33 -2417.02 -3108.53 -2224.39 -2732.59
Profit.To.Max.Draw     1.35     0.63     0.97     1.49     1.92
Avg.WinLoss.Ratio      2.78     1.28     2.22     1.84     2.18
Med.WinLoss.Ratio      1.48     1.27     3.06     1.65     2.77
Max.Equity          5888.75  1525.85  3093.03  3738.75  5376.79
Min.Equity          -164.65 -1872.16  -225.30 -1541.00 -1030.57
End.Equity          4188.91  1525.85  3015.76  3312.93  5243.37

                        XLK      XLP      XLU      XLV      XLY
Num.Txns              23.00    35.00    28.00    29.00    29.00
Num.Trades            12.00    18.00    14.00    15.00    15.00
Net.Trading.PL      3958.91  4574.37  4589.84  9011.73  3926.53
Avg.Trade.PL         329.91   254.13   327.85   600.78   261.77
Med.Trade.PL         341.28   -96.61     6.57   292.17   100.86
Largest.Winner      1620.74  2992.30  2675.47  4335.47  1417.86
Largest.Loser      -1651.62  -940.12  -720.35  -886.02 -1211.34
Gross.Profits       6146.50  7850.27  7382.03 10689.31  7051.77
Gross.Losses       -2187.59 -3275.90 -2792.19 -1677.58 -3125.25
Std.Dev.Trade.PL     881.64   930.65  1015.70  1235.25   813.32
Percent.Positive      66.67    38.89    50.00    60.00    53.33
Percent.Negative      33.33    61.11    50.00    40.00    46.67
Profit.Factor          2.81     2.40     2.64     6.37     2.26
Avg.Win.Trade        768.31  1121.47  1054.58  1187.70   881.47
Med.Win.Trade        688.70   925.57   994.86  1188.08   980.24
Avg.Losing.Trade    -546.90  -297.81  -398.88  -279.60  -446.46
Med.Losing.Trade    -225.21  -263.58  -350.39  -232.60  -258.03
Avg.Daily.PL         272.88   172.49   327.85   615.92   221.10
Med.Daily.PL         226.83  -109.64     6.57   255.91    37.76
Std.Dev.Daily.PL     901.16   890.38  1015.70  1280.43   828.05
Ann.Sharpe             4.81     3.08     5.12     7.64     4.24
Max.Drawdown       -3054.65 -2491.74 -1541.47 -2349.89 -2867.34
Profit.To.Max.Draw     1.30     1.84     2.98     3.83     1.37
Avg.WinLoss.Ratio      1.40     3.77     2.64     4.25     1.97
Med.WinLoss.Ratio      3.06     3.51     2.84     5.11     3.80
Max.Equity          4217.59  5106.00  5446.02  9151.52  4598.69
Min.Equity         -1749.61 -1029.25  -898.16  -380.08 -1603.57
End.Equity          3958.91  4574.37  4589.84  9011.73  3926.53

> (aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
[1] 1.463801
> (aggCorrect <- mean(tStats$Percent.Positive))
[1] 48.769
> (numTrades <- sum(tStats$Num.Trades))
[1] 482
> (meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio))
[1] 1.749667

Overall, it still was profitable, but definitely not impressively so. For the first time, the 50% mark got broken, which essentially contradicted the uniqueness of the indicator to begin with.

Here are the daily stats:

                         EFA       EPP       EWA       EWC       EWG
Total.Net.Profit     1006.37  -1067.24   -777.68   -239.11   1820.57
Total.Days            552.00    543.00    541.00    506.00    547.00
Winning.Days          298.00    277.00    271.00    268.00    289.00
Losing.Days           254.00    266.00    270.00    238.00    258.00
Avg.Day.PL              1.82     -1.97     -1.44     -0.47      3.33
Med.Day.PL             17.00      2.90      3.39     10.01     17.65
Largest.Winner        529.45    471.21    468.52    368.67    484.27
Largest.Loser        -680.76   -725.06   -519.09   -545.42   -655.86
Gross.Profits       33513.80  30081.53  30541.36  24265.15  34491.17
Gross.Losses       -32507.43 -31148.77 -31319.04 -24504.26 -32670.61
Std.Dev.Daily.PL      158.32    146.60    146.24    126.51    161.06
Percent.Positive       53.99     51.01     50.09     52.96     52.83
Percent.Negative       46.01     48.99     49.91     47.04     47.17
Profit.Factor           1.03      0.97      0.98      0.99      1.06
Avg.Win.Day           112.46    108.60    112.70     90.54    119.35
Med.Win.Day            97.46     89.05     94.25     74.00     97.54
Avg.Losing.Day       -127.98   -117.10   -116.00   -102.96   -126.63
Med.Losing.Day        -93.07    -91.19    -93.91    -84.12    -90.41
Avg.Daily.PL            1.82     -1.97     -1.44     -0.47      3.33
Med.Daily.PL           17.00      2.90      3.39     10.01     17.65
Std.Dev.Daily.PL.1    158.32    146.60    146.24    126.51    161.06
Ann.Sharpe              0.18     -0.21     -0.16     -0.06      0.33
Max.Drawdown        -2826.18  -3230.34  -3731.81  -3230.98  -2594.95
Profit.To.Max.Draw      0.36     -0.33     -0.21     -0.07      0.70
Avg.WinLoss.Ratio       0.88      0.93      0.97      0.88      0.94
Med.WinLoss.Ratio       1.05      0.98      1.00      0.88      1.08
Max.Equity           2628.13    352.15    229.19   1555.99   3032.24
Min.Equity          -1639.66  -2878.19  -3507.75  -1674.99  -1430.39
End.Equity           1006.37  -1067.24   -777.68   -239.11   1820.57

                         EWH       EWJ       EWS       EWT       EWU
Total.Net.Profit     -230.79  -1209.81   -348.15  -2362.66  -2162.86
Total.Days            485.00    483.00    473.00    467.00    523.00
Winning.Days          250.00    257.00    241.00    230.00    267.00
Losing.Days           235.00    226.00    232.00    237.00    256.00
Avg.Day.PL             -0.48     -2.50     -0.74     -5.06     -4.14
Med.Day.PL              7.87     18.54     10.40     -7.66      8.92
Largest.Winner        442.74    856.67    728.55    509.94    419.63
Largest.Loser        -588.60  -1761.76  -1098.11   -612.79   -859.75
Gross.Profits       26844.49  34106.49  27148.71  25496.48  28167.12
Gross.Losses       -27075.28 -35316.31 -27496.86 -27859.13 -30329.98
Std.Dev.Daily.PL      144.50    205.32    156.16    147.47    148.73
Percent.Positive       51.55     53.21     50.95     49.25     51.05
Percent.Negative       48.45     46.79     49.05     50.75     48.95
Profit.Factor           0.99      0.97      0.99      0.92      0.93
Avg.Win.Day           107.38    132.71    112.65    110.85    105.49
Med.Win.Day            84.03    116.20     83.22     89.79     88.48
Avg.Losing.Day       -115.21   -156.27   -118.52   -117.55   -118.48
Med.Losing.Day        -87.60   -120.69   -102.84    -89.96    -87.51
Avg.Daily.PL           -0.48     -2.50     -0.74     -5.06     -4.14
Med.Daily.PL            7.87     18.54     10.40     -7.66      8.92
Std.Dev.Daily.PL.1    144.50    205.32    156.16    147.47    148.73
Ann.Sharpe             -0.05     -0.19     -0.07     -0.54     -0.44
Max.Drawdown        -2594.56  -4926.69  -3641.30  -4719.71  -4105.55
Profit.To.Max.Draw     -0.09     -0.25     -0.10     -0.50     -0.53
Avg.WinLoss.Ratio       0.93      0.85      0.95      0.94      0.89
Med.WinLoss.Ratio       0.96      0.96      0.81      1.00      1.01
Max.Equity           2143.41   2447.92   1925.63    928.36    915.55
Min.Equity          -2540.25  -3809.54  -1715.67  -3791.36  -3190.00
End.Equity           -230.79  -1209.81   -348.15  -2362.66  -2162.86

                         EWY       EWZ       EZU       IEF       IGE
Total.Net.Profit    -2694.52  -2396.04    859.85   3933.39   1868.20
Total.Days            462.00    414.00    522.00    550.00    511.00
Winning.Days          234.00    191.00    272.00    301.00    266.00
Losing.Days           228.00    223.00    250.00    249.00    245.00
Avg.Day.PL             -5.83     -5.79      1.65      7.15      3.66
Med.Day.PL              2.90    -12.33     13.44     17.64      8.26
Largest.Winner        623.44    473.66    535.69    557.51    498.52
Largest.Loser        -770.87   -395.63   -693.00   -532.96   -700.75
Gross.Profits       25858.43  18710.54  30720.38  34230.37  30004.54
Gross.Losses       -28552.95 -21106.58 -29860.53 -30296.98 -28136.35
Std.Dev.Daily.PL      154.74    123.77    154.44    150.09    148.66
Percent.Positive       50.65     46.14     52.11     54.73     52.05
Percent.Negative       49.35     53.86     47.89     45.27     47.95
Profit.Factor           0.91      0.89      1.03      1.13      1.07
Avg.Win.Day           110.51     97.96    112.94    113.72    112.80
Med.Win.Day            85.46     78.98     91.36     95.75     94.04
Avg.Losing.Day       -125.23    -94.65   -119.44   -121.67   -114.84
Med.Losing.Day        -99.45    -75.00    -85.26    -96.03    -93.28
Avg.Daily.PL           -5.83     -5.79      1.65      7.15      3.66
Med.Daily.PL            2.90    -12.33     13.44     17.64      8.26
Std.Dev.Daily.PL.1    154.74    123.77    154.44    150.09    148.66
Ann.Sharpe             -0.60     -0.74      0.17      0.76      0.39
Max.Drawdown        -3796.28  -3435.45  -2154.32  -2166.31  -3368.63
Profit.To.Max.Draw     -0.71     -0.70      0.40      1.82      0.55
Avg.WinLoss.Ratio       0.88      1.03      0.95      0.93      0.98
Med.WinLoss.Ratio       0.86      1.05      1.07      1.00      1.01
Max.Equity            738.72    180.61   1947.51   4512.77   1868.20
Min.Equity          -3057.56  -3254.84  -1321.26   -576.85  -1540.72
End.Equity          -2694.52  -2396.04    859.85   3933.39   1868.20

                         IYR       IYZ       LQD       RWR       SHY
Total.Net.Profit     3676.71   1141.84   3885.88   2503.47   2087.28
Total.Days            585.00    529.00    608.00    582.00    481.00
Winning.Days          318.00    280.00    330.00    317.00    268.00
Losing.Days           267.00    249.00    278.00    265.00    213.00
Avg.Day.PL              6.28      2.16      6.39      4.30      4.34
Med.Day.PL             13.80     11.00     13.47     15.00     32.73
Largest.Winner        337.12    656.22    500.32    373.09    317.45
Largest.Loser        -553.69   -486.69   -917.90   -653.56   -379.80
Gross.Profits       29882.84  30424.77  34069.25  30740.26  24931.27
Gross.Losses       -26206.13 -29282.93 -30183.37 -28236.79 -22843.99
Std.Dev.Daily.PL      126.57    148.05    141.44    133.93    118.12
Percent.Positive       54.36     52.93     54.28     54.47     55.72
Percent.Negative       45.64     47.07     45.72     45.53     44.28
Profit.Factor           1.14      1.04      1.13      1.09      1.09
Avg.Win.Day            93.97    108.66    103.24     96.97     93.03
Med.Win.Day            75.05     86.40     88.16     80.63     76.66
Avg.Losing.Day        -98.15   -117.60   -108.57   -106.55   -107.25
Med.Losing.Day        -76.33    -91.42    -79.49    -79.00    -84.65
Avg.Daily.PL            6.28      2.16      6.39      4.30      4.34
Med.Daily.PL           13.80     11.00     13.47     15.00     32.73
Std.Dev.Daily.PL.1    126.57    148.05    141.44    133.93    118.12
Ann.Sharpe              0.79      0.23      0.72      0.51      0.58
Max.Drawdown        -3147.23  -3389.11  -2718.68  -3029.80  -1495.18
Profit.To.Max.Draw      1.17      0.34      1.43      0.83      1.40
Avg.WinLoss.Ratio       0.96      0.92      0.95      0.91      0.87
Med.WinLoss.Ratio       0.98      0.95      1.11      1.02      0.91
Max.Equity           4203.56   2147.66   4370.31   2604.67   2311.64
Min.Equity           -651.36  -3389.11   -594.50  -1612.16   -776.36
End.Equity           3676.71   1141.84   3885.88   2503.47   2087.28

                         TLT       XLB       XLE       XLF       XLI
Total.Net.Profit     4188.91   1525.85   3015.76   3312.93   5243.37
Total.Days            471.00    542.00    558.00    558.00    610.00
Winning.Days          249.00    284.00    287.00    296.00    340.00
Losing.Days           222.00    258.00    271.00    262.00    270.00
Avg.Day.PL              8.89      2.82      5.40      5.94      8.60
Med.Day.PL             11.18      8.22      6.80      9.76     17.38
Largest.Winner        862.40    403.44    588.27    479.24    546.63
Largest.Loser       -1060.30   -512.20   -714.31   -517.42   -683.29
Gross.Profits       35487.37  29115.38  32217.78  30912.49  35259.17
Gross.Losses       -31298.46 -27589.53 -29202.02 -27599.56 -30015.80
Std.Dev.Daily.PL      196.23    133.68    149.57    138.38    140.46
Percent.Positive       52.87     52.40     51.43     53.05     55.74
Percent.Negative       47.13     47.60     48.57     46.95     44.26
Profit.Factor           1.13      1.06      1.10      1.12      1.17
Avg.Win.Day           142.52    102.52    112.26    104.43    103.70
Med.Win.Day           102.88     87.66     92.71     78.10     83.42
Avg.Losing.Day       -140.98   -106.94   -107.76   -105.34   -111.17
Med.Losing.Day       -109.53    -80.39    -75.03    -79.98    -86.96
Avg.Daily.PL            8.89      2.82      5.40      5.94      8.60
Med.Daily.PL           11.18      8.22      6.80      9.76     17.38
Std.Dev.Daily.PL.1    196.23    133.68    149.57    138.38    140.46
Ann.Sharpe              0.72      0.33      0.57      0.68      0.97
Max.Drawdown        -3113.33  -2417.02  -3108.53  -2224.39  -2732.59
Profit.To.Max.Draw      1.35      0.63      0.97      1.49      1.92
Avg.WinLoss.Ratio       1.01      0.96      1.04      0.99      0.93
Med.WinLoss.Ratio       0.94      1.09      1.24      0.98      0.96
Max.Equity           5888.75   1525.85   3093.03   3738.75   5376.79
Min.Equity           -164.65  -1872.16   -225.30  -1541.00  -1030.57
End.Equity           4188.91   1525.85   3015.76   3312.93   5243.37

                         XLK       XLP       XLU       XLV       XLY
Total.Net.Profit     3958.91   4574.37   4589.84   9011.73   3926.53
Total.Days            602.00    626.00    601.00    654.00    630.00
Winning.Days          330.00    336.00    327.00    364.00    336.00
Losing.Days           272.00    290.00    274.00    290.00    294.00
Avg.Day.PL              6.58      7.31      7.64     13.78      6.23
Med.Day.PL             16.15     13.40     19.19     16.24     14.51
Largest.Winner        499.03    497.92    355.08    621.11    421.68
Largest.Loser        -683.59   -531.11   -428.09   -537.68   -741.65
Gross.Profits       34611.16  36209.36  33101.33  41438.23  34800.70
Gross.Losses       -30652.25 -31634.99 -28511.49 -32426.50 -30874.17
Std.Dev.Daily.PL      145.31    141.74    129.06    150.70    138.44
Percent.Positive       54.82     53.67     54.41     55.66     53.33
Percent.Negative       45.18     46.33     45.59     44.34     46.67
Profit.Factor           1.13      1.14      1.16      1.28      1.13
Avg.Win.Day           104.88    107.77    101.23    113.84    103.57
Med.Win.Day            83.12     85.88     81.77     88.52     82.08
Avg.Losing.Day       -112.69   -109.09   -104.06   -111.82   -105.01
Med.Losing.Day        -83.28    -79.67    -81.43    -78.85    -73.23
Avg.Daily.PL            6.58      7.31      7.64     13.78      6.23
Med.Daily.PL           16.15     13.40     19.19     16.24     14.51
Std.Dev.Daily.PL.1    145.31    141.74    129.06    150.70    138.44
Ann.Sharpe              0.72      0.82      0.94      1.45      0.71
Max.Drawdown        -3054.65  -2491.74  -1541.47  -2349.89  -2867.34
Profit.To.Max.Draw      1.30      1.84      2.98      3.83      1.37
Avg.WinLoss.Ratio       0.93      0.99      0.97      1.02      0.99
Med.WinLoss.Ratio       1.00      1.08      1.00      1.12      1.12
Max.Equity           4217.59   5106.00   5446.02   9151.52   4598.69
Min.Equity          -1749.61  -1029.25   -898.16   -380.08  -1603.57
End.Equity           3958.91   4574.37   4589.84   9011.73   3926.53

Basically, it seems that while the domestic side of equities seemed to do fairly well, the international side of the trade really hurt. For once, the perils of diversification are apparent. Here are the portfolio statistics:

> SharpeRatio.annualized(portfRets)
                                     [,1]
Annualized Sharpe Ratio (Rf=0%) 0.4074399
> Return.annualized(portfRets)
                        [,1]
Annualized Return 0.04448638
> maxDrawdown(portfRets)
[1] 0.1439702

So, ho-hum annualized returns, but the Sharpe Ratio at this point is far from stellar, and the drawdowns are definitely disappointing. Here is the resultant equity curve comparison.

out of sample equity curve

In other words, it kept pace with the S&P 500 up until the beginning of 2013 (when in fact, their performance was just about equal), with less violent (but longer-lasting) drawdowns. Unfortunately, starting in mid-2013, the S&P 500 roared away, while the strategy just seemed to be flat, at the new equity high. This would probably anger quite a few investors. Let’s check out an equity curve from a losing security and see what happened.


#Individual instrument equity curve
chart.Posn(portfolio.st, "EWJ")
tmp <- TVI(Cl(EWJ), period=period, delta=delta, triggerLag=1)
add_TA(tmp$vigor, lwd=3)
add_TA(tmp$trigger, on=5, col="red", lwd=1.5)
tmp2 <- lagATR(HLC=HLC(EWJ), n=period)
add_TA(tmp2$atr, col="blue", lwd=2)

In short, Trend Vigor as an entry indicator seems to be like the rest of the usual trend following indicators. Susceptible to buying tops and getting caught in cycles, gives back open equity (see that long trade in 2012-2013), takes counter-trend trades, but essentially at the price of incorporating a bunch of mathematics far more complex than the usual SMAs and EMAs, and at best, to marginally better results.

So does this mean that there was absolutely nothing to take away from this?

Well, no. Trend Vigor is something that can be added to a repertoire of indicators. Unlike the usual SMA and EMA fare, the advantage that Trend Vigor has is that over longer time horizons, assuming gradual trends (as opposed to the steep drops in things such as equities), is that it still can get things right more often than not, and when it does, the average win to loss profile is usually impressive. However, a marginal improvement over the basics isn’t what trend vigor was originally advertised to be, so much as a reliable market mode indicator. And in a trending environment, even your basic SMA crossovers will make money. In an oscillating environment, even the most basic RSI plug-and-chug will make money. The key, of course, is to try and find an indicator that will tell you what to deploy when. And for that purpose, unlike what was advertised in the original John Ehlers presentation, Trend Vigor, as far as this investigation has led me, is *not* that.

Thanks for reading.