---
title: "3.1.2 Curva de futuros: base, contango y roll yield"
description: "Coste de mantenimiento en índices, bonos, divisas y materias primas; contango, backwardation, rendimiento de conveniencia y roll yield medidos."
url: "https://pythonparatrading.com/curso-python-trading/nivel-3/curva-de-futuros-base-contango-y-roll-yield/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-3/curva-de-futuros-base-contango-y-roll-yield.png"
---

# 3.1.2 · Curva de futuros: base, cost of carry, contango y roll yield

> Cada día cotizan varios vencimientos del mismo futuro, y la forma de esa curva no es una previsión del mercado: es, sobre todo, el coste de mantener el subyacente hasta cada fecha. Esta lección mide ese coste en los cuatro tipos de futuro (índices, bonos, divisas y materias primas), separa lo que gana quien mantiene un futuro en cambio del precio y roll yield, y comprueba con la respuesta conocida de los datos sintéticos cuándo el roll yield es una ganancia y cuándo no.

- Curso: [Nivel 3: Derivados: futuros, spreads, forex y opciones](https://pythonparatrading.com/curso-python-trading/nivel-3/)
- Módulo: 3.1 · Futuros en profundidad
- Duración: 65 min
- Requisitos: [3.1.1 · Contratos de futuros principales](https://pythonparatrading.com/curso-python-trading/nivel-3/contratos-de-futuros-principales/), [1.2.3 · Futuros: especificaciones, margen, roll y contango](https://pythonparatrading.com/curso-python-trading/nivel-1/futuros-especificaciones-margen-y-roll/), [1.4.6 · Series continuas de futuros: reglas de roll, ajuste por diferencia y ajuste por ratio](https://pythonparatrading.com/curso-python-trading/nivel-1/series-continuas-de-futuros/), [2.3.4 · Carry en divisas, futuros y bonos; estacionalidad y efectos calendario](https://pythonparatrading.com/curso-python-trading/nivel-2/carry-estacionalidad-y-efectos-calendario/)

## Al terminar esta lección

- Escribir el coste de mantenimiento de un futuro sobre índice, bono, divisa y materia prima, y calcular su valor razonable y su base.
- Extraer de la curva lo que el mercado descuenta: el dividendo implícito de un índice y el rendimiento de conveniencia neto de una materia prima.
- Medir contango y backwardation en 23 mercados y distinguir la pendiente que es carry de la forma estacional de la curva.
- Descomponer el rendimiento de mantener un futuro en cambio del precio más roll yield, y relacionar el roll yield con el carry medio.
- Reconocer el efecto Samuelson (los vencimientos lejanos se mueven menos) y sus consecuencias para elegir el contrato.

## Una curva, muchos precios

La lección 1.2.3 presentó la base y el contango con un solo índice. Aquí se generaliza a los cuatro tipos de futuro.
La idea es la misma en todos: comprar el futuro equivale a comprar el subyacente hoy y mantenerlo hasta el
vencimiento, con lo que eso cuesta (financiación, almacenamiento) y lo que rinde (dividendos, cupones, intereses de
la otra divisa, la utilidad de tener la mercancía). La diferencia entre el futuro y el contado es ese **coste de
mantenimiento** (*cost of carry*):

$$
F(t, T) = S_t \, e^{(r + u - y)\,\tau}, \qquad \tau = T - t
$$

con $r$ el tipo de financiación, $u$ el coste de almacenamiento y $y$ todo lo que rinde tener el subyacente: el
dividendo de un índice, el tipo de interés de la divisa extranjera, o el **rendimiento de conveniencia** de una
materia prima (lo que vale tener el barril en el depósito en vez de un papel).

```mermaid
flowchart TB
  S[Contado] --> R[+ financiación r]
  R --> U[+ almacenamiento u]
  U --> Y[− lo que rinde y]
  Y --> F[Futuro a τ años]
  Y --> Q[Índice: dividendo]
  Y --> D[Divisa: su tipo]
  Y --> C[Rend. conveniencia]
```

Cuando el futuro lejano está por encima del cercano, la curva está en **contango** ($r + u > y$); por debajo, en
**backwardation**. En los financieros, todos los términos se observan y el arbitraje fija la curva. En las materias
primas, el rendimiento de conveniencia no se observa: se deduce de la curva, y cambia con la escasez.

## Índices: la base es el coste de mantenimiento

Los datos traen, para cada índice, el contado, la rentabilidad por dividendo y el tipo a un mes de su divisa:

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

curvas = pd.read_parquet("futuros3_curvas.parquet")
ctr = pd.read_csv("futuros3_contratos.csv",
                  parse_dates=["vencimiento", "primer_aviso",
                               "fecha_alta", "fecha_roll"])
esp = pd.read_csv("futuros3_especificaciones.csv",
                  index_col="mercado")
contado = pd.read_csv("futuros3_indices_contado.csv",
                      parse_dates=["fecha"])
venc = ctr.set_index("contrato")["vencimiento"]
curvas["dias"] = (venc.reindex(curvas["contrato"]).to_numpy()
                  - curvas["fecha"]).dt.days
DIA = "2025-11-26"
c = contado[(contado["fecha"] == DIA)].set_index("indice")
c[["contado", "rentabilidad_dividendo", "tipo_1m"]]
```
```output
         contado  rentabilidad_dividendo  tipo_1m
indice
RVA      7841.33                   1.891     3.87
RVB     26175.84                   0.959     3.87
RVC      3226.71                   1.227     3.87
RVE      6950.49                   2.947     2.09
RVH     19718.00                   3.570     2.09
```

El 26 de noviembre de 2025, los índices en dólares (`RVA`, `RVB`, `RVC`) se financian al 3,87 % y los de la zona del
evro (`RVE`, `RVH`), al 2,09 %. El dividendo va del 0,959 % de `RVB` (un índice tecnológico, como el Nasdaq-100) al
3,570 % de `RVH`. Con esos tres números, la curva entera está determinada:

```python
def curva(mercado, dia):
    d = curvas[(curvas["mercado"] == mercado)
               & (curvas["fecha"] == dia)]
    return d.sort_values("dias").set_index("contrato")[
        ["dias", "liquidacion"]]

for m in ["RVA", "RVE"]:
    S, q, r = c.loc[m, ["contado", "rentabilidad_dividendo",
                        "tipo_1m"]]
    d = curva(m, DIA)
    tau = d["dias"] / 365
    d["razonable"] = S * np.exp((r - q) / 100 * tau)
    d["base"] = d["liquidacion"] - S
    d["dif_pb"] = 1e4 * np.log(d["liquidacion"] / d["razonable"])
    print(m, f"S = {S}, r − q = {r - q:.2f} %")
    print(d.round(2).to_string(), "\n")
```
```output
RVA S = 7841.33, r − q = 1.98 %
          dias  liquidacion  razonable    base  dif_pb
contrato
RVAZ25      23      7851.75    7851.11   10.42    0.81
RVAH26     114      7892.00    7889.95   50.67    2.60
RVAM26     204      7930.50    7928.54   89.17    2.47
RVAU26     296      7970.25    7968.19  128.92    2.59
RVAZ26     387      8009.75    8007.60  168.42    2.68

RVE S = 6950.49, r − q = -0.86 %
          dias  liquidacion  razonable   base  dif_pb
contrato
RVEZ25      23       6946.0    6946.74  -4.49   -1.06
RVEH26     114       6929.0    6931.91 -21.49   -4.20
RVEM26     204       6914.0    6917.28 -36.49   -4.74
RVEU26     296       6900.0    6902.35 -50.49   -3.41
RVEZ26     387       6885.0    6887.62 -65.49   -3.80
```

En `RVA`, con el tipo por encima del dividendo ($r - q = 1{,}98$ %), la curva está en contango: la base crece de 10,42
puntos a 23 días a 168,42 a 387. En `RVE`, con el dividendo por encima del tipo ($r - q = -0{,}86$ %), en
backwardation: de −4,49 a −65,49. Los diez contratos están a menos de 5 puntos básicos de su valor razonable
(`dif_pb`, entre −4,74 y +2,68): la diferencia es el redondeo al tick y un pequeño ruido que el generador añade y
atenúa al acercarse el vencimiento. En los mercados reales la base tiene además la incertidumbre de los dividendos
futuros y el coste real de financiarse, que no es el tipo a un mes.

Dicho al revés: si el futuro y el tipo se observan, el dividendo que el mercado descuenta sale de la curva:

$$
q_{\text{impl}} = r - \frac{\ln(F/S)}{\tau}
$$

```python
def que_se_tiene(fechas, mercado):
    cm = ctr[ctr["mercado"] == mercado].reset_index(drop=True)
    pos = np.searchsorted(cm["fecha_roll"].to_numpy(),
                          fechas.to_numpy(), side="right")
    return cm["contrato"].to_numpy()[pos]

filas = {}
for m in ["RVA", "RVB", "RVC", "RVE", "RVH"]:
    s = contado[contado["indice"] == m].set_index("fecha")
    tiene = que_se_tiene(s.index, m)
    f = curvas.set_index(["fecha", "contrato"])
    f = f.loc[list(zip(s.index, tiene))]
    tau = f["dias"].to_numpy() / 365
    F = f["liquidacion"].to_numpy()
    q_imp = 100 * (s["tipo_1m"] / 100
                   - np.log(F / s["contado"]) / tau)
    lejos = tau > 30 / 365            # con τ pequeño, el tick manda
    err = (q_imp - s["rentabilidad_dividendo"])[lejos]
    filas[m] = {"q_medio": s["rentabilidad_dividendo"].mean(),
                "error_medio": err.mean(), "error_dt": err.std()}
pd.DataFrame(filas).T.round(3)
```
```output
     q_medio  error_medio  error_dt
RVA    1.855       -0.003     0.115
RVB    0.846        0.019     0.114
RVC    1.186        0.010     0.118
RVE    2.667        0.007     0.128
RVH    4.003        0.009     0.125
```

El dividendo implícito recupera el verdadero con un error medio de centésimas de punto y una desviación típica de
0,11 a 0,13 puntos porcentuales, en todas las sesiones de 2010 a 2025 en las que al contrato le quedan más de 30
días. Es la respuesta conocida: el generador construye cada futuro como el valor razonable más un ruido de unos 2
puntos básicos. Con menos de un mes al vencimiento, un tick de diferencia divide por un τ muy pequeño y el error se
dispara; por eso el filtro. Los operadores de dividendos hacen esto mismo con futuros reales, al revés: estiman los
dividendos y buscan futuros mal valorados.

## Bonos y divisas: cupón, financiación y tipos

En un futuro de divisa, lo que rinde el subyacente es el tipo de interés de la divisa extranjera: la curva es la
paridad cubierta de intereses (lección 1.2.4). En un futuro de bonos, es el cupón frente a la financiación del bono:

```python
for m in ["TPC", "DQE"]:
    print(m, curva(m, DIA)["liquidacion"].round(5).to_dict())
tipos = pd.read_csv("tipos3_curva.csv", index_col="fecha")
print(tipos.loc[DIA, ["usd_1m", "qev_1m", "usd_10a"]].to_dict())
```
```output
TPC {'TPCZ25': 113.04688, 'TPCH26': 113.07812, 'TPCM26': 113.125}
DQE {'DQEZ25': 1.36395, 'DQEH26': 1.37, 'DQEM26': 1.3761, 'DQEU26': 1.3822, 'DQEZ26': 1.38835}
{'usd_1m': 3.87, 'qev_1m': 2.09, 'usd_10a': 3.939}
```

El futuro de evros (`DQE`) sube con el plazo: de 1,36395 a 1,38835 en un año, un 1,8 % aproximadamente, la diferencia
entre el 3,87 % del dólar y el 2,09 % del evro. Quien compra evros a plazo no cobra el tipo del evro y no paga el del
dólar, y la curva lo compensa.

La nota a 10 años es más sutil. El bono que se entrega paga un cupón del 6 % y se financia al 3,87 %: el carry es
positivo, y eso empuja la curva hacia abajo (backwardation). Pero en estos datos, como en los contratos reales, cada
vencimiento entrega un bono distinto: el bono estándar de `TPC` vence 6,6 años después del vencimiento del futuro,
así que el contrato de marzo entrega un bono tres meses más largo que el de diciembre. Con un cupón por encima del
rendimiento, un bono más largo vale más, y eso empuja la curva hacia arriba:

```python
def precio_bono(y, anios, cupon=6.0):
    """Precio limpio de un bono con cupón anual que vence en `anios`
    (puede no ser entero), rendimiento y, por 100 de nominal."""
    t = np.arange(anios, 0, -1.0)[::-1]          # flujos
    sucio = (cupon * (1 + y) ** -t).sum() + 100 * (1 + y) ** -anios
    return sucio - cupon * (1 - anios % 1)   # sin el cupón corrido

y, r = 0.0375, tipos.loc[DIA, "usd_1m"] / 100
P1, P2 = precio_bono(y, 6.6), precio_bono(y, 6.85)
carry_trim = (6.0 - r * P1) / 4
print(f"precio del bono a 6,6 años {P1:.3f}, a 6,85 años {P2:.3f}")
print(f"un trimestre más de bono: {P2 - P1:+.3f}")
print(f"carry de un trimestre (cupón − financiación): "
      f"{-carry_trim:+.3f}")
```
```output
precio del bono a 6,6 años 112.916, a 6,85 años 113.359
un trimestre más de bono: +0.444
carry de un trimestre (cupón − financiación): -0.408
```

Con el rendimiento del bono de `TPC` de ese día (3,75 %, en `tipos3_dv01.csv`), un trimestre más de bono vale unos
0,44 puntos más y el carry de un trimestre resta unos 0,41: los dos efectos casi se anulan y la curva de `TPC` apenas
sube (113,04688, 113,07812, 113,125). El cálculo es aproximado (cupón anual, misma tasa para los dos bonos), pero
explica por qué en los futuros de bonos la pendiente entre vencimientos no es el carry: en la realidad, además, el
bono más barato de entregar (CTD) puede cambiar de un vencimiento a otro, y con él la curva. La lección 3.2.4 vuelve
a los bonos con su DV01.

## Materias primas: almacenamiento y rendimiento de conveniencia

En una materia prima nadie publica el rendimiento de conveniencia. Se lee en la curva. La figura de la cabecera
dibuja la del crudo en cuatro fechas:

```python
fechas = ["2020-04-20", "2022-06-08", "2024-03-13", DIA]
for dia in fechas:
    d = curva("PEC", dia)
    meses = (d["dias"] / 30.44).round(1)
    rel = 100 * np.log(d["liquidacion"]
                       / d["liquidacion"].iloc[0])
    print(dia, f"F1 = {d['liquidacion'].iloc[0]:.2f}",
          " ".join(f"{a:.0f}m:{b:+.1f}" for a, b
                   in zip(meses.iloc[[2, 5, 11, -1]],
                          rel.iloc[[2, 5, 11, -1]])))
```
```output
2020-04-20 F1 = 26.82 2m:+15.3 5m:+31.9 11m:+51.6 17m:+62.6
2022-06-08 F1 = 130.49 2m:-3.9 6m:-8.3 11m:-14.6 17m:-18.7
2024-03-13 F1 = 52.82 2m:+2.3 5m:+5.7 11m:+12.3 17m:+18.5
2025-11-26 F1 = 76.69 3m:-6.0 6m:-11.1 12m:-14.3 17m:-13.2
```

El 20 de abril de 2020, con el primer contrato a 26,82 USD, el de 17 meses cotiza un 62,6 % (en logaritmos) por
encima: es el contango de un mercado con exceso de oferta, el del guion de la pandemia de los datos. En junio de 2022,
con el primero a 130,49, la curva baja: los lejanos cotizan hasta un 18,7 % por debajo. En marzo de 2024, contango
suave; en noviembre de 2025, backwardation otra vez. La pendiente entre los dos primeros contratos, anualizada, da el
rendimiento de conveniencia neto del almacenamiento:

```python
def pendiente(mercado, dia, a=0, b=1):
    """ln(F_a / F_b) / (τ_b − τ_a): > 0 en backwardation."""
    d = curva(mercado, dia)
    fa, fb = d["liquidacion"].iloc[[a, b]]
    ta, tb = d["dias"].iloc[[a, b]] / 365
    return np.log(fa / fb) / (tb - ta)

for dia in fechas:
    r = tipos.loc[dia, "usd_1m"] / 100
    k = -pendiente("PEC", dia)          # ln(F2/F1)/Δτ = r + u − y
    print(dia, f"ln(F2/F1)/Δτ {100 * k:+.1f} %,",
          f"tipo {100 * r:.2f} %,",
          f"y − u {100 * (r - k):+.1f} %")
```
```output
2020-04-20 ln(F2/F1)/Δτ +96.5 %, tipo 0.21 %, y − u -96.3 %
2022-06-08 ln(F2/F1)/Δτ -24.1 %, tipo 1.56 %, y − u +25.6 %
2024-03-13 ln(F2/F1)/Δτ +13.5 %, tipo 5.30 %, y − u -8.2 %
2025-11-26 ln(F2/F1)/Δτ -38.6 %, tipo 3.87 %, y − u +42.4 %
```

La pendiente es $r + u - y$, así que $y - u = r - \text{pendiente}$. En abril de 2020, $y - u = -96{,}3$ %: guardar
un barril cuesta muchísimo más de lo que rinde tenerlo. En marzo de 2024, −8,2 %: con el tipo al 5,30 %, la curva sube
un 13,5 % anual, más que la financiación, porque almacenar cuesta. En junio de 2022 (+25,6 %) y noviembre de 2025
(+42,4 %), tener el barril vale mucho: el mercado paga por el producto disponible ahora y la curva baja. El
rendimiento de conveniencia es la forma que tienen los modelos de decir «escasez».

Para medir la pendiente de los 23 mercados en toda la muestra hace falta la serie del contrato que se tiene y la del
siguiente. Su carry anual es la pendiente entre los dos (positiva en backwardation, como en la lección 2.3.4):

$$
c_t = \frac{\ln\left(F_{1,t} / F_{2,t}\right)}{\tau_2 - \tau_1}
$$

```python
def serie(mercado):
    c = curvas[curvas["mercado"] == mercado]
    P = c.pivot(index="fecha", columns="contrato",
                values="liquidacion")
    D = c.pivot(index="fecha", columns="contrato", values="dias")
    tiene = que_se_tiene(P.index, mercado)
    i = P.columns.get_indexer(tiene)
    filas = np.arange(len(P))
    Pv = P.to_numpy()
    ayer = np.r_[i[0], i[:-1]]
    antes = np.r_[np.nan, Pv[filas[1:] - 1, ayer[1:]]]
    cm = ctr.loc[ctr["mercado"] == mercado, "contrato"].to_numpy()
    siguiente = dict(zip(cm[:-1], cm[1:]))
    sig = P.columns.get_indexer([siguiente.get(t) for t in tiene])
    ok = sig >= 0
    F2 = np.where(ok, Pv[filas, np.maximum(sig, 0)], np.nan)
    t1 = D.to_numpy()[filas, i] / 365
    t2 = np.where(ok, D.to_numpy()[filas, np.maximum(sig, 0)],
                  np.nan) / 365
    return pd.DataFrame({
        "contrato": tiene, "precio": Pv[filas, i],
        "ret": np.log(Pv[filas, ayer] / antes),
        "carry": np.log(Pv[filas, i] / F2) / (t2 - t1)},
        index=P.index)

mercados = esp[esp["mercado_base"].isna()].index
S = {m: serie(m) for m in mercados}
S["GNA"].loc["2025-11-07":"2025-11-14"].round(4)
```
```output
           contrato  precio     ret   carry
fecha
2025-11-07   GNAZ25   8.700 -0.0221  0.3067
2025-11-10   GNAZ25   8.681 -0.0022  0.2743
2025-11-11   GNAZ25   8.397 -0.0333  0.1896
2025-11-12   GNAZ25   8.089 -0.0374  0.1443
2025-11-13   GNAZ25   8.123  0.0042  0.2365
2025-11-14   GNAZ25   8.024 -0.0123  0.2217
```

`ret` es el rendimiento del contrato que se tiene y `carry`, la pendiente anualizada hacia el siguiente: en la
segunda semana de noviembre de 2025, el gas de diciembre cotiza por encima del de enero y el carry es positivo, entre
el 14 % y el 31 % anual, y salta de un día a otro porque dos contratos a un mes de distancia amplifican cualquier
movimiento relativo. Con las 23 series, la frecuencia del contango y el carry medio:

```python
carry = pd.DataFrame({m: S[m]["carry"] for m in mercados})
tabla = pd.DataFrame({
    "sector": esp.loc[mercados, "sector"],
    "contango_pct": 100 * (carry < 0).mean(),
    "carry_medio": 100 * carry.mean(),
    "carry_dt": 100 * carry.std(),
})
tabla.loc[["GNA", "GRM", "PEC", "ELB", "MTO", "DQE", "RVA", "TPC",
           "RVE", "RVH"]].round(1)
```
```output
        sector  contango_pct  carry_medio  carry_dt
GNA    energia          61.0        -16.5      92.6
GRM  agricolas          57.3         -4.3      34.5
PEC    energia          46.2         -3.6      29.9
ELB    energia          50.8         -2.7     200.2
MTO    metales          64.8         -1.4       1.8
DQE    divisas          85.2         -1.0       1.0
RVA    indices          33.6          0.3       2.0
TPC      tipos          26.8          0.9       1.4
RVE    indices          12.1          2.1       1.3
RVH    indices           4.2          3.4       1.5
```

Tres familias:

- **Materias primas almacenables**: contango cerca de la mitad de los días o más (del 46,2 % del crudo al 61,0 % del
  gas) y carry medio negativo: −3,6 % en el crudo, −4,3 % en el maíz, −16,5 % en el gas. El oro, con un carry medio de −1,4 % y una desviación típica de 1,8 puntos, es casi pura
  financiación.
- **Financieros**: la pendiente es $r - q$ o su equivalente, estable (desviación típica de 1 a 2 puntos). `RVH`, con un
  dividendo alto, está en backwardation casi siempre (contango el 4,2 % de los días); la divisa `DQE`, en contango el
  85,2 %, porque el tipo del dólar suele estar por encima del del evro en los datos.
- **Mercados con estacionalidad en la curva**: la electricidad (`ELB`) tiene una desviación típica del carry de 200
  puntos y el gas de 92,6: su pendiente entre dos meses consecutivos salta con la estación. La sección siguiente
  explica por qué esa parte no es carry.

## La forma estacional no es carry

```python
d = curva("GNA", DIA)
print(d["liquidacion"].iloc[:14].round(3).to_dict())
```
```output
{'GNAF26': 7.239, 'GNAG26': 6.866, 'GNAH26': 6.259, 'GNAJ26': 4.255, 'GNAK26': 4.253, 'GNAM26': 4.385, 'GNAN26': 4.53, 'GNAQ26': 4.597, 'GNAU26': 4.612, 'GNAV26': 4.727, 'GNAX26': 5.907, 'GNAZ26': 6.284, 'GNAF27': 6.561, 'GNAG27': 6.452}
```

La curva del gas no es una pendiente, es una onda: enero de 2026 a 7,239, abril y mayo a 4,25, enero de 2027 otra vez
a 6,561. El gas de invierno es más caro porque la demanda de calefacción lo es, y eso se sabe hoy. Entre el contrato
de marzo (6,259) y el de abril (4,255) hay una «backwardation» del 38 % en un mes, pero quien compra el de abril no
espera que suba hasta el precio del de marzo: son dos productos distintos (gas entregado en marzo y gas entregado en
abril).

En el modelo de los datos esa forma es el término $s(\text{mes de } T)$ del logaritmo del precio: depende del mes de
entrega, no del tiempo, así que un contrato concreto la arrastra toda su vida y no le da rendimiento (está
**descontada**). La trampa es medir el carry entre meses consecutivos y operarlo: en el gas, el carry de la serie
salta de signo con el calendario de rolls. Una alternativa habitual es comparar con el mismo mes del año siguiente,
que tiene la misma forma estacional; la lección 3.2.6 trata la estacionalidad que sí es operable, la de los spreads.

## Roll yield: lo que gana o pierde quien mantiene el futuro

Quien mantiene un futuro y lo rueda gana o pierde dos cosas: lo que se mueve el precio del contrato que se tiene, y
la diferencia que paga en cada roll al vender un contrato y comprar el siguiente. Llamando $P_t$ al precio del
contrato que se tiene cada día (la serie sin ajustar de la lección 1.4.6):

$$
\underbrace{\sum_t r_t}_{\text{rendimiento total}} = \underbrace{\ln\frac{P_T}{P_0}}_{\text{cambio del precio}}
+ \underbrace{\sum_{\text{rolls}} \ln\frac{F_{\text{viejo}}}{F_{\text{nuevo}}}}_{\text{roll yield}}
$$

```mermaid
flowchart LR
  T[Rendimiento total] --> P[Cambio del precio]
  T --> RY[Roll yield]
  RY --> CO[Contango: −]
  RY --> BA[Backwardation: +]
```

En contango, cada roll vende el contrato cercano, más barato, y compra el siguiente, más caro: el roll yield es
negativo. Que eso sea una pérdida o no depende de lo que haga el precio. Si el precio del primer contrato subiera
justo lo que marca el contango (la «hipótesis de expectativas»), el cambio del precio compensaría el roll yield y el
rendimiento total sería cero. Los datos permiten comprobarlo mercado a mercado:

```python
filas = {}
for m in mercados:
    s = S[m].loc["2011":]
    anios = len(s) / 252
    total = s["ret"].sum()
    precio = np.log(s["precio"].iloc[-1] / s["precio"].iloc[0])
    filas[m] = {"sector": esp.loc[m, "sector"],
                "total": 100 * total / anios,
                "precio": 100 * precio / anios,
                "roll": 100 * (total - precio) / anios,
                "carry_medio": 100 * s["carry"].mean()}
desc = pd.DataFrame(filas).T.infer_objects()
print(desc.loc[["RVA", "RVH", "TPD", "PEC", "GNA", "MTO",
                "GRA"]].round(2).to_string())
print("correlación roll–carry:",
      round(desc["roll"].corr(desc["carry_medio"]), 3))
```
```output
        sector  total  precio   roll  carry_medio
RVA    indices  12.47   12.34   0.13         0.19
RVH    indices   6.98    3.60   3.39         3.44
TPD      tipos   1.31   -0.03   1.34         1.27
PEC    energia  -2.42   -0.01  -2.41        -2.47
GNA    energia -13.89    3.11 -17.00       -15.32
MTO    metales   0.71    1.99  -1.28        -1.44
GRA  agricolas  -6.82   -0.12  -6.69        -5.08
correlación roll–carry: 0.99
```

Todo en porcentaje anual de 2011 a 2025. El roll yield medido coincide con el carry medio de la serie (correlación
de 0,99 entre los 23 mercados): el roll yield no es más que el carry acumulado. Lo interesante es la columna del
precio:

- En el **gas** (`GNA`), el precio del contrato que se tiene sube un 3,11 % al año, pero el roll yield es de −17,00 %
  y quien mantiene el futuro pierde un 13,89 % al año. La figura lo dibuja: 1 USD de 2011 termina en 0,12, mientras el
  precio que se ve en pantalla termina un 59 % más alto.
- En el **crudo** (`PEC`), el precio termina donde empezó (−0,01 % al año) y el rendimiento total es el roll yield,
  −2,41 %.
- En el **aceite de soja** (`GRA`), roll yield de −6,69 % y rendimiento total de −6,82 %.
- En los **índices**, el roll yield es pequeño ($r - q$): +0,13 % en `RVA`, +3,39 % en `RVH`, donde el dividendo
  supera al tipo. El rendimiento total es sobre todo el del precio.
- En el **bono a 30 años** (`TPD`), el roll yield (+1,34 %) es casi todo el rendimiento: el carry positivo de tener un
  bono con cupón alto financiado a tipos bajos.

*Fig. — Gas natural sintético, 2011-2025: el precio del contrato que se tiene frente a lo que gana quien lo mantiene y rueda.* Dos líneas en escala logarítmica desde 1 en enero de 2011. El precio del contrato que se tiene termina en 1,59 a finales de 2025, tras pasar por un mínimo semanal de 0,46 y un máximo de 2,79; el valor de mantener el futuro y rodarlo (destacado) cae casi sin pausa hasta 0,04 en 2023 y termina en 0,12. La diferencia es el roll yield acumulado: el gas pasa la mayor parte del tiempo en contango.

```python
desc.groupby("sector")[["total", "precio", "roll",
                        "carry_medio"]].mean().round(2)
```
```output
           total  precio  roll  carry_medio
sector
agricolas  -6.39   -1.30 -5.09        -4.02
divisas    -1.23   -0.10 -1.12        -1.07
energia    -3.62    1.42 -5.04        -4.55
indices    10.49    9.65  0.84         0.89
metales    -0.12    1.07 -1.19        -1.39
tipos       0.46   -0.14  0.60         0.59
```

En energía y agrícolas, el precio apenas compensa el contango (+1,42 % y −1,30 % al año, frente a un roll yield de
−5,04 % y −5,09 %). Esa es la **respuesta conocida** del generador: el rendimiento esperado de un futuro de materia
prima es

$$
E[\Delta \ln F] = -k + \sum_j w_j\, e^{-\kappa_Q \tau}\left[(\kappa_Q - \kappa_P)\,x_j + \text{deriva}_j\right]
$$

por año, donde $k$ es la pendiente de contango que tiene la curva cuando los factores $x_j$ están en cero. El primer
término dice que **el contango se pierde**: el precio no sube para alcanzarlo. El segundo es la prima de carry de los
agrícolas y la energía, donde la curva descuenta una reversión más rápida ($\kappa_Q$) que la real ($\kappa_P$): en
backwardation, el futuro se queda barato. En el oro, sin reversión ($\kappa_P = \kappa_Q = 0$), el roll yield negativo
(−1,28 %) es casi el tipo de interés: un futuro de oro es oro financiado, y quien compra el físico paga lo mismo.

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

En los datos, el roll yield de las materias primas es una pérdida o una ganancia real porque el generador lo
construye así. En los mercados reales, si el contango se compensa con subidas del contado o no es una pregunta
empírica que la literatura discute y que depende del mercado y del periodo. Lo que sí es contable en cualquier
mercado es la descomposición: el rendimiento de un futuro no es el cambio del precio del gráfico, y comparar un sistema
de futuros con el precio de la materia prima es comparar con algo que nadie puede comprar.

</aside>

## El efecto Samuelson: los lejanos se mueven menos

La curva tiene una última propiedad útil: no todos los vencimientos se mueven igual.

```python
x = curvas[curvas["mercado"].isin(["PEC", "GNA", "GRM", "MTO",
                                   "RVA"])].sort_values(
    ["contrato", "fecha"])
x = x.assign(r=np.log(x["liquidacion"]).groupby(
    x["contrato"]).diff())
x = x[(x["fecha"] >= "2011-01-01") & (x["dias"] > 15)]
x["tramo"] = (x["dias"] // 91) * 3          # meses: 0, 3, 6…
vol = (x.groupby(["tramo", "mercado"])["r"].std().unstack()
       * np.sqrt(252))
vol.loc[:18].round(3)
```
```output
mercado    GNA    GRM    MTO    PEC    RVA
tramo
0        0.341  0.254  0.154  0.320  0.188
3        0.229  0.188  0.154  0.248  0.190
6        0.185  0.144  0.154  0.198  0.190
9        0.163  0.123  0.155  0.167  0.190
12       0.146  0.106  0.154  0.146  0.182
15       0.132  0.094  0.155  0.132    NaN
18       0.119  0.091  0.154    NaN    NaN
```

En el gas, la volatilidad anual del contrato pasa del 34,1 % en el tramo de 0 a 3 meses al 11,9 % a 18; en el maíz,
del 25,4 % al 9,1 %; en el crudo, del 32,0 % al 13,2 % a 15 meses. En el oro y en el índice, nada: en torno al 15,4 % y
al 19 % en todos los tramos. Es el **efecto Samuelson**: en los mercados donde los choques de oferta y demanda se
corrigen con el
tiempo, mueven mucho el precio de entrega cercana y poco el de dentro de un año. En el modelo de los datos, un
vencimiento a τ años recibe $e^{-\kappa_Q \tau}$ de cada choque, con κ_Q de hasta 4 en el gas. La figura lo enseña
por meses:

*Fig. — Efecto Samuelson en los futuros sintéticos: volatilidad de cada contrato según los meses que le quedan, 2011-2025.* Cuatro líneas con la volatilidad anual de los rendimientos diarios según los meses hasta el vencimiento. El gas (destacado) baja del 41,7 % en el primer mes al 10,3 % a 23 meses; el crudo, del 35,5 % al 12,7 % a 17 meses; el maíz, del 26,3 % al 9,1 % a 18 meses; el oro se queda plano en torno al 15,5 %.

Para un sistema tiene dos consecuencias. La volatilidad de la serie continua salta en cada roll en los mercados con
reversión (el contrato nuevo está más lejos y se mueve menos), así que conviene estimarla con los rendimientos del
mismo contrato y con una ventana que no se alargue demasiado. Y elegir un vencimiento más lejano reduce el riesgo por
contrato, pero cambia la apuesta: en el gas, el contrato a un año apenas responde a lo que mueve el primero. La lección
3.1.3 mide qué pasa al rodar antes o después.

## Ejercicios

1. Calcula el tipo de financiación implícito (*repo* implícito) de los futuros de `RVE` el 26 de noviembre de 2025
   suponiendo conocido el dividendo, y compáralo con el tipo a un mes del evro.

**Solución.**

```python
S_, q_, r_ = c.loc["RVE", ["contado", "rentabilidad_dividendo",
                          "tipo_1m"]]
d = curva("RVE", DIA)
tau = d["dias"] / 365
d["r_impl"] = 100 * np.log(d["liquidacion"] / S_) / tau + q_
print(f"tipo a 1 mes {r_:.2f} %")
d.round(3)
```
```output
tipo a 1 mes 2.09 %
          dias  liquidacion  r_impl
contrato
RVEZ25      23       6946.0   1.921
RVEH26     114       6929.0   1.956
RVEM26     204       6914.0   2.005
RVEU26     296       6900.0   2.048
RVEZ26     387       6885.0   2.054
```

Es la misma ecuación despejada para el tipo. Los contratos con más de tres meses dan un tipo implícito unas décimas
por debajo del 2,09 %, la diferencia de 3 a 5 puntos básicos entre la liquidación y el valor razonable dividida por
τ; el de 23 días da un error mayor, porque un punto de precio (el tick de `RVE`) son casi 1,5 puntos básicos y se
divide por un τ de 0,063. En los mercados reales, ese tipo implícito se compara con el coste de financiarse de cada
uno: si es más alto, sale a cuenta vender el futuro y comprar el índice (*cash and carry*).

2. Repite la descomposición de rendimiento total, precio y roll yield del crudo (`PEC`) solo para 2020 y solo para
   2022. ¿Cuándo el roll yield ayudó y cuándo perjudicó?

**Solución.**

```python
for a in ["2020", "2022"]:
    s = S["PEC"].loc[a]
    total = s["ret"].sum()
    precio = np.log(s["precio"].iloc[-1] / s["precio"].iloc[0])
    roll = total - precio
    print(a, f"total {100 * total:+.1f} %, precio "
          f"{100 * precio:+.1f} %, roll {100 * roll:+.1f} %")
```
```output
2020 total -163.5 %, precio -89.1 %, roll -74.3 %
2022 total -22.9 %, precio -26.1 %, roll +3.2 %
```

En 2020, con la curva en contango fuerte durante la caída, el roll resta 74,3 puntos de logaritmo: el precio del
contrato que se tiene cae un 89,1 % en logaritmos y quien mantenía el futuro, un 163,5 % (de cada dólar le quedaban
unos 20 céntimos). En 2022, en backwardation, el roll suma 3,2 puntos: el futuro perdió menos (−22,9 %) que el precio
(−26,1 %). Es el mismo mecanismo que en el gas, con el signo de cada año.

3. ¿Por qué la volatilidad de una serie continua de gas construida con el contrato que se tiene puede saltar en cada
   roll, y cómo afecta a un sistema que dimensiona por volatilidad (lección 2.2.2)?

**Solución.**

Por el efecto Samuelson: el contrato que se tiene está cada vez más cerca del vencimiento y su volatilidad sube; en el
roll se pasa a uno un mes más lejano, que se mueve menos. Una volatilidad estimada con ventanas largas mezcla tramos
de los dos, y una que use rendimientos del contrato nuevo antes de tenerlo los infravalora. Un sistema que dimensiona
por volatilidad compra más contratos justo después del roll (vol baja) y menos al final de la vida del contrato (vol
alta). No es un error en sí, porque el riesgo real cambia de esa manera, pero hay que estimarla con rendimientos del
mismo contrato y con una ventana coherente con la duración de un contrato.

## Autoevaluación

1. El 26 de noviembre de 2025 el índice RVA está en 7.841,33, el tipo a 1 mes en el 3,87 % y el rendimiento por dividendo en el 1,891 %. ¿Dónde debería cotizar el futuro a 114 días?

   - A. Por debajo del contado, porque el dividendo reduce el precio
   - B. Unos 48 puntos por encima: S·e^((r − q)τ) con r − q = 1,98 % y τ = 114/365 da 7.889,95; el contrato liquida a 7.892
   - C. Al mismo precio que el contado
   - D. Donde el mercado espere que esté el índice en marzo

   *Respuesta B: Unos 48 puntos por encima: S·e^((r − q)τ) con r − q = 1,98 % y τ = 114/365 da 7.889,95; el contrato liquida a 7.892.* En un futuro financiero la curva la fija el arbitraje: quien compra el índice paga el tipo y cobra el dividendo; quien compra el futuro, no. El valor razonable es 7.889,95 y el contrato liquida 2,60 puntos básicos por encima.

2. La curva del crudo del 20 de abril de 2020 tiene una pendiente de +96,5 % anual entre los dos primeros contratos con un tipo del 0,21 %. ¿Qué dice del rendimiento de conveniencia neto (conveniencia menos almacenamiento)?

   - A. Que es muy positivo: tener crudo físico es valioso
   - B. Que es de −96,3 %: almacenar cuesta muchísimo más de lo que rinde tener el barril, la situación de un mercado con exceso de oferta y depósitos llenos
   - C. Que es cero
   - D. Nada: la pendiente depende solo del tipo de interés

   *Respuesta B: Que es de −96,3 %: almacenar cuesta muchísimo más de lo que rinde tener el barril, la situación de un mercado con exceso de oferta y depósitos llenos.* Con F = S·e^((r + u − y)τ), la pendiente es r + u − y y la conveniencia neta, y − u = r − pendiente = 0,21 − 96,5. Un contango así solo existe cuando sobra el producto y guardarlo es caro.

3. En el gas natural sintético, de 2011 a 2025, el precio del contrato que se tiene sube un 3,11 % al año y quien mantiene el futuro pierde un 13,89 % al año. ¿De dónde sale la diferencia?

   - A. De las comisiones
   - B. Del roll yield: −17,00 % al año, porque la curva está casi siempre en contango y cada roll vende el contrato barato que converge y compra el siguiente, más caro
   - C. De un error en la serie continua
   - D. Del margen inicial

   *Respuesta B: Del roll yield: −17,00 % al año, porque la curva está casi siempre en contango y cada roll vende el contrato barato que converge y compra el siguiente, más caro.* Rendimiento total = cambio del precio + roll yield. El precio del primer contrato no sube lo suficiente para alcanzar el contango, así que el contango se pierde. La figura del gas lo enseña: 1 USD en 2011 termina en 0,12.

4. ¿Por qué en el oro el roll yield es negativo (−1,28 % al año en los datos) y aun así no es una prima que cobre quien vende el futuro?

   - A. Porque en el oro el contango es el coste de financiación (y almacenamiento): quien tiene el oro físico lo paga igual, así que el futuro y el físico financiado rinden lo mismo
   - B. Porque el oro no tiene futuros
   - C. Porque el oro siempre está en backwardation
   - D. Porque la volatilidad del oro es cero

   *Respuesta A: Porque en el oro el contango es el coste de financiación (y almacenamiento): quien tiene el oro físico lo paga igual, así que el futuro y el físico financiado rinden lo mismo.* En los activos de inversión fáciles de almacenar, la pendiente es casi solo el tipo (y el almacenamiento). Un futuro de oro equivale a oro financiado: el roll yield negativo es el tipo que el comprador del físico también pagaría.

5. La volatilidad del gas sintético es del 41,7 % en el contrato al que le queda menos de un mes y del 10,3 % a 23 meses. ¿Cómo se llama el efecto y qué implica?

   - A. Efecto Samuelson: los choques que revierten mueven mucho el vencimiento cercano y poco los lejanos; elegir un contrato más lejano reduce el riesgo por contrato y cambia lo que se está apostando
   - B. Efecto calendario: el gas se mueve menos en verano
   - C. Un error de los datos
   - D. Sonrisa de volatilidad

   *Respuesta A: Efecto Samuelson: los choques que revierten mueven mucho el vencimiento cercano y poco los lejanos; elegir un contrato más lejano reduce el riesgo por contrato y cambia lo que se está apostando.* En los datos, la curva descuenta que los choques revierten con velocidad κ_Q, así que un vencimiento a τ años se mueve e^(−κ_Q·τ) veces lo que el contado. En el oro, sin reversión, la volatilidad es plana.

## Ideas clave

- La curva de un futuro financiero la fija el arbitraje: índice S·e^((r − q)τ), divisa X·e^((r_USD − r_divisa)τ), bono con cupón menos financiación. Los futuros de índices de los datos liquidan a menos de 5 puntos básicos de su valor razonable.
- En las materias primas, F = S·e^((r + u − y)τ): la pendiente mide almacenamiento menos rendimiento de conveniencia. El crudo pasa de +96,5 % anual de contango en abril de 2020 a −38,6 % en noviembre de 2025.
- La forma estacional de la curva (gas de invierno caro, electricidad de verano) no es carry: está descontada y no da rendimiento a quien compra el contrato.
- Rendimiento de mantener un futuro = cambio del precio + roll yield. En los 23 mercados, el roll yield medido y el carry medio tienen una correlación de 0,99; en el gas, el roll yield es de −17,00 % al año.
- En los datos, el precio de las materias primas no sube para alcanzar el contango: el roll yield negativo es una pérdida real (la respuesta conocida, −k). Que en un mercado real el contango sea o no una prima es una pregunta empírica, no una ley.
- Efecto Samuelson: en los mercados con reversión, los lejanos se mueven mucho menos que el primero (gas: 41,7 % frente a 10,3 %); en el oro, nada.

## Recursos

### Research

- [Política monetaria en Python](https://pythonparatrading.com/politica-monetaria-en-python/) · Research-0010 · 15 min

### Documentación

- [Eurex: Euro-Bund Futures (cupón del 6 %, entregables de 8,5 a 10,5 años)](https://www.eurex.com/ex-en/markets/int/long-term-interest-rates/fix/government-bonds/Euro-Bund-Futures-137298) · eurex.com
- [MEFF: futuros sobre IBEX 35 (precio de liquidación al vencimiento)](https://www.meff.es/esp/Derivados-Financieros/Futuros-sobre-IBEX35) · meff.es

### Libros

- [Robert Carver: Advanced Futures Trading Strategies (Harriman House, 2023)](https://www.harriman-house.com/authors/robert-carver/advanced-futures-trading-strategies/9780857199683) · harriman-house.com

### Datos

- [Datos del nivel 3: curvas de futuros (Parquet)](https://pythonparatrading.com/curso/datos/nivel-3/futuros3_curvas.parquet) · Descarga · futuros3_curvas.parquet
- [Datos del nivel 3: contado, dividendo y tipo de los índices (CSV)](https://pythonparatrading.com/curso/datos/nivel-3/futuros3_indices_contado.csv) · Descarga · futuros3_indices_contado.csv

Lección anterior: [3.1.1 · Contratos de futuros principales](https://pythonparatrading.com/curso-python-trading/nivel-3/contratos-de-futuros-principales/)

Lección siguiente: [3.1.3 · Gestión del roll, márgenes, SPAN y apalancamiento implícito](https://pythonparatrading.com/curso-python-trading/nivel-3/roll-margenes-span-y-apalancamiento/)
