Regresa a Python

Diferencial de tasas de rendimiento CETES - BONOS

¿Cómo se ha comportado el spread entre CETES y el BONO de 10 años?

Diferencial de tasas de rendimiento CETES - BONOS
Índice

La diferencia entre la tasa de rendimiento de 10 años y el rendimiento del CETE de 28 días representa la estructura de la curva de rendimientos y puede ser una señal del comportamiento de la economía en los siguientes meses.

Importancia del diferencial

Históricamente en la economía estadounidense, una curva “empinada” (diferencial positivo) es seguida por un desempeño económico favorable. Por otro lado, una curva “invertida” (diferencial negativo) es seguida por una desaceleración económica o recesión (area gris de la gráfica). Puedes encontrar un comparativo interesante de diferentes diferenciales en https://thoughtfulfinance.com/does-the-yield-curve-forecast-recessions/.

10-Year Treasury Constant Maturity Minus 3-Month Treasury Constant Maturity

Federal Reserve Bank of St. Louis, 10-Year Treasury Constant Maturity Minus 3-Month Treasury Constant Maturity [T10Y3M], retrieved from FRED, Federal Reserve Bank of St. Louis; https://fred.stlouisfed.org/series/T10Y3M, June 23, 2025.

Cálculo del diferencial

Para el cálculo del diferencial, vamos a utilizar la librería requests para consultar la base SIE de Bánco de México. También puedes consultar la API utilizando la librería banxico-api.

import requests
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
def consultar_banxico(series_ids, fecha_inicio, fecha_fin, token):
    """
    Consulta la API de Banxico para obtener datos de varias series y los organiza en un DataFrame.

    Args:
        series_ids (list): Lista de IDs de las series a consultar.
        fecha_inicio (str): Fecha de inicio en formato 'YYYY-MM-DD'.
        fecha_fin (str): Fecha de fin en formato 'YYYY-MM-DD'.
        token (str): Token de acceso proporcionado por Banxico.
    
    Returns:
        pd.DataFrame: DataFrame con las series y sus valores.
    """

    url_base = "https://www.banxico.org.mx/SieAPIRest/service/v1/series/" 
    series_str = ",".join(series_ids)
    url = f"{url_base}{series_str}/datos/{fecha_inicio}/{fecha_fin}" # existen varias entradas a la API
    headers = {"Bmx-Token": token}

    response = requests.get(url, headers=headers) # petición directa al API

    if response.status_code != 200:
        raise Exception(f"Error en la consulta: {response.status_code} - {response.text}")

    datos = response.json()
    series = datos.get("bmx", {}).get("series", [])

    # Extraer los datos de cada serie
    series_data = pd.DataFrame()

    for serie in datos['bmx']['series']:
        serie_id = serie['idSerie']
        valores = serie['datos']
        fechas = [datetime.strptime(d['fecha'], '%d/%m/%Y') for d in valores]
        valores = [float(d['dato'].replace(',', '')) if d['dato'] != 'N/E' else None for d in valores]
        serie_df = pd.DataFrame(data=valores, index=fechas)
        serie_df.columns = [serie_id]
        series_data = pd.concat([serie_df, series_data], axis=1)#ojo con el orden del concat, para 
		#después poder poner los headings correctos

    return series_data
# Sustituye con tu token de Banxico
token = "d62cc7ed09d1002fa1ee9_UTILIZA TU TOKEN________"
series_ids = ["SF282", # "Cetes 28" ,   días Tasa de rendimiento promedio mensual
"SF3338", # "Cetes 91" ,   días Tasa de rendimiento promedio mensual
"SF3270", # "Cetes 182",   días Tasa de rendimiento promedio mensual
"SF3367", # "Cetes 364",   días Tasa de rendimiento promedio mensual
"SF17990", # "M3", Bonos a tasa fija a 3 años Tasa de rendimiento promedio mensual, en por ciento anual
"SF18608",# "M5"  ,Bonos a tasa fija a 5 años Tasa de rendimiento promedio mensual
"SF33229", # M7
"SF30057",# "M10"  ,  Bonos a tasa fija a 10 años Tasa de rendimiento promedio mensual
"SF41841",# "M20"   ,   Bonos a tasa fija a 20 años Tasa de rendimiento promedio mensual
"SF60719"]  # "M30" ,   Bono a tasa fija a 30 años Tasa de rendimiento promedio mensual
# IDs de las series que deseas consultar

series_names = ["Cetes 28" ,
                "Cetes 91" ,
                "Cetes 182",
                "Cetes 364",
                "M3"       ,
                "M5"       ,
                "M7",
                "M10"      ,
                "M20"      ,
                "M30"]

fecha_inicio = "2004-01-01"
fecha_fin = "2025-05-31"
# la información diaria tiene muchos gaps
#series_ids = ["SF43936",     "SF43939","SF43942",      "SF43945","SF43883",
"SF43886","SF44946","SF44071","SF45384","SF60696"]

#series_names = ["Cetes 28" , "Cetes 91" , "Cetes 182", "Cetes 364","M3",     
"M5" ,     "M7",     "M10" , "M20"      ,"M30"]
try:
    df = consultar_banxico(series_ids, fecha_inicio, fecha_fin, token)
except Exception as e:
    print(f"Error: {e}")
df = df[series_ids] # truco para ordenar las columnas
df.columns = series_names # nombramos las columnas
df= df.ffill() # rellenamos los datos faltantes hacia adelante
# Calculamos los spreads
df["M10_Cetes28"] = df["M10"] - df["Cetes 28"]
df["M10_Cetes91"] = df["M10"] - df["Cetes 91"] # equivalente T10Y3M de FRED
plt.figure(figsize=(12, 10))

plt.title("Diferenciales en Tasa de Rendimiento promedio mensual")
plt.plot(df["Cetes 28"], label="CETE 28", linewidth=3)
plt.plot(df["M10"], label="Bono M10", linewidth=3)
plt.plot(df["M10_Cetes28"], label="M10-Cetes28", linewidth=2, linestyle='--')
plt.ylabel("Tasa de rendimiento promedio mensual (%)")
plt.legend()
# after plotting the data, format the labels
current_values = plt.gca().get_yticks()/100
plt.gca().set_yticklabels(['{:,.0%}'.format(x) for x in current_values])
plt.grid(True, which='both', axis='both', linestyle='--')
plt.annotate(f'Fuente: Banco de México, SIE',
                xy = (0, -0.075),
                xycoords='axes fraction')
plt.savefig("M10-CETE28.jpg")
plt.show()

Diferenciales en Tasa de Rendimiento promedio mensual

plt.figure(figsize=(12, 10))

plt.title("Diferenciales en Tasa de Rendimiento promedio mensual")
plt.plot(df["Cetes 91"], label="CETE 91", linewidth=3)
plt.plot(df["M10"], label="Bono M10", linewidth=3)
plt.plot(df["M10_Cetes91"], label="M10-Cetes91", linewidth=2, linestyle='--')
plt.ylabel("Tasa de rendimiento promedio mensual (%)")
plt.legend()
# after plotting the data, format the labels
current_values = plt.gca().get_yticks()/100
plt.gca().set_yticklabels(['{:,.0%}'.format(x) for x in current_values])
plt.grid(True, which='both', axis='both', linestyle='--')
plt.annotate(f'Fuente: Banco de México, SIE',
                xy = (0, -0.075),
                xycoords='axes fraction')
plt.savefig("M10-CETE91.jpg")
plt.show()

Diferenciales en Tasa de Rendimiento promedio mensual

df.to_csv("data.csv")

Estadísticos

df["M10_Cetes91"].describe()
count    257.000000
mean       1.089805
std        1.450538
min       -2.780000
25%        0.130000
50%        1.110000
75%        2.370000
max        3.500000
Name: M10_Cetes91, dtype: float64
df["M10_Cetes91"].loc["2025-05-01"]
np.float64(1.1099999999999994)
  • Podemos ver que en el periodo analizado el mayor diferencial ha sido de 3.5% y el menor diferencial -2.78%. El promedio ha sido 1.08%. La última observación (Mayo 2025) el spread fue ligeramente mayor que el promedio, 1.10%
df["M10_Cetes91"].hist(bins=20)
plt.title("Distribución del diferencial de rendimiento mensual M10 - CETE 91")
plt.xlabel("Diferencial en la Tasa de rendimiento promedio mensual (%)")
plt.ylabel("Frecuencia")
plt.annotate(f'Fuente: Gráfica del autor con datos de Banco de México, SIE',
                xy = (0, -0.2),
                xycoords='axes fraction', color="gray")
plt.show()

Histograma Diferenciales en Tasa de Rendimiento promedio mensual

  • Sesgo a la derecha, es decir, la mayor frecuencia se encuentra en curvas “normales” o “empinadas”
plt.boxplot(df["M10_Cetes91"], label="M10-CETE91")
plt.title("Distribución del diferencial de rendimiento mensual M10 - CETE 91")
plt.legend()
plt.annotate(f'Fuente: Gráfica del autor con datos de Banco de México, SIE',
                xy = (0, -0.15),
                xycoords='axes fraction', color="gray")
plt.show()

Whisker o Boxplot Diferenciales en Tasa de Rendimiento promedio mensual

Views