Lección 3.1.2 · 65 min
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.
- Módulo
- 3.1 · Futuros en profundidad
- Duración
- 65 min
Lecciones del nivel 3
3.1 · Futuros en profundidad
3.2 · Spreads de futuros
- 3.2.1 Spreads de calendario, butterflies y condors
- 3.2.2 Spreads intercommodity: crack, spark y crush
- 3.2.3 Spreads de tipos y de índices: 2s10s, NOB, NQ/ES y Europa frente a EE. UU.
- 3.2.4 Ratios de cobertura: por nominal, por DV01 y por volatilidad
- 3.2.5 Datos y backtesting de spreads: cotizados, sintéticos y por patas
- 3.2.6 Estacionalidad de spreads: análisis multi-año, patrones y trampas
3.3 · Forex avanzado
3.4 · Teoría y valoración de opciones
- 3.4.1 Payoffs, paridad put-call y límites de arbitraje
- 3.4.2 Árboles binomiales, Black-Scholes-Merton, Black-76 y Monte Carlo
- 3.4.3 Griegas: delta, gamma, vega, theta, rho, vanna, volga y charm
- 3.4.4 Volatilidad implícita: Newton-Raphson, Brent, py_vollib y QuantLib
- 3.4.5 Sonrisa, skew y estructura temporal; superficie de volatilidad con SVI
- 3.4.6 Volatilidad realizada frente a implícita; prima de riesgo de volatilidad; VIX y VSTOXX
- 3.4.7 Modelos avanzados: Heston, SABR y volatilidad local
3.5 · Estructuras de opciones en Python
- 3.5.1 Estructuras direccionales y verticales: call, put, covered call, collar y spreads
- 3.5.2 Estructuras de volatilidad y rango: straddles, strangles, cóndores y mariposas
- 3.5.3 Calendarios, diagonales, ratios, backspreads, risk reversals y jade lizard
- 3.5.4 Herramienta propia: payoff, griegas agregadas y escenarios
- 3.5.5 Gestión: ajustes, roll, cierre anticipado, asignación temprana y pin risk
3.6 · Estrategias sistemáticas de volatilidad y opciones
3.7 · Cripto derivados y estrategias específicas
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):
con el tipo de financiación, el coste de almacenamiento 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).
Fuente 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 (); 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:
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"]] 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.09El 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:
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")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.80En RVA, con el tipo por encima del dividendo ( %), 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 ( %), 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:
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) 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.125El 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:
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())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:
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}")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.408Con 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:
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]])))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.2El 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:
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} %")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 , así que . En abril de 2020, %: 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):
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) 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.2217ret 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:
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) 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.5Tres 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 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 divisaDQE, 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
d = curva("GNA", DIA)
print(d["liquidacion"].iloc[:14].round(3).to_dict()){'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 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 al precio del contrato que se tiene cada día (la serie sin ajustar de la lección 1.4.6):
Fuente 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:
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)) 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.99Todo 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 (): +0,13 % en
RVA, +3,39 % enRVH, 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.
desc.groupby("sector")[["total", "precio", "roll",
"carry_medio"]].mean().round(2) 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.59En 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
por año, donde es la pendiente de contango que tiene la curva cuando los factores 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 () que la real (): en backwardation, el futuro se queda barato. En el oro, sin reversión (), 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.
El efecto Samuelson: los lejanos se mueven menos
La curva tiene una última propiedad útil: no todos los vencimientos se mueven igual.
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)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 NaNEn 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 de cada choque, con κ_Q de hasta 4 en el gas. La figura lo enseña por meses:
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
- Calcula el tipo de financiación implícito (repo implícito) de los futuros de
RVEel 26 de noviembre de 2025 suponiendo conocido el dividendo, y compáralo con el tipo a un mes del evro.
Solución
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)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.054Es 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).
- 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
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} %")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.
- ¿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.
Respuesta plegada en cada pregunta
Autoevaluación
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?
- APor debajo del contado, porque el dividendo reduce el precio
- BUnos 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
- CAl mismo precio que el contado
- DDonde el mercado espere que esté el índice en marzo
Ver respuesta
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.
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)?
- AQue es muy positivo: tener crudo físico es valioso
- BQue 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
- CQue es cero
- DNada: la pendiente depende solo del tipo de interés
Ver respuesta
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.
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?
- ADe las comisiones
- BDel 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
- CDe un error en la serie continua
- DDel margen inicial
Ver respuesta
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.
¿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?
- APorque 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
- BPorque el oro no tiene futuros
- CPorque el oro siempre está en backwardation
- DPorque la volatilidad del oro es cero
Ver respuesta
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.
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?
- AEfecto 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
- BEfecto calendario: el gas se mueve menos en verano
- CUn error de los datos
- DSonrisa de volatilidad
Ver respuesta
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.