Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Contango and Roll Yield in Commodity ETFs

Contango explains why a commodity ETF can lose money while spot stays flat. See the roll trade, the arithmetic over twelve rolls, and real fund data.

Contango is a futures curve on which each later delivery month is priced above the month in front of it, and roll yield is what a fund collects or pays when it sells an expiring contract and buys the next one up that curve. A commodity ETF holding futures makes that trade on a schedule, and its return can separate from the spot price it tracks even in a year when spot goes nowhere. It is a separate mechanism from the daily rebalancing arithmetic in how leveraged ETFs work, and a fund can carry one without the other.

What is contango, and what is backwardation?

A futures contract is an agreement to buy or sell a set quantity of something at a set price on a set date. A commodity does not have one futures price. It has a strip of them, one per delivery month, running months or years out. Plot those prices against their delivery dates and you have the curve.

Contango is the shape where the far months sit above the near months. Backwardation is the shape where they sit below. Both words describe the slope of the curve, never the level of prices. A cheap commodity can sit in contango and an expensive one in backwardation, and a single commodity can change shape more than once a year.

An upward slope has a plain mechanical reading. Whoever holds the physical commodity until a later delivery date pays to finance and store it, and the far month carries those costs. A downward slope carries the opposite pressure, a premium for holding the goods right now, which appears when nearby supply is tight. Each delivery month trades under its own symbol, and reading a futures symbol decodes which one you are looking at.

Contango and roll yield in one transaction

A listed fund cannot let a futures contract expire in its hands. Taking delivery would mean arranging physical barrels or bushels in a warehouse. Some days before expiry the fund closes the expiring position and opens the same exposure in a later month. That trade is the roll.

Work it through as a transaction. A fund holds $100, the expiring month trades at $100, and the next month out trades at $101. On roll day the fund sells at $100 and buys at $101, and its $100 buys 0.990 of a contract where it used to buy 1.000. No money left the fund. Its dollar value one second after the roll matches its value one second before. What changed is how many contracts stand behind each dollar.

The arithmetic lands a month later. With spot unmoved, the contract bought at $101 is now the expiring one and it settles at $100. Multiply 0.990 contracts by $100 and the fund holds $99.01. It gave up about 1% on a commodity that sat still. Roll yield is the name for that transfer: negative rolling up the curve, positive rolling down.

Twelve rolls with the spot price pinned flat

One roll is a rounding error. Twelve of them compound. The script below is plain Python 3 with no packages and a synthetic curve hardcoded. It fixes spot for a year and prints what the fund holds after each roll. Save it as roll.py and run python3 roll.py.

#!/usr/bin/env python3
# Twelve monthly rolls up and down a synthetic futures curve. Standard library only.

START = 100.0                                    # dollars in the fund on day one
FLAT = [100.0] * 13                              # spot pinned for a year, 13 month ends
RISING = [100.0 * 1.02 ** m for m in range(13)]  # spot grinds 2 percent a month


def roll_year(name, spot, slope):
    # slope is the next contract's premium over spot: +0.01 is contango
    cash = START
    contracts = cash / (spot[0] * (1.0 + slope))
    print(name)
    for m in range(1, 13):
        settle = spot[m]                               # the held month expires at spot
        cash = contracts * settle                      # sell it
        contracts = cash / (settle * (1.0 + slope))    # buy the next month up the curve
        print(f'  roll {m:>2}  spot {settle:8.2f}  contracts {contracts:7.4f}  fund {cash:8.2f}')
    spot_ret = spot[12] / spot[0] - 1.0
    fund_ret = cash / START - 1.0
    print(f'  spot {spot_ret:+.2%}   fund {fund_ret:+.2%}   gap {fund_ret - spot_ret:+.2%}')


roll_year('flat spot, 1 percent contango', FLAT, 0.01)
roll_year('flat spot, 1 percent backwardation', FLAT, -0.01)
roll_year('spot up 2 percent a month, 1 percent contango', RISING, 0.01)

With spot pinned at 100.00 and a 1% monthly premium on the next contract, the twelfth roll leaves the fund at 88.74. Spot returned 0.00% over the year and the fund returned minus 11.26%. No forecast was wrong and no extra fee was charged. The loop bought a slightly more expensive contract twelve times.

Flip the slope to minus 1%, a backwardated curve, and the same twelve rolls finish at 112.82, a plus 12.82% year on that same flat spot. Roll yield has a sign, and the sign belongs to the curve. The two runs are not mirror images: dividing by 0.99 twelve times travels further than dividing by 1.01 twelve times. The third run gives spot a 2% monthly climb and keeps the 1% premium, and it ends with spot at plus 26.82% and the fund at plus 12.55%.

Does contango always cost the fund money?

One step gets skipped in most explanations. A futures position does not tie up its full notional value. The fund posts margin and keeps the rest in short-dated cash instruments, and that collateral earns interest. A carry-shaped curve is priced off the same financing rate the collateral pays, so a good part of the slope and the interest offset each other. What stays visible is the piece of the slope beyond financing, with storage and scarcity the usual remainder.

The financing leg moves. The panel below tracks the front of the Treasury curve month by month.

QueryThe financing leg: 3 month and 1 year Treasury yields by month
The exact SQL behind every number
SELECT
    toString(toStartOfMonth(date))                AS month,
    formatDateTime(toStartOfMonth(date), '%b %Y') AS as_of_label,
    round(avg(yield_3_month), 2)                  AS yield_3m_pct,
    round(avg(yield_1_year), 2)                   AS yield_1y_pct
FROM global_markets.treasury_yields
WHERE date >= '2019-01-01'
  AND date <  '2026-08-01'
GROUP BY toStartOfMonth(date)
HAVING count() > 5
ORDER BY toStartOfMonth(date)
Run this yourself

The 3 month bill averaged 2.42% in Jan 2019 and 3.87% in Jul 2026, with the 1 year point beside it, over 91 months. Collateral earning almost nothing and collateral earning several points a year turn one identical curve slope into two different fund outcomes. This is also where price return versus total return earns its keep: income held inside a fund lands in one of those numbers and not the other.

What two funds on one commodity print

Curve effects are hard to see in one fund's chart, since the commodity's own swings dwarf them. Two funds written on the same commodity, set side by side, isolate more of it. The first panel measures two listed crude oil funds, USO and USL, over each calendar year, first session close to last session close.

QueryTwo listed crude oil funds, calendar year price change
The exact SQL behind every number
SELECT
    toString(toYear(date)) AS year,
    round((argMaxIf(toFloat64(close), date, ticker = 'USO')
         / argMinIf(toFloat64(close), date, ticker = 'USO') - 1) * 100, 2) AS uso_pct,
    round((argMaxIf(toFloat64(close), date, ticker = 'USL')
         / argMinIf(toFloat64(close), date, ticker = 'USL') - 1) * 100, 2) AS usl_pct,
    round(((argMaxIf(toFloat64(close), date, ticker = 'USO')
          / argMinIf(toFloat64(close), date, ticker = 'USO'))
         - (argMaxIf(toFloat64(close), date, ticker = 'USL')
          / argMinIf(toFloat64(close), date, ticker = 'USL'))) * 100, 2) AS gap_pct
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('USO', 'USL')
  AND date >= '2021-01-01'
  AND date <  '2026-01-01'
GROUP BY toYear(date)
HAVING countIf(ticker = 'USO') > 100
   AND countIf(ticker = 'USL') > 100
ORDER BY year
Run this yourself

In 2025 the two fund columns printed -10.1% and -13.51% in the order they appear, and the gap column beside them printed 3.41 percentage points. Read that column down all 5 years: the distance between two funds on one commodity is no fixed annual toll, since each year is its own curve environment.

Endpoints hide the path, so the second panel rebases both funds to 100.00 at the start of the window and takes a reading at every month end.

QueryBoth funds rebased to 100 at the start of the window, month by month
The exact SQL behind every number
WITH
    month_close AS
    (
        SELECT
            toStartOfMonth(date)           AS m,
            ticker,
            argMax(toFloat64(close), date) AS px
        FROM global_markets.stocks_daily_aggs
        WHERE ticker IN ('USO', 'USL')
          AND date >= '2021-01-01'
          AND date <  '2026-01-01'
        GROUP BY toStartOfMonth(date), ticker
    ),
    first_close AS
    (
        SELECT
            ticker,
            argMin(px, m) AS px0
        FROM month_close
        GROUP BY ticker
    )
SELECT
    toString(mc.m)                                           AS month,
    formatDateTime(mc.m, '%b %Y')                            AS as_of_label,
    round(anyIf(mc.px / fc.px0 * 100, mc.ticker = 'USO'), 2) AS uso_index,
    round(anyIf(mc.px / fc.px0 * 100, mc.ticker = 'USL'), 2) AS usl_index
FROM month_close AS mc
INNER JOIN first_close AS fc ON mc.ticker = fc.ticker
GROUP BY mc.m
ORDER BY mc.m
Run this yourself

Across 60 month ends the lines begin together at 100 and 100 and finish at 196.59 and 183.27 as of Dec 2025. Two cautions on reading it. These are price changes rather than total returns, and fees plus each fund's own execution sit inside the distance along with the curve. A fund's market price can also drift from the value of what it holds, which premium and discount to NAV covers separately.

Structures built around the roll

Product design has answered the roll in a few ways over the years:

  • laddering holdings across a range of delivery months, so no single expiry dominates a fund
  • picking the contract by measured curve shape rather than by the calendar
  • holding the physical commodity where storage is practical, which swaps the roll for storage and insurance
  • spreading across many commodities, so one steep curve is a smaller share of the whole

None of these flattens a curve. A ladder swaps one large monthly roll for several small ones and moves exposure away from the front of the curve, where the commodity's own moves are sharpest. A contract picked by curve shape can settle in months that trade thinner. Which method a given fund follows today sits in that fund's prospectus, and prospectuses get amended, so no strategy is attached to a ticker here.

Roll decay and leveraged ETF decay are separate

Both get called decay. They start in different places.

Daily rebalancing decay comes from resetting exposure at the end of every session. A fund tracking a multiple of one day's move buys after gains and sells after losses to hold that multiple, and a choppy path costs it. It appears in funds with no commodity and no futures curve in them anywhere.

Roll decay comes from the shape of a futures curve. It appears at one times exposure with no rebalancing at all, in a fund that holds front contracts and rolls them forward.

They stack where a product does both. A leveraged commodity futures fund resets daily and rolls monthly. A leveraged equity fund carries the reset alone. A fund holding metal in a vault carries neither. Sorting out which one is in front of you is most of the work of reading a commodity product, and it is a question about mechanics rather than about the commodity's direction.

FAQ

What is contango in simple terms?

Contango is a futures curve where contracts for later delivery cost more than contracts for delivery soon. A fund rolling forward along that curve replaces each expiring contract with a pricier one, and the same money then buys fewer contracts.

Does contango always mean a commodity ETF loses money?

No. Contango sets the roll against the fund and backwardation sets it in the fund's favor, and the commodity's own price move is usually larger than either. Interest earned on the collateral behind the futures offsets part of a carry-shaped curve.

Can a commodity ETF fall while the commodity price stays flat?

Yes, and that is the case worth understanding. In the twelve roll example above, spot sits at 100.00 all year and the fund finishes at 88.74 against a 1% monthly premium on the next contract.

Is roll yield the same thing as leveraged ETF decay?

No. Roll yield comes from the shape of the futures curve and appears at one times exposure. Rebalancing decay comes from resetting a leveraged fund every day and appears with no futures anywhere near it. A single fund can carry both.

How do I tell whether a fund is exposed to the roll?

Read what it holds. A fund holding futures contracts has to roll them; a fund holding the physical commodity in a vault does not. The fund's own documents state which delivery months it holds and when it moves between them.

Data notes
  • every window is pinned to fixed calendar dates, so these figures do not move as new sessions arrive
  • the fund panels report price changes computed from daily closes, not total returns
  • calendar year figures run from a year's first session close to its last session close, not from the prior December close
  • the Treasury panel averages each month's daily readings for the 3 month and 1 year points

Every panel above ships with the SQL that produced it, so expand one and the arithmetic is in the open. To run the same comparison on another commodity or another window, ask it in plain English on the Strasmore terminal.

#etfs#futures#contango#commodities#tracking error