Investors face a number of challenges when seeking to estimate the prospective performance of a long-only investment in commodity futures. For instance, historically, the average annualized excess return of individual commodity futures has been approximately zero and commodity futures returns have been largely uncorrelated with one another. However, the prospective annualized excess return of a rebalanced portfolio of commodity futures can be equity-like. Certain security characteristics, such as the term structure of futures prices, and some portfolio strategies have historically been rewarded with above average returns. Avoiding naïve extrapolation of historical returns and striking a balance between dependable sources of return and possible sources of return is important. This is the unabridged version of our 2006 publication in the Financial Analysts Journal.
Keywords: Strategic asset allocation, Tactical asset allocation, Diversification return, Roll return, Momentum, Market timing, Convenience yield, Contango, Backwardation, Normal backwardation, Commodity correlation, Commodity risk factors, Commodity term structure, Trading strategies, Overlay strategies
- The investment universe is all commodity futures contracts.
- The approach involves buying the top quintile (20%) of commodities exhibiting the highest roll-returns.
- Simultaneously, sell short the bottom quintile (20%) of commodities showing the lowest roll-returns.
- Maintain the long-short positions for a duration of one month.
- Contracts in each quintile are equally-weighted.
import backtrader as bt class RollRankStrategy(bt.Strategy): def __init__(self): # divide investment universe into quintiles self.quintiles = 5 # get the number of contracts in each quintile self.contracts_per_quintile = len(self.datas) // self.quintiles # get the length of roll period self.roll_period = 30 # assuming the trading period is 1 month # calculate the number of contracts to trade long/short self.num_longs_shorts = self.contracts_per_quintile // 5 def next(self): # calculate roll-returns for all contracts roll_returns = [d.close[-1] / d(-self.roll_period).close for d in self.datas] # sort contracts by roll-returns ranks = sorted(range(len(roll_returns)), key=lambda x: roll_returns[x]) # go long on top 20% of contracts for i in ranks[-self.num_longs_shorts:]: self.buy(datalist[i], size=1/self.num_longs_shorts) # go short on bottom 20% of contracts for i in ranks[:self.num_longs_shorts]: self.sell(datalist[i], size=1/self.num_longs_shorts)
Note: This is just an example, and the code may need to be adjusted based on the specifics of the commodity futures contracts being traded.