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% (34921312 of 34921312) |############| Elapsed Time: 0:00:00 Time:  0:00:00
100% (267748 of 267748) |################| Elapsed Time: 0:00:00 Time:  0:00:00
Run strategy...
Load data for cleanup...
Output cleaning...
fix uniq
---------------------------------------------------------------------------
ValueError                                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)
    294     log_info("Load data for cleanup...")
    295     data = qndata.load_data_by_type(competition_type, assets=result.asset.values.tolist(), tail=60)
--> 296     result = qnout.clean(result, data)
    297     result.name = competition_type
    298     log_info("Write result...")

/usr/local/lib/python3.7/site-packages/qnt/output.py in clean(output, data, kind, debug)
     70         if single_day:
     71             output = output.drop(ds.TIME, errors='ignore')
---> 72             output = xr.concat([output], pd.Index([data.coords[ds.TIME].values.max()], name=ds.TIME))
     73         else:
     74             log_info("ffill if the current price is None...")

/usr/local/lib/python3.7/site-packages/numpy/core/_methods.py in _amax(a, axis, out, keepdims, initial, where)
     38 def _amax(a, axis=None, out=None, keepdims=False,
     39           initial=_NoValue, where=True):
---> 40     return umr_maximum(a, axis, None, out, keepdims, initial, where)
     41 
     42 def _amin(a, axis=None, out=None, keepdims=False,

ValueError: zero-size array to reduction operation maximum which has no identity