strategy

Futures - IMF Commodity Data

This template uses commodity data from the International Monetary Fund for creating an algorithm on futures contracts.

You can clone and edit this example there (tab Examples).


The International Monetary Fund (IMF) publishes a range of time series data on IMF lending, exchange rates and other economic and financial indicators.

In this template we show how to to use commodity data for developing a trading algorithm.

Need help? Check the Documentation and find solutions/report problems in the Forum section.

More help with Jupyter? Check the official Jupyter page.

Documentation on the IMF data can be found here.

Once you are done, click on Submit to the contest and take part to our competitions.

API reference:

  • data: check how to work with data;

  • backtesting: read how to run the simulation and check the results.

In this template we use the optimizer function described in:

  • optimization: read more on our article.
In [1]:
%%javascript
window.IPython && (IPython.OutputArea.prototype._should_scroll = function(lines) { return false; })
// disable widget scrolling
In [2]:
import xarray as xr
import numpy as np
import pandas as pd

import qnt.backtester as qnbt
import qnt.data as qndata
In [3]:
# commodity listing
commodity_list = qndata.imf_load_commodity_list()
pd.DataFrame(commodity_list)
100% (39563 of 39563) |##################| Elapsed Time: 0:00:00 Time:  0:00:00
Out[3]:
id COMMODITY_CODE COMMODITY_NAME COMMODITY_TOPIC COMMODITY_FULL_NAME COMMODITY_SDMX_CODE COMMODITY_SDMX_NAME COMMODITY_DEFINITION COMMODITY_SHORT_NAME COMMODITY_SOURCE_CODE
0 LMICS LMICS Low and Middle Income Commodity Index (World B... Real Sector Primary Commodity Prices, Low and Middle Incom... LMICS Primary Commodity Prices, Low and Middle Incom... World Bank Non-Energy Commodities Price Index ... Low and Middle Income Commodity Index (World B... LMICS
1 PALUM PALUM Aluminum Real Sector Primary Commodity Prices, Aluminum PALUM Primary Commodity Prices, Aluminum Aluminum, 99.5% minimum purity, LME spot price... Aluminum PALUM
2 PAPPLE PAPPLE Non-Citrus Fruit, Apple Real Sector Primary Commodity Prices, Non-Citrus Fruit, Apple PAPPLE Primary Commodity Prices, Non-Citrus Fruit, Apple Monthly average consumer prices in metropolita... Non-Citrus Fruit, Apple PAPPLE
3 PBANSOP PBANSOP Bananas Real Sector Primary Commodity Prices, Bananas PBANSOP Primary Commodity Prices, Bananas Bananas, Central American and Ecuador, FOB U.S... Bananas PBANSOP
4 PBARL PBARL Barley Real Sector Primary Commodity Prices, Barley PBARL Primary Commodity Prices, Barley Barley, Canadian no.1 Western Barley, spot pri... Barley PBARL
... ... ... ... ... ... ... ... ... ... ...
77 PVANPENT PVANPENT Vanadium Real Sector Primary Commodity Prices, Vanadium PVANPENT Primary Commodity Prices, Vanadium Vanadium pentoxide, CIF North West Europe, USD... Vanadium PVANPENT
78 PWHEAMT PWHEAMT Wheat Real Sector Primary Commodity Prices, Wheat PWHEAMT Primary Commodity Prices, Wheat Wheat, No.1 Hard Red Winter, ordinary protein,... Wheat PWHEAMT
79 PWOOLC PWOOLC Wool, Coarse Real Sector Primary Commodity Prices, Wool, Coarse PWOOLC Primary Commodity Prices, Wool, Coarse Wool, coarse, 23 micron, Australian Wool Excha... Wool, Coarse PWOOLC
80 PWOOLF PWOOLF Wool, Fine Real Sector Primary Commodity Prices, Wool, Fine PWOOLF Primary Commodity Prices, Wool, Fine Wool, fine, 19 micron, Australian Wool Exchang... Wool, Fine PWOOLF
81 PZINC PZINC Zinc Real Sector Primary Commodity Prices, Zinc PZINC Primary Commodity Prices, Zinc Zinc, high grade 98% pure, US$ per metric ton Zinc PZINC

82 rows × 10 columns

In [4]:
def load_data(period):
    # load Futures Gold and Gold data:
    futures   = qndata.futures_load_data(assets=['F_GC'], tail=period, dims=('time','field','asset'))
    commodity = qndata.imf_load_commodity_data(assets=['PGOLD'], tail=period).isel(asset=0)
    return dict(commodity=commodity, futures=futures), futures.time.values


def window(data, max_date: np.datetime64, lookback_period: int):
    # build sliding window for rolling evaluation:
    min_date = max_date - np.timedelta64(lookback_period, 'D')
    return dict(
        futures   = data['futures'].sel(time=slice(min_date, max_date)),
        commodity = data['commodity'].sel(time=slice(min_date, max_date))
    )


def strategy(data):
    # strategy uses both Futures Gold and Gold data:
    close = data['futures'].sel(field='close')
    commodity = data['commodity']
    if commodity.isel(time=-1) > commodity.isel(time=-2) and close.isel(time=-1) > close.isel(time=-20):
        return xr.ones_like(close.isel(time=-1))
    else:
        return xr.zeros_like(close.isel(time=-1))


weights = qnbt.backtest(
    competition_type='futures',
    load_data=load_data,
    window=window,
    lookback_period=365,
    start_date='2006-01-01',
    strategy=strategy,
    analyze=True,
    build_plots=True
)
Run last pass...
Load data...
100% (35574032 of 35574032) |############| Elapsed Time: 0:00:00 Time:  0:00:00
100% (272368 of 272368) |################| Elapsed Time: 0:00:00 Time:  0:00:00
Run strategy...
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-c9e98e7f379f> in <module>
     33     strategy=strategy,
     34     analyze=True,
---> 35     build_plots=True
     36 )

/usr/local/lib/python3.7/site-packages/qnt/backtester.py in backtest(competition_type, strategy, load_data, lookback_period, test_period, start_date, end_date, window, step, analyze, build_plots, collect_all_states)
    289     if is_submitted() and args_count > 1:
    290         state = qnstate.read()
--> 291     result = strategy_wrap(data, state)
    292     result, state = unpack_result(result)
    293 

/usr/local/lib/python3.7/site-packages/qnt/backtester.py in <lambda>(d, s)
    268 
    269     args_count = len(inspect.getfullargspec(strategy).args)
--> 270     strategy_wrap = (lambda d, s: strategy(d)) if args_count < 2 else strategy
    271 
    272     # ---

<ipython-input-4-c9e98e7f379f> in strategy(data)
     19     close = data['futures'].sel(field='close')
     20     commodity = data['commodity']
---> 21     if commodity.isel(time=-1) > commodity.isel(time=-2) and close.isel(time=-1) > close.isel(time=-20):
     22         return xr.ones_like(close.isel(time=-1))
     23     else:

/usr/local/lib/python3.7/site-packages/xarray/core/dataarray.py in isel(self, indexers, drop, missing_dims, **indexers_kwargs)
   1206         # lists, or zero or one-dimensional np.ndarray's
   1207 
-> 1208         variable = self._variable.isel(indexers, missing_dims=missing_dims)
   1209 
   1210         coords = {}

/usr/local/lib/python3.7/site-packages/xarray/core/variable.py in isel(self, indexers, missing_dims, **indexers_kwargs)
   1191 
   1192         key = tuple(indexers.get(dim, slice(None)) for dim in self.dims)
-> 1193         return self[key]
   1194 
   1195     def squeeze(self, dim=None):

/usr/local/lib/python3.7/site-packages/xarray/core/variable.py in __getitem__(self, key)
    784         """
    785         dims, indexer, new_order = self._broadcast_indexes(key)
--> 786         data = as_indexable(self._data)[indexer]
    787         if new_order:
    788             data = np.moveaxis(data, range(len(new_order)), new_order)

/usr/local/lib/python3.7/site-packages/xarray/core/indexing.py in __getitem__(self, key)
   1158     def __getitem__(self, key):
   1159         array, key = self._indexing_array_and_key(key)
-> 1160         return array[key]
   1161 
   1162     def __setitem__(self, key, value):

IndexError: index -1 is out of bounds for axis 0 with size 0