Dual Momentum According to Dr. Gary Antonacci
After reading "Dual Momentum Investing" by Dr. Gary Antonacci, I came across the "Quantopian QuantCon 2016" post by Lotanna Ezenwa - https://www.quantopian.com/posts/quantcon-2016-dual-momentum-strategy that offered a great starting point for my own research and backtesting of the strategy (nice work, Lotanna!).
I posted some questions about various details I had to the book's website at http://www.optimalmomentum.com/contact%20us.html, and was pleasantly surprised that Dr. Gary himself had graciously responded within a few hours. During an email thread with him, Dr. Gary clarified that the actual method he himself uses is detailed on page 98 of the printed book (2015 edition), and differs slightly from the flowchart depicted on page 101.
Basically, the first step is to determine absolute momentum by comparing the 12 month return of the SP500 (since the U.S. leads world equity markets) against that of the T-Bills, and if positive - to then compare the relative strengths of the SP500 and the ACWX (Global Stocks ex U.S.) - thus investing in the higher performing of the two. Otherwise, one would invest in AGG. The actual vehicles to be used can vary although Dr. Gary does have his own recommendations in the book's FAQ - http://www.optimalmomentum.com/faq.html - which contains a bunch of compact gems that really clarify DMI beyond his excellent book (Note: The FAQ webpage has been edited and the recommended ETF vehicles are no longer available. These can be found in Dr. Gary's "Dual Momentum Investing" book - Chapter 8’s “How To Use It” section).
Below is a modified flowchart that I ran by Dr. Gary to confirm for accuracy. This modification produced higher returns against the flowchart on page 101 for the few backtests that I executed. At his suggestion, I'm posting this to Quantopian to pass this on to anyone interested. It actually simplifies the rebalancing logic in my opinion.
The modified (and simplified) logic in my_assign_weights(context, data) looks like:
if returns[context.us] < returns[context.tbill]:
context.weights[context.agg] = 1
elif returns[context.us] > returns[context.worldExUS]:
context.weights[context.us] = 1
else:
context.weights[context.worldExUS] = 1
context.weights.fillna(0,inplace=True)
This logic also rebalances entirely to AGG when absolute momentum dictates it.