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.
- Module
- 3.2 · Futures spreads
- Duration
- 65 min
- Prerequisites
- 1.2.3 · Futures: specifications, margin, roll and contango, 1.4.6 · Continuous futures series: roll rules, difference adjustment and ratio adjustment, 3.1.2 · Futures curve: basis, cost of carry, contango and roll yield
- Author
- Jesús Cuesta
- Updated
Level 3 lessons
3.1 · Futures in depth
3.2 · Futures spreads
- 3.2.1 Calendar spreads, butterflies and condors
- 3.2.2 Intercommodity spreads: crack, spark and crush
- 3.2.3 Rate and index spreads: 2s10s, NOB, NQ/ES and Europe versus the U.S.
- 3.2.4 Hedge ratios: by notional, by DV01 and by volatility
- 3.2.5 Spread data and backtesting: listed, synthetic and legged
- 3.2.6 Seasonality of spreads: multi-year analysis, patterns and traps
3.3 · Advanced forex
3.4 · Option theory and pricing
- 3.4.1 Payoffs, put-call parity and arbitrage bounds
- 3.4.2 Binomial trees, Black-Scholes-Merton, Black-76 and Monte Carlo
- 3.4.3 Greeks: delta, gamma, vega, theta, rho, vanna, volga and charm
- 3.4.4 Implied volatility: Newton-Raphson, Brent, py_vollib and QuantLib
- 3.4.5 Smile, skew and term structure; volatility surface with SVI
- 3.4.6 Realized vs implied volatility; volatility risk premium; VIX and VSTOXX
- 3.4.7 Advanced models: Heston, SABR and local volatility
3.5 · Option structures in Python
- 3.5.1 Directional and vertical structures: call, put, covered call, collar and spreads
- 3.5.2 Volatility and range structures: straddles, strangles, condors and butterflies
- 3.5.3 Calendars, diagonals, ratios, backspreads, risk reversals and jade lizard
- 3.5.4 A custom tool: payoff, aggregate Greeks and scenarios
- 3.5.5 Management: adjustments, rolls, early closes, early assignment and pin risk
3.6 · Systematic volatility and options strategies
3.7 · Crypto derivatives and specific strategies
- 3.7.1 Perpetuals: funding mechanics, mark price and liquidations
- 3.7.2 Funding arbitrage, basis with futures and cross-exchange arbitrage
- 3.7.3 Crypto options, on-chain data and DeFi
- 3.7.4 ccxt for trading on multiple exchanges
- 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.
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)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 34244PEC 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:
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"]] k contrato liquidacion
68564 1 PECN25 53.35
68566 2 PECQ25 53.23
68568 3 PECU25 53.23
68570 4 PECV25 53.32With 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:
Mermaid source
flowchart LR S[Outright +1] --> C[Calendar +1 −1] C --> B[Butterfly +1 −2 +1] C --> K[Condor +1 −1 −1 +1]
| Structure | Weights (1st, 2nd, 3rd, 4th) | Price | What it neutralizes | What it bets on |
|---|---|---|---|---|
| Outright | +1 | nothing | direction | |
| Calendar | +1, −1 | almost all the direction | slope between two months | |
| Butterfly | +1, −2, +1 | direction and slope | curvature around the 2nd | |
| Condor | +1, −1, −1, +1 | direction and almost all the slope | curvature 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.
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%}")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:
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")Mean absolute change on roll days: 0.270 USD/barrel
Other sessions: 0.156 USD/barrelOn 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 and applied to the changes of those same contracts between and . With the multiplier:
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) 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.0The 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:
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}")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.74In 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:
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":]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.9The 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.
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:
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()) 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.438From 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:
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")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 USDWith 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:
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)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.27The 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:
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) 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.0The 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:
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 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.0Each 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:
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}")spread PECN25-PECQ25
liquidacion 0.12
bid 0.12
ask 0.13
1st − 2nd with the settlements: 0.12The 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 type | Example | Buying the spread | Price |
|---|---|---|---|
| SP, standard calendar | NGZ9-NGF0 | buys leg 1 (near), sells leg 2 | leg 1 − leg 2 |
| EQ, equity index calendar | ESU9-ESZ9 | sells leg 1 (near), buys leg 2 | leg 2 − leg 1 |
| BF, butterfly | SR1:BF M9-U9-Z9 | buys leg 1, sells 2 of leg 2, buys leg 3 | leg 1 − 2 × leg 2 + leg 3 |
| CF, condor | SR1:CF M9U9Z9H0 | buys leg 1, sells legs 2 and 3, buys leg 4 | leg 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
- 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
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}")Identical: True
cal_1_2 637.0
cal_3_4 332.0
Correlation: 0.38The 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 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).
- With the margins from the
PECspecification 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
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()}")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: 10In 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.
- A system buys 10
ESU9-ESZ9spreads 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 . If rises and 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
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.
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.
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.
On CME, buying the
ESU9-ESZ9spread (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.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.