Skip to content

Probabilistic Momentum Spreadsheet

February 12, 2014

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.

10 Comments leave one →
  1. Jim permalink
    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. Victor permalink
    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. James permalink
    February 12, 2014 5:02 pm

    Is the Information Ratio calculated in the spreadsheet?

  4. John French permalink
    February 13, 2014 9:01 am

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

  5. Patzoul permalink
    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?

    • James permalink
      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! 🙂

      • patzoul permalink
        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.

      • Patzoul permalink
        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
        write.table(cbind(tmp,tmp.weights,tmp.ir),"~/Google Drive/R/Rotation.csv",sep=",",row.names=index(tmp),col.names=TRUE)
        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. Patzoul permalink
    February 20, 2014 5:29 pm

    You can find the code on http://systematicinvestor.wordpress.com/2014/02/17/probabilistic-momentum/. The point that was not clear in the description from David is that you switch from SPY to TLT or to TLT from SPY once you reach the threshold of 60%, otherwise the weights stay as they are.

Trackbacks

  1. Probabilistic Momentum | Systematic Investor

Leave a comment