Home > Asset Allocation, Backtesting, R, Strategy > Probabilistic Momentum

Probabilistic Momentum

David Varadi has recently discussed an interesting strategy in the
Are Simple Momentum Strategies Too Dumb? Introducing Probabilistic Momentum post. David also provided the Probabilistic Momentum Spreadsheet if you are interested in doing computations in Excel. Today I want to show how you can test such strategy using the Systematic Investor Toolbox:

###############################################################################
# Load Systematic Investor Toolbox (SIT)
# https://systematicinvestor.wordpress.com/systematic-investor-toolbox/
###############################################################################
setInternet2(TRUE)
con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb'))
    source(con)
close(con)
	#*****************************************************************
	# Load historical data
	#****************************************************************** 
	load.packages('quantmod')
		
	tickers = spl('SPY,TLT')
		
	data <- new.env()
	getSymbols(tickers, src = 'yahoo', from = '1980-01-01', env = data, auto.assign = T)
		for(i in ls(data)) data[[i]] = adjustOHLC(data[[i]], use.Adjusted=T)
	bt.prep(data, align='remove.na', dates='2005::')
 
	
	#*****************************************************************
	# Setup
	#****************************************************************** 
	lookback.len = 60
	
	prices = data$prices
	
	models = list()
	
	#*****************************************************************
	# Simple Momentum
	#****************************************************************** 
	momentum = prices / mlag(prices, lookback.len)
	data$weight[] = NA
		data$weight$SPY[] = momentum$SPY > momentum$TLT
		data$weight$TLT[] = momentum$SPY <= momentum$TLT
	models$Simple  = bt.run.share(data, clean.signal=T) 	

The Simple Momentum strategy invests into SPY if SPY’s momentum if greater than TLT’s momentum, and invests into TLT otherwise.

	#*****************************************************************
	# Probabilistic Momentum
	#****************************************************************** 
	confidence.level = 60/100
	ret = prices / mlag(prices) - 1 

	ir = sqrt(lookback.len) * runMean(ret$SPY - ret$TLT, lookback.len) / runSD(ret$SPY - ret$TLT, lookback.len)
	momentum.p = pt(ir, lookback.len - 1)
		
	data$weight[] = NA
		data$weight$SPY[] = iif(cross.up(momentum.p, confidence.level), 1, iif(cross.dn(momentum.p, (1 - confidence.level)), 0,NA))
		data$weight$TLT[] = iif(cross.dn(momentum.p, (1 - confidence.level)), 1, iif(cross.up(momentum.p, confidence.level), 0,NA))
	models$Probabilistic  = bt.run.share(data, clean.signal=T) 	

The Probabilistic Momentum strategy is using Probabilistic Momentum measure and Confidence Level to decide on allocation. Strategy invests into SPY if SPY vs TLT Probabilistic Momentum is above Confidence Level and invests into TLT is SPY vs TLT Probabilistic Momentum is below 1 – Confidence Level.

To make Strategy a bit more attractive, I added a version that can leverage SPY allocation by 50%

	#*****************************************************************
	# Probabilistic Momentum + SPY Leverage 
	#****************************************************************** 
	data$weight[] = NA
		data$weight$SPY[] = iif(cross.up(momentum.p, confidence.level), 1, iif(cross.up(momentum.p, (1 - confidence.level)), 0,NA))
		data$weight$TLT[] = iif(cross.dn(momentum.p, (1 - confidence.level)), 1, iif(cross.up(momentum.p, confidence.level), 0,NA))
	models$Probabilistic.Leverage = bt.run.share(data, clean.signal=T) 	

	#*****************************************************************
	# Create Report
	#******************************************************************    
	strategy.performance.snapshoot(models, T)

plot1

The back-test results look very similar to the ones reported in the Are Simple Momentum Strategies Too Dumb? Introducing Probabilistic Momentum post.

However, I was not able to exactly reproduce the transition plots. Looks like my interpretation is producing more whipsaw when desired.

	#*****************************************************************
	# Visualize Signal
	#******************************************************************        
	cols = spl('steelblue1,steelblue')
	prices = scale.one(data$prices)

	layout(1:3)
	
	plota(prices$SPY, type='l', ylim=range(prices), plotX=F, col=cols[1], lwd=2)
	plota.lines(prices$TLT, type='l', plotX=F, col=cols[2], lwd=2)
		plota.legend('SPY,TLT',cols,as.list(prices))

	highlight = models$Probabilistic$weight$SPY > 0
		plota.control$col.x.highlight = iif(highlight, cols[1], cols[2])
	plota(models$Probabilistic$equity, type='l', plotX=F, x.highlight = highlight | T)
		plota.legend('Probabilistic,SPY,TLT',c('black',cols))
				
	highlight = models$Simple$weight$SPY > 0
		plota.control$col.x.highlight = iif(highlight, cols[1], cols[2])
	plota(models$Simple$equity, type='l', plotX=T, x.highlight = highlight | T)
		plota.legend('Simple,SPY,TLT',c('black',cols))	

plot2

David thank you very much for sharing your great ideas. I would encourage readers to play with this strategy and report back.

To view the complete source code for this example, please have a look at the bt.probabilistic.momentum.test() function in bt.test.r at github.

  1. Donm
    February 17, 2014 at 6:01 am

    Thanks for the code. I posted this on another site last week — “I was playing around with the spreadsheet today and decided to see how it did using the most extreme scenario I could find. I used Silver in 2011 and the ETF’s used was 2X leveraged Silver AGQ and the base security was 2X leveraged S+P 500 SOO. I used a 20 period daily lookback ( using weekly for Silver had some huge swings for this period) and an 80% threshold – above 80% you’re long and below 80% you’re flat. During the initial runup in 2010 and final top in 2011 this did very well. Some of the trades were 1-day in duration, but did better than most technical indicators I could find ( and I’ve looked at this scenario alot). After the collapse in 2011 a few trades did OK but the last 14 trades had only 1 winner and 10 loosers in a row [ go short if threshold>80% ?]( 38 trades in all from 2010 to present). So my initial feeling that this is very good in strong trends, so maybe 1 or 2 filters need to be added for daily data anyway. I could see where if you used a 60% threshold using weekly data you’d still get some false signals”. Is it correct to look at the performance of only 1 of the pairs? Do you think you can use it on more than 2 pairs, like several asset classes?

  2. February 17, 2014 at 11:30 am

    Thank you.
    And if you want be less drastic (allin equity or bond)? For example 60/40 (equity/bond) when it’s equity time and 40/60 (equity/bond) when it’s bond time?

    • February 17, 2014 at 12:43 pm

      The Difference between Simple and Probabilistic Momentum is less in this case

      	#*****************************************************************
      	# Simple Momentum
      	#****************************************************************** 
      	momentum = prices / mlag(prices, lookback.len)
      	
      	signal = momentum$SPY > momentum$TLT
      	
      	data$weight[] = NA
      		data$weight$SPY[] = iif(signal, 60, 40) / 100
      		data$weight$TLT[] = iif(signal, 40, 60) / 100
      	models$Simple  = bt.run.share(data, clean.signal=T) 	
      
      	#*****************************************************************
      	# Probabilistic Momentum
      	#****************************************************************** 
      	ir = sqrt(lookback.len) * runMean(ret$SPY - ret$TLT, lookback.len) / runSD(ret$SPY - ret$TLT, lookback.len)
      	momentum.p = pt(ir, lookback.len - 1)
      		
      	signal = iif(cross.up(momentum.p, confidence.level), 1, iif(cross.dn(momentum.p, (1 - confidence.level)), 0,NA))
      	signal = ifna.prev(signal) == 1
      	
      	data$weight[] = NA
      		data$weight$SPY[] = iif(signal, 60, 40) / 100
      		data$weight$TLT[] = iif(signal, 40, 60) / 100
      	models$Probabilistic  = bt.run.share(data, clean.signal=T) 	
      
          #*****************************************************************
          # Create Report
          #******************************************************************    
          strategy.performance.snapshoot(models, T)
      

      plot3

  3. gevans5585
    February 17, 2014 at 4:06 pm

    Any thoughts on how to apply this to a broader universe for Global Asset Allocation strategies?

  4. February 18, 2014 at 8:23 am

    Interesting question gevans. Considering indicator construction, the most natural way would be a pairwise comparison between asset classes. So we have to calculate “momentum.p” for each asset class with other, then rank these values and finally form portfolio with dominant asset classes (equal weighted or with a weight proportional to “momentum.p”). Not simple translate this in R using SIT.

  5. Gab
    February 18, 2014 at 2:03 pm

    Always interesting, and thanks for sharing!
    Just a question, that applies to this code and also to others: how is it possible to test an algo (in this case the probabilistic momentum, but could be the same with any other model) not between assets, but between strategies? I mean, using the output of different strategies (the equity line) as input for a different strategy? An example: apply the probabilistic momentum to a momentum strategy and an AAA strategy…
    Just in case you believe could be of general interest 🙂
    Thanks 🙂

  6. Pavel
    February 19, 2014 at 3:32 pm

    Taking QQQ instead of SPY produces better results

  7. buxus
    February 27, 2014 at 1:45 pm

    Thanks for this, you are really quick. I was searching for any possible realization of this concept to check of my AA model and it seems it is givin only a slight edge but the leveraged is lagging seriously behind.
    Re applying it to multy assets allocation, I think the simplest would be to first do the ususal ranking by momnetum(and vola) and if the first should be replaced by the new leader this prob check can be done between the old and the new strongest.
    I am saying simplest though it would definitely require SI’s expertise!
    I really appretiate your work SI!

  8. Gerald
    March 3, 2014 at 9:40 pm

    I really like the work on this blog – so much so that I started learning R just so I could access Michael’s work. Thank you!
    I found the idea of probabilistic momentum intellectually appealing but actual execution is all over the place. For example, if you use QQQ and TLT with a 90 day look-back and a 75% confidence interval, the simple momentum model significantly outperforms both the probabilistic and leveraged probabilistic model. CAGR/Sharpe 14.0/.9, 10.5/.7, 8.3/.5 for Simple, Prob and ProbLev respectively. I can produce better results but it appears to be a matter of curve fitting. In reading the comments on David’s excellent blog (thank you David!), it seems that his results there are not reproducible (similar to the the comment earlier in this post). This is not in any way a criticism but is simply an observation regarding the research of others using this idea. I look forward to continued work on this idea.
    Michael, this blog is a MUST READ for me these days. Thank you for your significant contributions!

  9. Marti13
    March 18, 2014 at 1:27 pm

    Hi, Thanks for this nice code. I have translated it to Matlab to play with other assets classes as suggested by tsudent201. Will be back with some results soon. BTW, I get very slightly lower Cumul Perfs but totally in line with results obtained in R.
    Really cool stuff. Thanks!!!

  1. February 17, 2014 at 10:19 pm
  2. March 31, 2014 at 2:57 am
  3. April 1, 2014 at 12:08 am

Leave a comment