Lesson 3.2.1 · 65 min

Calendar spreads, butterflies and condors

Buying one month of a future and selling another of the same market removes almost all exposure to the direction of the price and leaves a different one: the shape of the curve. This lesson builds calendars, butterflies and condors with each session's contracts, measures how much risk remains in the course's synthetic markets and why the margin falls in the same proportion.

Fig. — Daily risk of an outright, a calendar spread, a butterfly and a condor in six markets, 2025
Level 3 lessons
  1. 3.1 · Futures in depth

    1. 3.1.1 Main futures contracts
    2. 3.1.2 Futures curve: basis, cost of carry, contango and roll yield
    3. 3.1.3 Roll management, margins, SPAN and implied leverage
    4. 3.1.4 Diversified futures systems: Carver's approach
  2. 3.2 · Futures spreads

    1. 3.2.1 Calendar spreads, butterflies and condors
    2. 3.2.2 Intercommodity spreads: crack, spark and crush
    3. 3.2.3 Rate and index spreads: 2s10s, NOB, NQ/ES and Europe versus the U.S.
    4. 3.2.4 Hedge ratios: by notional, by DV01 and by volatility
    5. 3.2.5 Spread data and backtesting: listed, synthetic and legged
    6. 3.2.6 Seasonality of spreads: multi-year analysis, patterns and traps
  3. 3.3 · Advanced forex

    1. 3.3.1 FX microstructure: OTC, ECN, last look and sessions
    2. 3.3.2 Carry and crash risk, momentum and value in currencies; baskets
    3. 3.3.3 Currency risk in multi-currency portfolios; execution via OANDA, IBKR or MT5
  4. 3.4 · Option theory and pricing

    1. 3.4.1 Payoffs, put-call parity and arbitrage bounds
    2. 3.4.2 Binomial trees, Black-Scholes-Merton, Black-76 and Monte Carlo
    3. 3.4.3 Greeks: delta, gamma, vega, theta, rho, vanna, volga and charm
    4. 3.4.4 Implied volatility: Newton-Raphson, Brent, py_vollib and QuantLib
    5. 3.4.5 Smile, skew and term structure; volatility surface with SVI
    6. 3.4.6 Realized vs implied volatility; volatility risk premium; VIX and VSTOXX
    7. 3.4.7 Advanced models: Heston, SABR and local volatility
  5. 3.5 · Option structures in Python

    1. 3.5.1 Directional and vertical structures: call, put, covered call, collar and spreads
    2. 3.5.2 Volatility and range structures: straddles, strangles, condors and butterflies
    3. 3.5.3 Calendars, diagonals, ratios, backspreads, risk reversals and jade lizard
    4. 3.5.4 A custom tool: payoff, aggregate Greeks and scenarios
    5. 3.5.5 Management: adjustments, rolls, early closes, early assignment and pin risk
  6. 3.6 · Systematic volatility and options strategies

    1. 3.6.1 Systematic premium selling: put-write, strangles and their tail risk
    2. 3.6.2 Delta hedging, gamma scalping and dispersion
    3. 3.6.3 VIX and VSTOXX strategies: volatility futures and ETPs
    4. 3.6.4 Options backtesting: data, fills and event adjustments
  7. 3.7 · Crypto derivatives and specific strategies

    1. 3.7.1 Perpetuals: funding mechanics, mark price and liquidations
    2. 3.7.2 Funding arbitrage, basis with futures and cross-exchange arbitrage
    3. 3.7.3 Crypto options, on-chain data and DeFi
    4. 3.7.4 ccxt for trading on multiple exchanges
    5. 3.7.5 MiCA: the EU framework for crypto-assets and their service providers (CASPs)

A spread buys one month and sells another

A crude oil future is not one contract: it is eighteen, one per delivery month, all alive at the same time (lesson 1.2.3). Buying one and selling another of the same market is a calendar spread (also intra-commodity spread). The two contracts respond almost identically to whatever moves the price of crude, so the direction largely cancels out. What remains is the difference between them: the slope of the curve between those two months, which depends on the cost of carrying the underlying, on storage and on the scarcity of the moment (lesson 3.1.2).

The curves in this lesson are those of level 3 (futuros3_curvas.parquet): 23 fictitious markets that imitate real contracts, with the settlement of all listed contracts in every session from 2010 to 2025. They are synthetic data: the levels resemble history, but they are not market data.

IN [01]python
import numpy as np
import pandas as pd

cur = pd.read_parquet("futuros3_curvas.parquet")
ctr = pd.read_csv("futuros3_contratos.csv",
                  parse_dates=["vencimiento", "fecha_roll"])
esp = pd.read_csv("futuros3_especificaciones.csv",
                  index_col="mercado")
dia = cur[(cur["mercado"] == "PEC")
          & (cur["fecha"] == "2025-06-04")].merge(
    ctr[["contrato", "vencimiento"]]).sort_values("vencimiento")
print(len(dia), "PEC contracts listed on 2025-06-04")
dia[["contrato", "vencimiento", "liquidacion", "volumen"]].head(4)
OUT [01]The crude oil curve in one session
18 PEC contracts listed on 2025-06-04
   contrato vencimiento  liquidacion  volumen
6    PECN25  2025-06-20        53.35   256676
8    PECQ25  2025-07-22        53.23   288298
10   PECU25  2025-08-20        53.23    93695
12   PECV25  2025-09-22        53.32    34244

PEC imitates NYMEX WTI (1,000 barrels per contract). The July contract (PECN25) expires on June 20, but it already has less volume than the August one: liquidity has moved to the next contract before expiration, which is what the fecha_roll column of futuros3_contratos.csv marks. To build structures it is convenient to number the legs starting from the contract being held, the first one that has not yet reached its roll date:

IN [02]python
def patas(mercado, n=4):
    """1st, 2nd… contracts of each session. The 1st is the one
    held: the first that has not yet reached its fecha_roll."""
    c = cur[cur["mercado"] == mercado].merge(
        ctr[["contrato", "vencimiento", "fecha_roll"]],
        on="contrato")
    c = c[c["fecha"] < c["fecha_roll"]]
    c = c.sort_values(["fecha", "vencimiento"])
    c["k"] = c.groupby("fecha").cumcount() + 1
    return c[c["k"] <= n]

pec = patas("PEC")
pec.loc[pec["fecha"] == "2025-06-04",
        ["k", "contrato", "liquidacion"]]
OUT [02]The legs in order of expiration
       k contrato  liquidacion
68564  1   PECN25        53.35
68566  2   PECQ25        53.23
68568  3   PECU25        53.23
68570  4   PECV25        53.32

With the legs numbered, each structure is a vector of weights on them. The calendar is the difference of two legs; the butterfly, the difference of two consecutive calendars that share the center leg; the condor, the difference of two calendars that do not share it:

Outright +1Calendar +1 −1Butterfly +1 −2 +1Condor +1 −1 −1 +1
Mermaid source
flowchart LR
  S[Outright +1] --> C[Calendar +1 −1]
  C --> B[Butterfly +1 −2 +1]
  C --> K[Condor +1 −1 −1 +1]
StructureWeights (1st, 2nd, 3rd, 4th)PriceWhat it neutralizesWhat it bets on
Outright+1F1F_1nothingdirection
Calendar+1, −1F1−F2F_1 - F_2almost all the directionslope between two months
Butterfly+1, −2, +1F1−2F2+F3F_1 - 2F_2 + F_3direction and slopecurvature around the 2nd
Condor+1, −1, −1, +1(F1−F2)−(F3−F4)(F_1 - F_2) - (F_3 - F_4)direction and almost all the slopecurvature over a wider section

Buying the 1st − 2nd calendar (long the near, short the next) gains if the near contract rises more than the next one: if the curve steepens in backwardation or flattens in contango. Buying the butterfly gains if the center month falls relative to the average of its neighbors.

The level series and the P&L with the same contracts

The easiest series to build is the spread level: in each session, the settlement of the 1st minus that of the 2nd.

IN [03]python
niv = pec.pivot(index="fecha", columns="k", values="liquidacion")
cal = niv[1] - niv[2]                    # USD per barrel
print(cal.describe().round(2).to_string())
print(f"Sessions in backwardation (1st > 2nd): {(cal > 0).mean():.1%}")
OUT [03]The 1st − 2nd crude oil spread
count    4024.00
mean        0.15
std         1.64
min        -3.54
25%        -1.04
50%         0.14
75%         1.15
max         4.94
Sessions in backwardation (1st > 2nd): 53.5%

The spread of the synthetic crude ranges from −3.54 USD per barrel (deep contango) to +4.94 (backwardation) and spends slightly more than half of the sessions in backwardation. That series is useful to describe the curve, but it is nobody's P&L. On the roll date, “1st” and “2nd” become other contracts and the series jumps without any position having gained or lost that jump, the same problem as the continuous series of lesson 1.4.6:

IN [04]python
rolls = ctr.loc[ctr["mercado"] == "PEC", "fecha_roll"]
salto = cal.diff()
en_roll = salto.index.isin(rolls)
print(f"Mean absolute change on roll days: "
      f"{salto[en_roll].abs().mean():.3f} USD/barrel")
print(f"Other sessions:                    "
      f"{salto[~en_roll].abs().mean():.3f} USD/barrel")
OUT [04]The roll jump in the level series
Mean absolute change on roll days: 0.270 USD/barrel
Other sessions:                    0.156 USD/barrel

On roll days the series changes on average 70% more than on other days, and that change is an artifact. The correct P&L is computed with the contracts the position held at the previous close: the weights are fixed with the legs of session t−1t-1 and applied to the changes of those same contracts between t−1t-1 and tt. With MM the multiplier:

P&Lt=M∑kwk(Fk,t−Fk,t−1),k fixed at t−1.\text{P\&L}_t = M \sum_k w_k \left(F_{k,t} - F_{k,t-1}\right), \qquad k \text{ fixed at } t-1 .
IN [05]python
def cambios(mercado, n, log=False):
    """Daily change of each leg k = 1..n with the contracts the
    structure held at the previous close (no roll jumps)."""
    p = patas(mercado, n)
    px = cur[cur["mercado"] == mercado].pivot(
        index="fecha", columns="contrato", values="liquidacion")
    d = (np.log(px) if log else px).diff().stack()
    ley = p.pivot(index="fecha", columns="k",
                  values="contrato").shift(1).dropna()
    out = {k: d.reindex(pd.MultiIndex.from_arrays(
        [ley.index, ley[k]])).to_numpy() for k in ley.columns}
    return pd.DataFrame(out, index=ley.index)

def pyg(mercado, pesos):
    """Daily P&L in the contract's currency, per structure."""
    c = cambios(mercado, len(pesos))
    mult = esp.loc[mercado, "multiplicador"]
    return (c @ np.array(pesos) * mult).dropna()

ESTRUCTURAS = {"simple": [1], "calendario": [1, -1],
               "butterfly": [1, -2, 1], "condor": [1, -1, -1, 1]}
vol = pd.DataFrame({m: {n: pyg(m, w).loc["2025"].std()
                        for n, w in ESTRUCTURAS.items()}
                    for m in ["RVA", "MTO", "PEC", "PEG",
                              "GRM", "GNA"]}).T
vol.round(0)
OUT [05]P&L with the same contracts
     simple  calendario  butterfly  condor
RVA  3957.0        41.0       25.0    24.0
MTO  1622.0        15.0       10.0     8.0
PEC  1258.0       149.0       29.0    46.0
PEG  1816.0       321.0      100.0   159.0
GRM   302.0        53.0       28.0    35.0
GNA  1695.0       637.0      639.0   596.0

The table is the figure at the top of the lesson: standard deviation of the daily P&L per contract in 2025, in USD. In the RVA index (it imitates the E-mini S&P 500), the calendar moves one hundredth of what the future moves; in gold, even less. In crude, the calendar keeps 12% of the risk and the butterfly 2.3%. In corn and gasoline, the calendar keeps about 18%. Natural gas is the exception this lesson explains further below: its butterfly (639 USD) moves as much as its calendar (637).

What remains of the direction

A calendar is not direction neutral by construction. If the two contracts moved by the same amount, their difference would have no risk; they do not. In commodities, the near contract reacts more to shocks that are expected to revert (the Samuelson effect, measured in lesson 3.1.2), so long the 1st and short the 2nd keeps a small long:

IN [06]python
for m in ["RVA", "PEC", "GRM", "GNA"]:
    d = pd.concat([pyg(m, [1]), pyg(m, [1, -1])], axis=1,
                  keys=["simple", "cal"]).dropna()
    beta = d.cov().loc["cal", "simple"] / d["simple"].var()
    print(f"{m}: beta {beta:+.3f}  correlation "
          f"{d.corr().loc['cal', 'simple']:+.2f}")
OUT [06]How much direction remains in the calendar
RVA: beta -0.003  correlation -0.20
PEC: beta +0.106  correlation +0.92
GRM: beta +0.238  correlation +0.89
GNA: beta +0.228  correlation +0.74

In crude, for every 1,000 USD the near leg gains, the calendar gains 106. It is small in size, but the correlation of 0.92 says that the crude calendar is, above all, a scaled-down version of the outright position. In corn and natural gas the beta is larger (0.24 and 0.23). Whoever trades a calendar as if it were “neutral” and combines it with an outright position in the same market is adding exposure, not hedging it. The hedges that correct this beta (by notional, by DV01, by volatility) are the subject of lesson 3.2.4.

In the index the beta is almost zero, for a different reason: an index calendar does not depend on shocks that revert, but on financing. An index future is worth the spot price compounded at the interest rate minus dividends (lesson 3.1.2), so the difference between two expirations is, almost exactly, the net financing of the section that separates them:

F2−F1≈S (r−q) (τ2−τ1).F_2 - F_1 \approx S \, (r - q) \, (\tau_2 - \tau_1).
IN [07]python
idx = pd.read_csv("futuros3_indices_contado.csv",
                  parse_dates=["fecha"])
s = idx[idx["indice"] == "RVA"].set_index("fecha")
rva = patas("RVA", 2)
f = rva.pivot(index="fecha", columns="k", values="liquidacion")
v = rva.pivot(index="fecha", columns="k", values="vencimiento")
dias = v.sub(v.index.to_series(), axis=0).apply(
    lambda c: c.dt.days)
carry = (s["tipo_1m"] - s["rentabilidad_dividendo"]) / 100
teo = s["contado"].to_numpy()[:, None] * np.exp(
    carry.to_numpy()[:, None] * dias / 365)
eq = pd.DataFrame({"real": f[2] - f[1],          # 2nd − 1st
                   "teorico": teo[2] - teo[1]}).dropna()
print(f"Correlation: {eq.corr().iloc[0, 1]:.4f}; median "
      f"difference {(eq['real'] - eq['teorico']).abs().median():.2f}"
      " points")
eq.resample("YE").mean().round(1).loc["2019":]
OUT [07]An index calendar is financing
Correlation: 0.9997; median difference 0.14 points
            real  teorico
fecha
2019-12-31   2.9      3.0
2020-12-31  -7.9     -7.9
2021-12-31 -11.7    -11.7
2022-12-31   2.0      1.8
2023-12-31  30.1     30.1
2024-12-31  41.4     41.6
2025-12-31  39.1     38.9

The financing formula explains the index calendar with a correlation of 0.9997 and a median error of 0.14 points, less than its tick (0.25). With rates below the dividend yield (2020 and 2021), the deferred contract trades below the near one; with rates of 4-5% (2023-2025), about 40 points above. For whoever holds the future and rolls it every quarter, that spread is the price of the roll (lesson 3.1.3). For whoever trades it, it is a bet on short-term rates and dividends, not on the stock market.

CalendarIndex: r − qBond: coupon and repoStorable: inventoryGas: season
Mermaid source
flowchart TB
  C[Calendar] --> I["Index: r − q"]
  C --> B["Bond: coupon and repo"]
  C --> A[Storable: inventory]
  C --> E[Gas: season]

What moves a calendar depends on the market: net financing in indices and currencies; the coupon, the financing and the cheapest-to-deliver bond in bonds; storage and scarcity in crude, metals and grains; and, in natural gas and electricity, the jump between seasons.

Reduced margins

A clearing house does not require margin per leg, but for the risk of the portfolio (the SPAN of lesson 3.1.3). If a calendar moves 12% of what the outright position moves, its margin can be, approximately, 12% of the outright position's. In the data, margen_spread_calendario was set this way: the same number of daily standard deviations that the outright margin covers, applied to the standard deviation of the spread. The calculation with the 23 markets:

IN [08]python
base = esp[esp["mercado_base"].isna()].index
filas = {}
for m in base:
    s1 = pyg(m, [1]).loc["2025"].std()
    s2 = pyg(m, [1, -1]).loc["2025"].std()
    filas[m] = {"riesgo": s2 / s1,
                "margen": esp.loc[m, "margen_spread_calendario"]
                / esp.loc[m, "margen_inicial"]}
rel = pd.DataFrame(filas).T.sort_values("riesgo")
print(rel.round(3).T.to_string())
OUT [08]Spread margin versus outright margin
          RVH    MTO    RVC   RVB   RVA    RVE    MTP    DQE    TPD    TPE    TPC    TPB    PEC    TPA    PEH    GRM    PEG    GRT    GRS    GRA    GRH    GNA    ELB
riesgo  0.004  0.009  0.009  0.01  0.01  0.012  0.013  0.022  0.033  0.045  0.053  0.073  0.118  0.136  0.146  0.175  0.177  0.181  0.185  0.192  0.199  0.376  0.389
margen  0.030  0.030  0.030  0.03  0.03  0.029  0.030  0.029  0.033  0.043  0.052  0.072  0.120  0.140  0.147  0.179  0.174  0.186  0.178  0.188  0.195  0.388  0.438
Fig. — Calendar spread margin against its risk, relative to the outright, in the 23 markets

From bonds to electricity, the two rows almost coincide: the calendar margin is proportional to its risk, from 3.3% of the outright margin in the 30-year bond (TPD) to 44% in electricity (ELB). The indices, gold, silver and the currency stay at 3%, even though their relative risk is between 0.4 and 2.2%: the data generator applies a floor of 3% of the outright margin. Real clearing houses also apply minimums and surcharges to spreads (for example, between expirations that cross a delivery period) and publish them with their margin parameters.

A reduced margin is, above all, leverage:

IN [09]python
m = esp.loc["PEC"]
precio = niv[1].loc["2025"].median()
print(f"Notional of one leg: {10 * precio * m['multiplicador']:,.0f}"
      " USD")
print(f"Margin for 10 separate longs and 10 shorts: "
      f"{20 * m['margen_inicial']:,.0f} USD")
print(f"Margin for 10 calendar spreads: "
      f"{10 * m['margen_spread_calendario']:,.0f} USD")
OUT [09]Ten spreads versus ten outright positions
Notional of one leg: 618,500 USD
Margin for 10 separate longs and 10 shorts: 112,000 USD
Margin for 10 calendar spreads: 6,700 USD

With 6,700 USD of margin you control 618,500 USD of notional in each leg, more than 1.2 million across the two. The notional figure says nothing about the risk, which is that of the spread; but the margin does not say how much can be lost either.

Butterflies and condors: curvature

If the calendar leaves the slope, the butterfly tries to remove it as well. To see what remains you need to know how the whole curve moves, and that is what a principal component analysis (PCA) is for: the eigenvectors of the covariance matrix of the daily changes of the first twelve contracts. In rate and commodity curves, the first three are usually read as level (the whole curve at once), slope (the short end against the long end) and curvature (the center against the ends). In natural gas:

IN [10]python
r = cambios("GNA", 12, log=True).dropna()
val, vec = np.linalg.eigh(np.cov(r.T))
val, vec = val[::-1], vec[:, ::-1]
vec = vec * np.sign(vec[0])           # positive loading on the 1st
print("Explained variance:", (val[:4] / val.sum()).round(3))
cargas = pd.DataFrame(vec[:, :3], index=r.columns,
                      columns=["nivel", "pendiente", "curvatura"])
cargas.T.round(2)
OUT [10]Level, slope and curvature of natural gas
Explained variance: [0.841 0.118 0.022 0.008]
             1     2     3     4     5     6     7     8     9     10    11    12
nivel      0.50  0.42  0.36  0.31  0.27  0.24  0.22  0.20  0.19  0.18  0.17  0.17
pendiente  0.55  0.31  0.11 -0.04 -0.16 -0.24 -0.29 -0.31 -0.31 -0.29 -0.27 -0.25
curvatura  0.38  0.02 -0.28 -0.41 -0.40 -0.30 -0.09  0.10  0.25  0.33  0.32  0.27
Fig. — The first three principal components of daily changes in the gas curve, 2010–2025

The level explains 84.1% of the variance and loads more on the near contracts (the Samuelson effect again: from 0.50 on the first to 0.17 on the twelfth). The slope (11.8%) separates the first four from the rest. The curvature (2.2%) is positive at the ends and negative in the center. The exposure of each structure to each factor is the product of its weights by the loadings, and the split of its variance among the three factors, that exposure squared times the variance of each one:

IN [11]python
pesos = pd.DataFrame({n: (w + [0] * 12)[:12]
                      for n, w in ESTRUCTURAS.items()},
                     index=cargas.index)
expo = pesos.T @ cargas
print(expo.round(2))
reparto = expo ** 2 * val[:3]
(100 * reparto.div(reparto.sum(axis=1), axis=0)).round(0)
OUT [11]Which factor each structure is exposed to
            nivel  pendiente  curvatura
simple       0.50       0.55       0.38
calendario   0.08       0.24       0.35
butterfly    0.02       0.04       0.05
condor       0.03       0.09       0.23
            nivel  pendiente  curvatura
simple       84.0       14.0        1.0
calendario   37.0       45.0       19.0
butterfly    58.0       33.0        9.0
condor       31.0       29.0       40.0

The calendar reduces the exposure to the level from 0.50 to 0.08, and its variance within these three factors is split between level (37%), slope (45%) and curvature (19%): it is above all a slope bet with some level, the beta of the previous section. The butterfly is barely exposed to any of the three (0.02, 0.04 and 0.05): its +1 −2 +1 weights are almost orthogonal to the three loadings, and how its small exposure is split matters little. The condor is exposed mainly to curvature (0.23). According to this PCA, the gas butterfly should have very little risk; the table at the beginning says that in 2025 it moved 639 USD a day, as much as the calendar. What is missing is not in these three factors.

But the PCA by order of expiration averages contracts of different months: the 3rd is October in July and March in December. In a market with seasons, what matters is which delivery months the structure crosses. Natural gas has a winter factor (in the data model, it loads +0.5 from November to March and −0.5 from April to October), and a butterfly whose wings fall on the two sides of that jump does not neutralize it:

IN [12]python
g = cur[cur["mercado"] == "GNA"]
mes = g["contrato"].str[3].map("FGHJKMNQUVXZ".index)
n = (g["contrato"].str[4:].astype(int) + 2000) * 12 + mes
px = g.assign(n=n).pivot(index="fecha", columns="n",
                         values="liquidacion")
d = px.diff() * esp.loc["GNA", "multiplicador"]
hoy = px.index.year * 12 + px.index.month - 1
por_mes = {}
for c in px.columns[1:-1]:
    fly = d[c - 1] - 2 * d[c] + d[c + 1]
    ok = (c - hoy >= 3) & (c - hoy <= 9) & (px.index.year >= 2016)
    por_mes.setdefault("FGHJKMNQUVXZ"[c % 12], []).append(fly[ok])
riesgo = pd.Series({k: pd.concat(v).std()
                    for k, v in por_mes.items()})
riesgo.reindex(list("FGHJKMNQUVXZ")).round(0).to_frame("usd_dia").T
OUT [12]Natural gas butterflies by the months they cross
            F     G      H      J     K     M     N     Q     U      V      X     Z
usd_dia  54.0  55.0  391.0  489.0  45.0  31.0  43.0  44.0  71.0  422.0  511.0  35.0
Fig. — Daily risk of a consecutive-month gas butterfly by the month of its middle leg, 2016–2025

Each column is a butterfly of three consecutive delivery months, identified by its center month (futures month code: F January, G February… Z December), when it has 3 to 9 months left. Eight of the twelve move between 31 and 71 USD a day. The four that cross a change of season—centered in March and April (February-March-April and March-April-May) and in October and November—move between 391 and 511: seven or eight times more. They are the structures that turn a gas butterfly into a bet on the winter, and the ones that most surprise whoever treats it as “almost riskless”.

Listed spreads and sign conventions

Calendars, butterflies and condors are not only built leg by leg: exchanges list them as instruments with their own order book. An order on the spread is filled in full or not at all, without the risk of being left with a single leg. In the data, spreads3_cotizados.parquet has the closing book of the 1st − 2nd calendar of six markets:

IN [13]python
cot = pd.read_parquet("spreads3_cotizados.parquet")
fila = cot[(cot["mercado"] == "PEC")
           & (cot["fecha"] == "2025-06-04")].iloc[0]
print(fila[["spread", "liquidacion", "bid", "ask"]].to_string())
leg = niv.loc["2025-06-04"]
print(f"1st − 2nd with the settlements: {leg[1] - leg[2]:.2f}")
OUT [13]The book of a quoted spread
spread         PECN25-PECQ25
liquidacion             0.12
bid                     0.12
ask                     0.13
1st − 2nd with the settlements: 0.12

The PECN25-PECQ25 spread settles at 0.12 USD per barrel, the difference of the legs' settlements, and its book quotes one tick wide (0.12 to 0.13). Comparing that book with the “synthetic” spread obtained by crossing the bid-ask spreads of the two legs, and what it costs to execute one leg and wait for the other (legging risk), is the subject of lesson 3.2.5.

Before sending an order on a listed spread you have to read its sign convention, because it is not the same for every product. The CME Globex documentation defines, among others:

Globex typeExampleBuying the spreadPrice
SP, standard calendarNGZ9-NGF0buys leg 1 (near), sells leg 2leg 1 − leg 2
EQ, equity index calendarESU9-ESZ9sells leg 1 (near), buys leg 2leg 2 − leg 1
BF, butterflySR1:BF M9-U9-Z9buys leg 1, sells 2 of leg 2, buys leg 3leg 1 − 2 × leg 2 + leg 3
CF, condorSR1:CF M9U9Z9H0buys leg 1, sells legs 2 and 3, buys leg 4leg 1 − leg 2 − leg 3 + leg 4

What this changes in a system

  • The P&L, with the same contracts. A 1st − 2nd level series is useful to describe the curve; for a backtest you have to fix each day's contracts and roll the whole structure on a date decided in advance.
  • Neutral is not riskless. The crude calendar keeps a beta of 0.11 and a correlation of 0.92 with the outright position; the gas butterfly that crosses a season moves like a calendar.
  • The small margin is leverage. Position sizing in spreads is decided by their measured risk (and by their worst day), not by what the margin lets you open.
  • Each market has its own engine. In indices, the calendar is financing; in commodities, storage and seasons. A rule that works in one need not work in another.
  • The sign, first of all. Near minus deferred or deferred minus near: it depends on the exchange, the product and the data vendor.

Exercises

  1. Check that a natural gas condor is exactly the difference of two calendars (1st − 2nd and 3rd − 4th) and measure in 2025 the risk of each one and their correlation. Why does the condor (596 USD) not have less risk than the 1st − 2nd calendar (637)?
Solution
IN [14]python
c = cambios("GNA", 4) * esp.loc["GNA", "multiplicador"]
condor = c[1] - c[2] - c[3] + c[4]
dos_cal = (c[1] - c[2]) - (c[3] - c[4])
print("Identical:", np.allclose(condor.dropna(), dos_cal.dropna()))
d = pd.DataFrame({"cal_1_2": c[1] - c[2], "cal_3_4": c[3] - c[4]})
print(d.loc["2025"].std().round(0).to_string())
print(f"Correlation: {d.loc['2025'].corr().iloc[0, 1]:.2f}")
OUT [14]The condor, difference of two calendars
Identical: True
cal_1_2    637.0
cal_3_4    332.0
Correlation: 0.38

The condor is the difference of the two calendars. Subtracting two positions reduces risk only if they are highly correlated: with a correlation of 0.38 and risks of 637 and 332 USD, the variance of the condor is 6372+3322−2⋅0.38⋅637⋅332637^2 + 332^2 - 2 \cdot 0.38 \cdot 637 \cdot 332 and its standard deviation, about 596 USD. In natural gas, the near calendar and the deferred one move for different reasons (the near one, because of the current weather; the deferred one, because of the season jump it contains), so the condor cancels almost nothing. In crude, where consecutive calendars are very similar, the condor has a third of the risk of the calendar (46 versus 149 USD).

  1. With the margins from the PEC specification sheet, compare ten longs in the contract being held with ten 1st − 2nd calendars in 2016–2025: the worst day in USD, how many times the initial margin and in how many sessions the day's loss exceeds the entire margin.
Solution
IN [15]python
for nombre, w, marg in [
        ("10 longs", [1], 10 * m["margen_inicial"]),
        ("10 calendars", [1, -1],
         10 * m["margen_spread_calendario"])]:
    p = 10 * pyg("PEC", w).loc["2016":]
    peor = p.min()
    print(f"{nombre}: worst day {peor:,.0f} USD = "
          f"{-peor / marg:.1f} times the margin; "
          f"days exceeding the margin: {(p < -marg).sum()}")
OUT [15]The worst day in money and in margin
10 longs: worst day -92,800 USD = 1.7 times the margin; days exceeding the margin: 9
10 calendars: worst day -13,000 USD = 1.9 times the margin; days exceeding the margin: 10

In money, the calendar loses seven times less on its worst day (13,000 versus 92,800 USD). In proportion to the margin, it loses the same or more: 1.9 times the margin versus 1.7, and the day's loss exceeds the entire margin in nine sessions with the longs and in ten with the calendars. Since the spread margin was set with the same number of standard deviations as the outright, the leverage on the margin is the same; the spread is only “safer” if it is traded with the same number of contracts, not with the same margin. The margins in the specification sheet are those of 2025 applied to the whole decade: with those of each period, the comparison would change.

  1. A system buys 10 ESU9-ESZ9 spreads on CME (type EQ) thinking it is long September and short December. What position does it actually have and what happens to it if short-term rates rise and the dividend yield does not change?
Solution

It is short September and long December: in the EQ equity index calendar, buying the spread buys the deferred leg. The price of the spread is December minus September, which according to the financing formula is S⋅(r−q)⋅(τ2−τ1)S \cdot (r - q) \cdot (\tau_2 - \tau_1). If rr rises and qq does not change, the spread rises and the position, long the spread, gains. The system believed it had the opposite bet: if its intention was to profit from a fall in rates, by misreading the sign it has opened exactly the opposite. That is why the code should read the legs and the sign from the instrument definition, not from the name.

Each answer is folded under its question

Self-assessment

  1. In the crude oil of the data, the 1st − 2nd spread has a daily standard deviation of 149 USD per contract versus 1,258 for the outright position, and its beta to the near leg is +0.106. What does that beta mean?

    • AThat the spread has no exposure at all to the direction of crude
    • BThat the spread is cointegrated with crude
    • CThat for every 1,000 USD the near leg gains or loses, the calendar gains or loses about 106 in the same direction: the near contract moves more than the next one
    • DThat you must sell 0.106 contracts of the second month for each one of the first
    Show answer

    Answer C. That for every 1,000 USD the near leg gains or loses, the calendar gains or loses about 106 in the same direction: the near contract moves more than the next one

    It is the Samuelson effect from lesson 3.1.2: the near contract reacts more than the next one to shocks that revert, so long the near and short the next keeps a small long in crude. The correlation between the two P&Ls is 0.92: the calendar almost always moves in the direction of crude, but at a tenth of the size.

  2. How much initial margin do 10 PEC calendar spreads require versus 10 longs and 10 shorts of the same contracts treated separately?

    • A6,700 USD versus 112,000: the spread's margin follows its risk, 12% of the outright position's
    • BThe same: they are the same 20 legs
    • CHalf, because one leg offsets the other
    • DNothing, because the net risk is zero
    Show answer

    Answer A. 6,700 USD versus 112,000: the spread's margin follows its risk, 12% of the outright position's

    The clearing house looks at the risk of the portfolio, not the number of legs (lesson 3.1.3). The margin of the PEC calendar is 670 USD versus 5,600 for the outright position: 12%, the same proportion as its daily standard deviation. With that margin you control a notional of 618,500 USD per leg.

  3. A natural gas butterfly of consecutive months with the center leg in November has a daily risk of 511 USD; with the center in June, 31. Why?

    • ABecause there is less liquidity in November
    • BBecause the November contract expires earlier
    • CBy chance: with ten years of data the difference means nothing
    • DBecause its wings cross the change from summer to winter: the gas winter factor loads with opposite signs in October and November, and the +1 −2 +1 weights do not neutralize it
    Show answer

    Answer D. Because its wings cross the change from summer to winter: the gas winter factor loads with opposite signs in October and November, and the +1 −2 +1 weights do not neutralize it

    A butterfly neutralizes level and slope if the curve is smooth across its three months. The gas curve has a jump between October and November (and another between March and April): any butterfly that crosses it is exposed to that factor. The four that cross it give between 391 and 511 USD; the eight that do not, between 31 and 71.

  4. On CME, buying the ESU9-ESZ9 spread (equity index calendar, type EQ) means:

    • ABuying September and selling December; the price is September minus December
    • BSelling September and buying December; the price is December minus September
    • CBuying both contracts
    • DThe same as buying NGZ9-NGF0: buying the first leg
    Show answer

    Answer B. Selling September and buying December; the price is December minus September

    The Globex documentation flags it as an exception: in the standard calendar (SP, e.g. NGZ9-NGF0) buying the spread buys the first leg and the price is leg 1 − leg 2; in the equity index calendar (EQ), buying buys the deferred leg and the price is deferred − near. Misreading the convention reverses the position.

  5. In the indices, metals and currency of the data, the calendar margin is 3% of the outright margin, even though its risk is only 0.4 to 2.2% of the outright position's. What does that indicate?

    • AAn error in the data
    • BA floor: the margin does not go below a minimum, so in those markets it covers many more daily standard deviations of the spread than in the rest
    • CThat the risk of those spreads is greater than the standard deviations say
    • DThat those markets do not allow calendar spreads
    Show answer

    Answer B. A floor: the margin does not go below a minimum, so in those markets it covers many more daily standard deviations of the spread than in the rest

    The generator sets the spread margin with the same number of standard deviations as the outright and a floor of 3% of the outright margin. Where the spread barely moves (pure financing in indices, gold and currency) the floor rules: the margin of the RVA index calendar covers about 15 daily standard deviations of its P&L, versus 5 for the outright position.

Key points

  • A calendar spread buys one month and sells another of the same market: it removes almost all the direction and leaves the slope of the curve. In the data, its daily risk ranges from 0.4% (indices) to 39% (electricity) of the outright position's.
  • The P&L of a structure is computed with the same contracts from one day to the next; the 1st − 2nd level series jumps at every roll.
  • Butterfly (+1 −2 +1) and condor (+1 −1 −1 +1) also neutralize almost all the slope: they bet on curvature. In natural gas, the one that crosses the change between seasons has seven or eight times the risk of the others.
  • The spread margin follows its risk (12% of the outright in crude) with a floor; the notional leverage is huge and the worst day of 2016–2025 for 10 crude calendars cost 1.9 times their margin.
  • In an index, the calendar is financing: the 2nd − 1st difference follows S·(r − q)·Δτ with a correlation of 0.9997.
  • Sign conventions change by product: on CME the standard calendar buys the near leg; the equity index one, the deferred leg.

Resources