In the last post, I introduced the concept of viewing momentum as  a probability of one asset outperforming the other versus a binary decision driven by whichever return is greater between a pair of assets. This method incorporates the joint distribution between two assets that factors in their variance and covariance. The difference in the two mean returns are compared to the tracking error between two assets to compute the information ratio. This ratio is then converted to a probability via the t-distribution to provide a more intelligent confidence-based buffer to avoid costly switching.  A good article by Corey Hoffstein at Newfound discusses a related concept here.  Many readers have inquired about a spreadsheet example for probabilistic momentum which can be found here : probabilistic momentum worksheet.

1. February 12, 2014 12:06 pm

hi David, thanks for sharing. few questions:
1. in the degree of freedom, you used 60. however you mentioned earlier it is lookback period minus 1, right?
2. when i set T-stat < 0, the probability calculation shows error.
thanks!

2. February 12, 2014 12:33 pm

David, here’s a version that lets you tinker with rolling values. You can compare results easily for different values of window length and smoothing. (Sorry, don’t see how to post a file).

Victor

3. February 12, 2014 5:02 pm

Is the Information Ratio calculated in the spreadsheet?

4. February 13, 2014 9:01 am

Thanks David – I’m looking forward to playing around with it.

5. February 15, 2014 6:41 am

I still don’t get the same backtest. The standard momentum strategy does better. When did start your backtest?

• February 17, 2014 11:16 am

I have tried to replicate the research in excel and R (I know both very well) without any luck – not even close to the returns presented here.

It seems no one can replicate the results, which can only mean 2 things:

1. The backtest and the results are incorrect – can easily happen to anyone considering even a tiny mistake can have a drastic impact on end results.

2. The formula presented does not disclose the whole strategy – again all it takes is for one small detail to be left out which will make it very difficult to replicate the results.

The spreadsheet presented is also inconsistent with the explanation in the first article and will not allow to confirm the results…

Either way, I am suspect now having seen the lack of feedback!!

Someone please shed light on us all! 🙂

• February 17, 2014 4:25 pm

Hi James,
I’ll post what I’ve got so far tomorrow or the day after so that we can compare our understandings.

• February 18, 2014 5:43 am

Here is my code in R.

Strategy.ProbaMomentum = function (tickers=c(“SPY”,”TLT”),keep=2,perf.days=60,switch.threshold=0.6,chart=”n”) {
nb.tickers = length(tickers)
data = new.env()
getSymbols(tickers,src=”yahoo”,from=”2009-12-30″,env=data,auto.assign=T)
i = 1
tmp = data[[tickers[i]]][,paste(tickers[i],”.Close”,sep=””)]
for (i in 2:nb.tickers) {
tmp = cbind(tmp,data[[tickers[i]]][,paste(tickers[i],”.Close”,sep=””)])
}
tmp = tmp[“2010-09-30::”,]
tmp = tmp[!is.na(rowSums(tmp)),]
tmp.perfs = ROC(tmp,type=”discrete”)
tmp.weights = tmp.perfs
tmp.weights[] = 0
tmp.weights.momentum = tmp.weights
colnames(tmp.weights) = paste(tickers,”.weights”,sep=””)
tmp.perfs.momentum = ROC(tmp,n=perf.days,type=”discrete”)
tmp.ir = tmp.perfs[,1]
tmp.ir[] = NA
colnames(tmp.ir) = “InformationRatio”
for (i in (perf.days+1):nrow(tmp.perfs)) {
tmp.ir[i] = InformationRatio(tmp.perfs[(i-perf.days+1):i,1],tmp.perfs[(i-perf.days+1):i,2],scale=60)
if (tmp.perfs.momentum[i,1]>tmp.perfs.momentum[i,2]) {
tmp.weights.momentum[i,1] = 1
} else {
tmp.weights.momentum[i,2] = 1
}
}
tmp.proba = pt(tmp.ir,df=perf.days-1)
tmp.weights[tmp.proba>switch.threshold,1] = 1
tmp.weights[tmp.proba<(1-switch.threshold),2] = 1
tmp.weights = lag(tmp.weights,1)
tmp.weights[1,] = 0
tmp.weights.momentum = lag(tmp.weights.momentum,1)
tmp.weights.momentum[1,] = 0
tmp.ret = ROC(tmp,type="discrete")
tmp.ret[1,] = 0
strat = xts(rowSums(tmp.ret*tmp.weights),order.by=index(tmp.ret))
strat = cumprod(1+strat)
colnames(strat) = "Strat"
strat.bench = xts(rowSums(tmp.ret*tmp.weights.momentum),order.by=index(tmp.ret))
strat.bench[1,]=0
strat.bench = cumprod(1+strat.bench)
colnames(strat.bench) = "Bench"
if (chart=="y") {
par(mar=c(2,2,1,2))
layout(rbind(c(1,1),c(1,1),c(2,2),c(3,3),c(4,4)))
plot(as.Date(time(strat)),coredata(strat),type="l",main="Rotation",cex.main=0.65,cex.axis=0.65,xaxt="n",ylab="",xlab="")
axis.dates = seq(as.Date(first(time(strat))),as.Date(last(time(strat))),length.out=8)
axis.Date(1,at=axis.dates,labels=TRUE,cex.axis=0.65,format="%d-%b-%Y")
abline(v=axis.dates,col="lightgray")
lines(as.Date(time(strat.bench)),coredata(strat.bench),col="red",lty="dashed")
barplot(tmp.weights[,1],main="",cex.main=0.65,cex.axis=0.65,xaxt="n",ylab="",xlab="")
barplot(tmp.weights.momentum[,1],main="",cex.main=0.65,cex.axis=0.65,xaxt="n",ylab="",xlab="")
plot.table(as.matrix(table.CalendarReturns(ROC(strat[endpoints(strat),],type="discrete"))),text.cex=0.75)
}
return(cbind(strat,strat.bench,tmp.weights))
}

Backtesting from Sep 2010, I get a CAGR of 10.3% on this probabilistic momentum and a CAGR of 13.7% for the normal momentum strategy.

Do you get similar results?

6. 