Trend Following – Skewness Signal

Oxford Dictionary Definition of a trend

  • noun: a general direction in which something is developing or changing
Often people try to capture a trend using technical indicators such as moving average cross overs. If there is a trend this technique is generally good at capturing it. However if there isn’t a trend it becomes difficult to identify side ways markets with moving averages (see http://gekkoquant.com/2012/08/29/parameter-optimisation-backtesting-part-2/ moving averages performing excellently well during 2009 when there was a real trend).

Is there a better way to identify a trend? Potentially. A common modelling assumption in finance is to say that stock returns follow Brownian motion and that over short periods the mean return is 0, or in other words daily returns are Gaussian distributed with 0 mean.

This post will use the same assumption, asset returns are Guassian and hence returns are symmetrically distributed around their mean (assume 0 mean). Symmetric distributions have a skewness of 0, any deviation from 0 indicates that one of the tails is beginning to get bigger and possibly that the stock is trending.

Skewness is a measure of assymetery for a probability distribution, the skewness tells us the amount and direction of the skew. This strategy will take a rolling distribution of returns and calculate the skew for those returns. The skew will then be used to take trades.

Strategy:
  • If skewness > UptrendSkewLimit then go Long
  • If skewness < DowntrendSkewLimit then go Short
  • If skewness between UptrendSkewLimit and DowntrendSkewLimit then returns are symmetrical, it’s a sideways market do nothing
 

 

Trend Following – Annualized Sharpe Ratio (Rf=0%) 0.7162438

S&P500 Long Open Close Returns – Annualized Sharpe Ratio (Rf=0%) 0.2129997

?View Code RSPLUS
library("quantmod")
library("PerformanceAnalytics")
library(e1071)     #For the skewness command
 
#Model paramters
nLookback <- 30 #When calculating the rolling skew use the nLookback number of days
UptrendSkewLimit <- 0.3 #If the rolling skew is greater than this value go long
DowntrendSkewLimit <- -0.5 #If the rolling skew is lower than this value go short
#If the rolling skew is between the two limits do nothing, the skew is too weak to indicate a trend
 
#Script parameters
symbol <- "^GSPC"     #Symbol
 
#Specify dates for downloading data
startDate = as.Date("2005-01-01") #Specify what date to get the prices from
symbolData <- new.env() #Make a new environment for quantmod to store data in
getSymbols(symbol, env = symbolData, src = "yahoo", from = startDate)
mktdata <- eval(parse(text=paste("symbolData$",sub("^","",symbol,fixed=TRUE))))
mktdata <- head(mktdata,-1) #Hack to fix some stupid duplicate date problem with yahoo
dayOpClRet <- Cl(mktdata)/Op(mktdata)    - 1
cat("About to calculate the rolling skew")
 
#Lets calculate the rolling skew
#Lag the rolling skew by one day so the skew measured at the close of day T is sifted to day T+1
#The skew will be used to determine the trade at the open
rollingSkew <- Lag(rollapply(dayOpClRet,FUN="skewness",width=nLookback, align="right"),1)
 
#Possible improvement - Do a exponential moving average on the skew signal to smooth it
#rollingSkew <- EMA(rollingSkew,n=nLookback)
 
longSignals <- (rollingSkew>UptrendSkewLimit)
longReturns <- longSignals*dayOpClRet
shortSignals <- (rollingSkew<DowntrendSkewLimit)
shortReturns <- -1*shortSignals*dayOpClRet
totalReturns <- longReturns + shortReturns
#Uncomment the line below to increase the position size for larger skews
#totalReturns <- totalReturns * (abs(rollingSkew)+1)
totalReturns[is.na(totalReturns)] <- 0
 
GEKKORed <- rgb(255/255, 0/255, 0/255, 0.1)
GEKKOGreen <- rgb(0/255, 255/255, 0/255, 0.1)
 
dev.new()
par(mfrow=c(3,1))
plot(Cl(mktdata), main="Close of S&P 500")
lines(longSignals*max(Cl(mktdata)), col=(GEKKOGreen),type="h")
lines(shortSignals*max(Cl(mktdata)), col=(GEKKORed),type="h")
plot(rollingSkew)
abline(UptrendSkewLimit,0,col="green")
abline(DowntrendSkewLimit,0,col="red")
plot(cumsum(totalReturns), main="Cumulative Returns - Trend Following")
 
 
#### Performance Analysis ###
colnames(dayOpClRet) <- "Long IndexOpCloseRet"
zooTradeVec <- cbind(as.zoo(totalReturns),as.zoo(dayOpClRet)) #Convert to zoo object
colnames(zooTradeVec) <- c("Trend Following","S&P500 Long Open Close Returns")
zooTradeVec <- na.omit(zooTradeVec)
#Lets see how all the strategies faired against the index
dev.new()
charts.PerformanceSummary(zooTradeVec,main="Performance of Trend Following",geometric=FALSE)
 
#Calculate the sharpe ratio
cat("Sharpe Ratio")
print(SharpeRatio.annualized(zooTradeVec))

12 thoughts on “Trend Following – Skewness Signal

  1. Pingback: Trend Following – Skewness Signal | European Edges

  2. I simulated the numbers myself and it looks like that it only works from 2005 and on the sp500… i’d suggest you test on longer periods (from 1990) and on various assets ( like the 60 most liquid futures). The picture is then a *lot* different. Only my two cents. Happy research!

  3. Interesting post – one issue: skewness measures deviations around the mean. So if i have returns clustered around positive mean, still my price trend will be upward, while skew can be anything. How do you account for the mean in your case, or do you assume it’s 0, when calculating skew?

    • That is correct, skewness is about asymmetry around the mean. If the mean is large and positive and the skew negative then potentially the returns are skewed to small positive returns.

      If you are short and the market has small positive returns it’s not a sizeable hit to p&l.

      My assumption that mean open close returns are zero could be poor if using a small look back period.

  4. Judging by the poor Sharpe ratio and poor long term results (according pietro) then maybe using skewness for trend direction is misguided.

    I’m tempted to revisit this idea in a new post. This time using the skew as a regime indicator. A skew close to zero implies sideways market use a mean reversion strategy, and large absolute skew suggests a trend following model.

  5. Pingback: Skewness Revisited | Gekko Quant – Quantitative Trading

  6. Hi,

    Great blog post, thanks.

    When I try to run, I get an error:
    > rollapply(dayOpClRet,FUN=”skewness”,width=nLookback, align=”right”)
    Error: evaluation nested too deeply: infinite recursion / options(expressions=)?

    Any ideas why this might happen?

    Best,
    Sam

  7. Fantastic blog! thank you for such lucid explanations, not available ANYwhere!

    Question: does this (trend following) also use low latency HFT technique? is latency an issue in stat arb and trend following

Leave a Reply to Mike Cancel reply

Your email address will not be published. Required fields are marked *