---
title: "3.2.3 Rate and index spreads: 2s10s, NOB and NQ/ES"
description: "DV01-neutral rate spreads (2s10s, NOB) and index spreads (NQ/ES, Europe versus the U.S.): what they bet on, residual betas and currency risk."
url: "https://pythonparatrading.com/en/python-trading-course/level-3/interest-rate-and-index-spreads/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-3/interest-rate-and-index-spreads.png"
---

# 3.2.3 · Rate and index spreads: 2s10s, NOB, NQ/ES and Europe versus the U.S.

> With bond futures you bet on the slope of the curve without betting on the level of rates, and with index futures, on one market against another without betting on the stock market. In both cases the ratio between the legs decides what really remains in the position. This lesson builds the DV01-neutral 2s10s steepener and NOB, the notional-neutral and beta-neutral NQ/ES and the Europe versus U.S. spread, and measures what exposure they are left with.

- Course: [Level 3: Derivatives: futures, spreads, forex and options](https://pythonparatrading.com/en/python-trading-course/level-3/)
- Module: 3.2 · Futures spreads
- Duration: 60 min
- Prerequisites: [3.2.1 · Calendar spreads, butterflies and condors](https://pythonparatrading.com/en/python-trading-course/level-3/calendar-spreads-butterflies-condors/), [1.2.7 · Fixed income and interest rates; CFDs and leveraged products](https://pythonparatrading.com/en/python-trading-course/level-1/fixed-income-rates-and-leveraged-products/)

## By the end of this lesson

- Build a DV01-neutral rate spread with futures and explain why it gains or loses with the slope and not with the level.
- Measure with a regression the residual exposure of a steepener to the level and explain why it is not zero near zero rates.
- Build a notional-neutral and a beta-neutral index spread, and measure its relative drift and its exposure to the market.
- Distinguish the currency risk of a spread between indices in different currencies traded with futures and with cash.

## Betting on the slope, not on the level

The 2s10s slope is the difference between the 10-year yield and the 2-year yield (lesson 1.2.7). A view on it (that
the curve will steepen, or flatten) is not a view on rates: the curve can steepen with rising rates (the 10-year rises
more) or with falling rates (the 2-year falls more). To trade only the slope with futures you have to buy one and sell
another in such a way that a parallel move of the whole curve neither gains nor loses.

The tool is the **DV01** of each future: how many dollars a contract gains or loses if the yield of its reference bond
falls one basis point. The data (`tipos3_dv01.csv`) give it for the contract held of each rate future. They are
synthetic data: `TPA`, `TPB`, `TPC` and `TPD` imitate the U.S. Treasury 2, 5 and 10-year notes and bond (ZT, ZF, ZN,
ZB), and `TPE` the Bund; each contract is the forward price of a standard bond with a 6% coupon (the model's “CTD”,
with a conversion factor of 1).

```python
import numpy as np
import pandas as pd

dv = pd.read_csv("tipos3_dv01.csv", parse_dates=["fecha"])
cols = ["mercado", "contrato", "liquidacion", "rendimiento_ctd",
        "vencimiento_ctd_anos", "duracion_modificada", "dv01"]
dv.loc[dv["fecha"] == "2025-12-31", cols].set_index("mercado")
```
```output
        contrato  liquidacion  rendimiento_ctd  vencimiento_ctd_anos  duracion_modificada    dv01
mercado
TPA       TPAH26   104.273438           3.6651                 2.147                2.040   42.55
TPB       TPBH26   108.750000           3.7311                 4.447                3.973   43.21
TPC       TPCH26   112.546875           3.8286                 6.816                5.729   64.47
TPD       TPDH26   121.625000           4.1319                16.216               11.056  134.46
TPE       TPEH26   126.910000           2.5060                 8.878                7.240   91.88
```

A `TPC` contract gains or loses 64.47 USD per basis point; a `TPA` one, 42.55, even though its notional is twice as
large (200,000 versus 100,000): the 2-year bond is much less sensitive to rates. To match the DV01 of one `TPC`
contract you need $h = 64.47 / 42.55 = 1.52$ `TPA` contracts. That is the DV01-neutral hedge:

$$
\text{P\&L} = h \cdot \text{P\&L}_{\text{short}} - \text{P\&L}_{\text{long}}, \qquad
h = \frac{\text{DV01}_{\text{long}}}{\text{DV01}_{\text{short}}},
$$

where “short” and “long” refer to the maturity, not to the sign: the **steepener** is long $h$ contracts of the short
maturity and short one of the long maturity.

```mermaid
flowchart TB
  M[Curve moves] --> P[Parallel]
  M --> E[Steepens]
  M --> A[Flattens]
  P --> P0[Steepener: 0]
  E --> E1[Steepener: gains]
  A --> A1[Steepener: loses]
```

The NOB (*notes over bonds*) is the same spread between the 10-year note and the bond. The ratio is not fixed: the
DV01 of each future changes with the level of rates and with the bond that is delivered.

```python
d01 = dv.pivot(index="fecha", columns="mercado", values="dv01")
ratios = pd.DataFrame({"2s10s (TPA per TPC)": d01["TPC"] / d01["TPA"],
                       "NOB (TPC per TPD)": d01["TPD"] / d01["TPC"]})
print(ratios.describe().loc[["min", "50%", "max"]].round(2))
ratios.resample("YE").mean().round(2).T.iloc[:, ::3]
```
```output
     2s10s (TPA per TPC)  NOB (TPC per TPD)
min                 1.41               1.94
50%                 1.59               2.23
max                 1.87               2.93
fecha                2010-12-31  2013-12-31  2016-12-31  2019-12-31  2022-12-31  2025-12-31
2s10s (TPA per TPC)        1.52        1.55        1.62        1.68        1.56        1.53
NOB (TPC per TPD)          2.03        2.11        2.30        2.53        2.22        2.15
```

*Fig. — Short-tenor contracts per long-tenor contract that make the 2s10s and the NOB DV01-neutral in the data, 2010–2025, against the fixed 2:1 ratio listed by CME.* Two lines from 2010 to 2025. The NOB DV01 ratio (10-year notes per 30-year bond) moves between 1.94 and 2.93, peaking around 2019-2020; the 2s10s ratio (2-year notes per 10-year note), between 1.41 and 1.87. A highlighted line marks the fixed 2:1 ratio at which CME lists the TUT and the NOB.

In the data, the 2s10s needs between 1.41 and 1.87 `TPA` contracts for each `TPC` one, and the NOB between 1.94 and
2.93 `TPC` contracts for each `TPD`. CME lists these spreads as instruments with fixed ratios: 2:1 in the 2-year versus
the 10-year (TUT) and 2:1 in the 10-year versus the bond (NOB), among others, which according to CME itself are not
expected to change except for substantial market changes. A fixed ratio is convenient (a single order, a single
price), but it is only DV01-neutral when it matches the ratio of the DV01s. Hedges by notional, by DV01 and by
volatility, and how to choose among them, are the subject of lesson 3.2.4.

## The DV01-neutral steepener

Before building the spread it is worth checking that the DV01 says what it promises. The daily P&L of a long in `TPC`
(with the same contracts from one day to the next, as in lesson 3.2.1), divided by the change in the yield of its
reference bond in basis points:

```python
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")

def cambio(mercado, log=False):
    """Daily change of the contract held at the previous close
    (the first that has not reached its fecha_roll; lesson 3.2.1)."""
    c = cur[cur["mercado"] == mercado].merge(
        ctr[["contrato", "vencimiento", "fecha_roll"]], on="contrato")
    px = c.pivot(index="fecha", columns="contrato",
                 values="liquidacion")
    d = (np.log(px) if log else px).diff().stack()
    c = c[c["fecha"] < c["fecha_roll"]].sort_values(
        ["fecha", "vencimiento"])
    ley = c.groupby("fecha")["contrato"].first().shift(1).dropna()
    idx = pd.MultiIndex.from_arrays([ley.index, ley.to_numpy()])
    return pd.Series(d.reindex(idx).to_numpy(), index=ley.index)

def pyg(mercado):
    """Daily P&L per contract, in the contract's currency."""
    return cambio(mercado) * esp.loc[mercado, "multiplicador"]

usd_pb = pyg("TPC") / (dv[dv["mercado"] == "TPC"].set_index("fecha")
                       ["rendimiento_ctd"].diff() * 100)
print(f"P&L of a long in TPC per bp rise in the CTD: "
      f"{usd_pb.median():.1f} USD (mean DV01 "
      f"{d01['TPC'].mean():.1f})")
```
```output
P&L of a long in TPC per bp rise in the CTD: -70.1 USD (mean DV01 70.4)
```

A long in `TPC` loses a median 70.1 USD for every basis point the yield rises, and the mean DV01 of the series is
70.4. With that, the steepener:

```python
yld = dv.pivot(index="fecha", columns="mercado",
               values="rendimiento_ctd")

def empinador(corto, largo):
    """Long h contracts of the short maturity and short 1 of the long,
    h = long DV01 / short DV01 of the previous session."""
    h = (d01[largo] / d01[corto]).shift(1)
    p = (h * pyg(corto) - pyg(largo)).dropna()
    nivel = yld[largo].diff().reindex(p.index) * 100       # bp
    pendiente = (yld[largo] - yld[corto]).diff().reindex(
        p.index) * 100
    return p, nivel, pendiente

p, nivel, pend = empinador("TPA", "TPC")
dp = (yld["TPC"] - yld["TPA"]).iloc[[0, -1]].diff().iloc[-1]
print(f"Cumulative P&L 2010-2025: {p.sum():,.0f} USD per TPC "
      "contract")
print(f"Change in the CTD slope over the period: {100 * dp:+.0f} bp")
print(f"Correlation of the daily P&L with the change in slope: "
      f"{p.corr(pend):.2f}; with the change in level: {p.corr(nivel):.2f}")
```
```output
Cumulative P&L 2010-2025: -29,452 USD per TPC contract
Change in the CTD slope over the period: -193 bp
Correlation of the daily P&L with the change in slope: 0.99; with the change in level: 0.49
```

The figure at the top sums it up: between 2010 and 2025 the synthetic slope (yield of the 10-year reference bond minus
that of the 2-year) went from about 200 bp to negative values, with a total change of −193 bp, and the steepener lost
29,452 USD per `TPC` contract. Its daily P&L has a correlation of 0.99 with the change in slope. The cumulative P&L is
not exactly the slope times the DV01, because over 16 years the DV01s, the reference bond of each contract and the
financing of the two legs change; but day to day it is.

The correlation with the level (0.49) is not zero. Is the position neutral to the level or not? To separate the two
things, a regression of the daily P&L on the change in the 10-year yield (“level”), alone and together with the change
in slope:

$$
\text{P\&L}_t = a + \beta_{\text{level}} \, \Delta y^{10}_t + \beta_{\text{slope}} \, \Delta\left(y^{10}_t - y^{2}_t\right) + \varepsilon_t .
$$

```python
def betas(p, nivel, pend):
    filas = {}
    for per, (a, b) in {"2010-2015": ("2010", "2015"),
                        "2016-2025": ("2016", "2025")}.items():
        y, l, s = p.loc[a:b], nivel.loc[a:b], pend.loc[a:b]
        sola = np.polyfit(l, y, 1)[0]
        X = np.column_stack([np.ones(len(y)), l, s])
        _, b_niv, b_pend = np.linalg.lstsq(X, y, rcond=None)[0]
        filas[per] = {"nivel_sola": sola, "nivel_con_pend": b_niv,
                      "pendiente": b_pend}
    return pd.DataFrame(filas).T.round(1)

pd.concat({"2s10s": betas(p, nivel, pend),
           "NOB": betas(*empinador("TPC", "TPD"))})
```
```output
                 nivel_sola  nivel_con_pend  pendiente
2s10s 2010-2015        34.0            -0.2       71.7
      2016-2025        15.1             1.1       69.5
NOB   2010-2015        13.0             0.1      156.6
      2016-2025        14.2             1.0      158.2
```

*Fig. — Level exposure of the DV01-neutral rate spreads: beta of daily P&L to the change in the long yield, alone and with the slope in the regression.* Horizontal bars by spread and period, in USD per basis point. With the slope in the regression, the level beta is close to zero in every case (−0.2, 1.1, 0.1 and 1.0). Alone, it is positive: 34.0 in the 2010-2015 2s10s (highlighted), 15.1 in the 2016-2025 one, 13.0 and 14.2 in the NOB.

With the slope in the regression, the beta to the level is almost zero (−0.2 and 1.1 USD per bp in the 2s10s; 0.1 and
1.0 in the NOB) and the beta to the slope is almost the DV01 of the long-maturity leg: 69.5-71.7 USD per bp in the
2s10s (DV01 of the 10-year, 70-71) and 156.6-158.2 in the NOB (DV01 of the bond, 155-163). It is the **known answer**
from the data README, and the regression recovers it. The position is neutral to a parallel shift. But the beta to the
level alone, without the slope, is 34 USD per bp in the 2s10s of 2010-2015 and 13-15 in the other cases. The
explanation lies in how the two maturities moved:

```python
dy = yld.diff() * 100
vol = dy.groupby(dy.index.year >= 2016)[["TPA", "TPC", "TPD"]].std()
vol.index = ["2010-2015", "2016-2025"]
vol.round(2)
```
```output
mercado     TPA   TPC   TPD
2010-2015  4.00  6.22  6.33
2016-2025  6.95  7.13  7.30
```

In 2010-2015, with short-term rates near zero, the 2-year yield moved with a daily standard deviation of 4.00 bp versus
6.22 for the 10-year. A rise in the 10-year was almost always a steepening, so, in practice, the steepener behaved
largely like a short in the 10-year note. DV01-neutral means neutral to a parallel move, not neutral to what the curve
actually does. From 2016, with the 2-year as volatile as the 10-year (6.95 versus 7.13 bp), the beta to the level
alone falls to 15.

<aside class="callout" data-kind="aviso">

A “neutral” steepener combined with a long bond portfolio does not hedge it near zero rates: it adds a short in the
long maturity. Before adding positions together, measure the beta to the level with data from the regime you are in,
not the one the formula says.

</aside>

## NQ/ES: one index against another

The most traded index spread in the U.S. is the Nasdaq-100 versus the S&P 500 (NQ/ES): a bet on tech stocks against
the broad market. In the data, `RVB` imitates the E-mini Nasdaq-100 and `RVA` the E-mini S&P 500. In the model, the log
return of each future is its own drift (7.0% a year in `RVA`, 9.5% in `RVB`), plus a beta to a common market factor
(1.00 and 1.15) and its own noise. With the same contracts from one day to the next:

```python
r = pd.DataFrame({m: cambio(m, log=True)
                  for m in ["RVA", "RVB", "RVE"]}).dropna()
beta = r.cov().loc["RVB", "RVA"] / r["RVA"].var()
print(f"Beta of RVB to RVA: {beta:.3f}; correlation: "
      f"{r['RVB'].corr(r['RVA']):.2f}")
for nombre, s in {"notional (1:1)": r["RVB"] - r["RVA"],
                  "beta": r["RVB"] - beta * r["RVA"]}.items():
    media, vol = s.mean() * 252, s.std() * 252 ** 0.5
    t = media / vol * (len(s) / 252) ** 0.5
    print(f"Neutral by {nombre}: drift {media:+.1%}/year, "
          f"vol {vol:.1%}, t = {t:.2f}, "
          f"correlation with RVA {s.corr(r['RVA']):+.2f}")
```
```output
Beta of RVB to RVA: 1.151; correlation: 0.91
Neutral by notional (1:1): drift +3.2%/year, vol 10.4%, t = 1.21, correlation with RVA +0.28
Neutral by beta: drift +1.4%/year, vol 10.0%, t = 0.56, correlation with RVA +0.00
```

The measured beta (1.151) recovers the planted one (1.15). There are two usual ways to build the spread:

- **Notional-neutral** (one dollar long for one dollar short): it is the one CME describes with its *spread ratio*,
  the ratio of the notionals of the two contracts computed with the spot index. It keeps the difference in betas:
  1.15 − 1 = 0.15 of the market. In the data, its correlation with `RVA` is 0.28 and its drift, +3.2% a year.
- **Beta-neutral** (1.15 dollars of `RVA` per dollar of `RVB`): it removes the market factor. Its correlation with
  `RVA` is 0.00 and its drift is the pure relative one, which in the model is $9.5 - 1.15 \cdot 7.0 = 1.45$% a year.
  The measured one is +1.4%.

*Fig. — RVB/RVA spread (they mimic the Nasdaq-100 and the S&P 500): cumulative relative return, notional-neutral and beta-neutral, 2010–2025.* Two lines from 2010 to 2025 in cumulative log percent. The notional-neutral spread (one dollar long RVB per dollar short RVA) ends at +50%, with drops when the market falls. The beta-neutral one, highlighted (1.15 dollars of RVA per dollar of RVB), ends at +22% on a flatter path, between −25 and +29%, with a t of 0.56.

The estimate of the beta-neutral drift is almost exact, and even so it means nothing: with a volatility of 10% a year
and 16 years, the standard error of the mean is $10/\sqrt{16} = 2.5$ points, and $t = 0.56$. The notional-neutral
spread looks better (t = 1.21, +50% cumulative in the figure), but its advantage comes almost entirely from the 0.15
of market it carries inside, which gains while the stock market rises and loses when it falls (the 2016 stretch of the
figure is the most visible). It is the same point as in lesson 3.2.1 with the crude calendar: what is called a
“spread” can be, in part, a directional position.

Converting to contracts:

```python
idx = pd.read_csv("futuros3_indices_contado.csv",
                  parse_dates=["fecha"])
s = idx[idx["fecha"] == "2025-12-31"].set_index("indice")["contado"]
nominal = s[["RVA", "RVB"]] * esp.loc[["RVA", "RVB"], "multiplicador"]
print(nominal.round(0).to_string())
n_b = 1e6 / nominal["RVB"]
print(f"Long RVB: {n_b:.1f} contracts; short RVA notional-neutral: "
      f"{n_b * nominal['RVB'] / nominal['RVA']:.1f}; "
      f"beta-neutral: {beta * n_b * nominal['RVB'] / nominal['RVA']:.1f}")
```
```output
indice
RVA    335966.0
RVB    435294.0
Long RVB: 2.3 contracts; short RVA notional-neutral: 3.0; beta-neutral: 3.4
```

One million USD in `RVB` is 2.3 contracts (435,294 USD of notional each as of 2025-12-31); the short side, 3.0 `RVA`
contracts notional-neutral or 3.4 beta-neutral. With large contracts, a one-million spread cannot be sized precisely:
the micros (`RVAM`, `RVBM`, a tenth of the multiplier, lesson 3.1.1) exist, among other things, for that. And the beta
is not fixed: the second exercise measures it year by year.

## Europe versus the U.S.: the currency

The Europe versus U.S. spread adds a difficulty: the legs are quoted in different currencies. In the data, `RVE`
imitates the Euro Stoxx 50 and is quoted in QEV (the Vesperian evro, which imitates the euro); `RVA`, in USD. If an
investor buys the European stocks with dollars, they have the European index and also the currency: their return in
USD is that of the index in QEV plus that of the QEV against the USD. With a future there is no need to exchange the
notional: a margin is posted and the P&L is settled every day, in QEV. The currency risk is limited to that
accumulated P&L and to the margin.

```mermaid
flowchart LR
  C[European cash] --> N[Notional in QEV]
  N --> X1[Fully exposed]
  F[RVE future] --> P[Result in QEV]
  P --> X2[Only the result]
```

```python
fx = pd.read_parquet("fx3_diario.parquet")
x = fx[fx["divisa"] == "QEV"].set_index("fecha")["usd_por_unidad"]
r_fx = np.log(x).diff().reindex(r.index)
contado_usd = r["RVE"] + r_fx          # European index converted to USD
futuro_usd = np.expm1(r["RVE"]) * x.reindex(r.index) / x.shift(
    1).reindex(r.index)                # P&L of the future, in USD
for nombre, s in {"RVE in QEV": r["RVE"],
                  "RVE in USD (cash)": contado_usd,
                  "RVE future, P&L in USD": futuro_usd}.items():
    print(f"{nombre:24s} vol {s.std() * 252 ** 0.5:.1%}  "
          f"correlation with RVA {s.corr(r['RVA']):.2f}")
```
```output
RVE in QEV               vol 20.9%  correlation with RVA 0.86
RVE in USD (cash)        vol 22.9%  correlation with RVA 0.78
RVE future, P&L in USD   vol 20.9%  correlation with RVA 0.86
```

In QEV, the European index has a volatility of 20.9% and a correlation of 0.86 with `RVA`. Converted to USD as cash,
the volatility rises to 22.9% and the correlation falls to 0.78: the currency adds noise that has nothing to do with
the stock market. The P&L of the future converted to USD each day stays like the index in QEV (20.9% and 0.86). A
Europe versus U.S. spread with futures is, therefore, almost a spread in local currency; with cash, it is also a
position in the currency. How to manage that risk in a multi-currency portfolio is the subject of lesson 3.3.3.

<aside class="callout" data-kind="nota">

In the data, all series use NYSE sessions (a simplification of the generator). In reality, European and U.S. futures
have different trading hours and settlement prices at different times, so daily correlations computed with
settlements underestimate the real relationship, and a backtest that “executes” both legs at the close mixes prices
from different moments. In addition, each leg clears at its own clearing house (Eurex Clearing and CME Clearing):
there is no margin offset between them, unlike a spread within the same clearing house.

</aside>

## What this changes in a system

- **The ratio decides the bet.** DV01-neutral, the position bets on the slope; with another ratio, also on the level.
  Notional-neutral, an index spread also bets on the market; beta-neutral, it does not.
- **Neutral to one move is not neutral to all.** Near zero rates, a DV01-neutral steepener has a beta to the level of
  34 USD per bp. The residual exposure is measured on data from the regime, with a regression.
- **Ratios move.** The DV01 and the beta change over time; listed spreads with a fixed ratio are convenient, but not
  always neutral. Redoing the ratio every day has an execution cost (lesson 3.2.5).
- **A relative drift is not validated with 16 years.** With 10% volatility, you need a drift of more than 5 points a
  year for the t to exceed 2.
- **The currency, only in the P&L.** With futures, the currency risk is that of the P&L and the margin; with cash, that
  of the notional.

## Exercises

1. Build the 2s10s with the fixed 2:1 ratio (two `TPA` contracts for one `TPC`) and repeat the beta regression. What
   exposure to the level remains with the slope in the regression and why does it have that sign?

**Solution.**

```python
p_fijo = (2 * pyg("TPA") - pyg("TPC")).dropna()
betas(p_fijo, nivel.reindex(p_fijo.index),
      pend.reindex(p_fijo.index))
```
```output
           nivel_sola  nivel_con_pend  pendiente
2010-2015        23.8           -19.6       91.1
2016-2025         1.0           -16.6       87.7
```

With 2:1, the beta to the level with the slope in the regression is no longer zero: −19.6 USD per bp in 2010-2015 and
−16.6 in 2016-2025. The sign is negative because two `TPA` contracts have more DV01 than one `TPC` contract
(2 × 42.55 = 85.1 versus 64.47 at the end of 2025): the position is net long duration and loses when rates rise in
parallel. The beta to the slope rises to 88-91 USD per bp because the short-maturity side, with more contracts, weighs
more. With the fixed ratio you bet on the slope and, in addition, on a fall in rates.

2. Estimate the beta of `RVB` to `RVA` in each year from 2010 to 2025. How much does it vary and what consequence does
   it have for a beta-neutral spread calibrated with one year of data?

**Solution.**

```python
por_ano = r.groupby(r.index.year).apply(
    lambda g: g["RVB"].cov(g["RVA"]) / g["RVA"].var())
print(por_ano.round(2).to_frame("beta").T.to_string())
print(f"From {por_ano.min():.2f} to {por_ano.max():.2f}")
```
```output
fecha  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  2025
beta   1.15  1.21  1.15  1.12  1.13  1.15  1.14  1.15  1.17  1.06  1.15   1.1  1.15  1.19  1.16  1.17
From 1.06 to 1.21
```

The annual beta ranges from 1.06 (2019) to 1.21 (2011), around the true value of 1.15, which does not change in the
model: all the variation is estimation error with about 250 sessions. A spread calibrated with the 2019 beta would have
been 8% short in `RVA` (1.06 versus 1.15), with a residual exposure of 0.09 to the market. In real data the beta also
truly changes, so estimation with short windows mixes the two effects: more noise with short windows, more lag with
long windows (the Kalman filter of lesson 2.1.6 is one way to choose).

3. A manager with a portfolio of European stocks in USD wants to bet that Europe will outperform the U.S. without
   adding currency risk. Compare doing it with futures (long the European future, short the U.S. one) and with cash
   (buying European stocks and selling a U.S. ETF).

**Solution.**

With futures, the notional is not converted into another currency: the manager posts margin in QEV (or in USD,
depending on the broker) and receives or pays every day the P&L of the European leg in QEV. Their currency exposure is
that of the accumulated P&L and the margin, a small fraction of the notional; in the data, the P&L of the future
converted to USD has the same volatility as the index in QEV (20.9%). With cash, they buy stocks in QEV with the whole
notional and are exposed to the currency for the full amount: the European index in USD has 22.9% volatility and a
lower correlation with the U.S. (0.78 versus 0.86). To have only the relative bet with cash they would have to hedge
the currency separately (with a forward, lesson 3.3.3). The cost of the futures version lies elsewhere: two clearing
houses with no margin offset, different trading hours and the roll of both legs every quarter.

## Self-assessment

1. A DV01-neutral 2s10s steepener is long 2-year notes and short 10-year notes. When does it gain?

   - A. When all rates rise by the same amount
   - B. When the slope of the curve rises: the 10-year yield rises more (or falls less) than the 2-year
   - C. When rates fall
   - D. When the curve flattens

   *Answer B: When the slope of the curve rises: the 10-year yield rises more (or falls less) than the 2-year.* With the same dollars per basis point in both legs, a parallel shift cancels out. What remains is the slope: in the data, the beta of the P&L to the slope is 69.5-71.7 USD per bp, almost the DV01 of the 10-year note (70-71), and the daily correlation with the change in slope, 0.99.

2. In 2010-2015, a regression of the steepener's P&L on the change in the 10-year yield gives a beta of 34 USD per bp; with the slope in the regression, −0.2. What does it indicate?

   - A. That the DV01 hedge is miscalculated
   - B. That the position is not neutral to the level
   - C. That the 2-year moved more than the 10-year
   - D. That it is neutral to a parallel shift, but level and slope did not move in the same way: near zero rates the 2-year barely moved, and a rise in the 10-year was almost always a steepening

   *Answer D: That it is neutral to a parallel shift, but level and slope did not move in the same way: near zero rates the 2-year barely moved, and a rise in the 10-year was almost always a steepening.* The beta alone mixes level and slope. In 2010-2015 the daily standard deviation of the 2-year yield was 4.00 bp versus 6.22 for the 10-year: when the 10-year rose, the curve steepened. With the slope in the regression, the beta to the level is −0.2: the position is indeed neutral to a parallel move.

3. CME lists the NOB (10-year notes versus the bond) with a fixed 2:1 ratio. In the data, the DV01 ratio ranges from 1.94 to 2.93. What does trading with the fixed ratio imply?

   - A. Nothing: both are neutral
   - B. That the fixed ratio always has less risk
   - C. That the position is only DV01-neutral when the ratio of the DV01s is close to 2; the rest of the time an exposure to the level remains
   - D. That the DV01 ratio is an error in the data

   *Answer C: That the position is only DV01-neutral when the ratio of the DV01s is close to 2; the rest of the time an exposure to the level remains.* The DV01 of each future changes with the level of rates and with the cheapest-to-deliver bond. A fixed ratio is convenient to execute in a single instrument, but it leaves exposure to the level when it drifts away from the DV01 ratio. The first exercise measures it in the 2s10s with a 2:1 ratio: −17 to −20 USD per bp of exposure to the level.

4. The beta-neutral (1.15) RVB/RVA spread has a measured drift of +1.4% a year with a volatility of 10% over 16 years (t = 0.56). The planted drift is 1.45%. What do you conclude?

   - A. That the estimate is almost exact, but 16 years are not enough to distinguish it from zero: the standard error of the mean is about 2.5 points a year
   - B. That there is no drift
   - C. That there is drift and it is significant
   - D. That the beta is badly estimated

   *Answer A: That the estimate is almost exact, but 16 years are not enough to distinguish it from zero: the standard error of the mean is about 2.5 points a year.* With a volatility of 10% a year, the standard error of an annual mean estimated with 16 years is 10/√16 = 2.5 points. A real drift of 1.45% is 0.6 standard errors away from zero. In real data the truth is not known: an index spread with a drift like that cannot be validated with its own history.

5. A spread long the European index future RVE (in QEV) and short RVA (in USD). What currency risk does it have?

   - A. Only that of the accumulated P&L and the margin in QEV: a future does not invest the notional, so its P&L in USD has the volatility of the index in QEV (20.9%), not that of the index converted to USD (22.9%)
   - B. That of the entire notional of the European leg, as if the stocks had been bought
   - C. None
   - D. Twice that of cash

   *Answer A: Only that of the accumulated P&L and the margin in QEV: a future does not invest the notional, so its P&L in USD has the volatility of the index in QEV (20.9%), not that of the index converted to USD (22.9%).* With futures the notional is not exchanged into QEV: margins are posted and the P&L is settled. The P&L in USD is the P&L in QEV times the day's exchange rate, and its volatility is that of the index in QEV. European cash converted to USD has more volatility (22.9%) and less correlation with RVA (0.78 versus 0.86).

## Key points

- A DV01-neutral rate spread equalizes the dollars per basis point of the two legs: it neither gains nor loses with a parallel shift and its P&L follows the slope (beta of 70 USD per bp, correlation of 0.99).
- DV01-neutral is not level-neutral in a simple regression: near zero rates the 2-year barely moves, and a steepener behaves partly like a short in the 10-year (beta of 34 USD per bp in 2010-2015).
- The DV01 ratio moves (1.41-1.87 in the 2s10s, 1.94-2.93 in the NOB of the data); listed spreads use fixed ratios and leave exposure to the level when they drift away.
- A notional-neutral index spread keeps the difference in betas (correlation of 0.28 with the market); a beta-neutral one does not (0.00). Its drift, even when planted, cannot be distinguished from zero with 16 years.
- With futures, a spread between indices in different currencies only has currency risk in the P&L and the margin, not in the notional.

## Resources

### Research

- [Impact of Seasonal Factor on US Bonds](https://pythonparatrading.com/en/seasonality-usa-yields/) · Research-0017 · 2 min

### Documentation

- [CME Group: Interest Rate Futures Inter-Commodity Spreads (fixed ratios of the TUT, NOB and other Treasury spreads)](https://www.cmegroup.com/trading/interest-rates/intercommodity-spread.html) · cmegroup.com
- [CME Group Client Systems Wiki: Spreads and Combinations Available on CME Globex (price of inter-commodity spreads)](https://cmegroupclientsite.atlassian.net/wiki/spaces/EPICSANDBOX/pages/457089763/Spreads+and+Combinations+Available+on+CME+Globex) · cmegroupclientsite.atlassian.net
- [CME Group: Stock Index Spread Opportunities (ratio of a spread between indices by notional value)](https://www.cmegroup.com/education/whitepapers/stock-index-spread-opportunities.html) · cmegroup.com
- [CME Group: Trading Opportunities in Equity Index Futures (Nasdaq-100 versus S&P 500 spread)](https://www.cmegroup.com/education/courses/introduction-to-equity-index-products/trading-opportunities-in-equity-index-futures.html) · cmegroup.com

### Data

- [Level 3 data: DV01 and yield of the reference bond of each rate future (CSV)](https://pythonparatrading.com/curso/datos/nivel-3/tipos3_dv01.csv) · Download · tipos3_dv01.csv
- [Level 3 data: spot indices, dividends and rates (CSV)](https://pythonparatrading.com/curso/datos/nivel-3/futuros3_indices_contado.csv) · Download · futuros3_indices_contado.csv

Previous lesson: [3.2.2 · Intercommodity spreads: crack, spark and crush](https://pythonparatrading.com/en/python-trading-course/level-3/intercommodity-spreads-crack-spark-crush/)

Next lesson: [3.2.4 · Hedge ratios: by notional, by DV01 and by volatility](https://pythonparatrading.com/en/python-trading-course/level-3/hedge-ratios-notional-dv01-volatility/)
