Introduction
The term structure of commodities usually refers to the difference between Futures prices of different maturities at a given time point. The shape of the Futures curve is essential to commodity hedgers and speculators as Futures price serves as a forecast of future spot price. The Futures price curve contains the information about Futures supply and demand conditions. This algorithm will examine the role of term structure signals for the design of profitable trading strategies in commodity Futures markets.
Method
Concepts of Futures
Before starting the algorithm, we first introduce a few concepts related to commodity Futures. Backwardation is defined as conditions when the Futures price is below the current spot price and contango as conditions when the Futures price is above the current spot price. The Futures term structure curve is a representation of the backwardation/contango rate for the different maturities of Futures contracts. This price gap between different maturity contracts is quantified as the roll return. The contango/backwardation rates are calculated by taking the price difference between the spot market price and the Futures contract price. This difference can be expressed as a percentage over the time to expiration and then annualised to calculate an annual roll return.
\[R_t = \left[ln(P_{t,n})-ln(P_{t,d})\right]\times\frac{365}{N_{t,d}-N_{t,n}}\]
Where \(P_{t,n}\) is the price of the nearest-to-maturity contract at time \(t\). \(P_{t,d}\) is the price of the distant contract at time \(t\). \(N_{t,n}\) is the number of days between time \(t\) and the maturity of the nearby contract and \(N_{t,d}\) is the number of days between time \(t\) and the maturity of the distant contract.
Calculation of Roll Return
To calculate the roll return, first we sort the FuturesChain by expiry and select the first two contracts as the nearest-to-maturity contract and the the distant contract.
for symbol, chain in self.chains.items():
contracts = sorted(chain, key = lambda x: x.expiry)
# R = (log(Pn) - log(Pd)) * 365 / (Td - Tn)
# R - Roll returns
# Pn - Nearest contract price
# Pd - Distant contract price
# Tn - Nearest contract expire date
# Pd - Distant contract expire date
near_contract = contracts[0]
distant_contract = contracts[-1]
price_near = near_contract.last_price if near_contract.last_price>0 else 0.5*float(near_contract.ask_price+near_contract.bid_price)
price_distant = distant_contract.last_price if distant_contract.last_price>0 else 0.5*float(distant_contract.ask_price+distant_contract.bid_price)
if distant_contract.expiry == near_contract.expiry:
self.debug("ERROR: Near and distant contracts have the same expiry!" + str(near_contract))
return
expire_range = 365 / (distant_contract.expiry - near_contract.expiry).days
roll_returns[symbol] = (np.log(float(price_near)) - np.log(float(price_distant)))*expire_range
Backwardation and Contango
In the next step, we will split the Futures based on backwardation and contango. If the roll return is greater than 0, the term structure of commodity Futures prices is downward-sloping and so that the market is in backwardation. Conversely, a negative roll return signals an upward-sloping price curve and a contangoed market.
positive_roll_returns = { symbol: returns for symbol, returns in roll_returns.items() if returns > 0 }
negative_roll_returns = { symbol: returns for symbol, returns in roll_returns.items() if returns < 0 }
backwardation = sorted(positive_roll_returns , key = lambda x: positive_roll_returns[x], reverse = True)[:quintile]
contango = sorted(negative_roll_returns , key = lambda x: negative_roll_returns[x])[:quintile]
Algorithm Trade
The algorithm buys 20% of commodities with the highest roll-returns and shorts the 20% of commodities with the lowest roll-returns and holds the long-short positions for one month.
for short_symbol in contango:
sort = sorted(self.chains[short_symbol], key = lambda x: x.expiry)
self.set_holdings(sort[1].symbol, -0.5/count)
for long_symbol in backwardation:
sort = sorted(self.chains[long_symbol], key = lambda x: x.expiry)
self.set_holdings(sort[1].symbol, 0.5/count)
Reference
- Fuertes, Ana-Maria and Miffre, Joƫlle and Rallis, Georgios, Tactical Allocation in Commodity Futures Markets: Combining Momentum and Term Structure Signals (April 22, 2010). Journal of Banking and Finance 34, 2530-2548. Online Copy
- Quantpedia - Term Structure Effect in Commodities
Pavel Fedorov
So this is clear,, but question,, why is it that many books and research papers are saying this strategy is profitable and yet it is not when you run the backtest??
Ashutosh
Okay, so I threw on my mad scientist hat and tinkered with every time horizon but no money-minting magic happened with this strategy. The profit parties I read about seem to be stuck in a time capsule between '97 and '07.
Paper1
Paper2
Paper3
Paper4
Here is the big villain that I think is crashing the non-profitability party today:
Massive Jumps and Volatility's Mood Swings: The commodities market is playing hopscotch with giant leaps, making it harder to stick the landing.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Derek Melchin
See the attached backtest for an updated version of the algorithm with the following changes:
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Jing Wu
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!