Which Commodities Have the Strongest Momentum? Rotation Backtest in Python
June 18, 2026
What's the question?
Commodity markets do not move as one asset class. Gold can rise while energy falls. Natural gas can crash while agricultural commodities remain stable. Base metals can behave like a growth signal, while precious metals behave like a defensive store of value.
Momentum is a direct way to test whether that rotation can be exploited. The rule is not based on a view about supply, inventories, central banks, or geopolitics. It asks whether the strongest recent commodity exposures continue to lead over the next month.
The question is whether a simple momentum rotation across liquid commodity ETFs outperforms an equal-weight commodity basket and a broad commodity benchmark.
The approach
The universe is GLD, SLV, USO, UNG, DBA, DBB, and DBC. It covers gold, silver, oil, natural gas, agriculture, base metals, and broad commodities. Built from SEC EDGAR public filings and market data, the backtest uses five years of daily prices and daily returns.
- Pull five years of daily adjusted prices and daily returns
- Convert daily returns into complete monthly returns
- Rank ETFs each month by prior six-month price return
- Hold the top two ETFs equal-weighted for the next month
- Compare the strategy with an equal-weight basket and DBC
The momentum signal is shifted by one month. This avoids using the same month's return both to rank the ETFs and to measure the portfolio result.
Code
import xfinlink as xfl
import pandas as pd
xfl.set_api_key("YOUR_API_KEY") # free at https://xfinlink.com/signup
tickers = ["GLD", "SLV", "USO", "UNG", "DBA", "DBB", "DBC"]
prices = xfl.prices(tickers, period="5y", fields=["adj_close", "return_daily"])
price_daily = prices.pivot_table(index="date", columns="ticker", values="adj_close").dropna()
return_daily = prices.pivot_table(index="date", columns="ticker", values="return_daily").dropna()
monthly_prices = price_daily.resample("ME").last()
monthly_returns = (1 + return_daily).resample("ME").prod() - 1
signal = monthly_prices.pct_change(6).shift(1)
dates = signal.dropna().index.intersection(monthly_returns.index)
strategy_returns = []
for dt in dates:
leaders = signal.loc[dt].sort_values(ascending=False).head(2).index
strategy_returns.append(monthly_returns.loc[dt, leaders].mean())
strategy = pd.Series(strategy_returns, index=dates)
equal_weight = monthly_returns.loc[dates, tickers].mean(axis=1)
print((1 + strategy).prod(), (1 + equal_weight).prod())
Full script with formatting and visualisation: commodity-momentum-rotation-python.py
Output
=== Commodity Momentum Rotation Backtest ===
Universe: GLD, SLV, USO, UNG, DBA, DBB, DBC
Sample: 2022-01-31 to 2026-05-31 (53 monthly rebalances)
Signal: prior 6-month price return; portfolio: top 2 equal-weighted ETFs
Portfolio comparison:
Top-2 momentum return=+20.3% vol=29.5% max_drawdown=-35.5% positive_months=62.3%
Equal commodity basket return=+13.5% vol=15.4% max_drawdown=-23.3% positive_months=62.3%
DBC broad commodity return=+10.9% vol=15.9% max_drawdown=-23.0% positive_months=56.6%
Most frequent momentum leaders:
GLD selected in 31 months
SLV selected in 26 months
USO selected in 19 months
UNG selected in 13 months
DBA selected in 9 months
DBC selected in 5 months
DBB selected in 3 months
Latest signal ranking:
USO trailing_6m_return=+102.7%
SLV trailing_6m_return=+51.5%
DBC trailing_6m_return=+35.9%
GLD trailing_6m_return=+15.1%
DBB trailing_6m_return=+13.3%
DBA trailing_6m_return= +6.3%
UNG trailing_6m_return=-20.0%
What this tells us
Momentum improved returns in this commodity universe. The top-two strategy returned 20.3% annualized, compared with 13.5% for the equal-weight basket and 10.9% for DBC. The positive-month rate was also higher than DBC, at 62.3% versus 56.6%.
The improvement came with a clear cost. Volatility rose to 29.5%, almost double the equal-weight basket. Maximum drawdown also worsened to -35.5%. That is the signature of commodity momentum: it can catch strong trends, but it concentrates in the most volatile contracts when they are moving.
Gold and silver dominated the leader counts. GLD was selected in 31 months and SLV in 26 months. USO also appeared often, but natural gas was selected only 13 months despite its large swings. The latest signal shows the same concentration problem: USO had the strongest six-month return, but that does not make it low risk.
So what?
Commodity momentum is useful for identifying leadership, not for controlling risk by itself. A top-two ranking can improve return, but position sizing has to account for volatility, drawdown, and the tendency for energy ETFs to reverse sharply.
The practical implementation is to separate the signal from the allocation. Use six-month momentum to identify candidates, then cap position weights by volatility or expected shortfall. In commodities, the strongest trend can also be the largest source of portfolio damage.
Built with xfinlink — free financial data API for Python. pip install xfinlink
pip install xfinlink