Commodity future returns#

This category group contains daily USD-denominated commodity future returns. These returns are based on positions in the front contract of the most liquid available future of each type of commodity.

Commodity future returns in % of notional#

Ticker: COXR_NSA

Label: Commodity future returns, % of notional.

Definition: Return on front future of selected global commodity, % of notional of the contract

Notes:

  • The return is the % change in the futures price. The return calculation assumes the future position is rolled (from front to second) on the first day of the month when the contract is deliverable.

  • For futures return calculations, the following groups of commodity contracts have been used: energy, base metals, precious metals, agricultural commodities and livestock.

  • For information on the components comprising each commodity group, see Appendix 1.

Vol-targeted commodity future return#

Ticker: COXR_VT10

Label: Commodity future return for 10% vol target.

Definition: Return on front future of selected commodity contract, % of risk capital on position scaled to 10% (annualized) volatility target.

Notes:

  • Positions are scaled to a 10% volatility target based on the historic standard deviation for an exponential moving average with a half-life of 11 days. Positions are rebalanced at the end of each month.

  • See further the related notes above on “Commodity return in % of notional” (COXR_NSA).

  • For information on the components comprising each commodity group, see Appendix 1.

Imports#

Only the standard Python data science packages and the specialized macrosynergy package are needed.

import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import math

import json
import yaml

import macrosynergy.management as msm
import macrosynergy.panel as msp
import macrosynergy.signal as mss
import macrosynergy.pnl as msn


from macrosynergy.download import JPMaQSDownload

from timeit import default_timer as timer
from datetime import timedelta, date, datetime

import warnings

warnings.simplefilter("ignore")

The JPMaQS indicators we consider are downloaded using the J.P. Morgan Dataquery API interface within the macrosynergy package. This is done by specifying ticker strings, formed by appending an indicator category code <category> to a currency area code <cross_section>. These constitute the main part of a full quantamental indicator ticker, taking the form DB(JPMAQS,<cross_section>_<category>,<info>), where <info> denotes the time series of information for the given cross-section and category. The following types of information are available:

  • value giving the latest available values for the indicator

  • eop_lag referring to days elapsed since the end of the observation period

  • mop_lag referring to the number of days elapsed since the mean observation period

  • grade denoting a grade of the observation, giving a metric of real time information quality.

After instantiating the JPMaQSDownload class within the macrosynergy.download module, one can use the download(tickers,start_date,metrics) method to easily download the necessary data, where tickers is an array of ticker strings, start_date is the first collection date to be considered and metrics is an array comprising the times series information to be downloaded.

# Define cross sections (currency tickers)

cids_nfm = ["GLD", "SIV", "PAL", "PLT"]
cids_fme = ["ALM", "CPR", "LED", "NIC", "TIN", "ZNC"]
cids_ene = ["BRT", "WTI", "NGS", "GSO", "HOL"]
cids_sta = ["COR", "WHT", "SOY", "CTN"]
cids_liv = ["CAT", "HOG"]
cids_mis = ["CFE", "SGR", "NJO", "CLB"]
cids = cids_nfm + cids_fme + cids_ene + cids_sta + cids_liv + cids_mis
# Define quantamental indicators (category tickers)

main = ["COXR_NSA", "COXR_VT10"]
econ = []
mark = []
xcats = main + econ + mark
# Download series from J.P. Morgan DataQuery by tickers

start_date = "2000-01-01"
tickers = [cid + "_" + xcat for cid in cids for xcat in xcats]
print(f"Maximum number of tickers is {len(tickers)}")

# Retrieve credentials

client_id: str = os.getenv("DQ_CLIENT_ID")
client_secret: str = os.getenv("DQ_CLIENT_SECRET")

# Download from DataQuery

with JPMaQSDownload(client_id=client_id, client_secret=client_secret) as downloader:
    start = timer()
    assert downloader.check_connection()
    df = downloader.download(
        tickers=tickers,
        start_date=start_date,
        metrics=["value", "eop_lag", "mop_lag", "grading"],
        suppress_warning=True,
        show_progress=True,
    )
    end = timer()

dfd = df

print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 50
Downloading data from JPMaQS.
Timestamp UTC:  2023-09-06 12:51:56
Connection successful!
Number of expressions requested: 200
Requesting data: 100%|█████████████████████████████████████████████████████████████████| 10/10 [00:03<00:00,  3.21it/s]
Downloading data: 100%|████████████████████████████████████████████████████████████████| 10/10 [00:15<00:00,  1.52s/it]
Download time from DQ: 0:00:23.108052

Availability#

cids_exp = cids
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df:  []
Missing cids for COXR_NSA:  []
Missing cids for COXR_VT10:  []

The vast majority of commodity futures return series’ are available by 2000. Heating oil is the notable late-starter, only available from 2007 onwards.

For the explanation of commodity symbols, please view Appendix 1.

xcatx = main
cidx = cids_exp

dfx = msm.reduce_df(dfd, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(
    dfx,
)
msm.visual_paneldates(dfs, size=(18, 2))

print("Last updated:", date.today())
../_images/Commodity future returns_16_0.png
Last updated: 2023-09-06
xcatx = main
cidx = cids_exp

plot = msm.check_availability(
    dfd, xcats=xcatx, cids=cidx, start_size=(18, 2), start_years=False
)
../_images/Commodity future returns_17_0.png
xcatx = main
cidx = cids_exp

plot = msp.heatmap_grades(
    dfd,
    xcats=xcatx,
    cids=cidx,
    size=(18, 2),
    title=f"Average vintage grades from {start_date} onwards",
)
../_images/Commodity future returns_18_0.png
xcatx = main
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    val="eop_lag",
    title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
    start=start_date,
    kind="box",
    size=(16, 4),
)
msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    val="mop_lag",
    title="Median of observation period lags (ranges of time elapsed since middle of observation period in days)",
    start=start_date,
    kind="box",
    size=(16, 4),
)
../_images/Commodity future returns_19_0.png ../_images/Commodity future returns_19_1.png

History#

Commodity future returns in % of notional#

Daily returns have displayed modest differences in variations across commodities and a general proclivity to occasional outliers on both the negative and positive side. Natural gas has seen the greatest daily volatility, whilst aluminium, gold and cattle have been subject to the least.

Note: On April 20th 2020, during the COVID pandemic-related market turmoil, the front future price for WTI crude collapsed to a recorded -37 dollars from over 18 dollars, marking a loss of almost 300%. Since negative prices are not valid for standard return calculations, we have replaced the value by the average of the two surrounding days.

xcatx = ["COXR_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="std",
    start=start_date,
    kind="box",
    title="Boxplots of commodity future returns, % of notional, since 2000",
    xcat_labels=["Commodity future returns"],
    size=(16, 8),
)
../_images/Commodity future returns_23_0.png

Long-term performance (since 2000) has been vastly different across commodities. Crude, Nickel, Palladium, Silver and Soy produced near 300% cumulative return, while aluminum, coffee and wheat recorded small negative returns.

xcatx = ["COXR_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="Cumulative returns on USD-denominated commodity futures",
    title_fontsize=27,
    legend_fontsize=17,
    cumsum=True,
    title_adj=1.03,
    title_xadj=0.52,
    ncol=4,
    same_y=True,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/Commodity future returns_25_0.png

Despite the vast long-term return differences, short-term commodity returns have been mostly positively correlated. Cattle, hogs, lumber, natural gas, and orange juice have been the most idiosyncratic contracts.

xcatx = "COXR_NSA"
cidx = cids_exp

msp.correl_matrix(
    dfd,
    xcats=xcatx,
    cids=cidx,
    title="Cross-sectional correlations for commodity future returns, since 2000",
    size=(20, 14),
)
../_images/Commodity future returns_27_0.png

Vol-targeted commodity future returns#

Volatility targeting (on a monthly basis) can be perilous in the commodity space and produced some spectacular daily return outliers for targeted posititions, particularly in nickel, cotton, live cattle, gold and silver.

xcatx = ["COXR_VT10"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="std",
    start=start_date,
    kind="box",
    title="Boxplots of commodity future returns, 10% vol-target, since 2000",
    xcat_labels=["Commodity future returns"],
    size=(16, 8),
)
../_images/Commodity future returns_30_0.png

Volatility targeting makes a big difference in the long-term return performance. On a notional basis, the return differential between Brent and aluminum has been close to 200%, whilst on a vol-targeted basis, it was less than 30%.

xcatx = ["COXR_NSA", "COXR_VT10"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    title="Cumulative outright and vol-targeted returns on commodities future",
    xcat_labels=["Standard", "Vol-targeted"],
    title_fontsize=27,
    legend_fontsize=17,
    title_adj=1.03,
    title_xadj=0.47,
    cumsum=True,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/Commodity future returns_32_0.png

Importance#

Appendices#

Appendix 1: Commodity group definitions and symbols#

The commodity groups considered are: energy, base metals, precious metals, agricultural commodities and livestock.

  • The energy commodity group contains:

    • BRT : ICE Brent crude

    • WTI : NYMEX WTI light crude

    • NGS : NYMEX natural gas, Henry Hub

    • GSO : NYMEX RBOB Gasoline

    • HOL : NYMEX Heating oil, New York Harbor ULSD

  • The base metals group contains:

    • ALM : London Metal Exchange aluminium

    • CPR : Comex copper

    • LED : London Metal Exchange Lead

    • NIC : London Metal Exchange Nickel

    • TIN : London Metal Exchange Tin

    • ZNC : London Metal Exchange Zinc

  • The precious metals group contains:

    • GLD : COMEX gold 100 Ounce

    • SIV : COMEX silver 5000 Ounce

    • PAL : NYMEX palladium

    • PLT : NYMEX platinum

  • The agricultural commodity group contains:

    • COR : Chicago Board of Trade corn composite

    • WHT : Chicago Board of Trade wheat composite

    • SOY : Chicago Board of Trade soybeans composite

    • CTN : NYBOT / ICE cotton #2

    • CFE : NYBOT / ICE coffee ‘C’ Arabica

    • SGR : NYBOT / ICE raw cane sugar #11

    • NJO : NYBOT / NYCE FCOJ frozen orange juice concentrate

    • CLB : Chicago Mercantile Exchange random length lumber

  • The (U.S.) livestock commodity group contains:

    • CAT : Chicago Mercantile Exchange live cattle composite

    • HOG : Chicago Mercantile Exchange lean hogs composite