Market Returns Are Log Normal After All!

If you are a technical trader, I will go over R-Language code to show you how you can convince yourself of this painful fact. I know it is hard for "successful" technical analysts and hedge fund managers to accept it, let alone admit it, but the reality is this is the nature of the beast, and if you want to tame the beast, you might as well know its nature!
I would not be wasting your time nor mine if I did not believe what I post in my personal blog, about the ability to detect patterns in the market, but the moral here is that if you look at a long enough horizon, then the Nobel Laureates and Finance Professors are right and the market is pretty much behaving like a log-normal stochastic process. That is, if you are in for the long-run (call it buy and hold), then your periodic returns will be as random as repeatedly spinning a (slightly biased) roulette wheel!
We use the R-Language for this exercise. And even though I am more comfortable with Java, and have been since its inception almost 20 years ago, but I think that the wealth of ready code and libraries in R-Language make it my recommended tool for those who want to become Quants.
If you have not downloaded and installed R, then you should do so at their website. I normally use R-Studio, which makes R a bit easier to use. To download that, go to the R-Studio website. If these are not already on your system, then you really need to familiarize yourself with it before you dive into the remainder of this post, as straightforward as this particular code is.
Create an R-File and put this function in it:
[code language="R"]
evaluateSymbol <- function(data.Frame, period)
{
nPoints <- nrow(data.Frame)
symbolSkewness <- 0*c(1:(nPoints-period-1))
dates<-as.Date(rownames(data.Frame))[1:nPoints,drop=FALSE]
log.Return<- as.numeric(log(data.Frame1[1:(nPoints-1),1]/
data.Frame[2:nPoints,1]))
cumReturn <- cumsum(log.Return[(nPoints-1):1])[(nPoints-1):1]
# plot log return histogram
increment<- 0.001
hist(log.Return, breaks=seq(from=(min(log.Return)-increment), to=(max(log.Return)+increment),
by=increment),
main=c("Daily Log Return Histogram since ", toString(dates[nPoints])))
#calculate skewness on a day by day sliding scale of size "period"
for (i in 1:(nPoints-period-1))
{
lowerRange = i;
upperRange = i+period;
symbolSkewness[i] = skewness(log.Return[lowerRange:upperRange])
}
# plot hist to check that the skewness is centered around zero,
# otherwise the period is too long or too short
hist(symbolSkewness, breaks=seq(from=(min(symbolSkewness)-increment),
to=(max(symbolSkewness)+increment),
by=increment),
main=c("LR Skewness for Period = ", toString(period)))
# plot the skewness
plotPeriod <- (nPoints-period-1)
plot(dates[1:plotPeriod], symbolSkewness[1:plotPeriod], type="l",
panel.first=grid(25,25),
main=c("LR Skewness for Period = ", toString(period)))
# run the correlation between the log return and the skewness
ccfResult <- ccf(symbolSkewness[1:(nPoints-period-1)],
log.Return[1:(nPoints-period-1)],
type = "correlation",
main=c("Skewness-LR Correlation for Period = ", toString(period)))
# figure out the best lag associated with the correlation (most positive)
maxIndex <- which.max(abs(ccfResult[["acf"]][ccfResult[["lag"]]>=0]))
maxCC <- (ccfResult[["acf"]][ccfResult[["lag"]]>=0])[maxIndex]
maxLag <- (ccfResult[["lag"]][ccfResult[["lag"]]>=0])[maxIndex]
# replot the lagged skewness
plot(dates[maxLag:(maxLag+plotPeriod-1)+1],
symbolSkewness[1:plotPeriod], type="l",
panel.first=grid(10,10),
main=c("Lagged LR Skewness for Period with Lag= ", toString(maxLag),
"and Correlation= ", toString(maxCC)))
return (symbolSkewness)
}
[/code]
You will need to source your file to be able to run the function.
Once you have sourced your file and it is available to the R-language, then you can will need to run the following script. Note that to run the script you will need to have made the requested libraries available in R. See the R-Language help to do that, or use R-Studio.
The script below simply loads the libraries, then downloads the historic quotes for the symbol SPY. Then it calls the above function which computes the daily log returns, their skewness and plot them to show you how "normal" the log return series is.
[code language="R"]
library(quantmod);
library(tseries);
library(zoo);
library(PerformanceAnalytics);
library(caTools);
ticker <- "SPY"
####GET ADJUSTED CLOSE AND ORDER CORRECTLY
symbol<- get.hist.quote(ticker, quote="Adj", retclass="zoo")
symbol<-as.data.frame(symbol)[NROW(symbol):1,,drop=FALSE]
symbol<-na.locf(symbol, na.rm=FALSE)
evaluateSymbol(symbol, 100)
[/code]
The one graph you really need to pay special attention to is the histogram for the daily log-returns:
Enlightening, isn't it?
Disclosure: I have no positions in any stocks mentioned, and no plans to initiate any positions within the next 72 hours.
Seeking Alpha's Disclosure: Past performance is no guarantee of future results. No recommendation or advice is being given as to whether any investment is suitable for a particular investor. Any views or opinions expressed above may not reflect those of Seeking Alpha as a whole. Seeking Alpha is not a licensed securities dealer, broker or US investment adviser or investment bank. Our analysts are third party authors that include both professional investors and individual investors who may not be licensed or certified by any institute or regulatory body.