Strategy Library

Time Series Momentum Effect

Introduction

Time series momentum is related to, but different from the phenomenon known as “momentum” in the finance literature, which is primarily cross-sectional in nature. The momentum literature focuses on the relative performance of securities in the cross section, finding that securities that recently outperformed their peers over the past 3 to 12 months continue to do so on average over the next month. Rather than focus on the relative returns of securities in the cross section, this time series momentum strategy focuses purely on the past returns of each individual futures contract. Every month, the investor considers whether the excess return of each asset over the past 12 months is positive or negative and goes long on the contract if it is positive and short if negative. The position size is set to be inversely proportional to the volatility of the security's returns. A univariate GARCH model could be used to estimate volatility. However, other simple models could probably be easily used with good results (for example, the easiest one would be using historical volatility). For the sake of simplicity, we will use historical volatility. The portfolio is rebalanced monthly.

Method

As the strategy needs the continuous futures contract, we import the custom data from Quandl. We create a universe of highly liquid commodity futures that are traded on CME, ICE and CBOT (more contracts can be added using the Quandl API). We will use Quandl's daily, non-adjusted price data, which is based on spot-month continuous contract calculations.

The first step is importing the data.

from QuantConnect.Python import PythonQuandl
for symbol in self.symbols  :
    self.AddData(QuandlFutures, symbol, Resolution.Daily)

class QuandlFutures(PythonQuandl):
    def __init__(self):
        self.ValueColumnName = "Settle"

Here we use a 12-month RateOfChange(period) indicator to simulate the momentum returns. All indicators are saved in the dictionary self.roc.

self.period = 252
self.roc = {}
for symbol in self.symbols:
  self.roc[symbol] = self.ROC(symbol, self.period)   #Initialize ROC indicator : ROC is short for RateofChange

We use history request to obtain historical prices. Here, history is the daily returns, which will be used to calculate volatilities.

        history = self.History(self.symbols, self.period, Resolution.Daily)
        history = history.value.unstack(level=0).pct_change().dropna()
    

Then we calculate the historical volatilities and place orders. Note that the weights are inversely proportional to volatilities and np.sign determines whether to long or short.

        vol_inv = 1 / history.std(ddof=1)
        vol_sum = vol_inv.sum()
        weights = (vol_inv / vol_sum).fillna(0).to_dict()
        self.Liquidate()
        for symbol, roc in self.roc.items():
            percentage = np.sign(roc.Current.Value) * weights[symbol] *.5
            self.SetHoldings(symbol, percentage)
    

Algorithm

div class="qc-embed-frame" style="display: inline-block; position: relative; width: 100%; min-height: 100px; min-width: 300px;">