Commodity inventories #
This category group contains information states of seasonally adjusted excess inventories, compared to estimated normal levels, and excess inventory scores. The underlying data are based on information from commodity exchanges and national national agencies.
Excess commodity inventories #
Ticker : XINVCN_SA / XINVEU_SA / XINVLME_SA / XINVUS_SA / XINVRUS_SA
Label : Excess commodity inventories seasonally adjusted: China / Europe / London Metal Exchange / U.S. / U.S. including reserves
Definition : Excess commodity inventories seasonally adjusted: China / Europe / London Metal Exchange / U.S. / U.S. including reserves
Notes :
-
Excess inventories are calculated by taking a rolling median with a window of a minimum of two years up to a maximum of fifteen years and subtracting the current value from that.
-
Excess levels are seasonally adjusted by JPMaQS.
-
The underlying data come have various frequencies, depending on the commodity. Data with daily and weekly frequenices are aggreated by taking monthly averages at the end of the month to allow for seasonal adjustment. The exact details of underlying frequencies can be found in Appendix 1 .
-
The meaning of geographical locations as well as on sources and units can also be found in Appendix 1 . Each commodity unit has been standardised accross each cross section.
-
The cross-section tickers and definitions of each commodity are summarized in Appendix 2 .
Excess commodity inventories scores #
Ticker : XINVSCORECN_SA / _3MMA / _D1M1ML1 / _D3M3ML3 / _D6M6ML6 / _3MMA_D1M1ML12
Label : Excess commodity inventories in China, sa: z-score / z-score, 3mma, / z-score, diff m/m / z-score, diff 3m/3m / z-score, diff 6m/6m / z-score, diff 3mma oya
Definition : Excess commodity inventories in China, sa: z-score / z-score three month moving average / z-score difference over a month ago / z-score difference of last 3 months over previous 3 months / z-score difference of last 6 months over previous 6 months / z-score difference over a year ago, 3-month moving average
Notes :
-
The z-scores are calculated directly from the excess inventories by sequential normalization. The notes of excess commodity inventories apply.
Ticker : XINVSCOREEU_SA / _3MMA / _D1M1ML1 / _D3M3ML3 / _D6M6ML6 / _3MMA_D1M1ML12
Label : Excess commodity inventories in Europe, sa: z-score / z-score, 3mma, / z-score, diff m/m / z-score, diff 3m/3m / z-score, diff 6m/6m / z-score, diff 3mma oya
Definition : Excess commodity inventories from Europe, seasonally adjusted: z-score / z-score three month moving average / z-score difference over a month ago / z-score difference of last 3 months over previous 3 months / z-score difference of last 6 months over previous 6 months / z-score difference over a year ago, 3-month moving average
Notes :
-
The z-scores are calculated directly from the excess inventories by sequential normalization. The notes of excess commodity inventories apply.
Ticker : XINVSCORELME_SA / _3MMA / _D1M1ML1 / _D3M3ML3 / _D6M6ML6 / _3MMA_D1M1ML12
Label : Excess commodity inventories related to the London Metal Exchange, sa: z-score / z-score, 3mma, / z-score, diff m/m / z-score, diff 3m/3m / z-score, diff 6m/6m / z-score, diff 3mma oya
Definition : Excess commodity inventories related to the London Metal Exchange (global reach), seasonally adjusted: z-score / z-score three month moving average / z-score difference over a month ago / z-score difference of last 3 months over previous 3 months / z-score difference of last 6 months over previous 6 months / z-score difference over a year ago, 3-month moving average
Notes :
-
The z-scores are calculated directly from the excess inventories by sequential normalization. The notes of excess commodity inventories apply.
Ticker : XINVSCOREUS_SA / _3MMA / _D1M1ML1 / _D3M3ML3 / _D1Q1QL1 / _D6M6ML6 / _D2Q2QL2 / _3MMA_D1M1ML12 / _D1Q1QL4
Label : Excess commodity inventories in the U.S., sa: z-score / z-score, 3mma, / z-score, diff m/m / z-score, diff 3m/3m / z-score, diff q/q / z-score, diff 6m/6m / z-score, diff 2q/2q / z-score, diff oya, 3mma / z-score, diff oya (q)
Definition : Excess commodity inventories in the U.S., seasonally adjusted: z-score / z-score three month moving average / z-score difference over a month ago / z-score, difference of last three months over previous three months / z-score, difference of last quarter over previous quarter / z-score, difference of last 6 months over previous 6 months / z-score, difference of last 2 quarters over previous 2 quarters / z-score, difference over a year ago, 3-month moving average / z-score, difference over a year ago, quarterly values
Notes :
-
The z-scores are calculated directly from the excess inventories by sequential normalization. The notes of excess commodity inventories apply.
Ticker : XINVSCORERUS_SA / _3MMA / _D1M1ML1 / _D3M3ML3 / _D6M6ML6 / _3MMA_D1M1ML12
Label : Excess commodity inventories in the U.S. including reserves, sa: z-score / z-score, 3mma, / z-score, diff m/m / z-score, diff 3m/3m / z-score, diff 6m/6m / z-score, diff 3mma oya
Definition : Excess commodity inventories in the U.S. including reserves, seasonally adjusted: z-score / z-score three month moving average / z-score difference over a month ago / z-score difference of last 3 months over previous 3 months / z-score difference of last 6 months over previous 6 months / z-score difference over a year ago, 3-month moving average
Notes :
-
The z-scores are calculated directly from the excess inventories by sequential normalization. The notes of excess commodity inventories apply.
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_fme = ["ALM", "CPR", "LED", "NIC", "ZNC"]
cids_oil = ["WTI", "GSO", "HOL"]
cids_ngs = ["NGS", ]
cids_sta = ["COR", "SOY"]
cids = cids_fme + cids_oil + cids_sta + cids_ngs
cats_inv = [
"XINVCN_SA", "XINVEU_SA", "XINVLME_SA", "XINVUS_SA", "XINVRUS_SA"
]
cats_score = [
"XINVSCORECN_SA", "XINVSCORECN_SA_D1M1ML1", "XINVSCORECN_SA_D3M3ML3", "XINVSCORECN_SA_D6M6ML6", "XINVSCORECN_SA_3MMA_D1M1ML12", "XINVSCORECN_SA_3MMA",
"XINVSCOREEU_SA", "XINVSCOREEU_SA_D1M1ML1", "XINVSCOREEU_SA_D3M3ML3", "XINVSCOREEU_SA_D6M6ML6", "XINVSCOREEU_SA_3MMA_D1M1ML12", "XINVSCOREEU_SA_3MMA",
"XINVSCORELME_SA", "XINVSCORELME_SA_D1M1ML1", "XINVSCORELME_SA_D3M3ML3", "XINVSCORELME_SA_D6M6ML6", "XINVSCORELME_SA_3MMA_D1M1ML12", "XINVSCORELME_SA_3MMA",
"XINVSCOREUS_SA", "XINVSCOREUS_SA_D1M1ML1", "XINVSCOREUS_SA_D3M3ML3", "XINVSCOREUS_SA_D6M6ML6", "XINVSCOREUS_SA_3MMA_D1M1ML12", "XINVSCOREUS_SA_3MMA",
"XINVSCORERUS_SA", "XINVSCORERUS_SA_D1M1ML1", "XINVSCORERUS_SA_D3M3ML3", "XINVSCORERUS_SA_D6M6ML6", "XINVSCORERUS_SA_3MMA_D1M1ML12", "XINVSCORERUS_SA_3MMA",
"XINVSCOREUS_SA_D1Q1QL1","XINVSCOREUS_SA_D2Q2QL2","XINVSCOREUS_SA_D1Q1QL4"
]
econ = ["COXR_NSA", "COXR_VT10", "COCRY_NSA", "COCRY_VT10","COCRY_SA"]
main = cats_inv + cats_score
xcats = main+ econ
# Download series from J.P. Morgan DataQuery by tickers
start_date = "1990-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,
show_progress=True,
metrics=["value", "eop_lag", "mop_lag", "grading"],
suppress_warning=True,
)
end = timer()
dfd = df
print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 473
Downloading data from JPMaQS.
Timestamp UTC: 2024-03-27 11:35:14
Connection successful!
Requesting data: 100%|██████████| 95/95 [00:20<00:00, 4.74it/s]
Downloading data: 100%|██████████| 95/95 [00:16<00:00, 5.60it/s]
Some expressions are missing from the downloaded data. Check logger output for complete list.
1100 out of 1892 expressions are missing. To download the catalogue of all available expressions and filter the unavailable expressions, set `get_catalogue=True` in the call to `JPMaQSDownload.download()`.
Some dates are missing from the downloaded data.
8935 out of 8935 dates are missing.
Download time from DQ: 0:00:41.237013
Availability #
msm.missing_in_df(df, xcats=main, cids=cids)
Missing xcats across df: []
Missing cids for XINVCN_SA: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVEU_SA: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVLME_SA: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVRUS_SA: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORECN_SA: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORECN_SA_3MMA: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORECN_SA_3MMA_D1M1ML12: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORECN_SA_D1M1ML1: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORECN_SA_D3M3ML3: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORECN_SA_D6M6ML6: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCOREEU_SA: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCOREEU_SA_3MMA: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCOREEU_SA_3MMA_D1M1ML12: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCOREEU_SA_D1M1ML1: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCOREEU_SA_D3M3ML3: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCOREEU_SA_D6M6ML6: ['WTI', 'ALM', 'ZNC', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORELME_SA: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORELME_SA_3MMA: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORELME_SA_3MMA_D1M1ML12: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORELME_SA_D1M1ML1: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORELME_SA_D3M3ML3: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORELME_SA_D6M6ML6: ['WTI', 'NGS', 'GSO', 'COR', 'HOL', 'SOY']
Missing cids for XINVSCORERUS_SA: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORERUS_SA_3MMA: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORERUS_SA_3MMA_D1M1ML12: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORERUS_SA_D1M1ML1: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORERUS_SA_D3M3ML3: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCORERUS_SA_D6M6ML6: ['ALM', 'ZNC', 'NGS', 'GSO', 'COR', 'NIC', 'HOL', 'CPR', 'SOY', 'LED']
Missing cids for XINVSCOREUS_SA: ['LED', 'NIC']
Missing cids for XINVSCOREUS_SA_3MMA: ['SOY', 'LED', 'COR', 'NIC']
Missing cids for XINVSCOREUS_SA_3MMA_D1M1ML12: ['SOY', 'LED', 'COR', 'NIC']
Missing cids for XINVSCOREUS_SA_D1M1ML1: ['SOY', 'LED', 'COR', 'NIC']
Missing cids for XINVSCOREUS_SA_D1Q1QL1: ['WTI', 'ALM', 'ZNC', 'NGS', 'GSO', 'NIC', 'HOL', 'CPR', 'LED']
Missing cids for XINVSCOREUS_SA_D1Q1QL4: ['WTI', 'ALM', 'ZNC', 'NGS', 'GSO', 'NIC', 'HOL', 'CPR', 'LED']
Missing cids for XINVSCOREUS_SA_D2Q2QL2: ['WTI', 'ALM', 'ZNC', 'NGS', 'GSO', 'NIC', 'HOL', 'CPR', 'LED']
Missing cids for XINVSCOREUS_SA_D3M3ML3: ['SOY', 'LED', 'COR', 'NIC']
Missing cids for XINVSCOREUS_SA_D6M6ML6: ['SOY', 'LED', 'COR', 'NIC']
Missing cids for XINVUS_SA: ['LED', 'NIC']
London metals are available since the 1990’s, with Chinese and American data starting to show up after the 2000’s. Most of the energy commodities from the U.S. are available since the 1990’s with gasoline being an exception, whereas for Europe it is only available since 2013. The aggriculture commodities are avaible since the 90s.
xcatx = [
"XINVCN_SA",
"XINVEU_SA",
"XINVLME_SA",
"XINVUS_SA",
"XINVRUS_SA",
]
cidx = cids
dfx = msm.reduce_df(df, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(
dfx,
)
msm.visual_paneldates(dfs, size=(18, 2))
print("Last updated:", date.today())
Last updated: 2024-03-27
xcatx = [
"XINVSCORECN_SA",
"XINVSCOREEU_SA",
"XINVSCOREUS_SA",
"XINVSCORERUS_SA",
"XINVSCORELME_SA",
]
cidx = cids
dfx = msm.reduce_df(df, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(
dfx,
)
msm.visual_paneldates(dfs, size=(18, 2))
print("Last updated:", date.today())
Last updated: 2024-03-27
A lot the data is grade 1 as it is based on fininacial data, however the ones which are based off monthly releases do not have that much vintage information availble.
xcatx = [
"XINVCN_SA", "XINVEU_SA", "XINVLME_SA", "XINVUS_SA", "XINVRUS_SA",
"XINVSCORECN_SA","XINVSCOREEU_SA","XINVSCOREUS_SA","XINVSCORERUS_SA","XINVSCORELME_SA"
]
plot = msm.check_availability(
df, xcats=xcatx, cids=cids, start_size=(20, 2), start_years=False
)
xcatx = [
"XINVCN_SA", "XINVEU_SA", "XINVLME_SA", "XINVUS_SA", "XINVRUS_SA",
"XINVSCORECN_SA","XINVSCOREEU_SA","XINVSCOREUS_SA","XINVSCORERUS_SA","XINVSCORELME_SA"
]
plot = msp.heatmap_grades(
df,
xcats=xcatx,
cids=cids,
size=(18, 4),
title=f"Average vintage grades from {start_date} onwards",
)
History #
Base metal inventories #
LME inventories, which include warehouses around the world to serve the needs of the global metals market, contain the largest physical quantities of recorded base metal inventories, as well as the longest history. Information states are available back to the 1990s.
cidx = cids_fme
xcatx = ["XINVLME_SA", "XINVUS_SA", "XINVCN_SA"]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Base metals excess inventories, physical units",
xcat_labels=["London Metal Exchange", "U.S.", "China"],
ncol=3,
same_y=False,
legend_fontsize=17,
title_fontsize=27,
size=(12, 7),
aspect=1.7,
all_xticks=True,
legend_ncol=2,
label_adj=0,
)
The dynamics of normalized based metal inventory scores have been quite different across economic areas. Part of the difference come from different normalization horizons, however.
cidx = cids_fme
xcatx = ["XINVSCORELME_SA", "XINVSCORECN_SA", "XINVSCOREUS_SA",]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Base metals excess inventories, z-scores",
xcat_labels=["London Metal Exchange", "China", "U.S."],
ncol=3,
same_y=False,
legend_fontsize=17,
title_fontsize=27,
size=(12, 7),
aspect=1.7,
all_xticks=True,
legend_ncol=2,
label_adj=0,
)
Also, changes in normalized inventory scores have been quite different across geographies.
cidx = cids_fme
xcatx = ["XINVSCORELME_SA_D6M6ML6", "XINVSCORECN_SA_D6M6ML6", "XINVSCOREUS_SA_D6M6ML6", ]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Base metals excess inventories z-scores 6 month trends",
xcat_labels=["London Metal Exchange", "China", "U.S."],
ncol=3,
same_y=False,
legend_fontsize=17,
title_fontsize=27,
size=(12, 7),
aspect=1.7,
all_xticks=True,
legend_ncol=2,
label_adj=0,
)
Information states of inventory changes across base metals have been positively correlated.
cidx = cids_fme
msp.correl_matrix(
df, xcats="XINVSCORELME_SA_D6M6ML6", freq="M", cids=cidx, size=(10, 5)
)
U.S. fuels commodities #
Seasonally-adjusted U.S. inventory scores of fuels have displayed pronounced cyclical patterns.
cidx = cids_oil
xcatx = ["XINVSCORERUS_SA", "XINVSCOREUS_SA"]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="U.S. fuels commodities, z-scores",
xcat_labels=
[
"Including strategic reserves",
"Excluding strategic reserves"
],
ncol=3,
same_y=False,
legend_fontsize=17,
title_fontsize=27,
size=(12, 7),
aspect=1.7,
all_xticks=True,
legend_ncol=2,
label_adj=0.05,
)
cidx = cids_oil
xcatx = ["XINVSCORERUS_SA_D6M6ML6", "XINVSCOREUS_SA_D6M6ML6"]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="U.S. fuels commodities, z-scores, 6 months over previous 6 months",
xcat_labels=
[
"Including strategic reserves",
"Excluding strategic reserves"
],
ncol=3,
same_y=False,
legend_fontsize=17,
title_fontsize=27,
size=(12, 7),
aspect=1.7,
all_xticks=True,
legend_ncol=2,
label_adj=0.05,
)
Natural gas commodities #
History of natural gas inventories has been available much longer for the U.S. than for Europe. Since 2015 fluctuations in EU inventories have been more pronounced than in the U.S.
cidx = cids_ngs
xcatx = ["XINVEU_SA", "XINVUS_SA"]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Natural gas excess inventories, cubic feet",
xcat_labels=["EU", "U.S."],
size=(12, 4),
)
U.S. agricultural commodities #
Corn and soybean investories in the U.S. have displayed both short-term fluctuations and multi-year cycles.
cidx = cids_sta
xcatx = ["XINVSCOREUS_SA"]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
title="U.S. agricultural commodities inventories, z-scores",
start=start_date,
xcat_labels=None,
)
Importance #
Research links #
“The expectation of a positive convenience yield is the reason why firms maintain positive stocks of inventory even when the expected capital gain is below the risk-adjusted rate or negative. For many commodities…this expectation is large enough to motivate firms to maintain positive inventory levels even in the presence of expected costs (negative capital gain), in addition to funding and storage costs, of 5% to 10% per month….The literature on the convenience yield explains it as a function of the net cost of storage, through the interaction of supply and demand in the cash market and the storage market…Models… predict that the convenience yield is high (low) during periods of time where inventory levels are at their lowest (highest), e.g. during times of scarcity. Related empirical literature…shows that the convenience yield is mean-reverting.” Bredin, Poti, and Salvador
“According to the theory of storage, inventory and supply shortages bias spot and front futures’ prices to the high side relative to back futures. Under both popular hypotheses, backwardated futures curves are – all other influence being neutral– indicative of premia paid to longs in back futures.” Macrosynergy
Empirical clues #
Theoretically, low inventories should indicate higher convenience yields of physical commodities versus futures-based entitlements and thus predict subsequent futures returns positively. For the base metals there has indeed been a negative predictive correlation for a panel of all 5 contracts since 1990, but it is not quite significant. Looking at individual commodities, there has been negative predictive relation of inventories with subsequent futures returns only for copper, lead, and nickel. Only for nickel has the relation been significant.
cidx = ["NIC"]
sdate = "1995-01-01"
cr = msp.CategoryRelations(
dfd,
xcats=["XINVSCORELME_SA", "COXR_NSA"],
cids=cidx,
freq="M",
lag=1,
xcat_aggs=["last", "sum"],
fwin=1,
start=sdate,
years=None,
)
cr.reg_scatter(
labels=False,
coef_box="lower right",
prob_est="map",
xlab="Excess nickel inventory score (LME), seasonally adjusted",
ylab="Nickel futures return, nex month",
title = "Excess nickel inventory score (LME) and subsequent returns, since 1995"
)
In the natural gas sector there has been a much stronger and highly signficant negative relation between inventory scores and subsequent futures returns.
calcs = [
"XINVSCOREGAS_SA = 0.5 * XINVSCOREEU_SA + 0.5 * XINVSCOREUS_SA",
]
dfa = msp.panel_calculator(dfd, calcs=calcs, cids=["NGS"])
dfd = msm.update_df(dfd, dfa)
cidx = ["NGS"]
sdate = "1995-01-01"
cr = msp.CategoryRelations(
dfd,
xcats=["XINVSCOREGAS_SA", "COXR_NSA"],
cids=cidx,
freq="M",
lag=1,
xcat_aggs=["last", "sum"],
fwin=1,
start=sdate,
years=None,
)
cr.reg_scatter(
labels=False,
coef_box="lower right",
prob_est="map",
xlab="Natural gas inventory score (U.S. and EU average), seasonally adjusted",
ylab="U.S. gas futures return, nex month",
title = "Excess natural gas inventory score and subsequent futures returns since 1995"
)
There has been a modest negative relation between U.S. fuel inventories and subsequent futures returns at a panel basis, i.e., cross-sectionally and intertemporally.
cidx = ["HOL","GSO","WTI"]
sdate = "1995-01-01"
cr = msp.CategoryRelations(
dfd,
xcats=["XINVSCOREUS_SA", "COXR_NSA"],
cids=cidx,
freq="M",
lag=1,
xcat_aggs=["last", "sum"],
fwin=1,
start=sdate,
years=None,
)
cr.reg_scatter(
labels=False,
coef_box="lower right",
prob_est="map",
xlab="U.S. fuels inventory scores (WTI, heating oil, gasoline), seasonally adjusted",
ylab="U.S. gas futures return, nex month",
title="Excess U.S. fuels inventory scores and subsequent futures returns, since 1995"
)
Appendices #
Appendix 1: Commodity source details #
Base metal inventory mainly refer to specialized warehouses operated by commodities exchanges(LME, Comex, and the Shanghai Futures Exchange). These warehouses are often strategically located near major transportation hubs such as ports, rail terminals, or industrial centers. Fuels storage in the U.S. refers to storage and refineries, terminals and bulk storage facilities. Agricultural commodities storage data refer to bulk storage facilities, processing facilities and farms.
surveys = pd.DataFrame(
[
{
"Commodity group": "Base metals",
"market": "U.S.",
"source": "Commodity Exchange, Inc",
"unit": "Metric Tonne",
"Storage facilities and locations":"Warehouses ran by the exchange: Copper North America only, rest global",
"frequency":"Daily"
},
{
"Commodity group": "Base metals",
"market": "China",
"source": "Shanghai Futures Exchange",
"unit": "Metric Tonne",
"Storage facilities and locations":"Warehouses ran by the exchange: China only",
"frequency":"Weekly"
},
{
"Commodity group": "Base metals",
"market": "London Metal Exchange",
"source": "London Metal Exchange",
"unit": "Metric Tonne",
"Storage facilities and locations":"Warehouses ran by the exchange: Global",
"frequency":"Daily"
},
{
"Commodity group": "Oil",
"market": "U.S.",
"source": "Energy Information Administration",
"unit": "Barrels",
"Storage facilities and locations":"Refineries, bulk storage, blending terminals: U.S. only",
"frequency":"Monthly"
},
{
"Commodity group": "Natural gas",
"market": "U.S.",
"source": "Energy Information Administration",
"unit": "Cubic feet",
"Storage facilities and locations":"Refineries, bulk storage, blending terminals: U.S. only",
"frequency":"Monthly"
},
{
"Commodity group": "Natural gas",
"market": "Europe",
"source": "Gas Infrastructure Europe",
"unit": "Cubic feet",
"Storage facilities and locations":"Bulk storage: European union only",
"frequency":"Daily"
},
{
"Commodity group": "Agriculture",
"market": "U.S.",
"source": "United States Department of Agriculture",
"unit": "Bushel",
"Storage facilities and locations":"Farms, various processing facilities and bulk storage: U.S. only",
"frequency":"Quaterly"
},
]
)
from IPython.display import HTML
HTML(surveys.to_html(index=False))
Commodity group | market | source | unit | Storage facilities and locations | frequency |
---|---|---|---|---|---|
Base metals | U.S. | Commodity Exchange, Inc | Metric Tonne | Warehouses ran by the exchange: Copper North America only, rest global | Daily |
Base metals | China | Shanghai Futures Exchange | Metric Tonne | Warehouses ran by the exchange: China only | Weekly |
Base metals | London Metal Exchange | London Metal Exchange | Metric Tonne | Warehouses ran by the exchange: Global | Daily |
Oil | U.S. | Energy Information Administration | Barrels | Refineries, bulk storage, blending terminals: U.S. only | Monthly |
Natural gas | U.S. | Energy Information Administration | Cubic feet | Refineries, bulk storage, blending terminals: U.S. only | Monthly |
Natural gas | Europe | Gas Infrastructure Europe | Cubic feet | Bulk storage: European union only | Daily |
Agriculture | U.S. | United States Department of Agriculture | Bushel | Farms, various processing facilities and bulk storage: U.S. only | Quaterly |
Appendix 2: Commodity details #
The commodity groups considered are: energy, base metals and agricultural commodities.
-
The energy commodity group contains:
-
WTI: Crude oil
-
GSO: Gasoline
-
HOL: Heating oil
-
NGS: Natural gas
-
-
The base metals contains:
-
ALM: Aluminium
-
CPR: Copper
-
LED: Lead
-
NIC: Nickel
-
ZNC: Zinc
-
-
The agriculture group contains:
-
COR: Corn
-
SOY: Soybeans
-