Introduction

Commodity Futures are excellent portfolio diversifiers and some of them are an effective hedge against inflation. This algorithm will explore the momentum effect in commodity Futures with the momentum return.

Method

As the strategy needs the continuous Futures contract, we import the custom data from Quandl. We create a universe of tradable commodity Futures from all available commodity Futures traded on CME. They are all liquid and active continuous contracts #1. The Futures are mapped by largest open interest contract, while adjusted by backward ratio. The data resolution is daily.

The first step is importing the data.

  1. self.symbols = [
  2. Futures.dairy.cash_settled_butter,
  3. Futures.dairy.cash_settled_cheese,
  4. Futures.dairy.class_i_i_i_milk,
  5. Futures.dairy.dry_whey,
  6. Futures.dairy.class_i_v_milk,
  7. Futures.dairy.nonfat_dry_milk,
  8. Futures.meats.live_cattle,
  9. Futures.meats.feeder_cattle,
  10. Futures.meats.lean_hogs,
  11. Futures.forestry.random_length_lumber
  12. ]
  13. for symbol in self.symbols:
  14. future = self.add_future(symbol,
  15. resolution = Resolution.DAILY,
  16. extended_market_hours = True,
  17. data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
  18. data_mapping_mode = DataMappingMode.OPEN_INTEREST,
  19. contract_depth_offset = 0
  20. )
  21. future.set_leverage(1)
+ Expand

We use the indicator RateOfChange(period) to simulate the momentum return with a period of 12 months. Then it is warmed up by the method WarmUpIndicator(Symbol, Indicator, Resolution). All Futures' indicators, and their respective informations (e.g. mapped contract, contract multiplier, updating method of ROC indicator) are stored in a SymbolData class object, and save within a dictionary self.data.

  1. self.data = {}
  2. for symbol in self.symbols:
  3. self.data[future.symbol] = SymbolData(self, future, period)
  4. class SymbolData:
  5. def __init__(self, algorithm, future, period):
  6. self._future = future
  7. self.symbol = future.symbol
  8. self.ROC = RateOfChange(period)
  9. # warm up indicator
  10. algorithm.warm_up_indicator(self.symbol, self.ROC, Resolution.DAILY)
  11. @property
  12. def is_ready(self):
  13. return self.ROC.is_ready and self._future.mapped is not None
  14. @property
  15. def value(self):
  16. return self.ROC.current.value
  17. @property
  18. def mapped(self):
  19. return self._future.mapped
  20. @property
  21. def multiplier(self):
  22. return self._future.symbol_properties.contract_multiplier
  23. def update(self, bar):
  24. self.ROC.update(bar.end_time, bar.close)
+ Expand

In OnData(self, data), indicators for all Futures contracts are updated every day with the close price.

  1. def on_data(self, slice):
  2. # Update the indicator value every day
  3. for symbol, symbol_data in self.data.items():
  4. if slice.bars.contains_key(symbol):
  5. symbol_data.update(slice.bars[symbol])

We rank the contracts by the last 12-month return and divide them into quintiles. In the trading part, the algorithm goes long on the quintile with the highest momentum return and goes short on the quintile with the lowest momentum return.

  1. def initialize(self):
  2. # Rebalance the portfolio every month
  3. self.rebalance = self.time
  4. def on_data(self, slice):
  5. if self.rebalance <= self.time:
  6. # sorted futures by 12-month return reversely
  7. self.sorted_roc = sorted([x for x in self.data.values() if x.is_ready], key = lambda x: x.value, reverse=True)
  8. number_futures = int(0.25*len(self.sorted_roc))
  9. if number_futures == 0: return
  10. self.long = [x for x in self.sorted_roc[:number_futures]]
  11. self.short = [x for x in self.sorted_roc[-number_futures:]]
  12. for symbol in self.portfolio.keys:
  13. # liquidate the futures which is no longer in the trading list
  14. if self.portfolio[symbol].invested and symbol not in [x.mapped for x in self.long + self.short]:
  15. self.liquidate(symbol)
  16. for long in self.long:
  17. qty = self.calculate_order_quantity(long.mapped, 0.5/number_futures)
  18. self.market_order(long.mapped, qty // long.multiplier)
  19. for short in self.short:
  20. qty = self.calculate_order_quantity(short.mapped, -0.5/number_futures)
  21. self.market_order(short.mapped, qty // short.multiplier)
  22. self.rebalance = Expiry.end_of_month(self.time)
+ Expand


Reference

  1. Quantpedia - Momentum Effect in Commodities

Author

Jing Wu

July 2018