Valorización y Sensibilidad
Abrir en la demoConfiguración
Para ejecutar todos los ejemplos se debe importar la librería. Se sugiere utilizar siempre el alias qcf.
import qcfinancial as qcf
import pickleqcf.id()'version: 1.11.3, build: 2026-06-29 16:00'Librerías adicionales.
import aux_functions as aux # Aquí se guardó la función leg_as_dataframe del notebook 3
from math import exp, log
import pandas as pd
import numpy as nppd.options.display.max_columns=300Para formateo de pandas.DataFrames.
format_dict = {
'nominal': '{:,.2f}',
'amort': '{:,.2f}',
'interes': '{:,.2f}',
'flujo': '{:,.2f}',
'nocional': '{:,.2f}',
'amortizacion': '{:,.2f}',
'icp_inicial': '{:,.2f}',
'icp_final': '{:,.2f}',
'uf_inicial': '{:,.2f}',
'uf_final': '{:,.2f}',
'plazo': '{:,.0f}',
'tasa': '{:,.4%}',
'valor_tasa': '{:,.4%}',
'valor_tasa_equivalente': '{:,.4%}',
'spread': '{:,.4%}',
'gearing': '{:,.2f}',
'amort_moneda_pago': '{:,.2f}',
'interes_moneda_pago': '{:,.2f}',
'valor_indice_inicial': '{:,.2f}',
'valor_indice_final': '{:,.2f}',
'valor_indice_fx': '{:,.2f}',
'flujo_en_clp': '{:,.2f}',
}Construcción de la Curva
La construcción de una curva se hace en varios pasos.
Vectores de Float e Int
Este es un vector de números enteros (grandes, de ahí la l (long))
lvec = qcf.long_vec()Agregar un elemento.
lvec.append(1000)Este es un vector de números float.
vec = qcf.double_vec()Agregar un elemento.
vec.append(.025)Obtener ese elemento.
print(f"Plazo: {lvec[0]:,.0f}")
print(f"Tasa: {vec[0]:,.2%}")Plazo: 1,000
Tasa: 2.50%Objeto Curva
Es simplemente un long_vec que representa las abscisas de la curva y un double_vec que representa las ordenadas. Ambos vectores deben tener el mismo largo.
curva = qcf.QCCurve(lvec, vec)Un elemento de una curva se representa como un par abscisa, ordenada.
curva.get_values_at(0)(1000, 0.025)Se obtiene el plazo en una posición de la curva.
curva.get_values_at(0)[0]1000Se obtiene la tasa en una posición de la curva.
curva.get_values_at(0)[1]0.025Se agrega un par (plazo, valor) a la curva.
curva.set_pair(100, .026)Se verifica. Notar que se debe usar el índice 0 ya que la curva se ordena automáticamente por plazos ascendentes.
curva.get_values_at(0)[0]100curva.get_values_at(0)[1]0.026Se agrega un par más.
curva.set_pair(370, .03)Se itera sobre la curva mostrando sus valores
for i in range(curva.get_length()):
pair = curva.get_values_at(i)
print("Tenor: {0:,.0f} Valor: {1:.4%}".format(pair[0], pair[1]))Tenor: 100 Valor: 2.6000%
Tenor: 370 Valor: 3.0000%
Tenor: 1,000 Valor: 2.5000%Interpolador
Se agrega un interpolador. En este caso, un interpolador lineal.
lin = qcf.QCLinearInterpolator(curva)Se puede ahora obtener una tasa a un plazo cualquiera.
plazo = 120
print(f"Tasa a {plazo:,.0f} días es igual a {lin.interpolate_at(plazo):.4%}")Tasa a 120 días es igual a 2.6296%Curva Cero Cupón
Para completar el proceso se define un objeto de tipo QCInterestRate. Con este objeto, que representa la convención de las tasas de interés asociadas a la curva, se termina de dar de alta una curva cero cupón.
yf = qcf.QCAct365()
wf = qcf.QCContinousWf()
tasa = qcf.QCInterestRate(.0, yf, wf)zcc = qcf.ZeroCouponCurve(lin, tasa)El interpolador permite obtener una tasa a cualquier plazo.
plazo = 300
print(f"Tasa en {plazo:,.0f} es igual a {zcc.get_rate_at(plazo):.4%}")Tasa en 300 es igual a 2.8963%Otros métodos:
Discount factor.
print(f"Discount factor at {plazo}: {zcc.get_discount_factor_at(plazo):.6%}")
print(f"Check: {exp(-zcc.get_rate_at(plazo) * plazo / 365):.6%}")Discount factor at 300: 97.647593%
Check: 97.647593%Tasa Forward
d1 = 30
d2 = 90
print(f"Tasa forward entre los días {d1:,.0f} y {d2:,.0f}: {zcc.get_forward_rate(d1, d2):.4%}")Tasa forward entre los días 30 y 90: 2.6000%Se verifica el cálculo.
df1 = zcc.get_discount_factor_at(d1)
df2 = zcc.get_discount_factor_at(d2)
df12 = df1 / df2
print(f"Check: {log(df12) * 365 / (d2 - d1):.4%}")Check: 2.6000%Valorizar
Para valorizar es necesario dar de alta un objeto de tipo PresentValue.
pv = qcf.PresentValue()Depósito a Plazo
Se utilizará como instrumento un depósito a plazo en CLP o USD. Este instrumento se modela como un SimpleCashflow. Este, a su vez se construye con un monto, una fecha y una moneda.
fecha_vcto = qcf.QCDate(13, 1, 2025)
monto = 10_000_000.0
clp = qcf.QCCLP()
# Se construye el depósito
depo = qcf.SimpleCashflow(fecha_vcto, monto, clp)print(f"Monto del depósito: {depo.amount():,.0f}")Monto del depósito: 10,000,000Se define una fecha de valorización y se calcula el valor presente del depo.
fecha_hoy = qcf.QCDate(31, 1, 2024)
print(f"Valor presente depo: {pv.pv(fecha_hoy, depo, zcc):,.2f}")Valor presente depo: 9,721,044.77Se verifica a mano el resultado.
plazo = fecha_hoy.day_diff(fecha_vcto)
print("Plazo:", plazo)Plazo: 348tasa_int = zcc.get_rate_at(plazo)
print(f"Tasa: {tasa_int:,.4%}")Tasa: 2.9674%valor_presente = monto * exp(-tasa_int * plazo / 365)
print(f"Valor presente a mano: {valor_presente:,.2f}")Valor presente a mano: 9,721,044.77Renta Fija de Chile
Se muestra el ejemplo de valorización de un bono bullet a tasa fija con las convenciones de la Bolsa de Comercio de Santiago. Para el ejemplo usamos las características del BTU0150326.
Se dan de alta los parámetros requeridos para instanciar un objeto de tipo FixedRateLeg.
rp = qcf.RecPay.RECEIVE
fecha_inicio = qcf.QCDate(1, 3, 2015)
fecha_final = qcf.QCDate(1, 3, 2026)
bus_adj_rule = qcf.BusyAdjRules.NO
periodicidad = qcf.Tenor('6M')
periodo_irregular = qcf.StubPeriod.NO
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
nominal = 100.0
amort_es_flujo = True
valor_tasa_fija = .015
tasa_cupon = qcf.QCInterestRate(
valor_tasa_fija,
qcf.QC30360(),
qcf.QCLinearWf()
)
moneda = qcf.QCCLP()
es_bono = TrueSe da de alta el objeto.
pata_bono = qcf.LegFactory.build_bullet_fixed_rate_leg(
rp,
fecha_inicio,
fecha_final,
bus_adj_rule,
periodicidad,
periodo_irregular,
calendario,
lag_pago,
nominal,
amort_es_flujo,
tasa_cupon,
moneda,
es_bono
)Se da de alta el valor de la TERA y luego se construye un objeto de tipo ChileanFixedRateBond.
tera = qcf.QCInterestRate(.015044, qcf.QCAct365(), qcf.QCCompoundWf())
bono_chileno = qcf.ChileanFixedRateBond(pata_bono, tera)Se valoriza al 2021-09-28 a una TIR de mercado del 1.61%.
fecha_valor = qcf.QCDate(28, 9, 2021)
tir = qcf.QCInterestRate(.0161, qcf.QCAct365(), qcf.QCCompoundWf())
valor_presente = bono_chileno.present_value(fecha_valor, tir)
precio = bono_chileno.precio(fecha_valor, tir)
valor_par = bono_chileno.valor_par(fecha_valor)
print(f'Valor presente: {valor_presente:,.8f}')
print(f'Precio: {precio:,.2%}')
print(f'Valor par: {valor_par:,.18f}')Valor presente: 99.67188455
Precio: 99.56%
Valor par: 100.110516628864033351Con esto el valor a pagar es:
valor_uf = 30_080.37
valor_pago = precio * valor_par * valor_uf
print(f'Valor a pagar: {valor_pago:,.0f}')Valor a pagar: 2,998,111Con 4 decimales en el precio (4 decimales porcentuales, 6 decimales en el número):
precio2 = bono_chileno.precio2(fecha_valor, tir, 6)
print(f'Precio a 4 decmales: {precio2:.4%}')Precio a 4 decmales: 99.5619%La función precio2 entrega el mismo resultado que la función precio cuando se utiliza con 2 decimales porcentuales.
precio22 = bono_chileno.precio2(fecha_valor, tir, 4)
print(f'Precio a 4 decmales: {precio22:.4%}')Precio a 4 decmales: 99.5600%Se muestran las diferencias con la convención de precio usual en mercados desarrollados.
bono = qcf.FixedRateBond(pata_bono)
print(f'Valor presente: {bono.present_value(fecha_valor, tir):,.8f}')
print(f'Precio: {bono.price(fecha_valor, tir):,.8f}')Valor presente: 99.67188455
Precio: 99.55938455Curvas Reales
Construyamos dos curvas a partir de data real. Primero una curva en CLP.
curva_clp = pd.read_excel("./input/curva_clp.xlsx")
curva_clp.style.format(format_dict)| plazo | tasa | |
|---|---|---|
| 0 | 1 | 1.7500% |
| 1 | 4 | 1.7501% |
| 2 | 96 | 1.4867% |
| 3 | 188 | 1.3049% |
| 4 | 279 | 1.2870% |
| 5 | 369 | 1.3002% |
| 6 | 553 | 1.3035% |
| 7 | 734 | 1.2951% |
| 8 | 1,099 | 1.4440% |
| 9 | 1,465 | 1.6736% |
| 10 | 1,830 | 1.9860% |
| 11 | 2,195 | 2.2766% |
| 12 | 2,560 | 2.5812% |
| 13 | 2,926 | 2.8216% |
| 14 | 3,291 | 3.0373% |
| 15 | 3,656 | 3.2154% |
| 16 | 4,387 | 3.4525% |
| 17 | 5,482 | 3.7636% |
| 18 | 7,309 | 4.1641% |
Se da de alta un vector con los plazos (variable de tipo long) y un vector con las tasas (variable de tipo double).
lvec1 = qcf.long_vec()
vec1 = qcf.double_vec()
for t in curva_clp.itertuples():
lvec1.append(int(t.plazo))
vec1.append(t.tasa)Luego, con una curva, un interpolador y un objeto QCInterestRate(que indica la convención de las tasas de la curva) se construye una curva cupón cero.
curva1 = qcf.QCCurve(lvec1, vec1)
lin1 = qcf.QCLinearInterpolator(curva1)
zcc_clp = qcf.ZeroCouponCurve(lin1, tasa)Luego, una curva en USD.
curva_usd = pd.read_excel("./input/curva_usd.xlsx")
curva_usd.style.format(format_dict)| plazo | tasa | |
|---|---|---|
| 0 | 3 | 1.5362% |
| 1 | 4 | 1.1521% |
| 2 | 7 | 1.5536% |
| 3 | 14 | 1.5850% |
| 4 | 31 | 1.6595% |
| 5 | 60 | 1.7698% |
| 6 | 91 | 1.8010% |
| 7 | 123 | 1.7711% |
| 8 | 152 | 1.7542% |
| 9 | 182 | 1.7394% |
| 10 | 213 | 1.7288% |
| 11 | 244 | 1.7183% |
| 12 | 276 | 1.7027% |
| 13 | 305 | 1.6917% |
| 14 | 335 | 1.6820% |
| 15 | 367 | 1.6722% |
| 16 | 549 | 1.6207% |
| 17 | 731 | 1.5947% |
| 18 | 1,096 | 1.5788% |
| 19 | 1,461 | 1.5906% |
| 20 | 1,827 | 1.6190% |
| 21 | 2,558 | 1.7028% |
| 22 | 3,653 | 1.8533% |
| 23 | 4,385 | 1.9547% |
| 24 | 5,479 | 2.0893% |
| 25 | 7,305 | 2.2831% |
| 26 | 9,132 | 2.4306% |
| 27 | 10,958 | 2.5576% |
Se encapasulará todo el procedimiento anterior en una función que dado un DataFrame construya un objeto ZeroCouponCurve.
def zcc_from_df(df: pd.DataFrame, tasa: qcf.QCInterestRate) -> qcf.ZeroCouponCurve:
lvec = qcf.long_vec()
vec = qcf.double_vec()
for t in df.itertuples():
lvec.append(int(t.plazo))
vec.append(t.tasa)
curva = qcf.QCCurve(lvec, vec)
lin = qcf.QCLinearInterpolator(curva)
return qcf.ZeroCouponCurve(lin, tasa)zcc_usd = zcc_from_df(curva_usd, tasa)Finalmente, una curva en CLF.
curva_clf = pd.read_excel("./input/curva_clf.xlsx")
curva_clf.style.format(format_dict)| plazo | tasa | |
|---|---|---|
| 0 | 1 | -5.6780% |
| 1 | 4 | -5.6744% |
| 2 | 35 | -0.9340% |
| 3 | 64 | -2.1183% |
| 4 | 96 | -2.0079% |
| 5 | 126 | -2.0762% |
| 6 | 155 | -1.9197% |
| 7 | 188 | -1.9347% |
| 8 | 218 | -1.7626% |
| 9 | 249 | -1.7987% |
| 10 | 279 | -1.9335% |
| 11 | 309 | -1.8159% |
| 12 | 341 | -1.5940% |
| 13 | 369 | -1.5994% |
| 14 | 400 | -1.5068% |
| 15 | 428 | -1.6115% |
| 16 | 461 | -1.5923% |
| 17 | 491 | -1.5780% |
| 18 | 522 | -1.5186% |
| 19 | 553 | -1.5533% |
| 20 | 582 | -1.5649% |
| 21 | 734 | -1.6594% |
| 22 | 1,099 | -1.4881% |
| 23 | 1,465 | -1.2740% |
| 24 | 1,830 | -1.0201% |
| 25 | 2,195 | -0.8009% |
| 26 | 2,560 | -0.5868% |
| 27 | 2,926 | -0.4145% |
| 28 | 3,291 | -0.3047% |
| 29 | 3,656 | -0.2242% |
| 30 | 4,387 | -0.1871% |
| 31 | 5,482 | -0.1056% |
| 32 | 7,309 | -0.0639% |
zcc_clf = zcc_from_df(curva_clf, tasa)curva_sofr = pd.read_excel("./input/sofr.xlsx")
curva_sofr.style.format(format_dict)| curva | fecha | plazo | tasa | |
|---|---|---|---|---|
| 0 | SOFR | 2024-06-11 00:00:00 | 1 | 5.3935% |
| 1 | SOFR | 2024-06-11 00:00:00 | 2 | 5.3930% |
| 2 | SOFR | 2024-06-11 00:00:00 | 9 | 5.3911% |
| 3 | SOFR | 2024-06-11 00:00:00 | 16 | 5.3909% |
| 4 | SOFR | 2024-06-11 00:00:00 | 32 | 5.3936% |
| 5 | SOFR | 2024-06-11 00:00:00 | 63 | 5.3894% |
| 6 | SOFR | 2024-06-11 00:00:00 | 94 | 5.3843% |
| 7 | SOFR | 2024-06-11 00:00:00 | 124 | 5.3598% |
| 8 | SOFR | 2024-06-11 00:00:00 | 155 | 5.3375% |
| 9 | SOFR | 2024-06-11 00:00:00 | 185 | 5.3127% |
| 10 | SOFR | 2024-06-11 00:00:00 | 216 | 5.2777% |
| 11 | SOFR | 2024-06-11 00:00:00 | 247 | 5.2404% |
| 12 | SOFR | 2024-06-11 00:00:00 | 275 | 5.2070% |
| 13 | SOFR | 2024-06-11 00:00:00 | 367 | 5.0882% |
| 14 | SOFR | 2024-06-11 00:00:00 | 550 | 4.8542% |
| 15 | SOFR | 2024-06-11 00:00:00 | 732 | 4.6727% |
| 16 | SOFR | 2024-06-11 00:00:00 | 1,097 | 4.4059% |
| 17 | SOFR | 2024-06-11 00:00:00 | 1,463 | 4.2398% |
| 18 | SOFR | 2024-06-11 00:00:00 | 1,828 | 4.1384% |
| 19 | SOFR | 2024-06-11 00:00:00 | 2,193 | 4.0790% |
| 20 | SOFR | 2024-06-11 00:00:00 | 2,558 | 4.0405% |
| 21 | SOFR | 2024-06-11 00:00:00 | 2,924 | 4.0173% |
| 22 | SOFR | 2024-06-11 00:00:00 | 3,289 | 4.0022% |
| 23 | SOFR | 2024-06-11 00:00:00 | 3,654 | 3.9939% |
| 24 | SOFR | 2024-06-11 00:00:00 | 4,385 | 3.9904% |
| 25 | SOFR | 2024-06-11 00:00:00 | 5,480 | 3.9912% |
| 26 | SOFR | 2024-06-11 00:00:00 | 7,307 | 3.9428% |
| 27 | SOFR | 2024-06-11 00:00:00 | 9,133 | 3.8068% |
| 28 | SOFR | 2024-06-11 00:00:00 | 10,959 | 3.6594% |
zcc_sofr = zcc_from_df(curva_sofr, tasa)
zcc_sofr.get_rate_at(10_959)0.0365940888755curva_sol = {
"curve_code": "CSOFR",
"type_of_rate": "CON_ACT/365",
"process_date": "2024-06-11",
"jacobians": {},
"values": [
{
"tenor": "2D",
"maturity": 2,
"rate": 0.05393091948998677
},
{
"tenor": "7D",
"maturity": 9,
"rate": 0.05391149528761137
},
{
"tenor": "14D",
"maturity": 16,
"rate": 0.053909500139491626
},
{
"tenor": "1M",
"maturity": 34,
"rate": 0.05392893501738643
},
{
"tenor": "2M",
"maturity": 63,
"rate": 0.053894362943625894
},
{
"tenor": "3M",
"maturity": 94,
"rate": 0.053843482977097415
},
{
"tenor": "4M",
"maturity": 125,
"rate": 0.05359443451658483
},
{
"tenor": "5M",
"maturity": 155,
"rate": 0.05337515689463274
},
{
"tenor": "6M",
"maturity": 185,
"rate": 0.05312657866085667
},
{
"tenor": "7M",
"maturity": 216,
"rate": 0.052777204713019304
},
{
"tenor": "8M",
"maturity": 247,
"rate": 0.05240381230639302
},
{
"tenor": "9M",
"maturity": 275,
"rate": 0.0520700981638382
},
{
"tenor": "1Y",
"maturity": 367,
"rate": 0.05088167612943658
},
{
"tenor": "18M",
"maturity": 552,
"rate": 0.04852936930616741
},
{
"tenor": "2Y",
"maturity": 734,
"rate": 0.04659950874645961
},
{
"tenor": "3Y",
"maturity": 1098,
"rate": 0.04394041235592892
},
{
"tenor": "4Y",
"maturity": 1463,
"rate": 0.04231284987923743
},
{
"tenor": "5Y",
"maturity": 1828,
"rate": 0.041317554448529636
},
{
"tenor": "6Y",
"maturity": 2193,
"rate": 0.04073549138440949
},
{
"tenor": "7Y",
"maturity": 2558,
"rate": 0.04035917885531376
},
{
"tenor": "8Y",
"maturity": 2925,
"rate": 0.040119782655144265
},
{
"tenor": "9Y",
"maturity": 3289,
"rate": 0.039975186953621844
},
{
"tenor": "10Y",
"maturity": 3654,
"rate": 0.03989648083295584
},
{
"tenor": "12Y",
"maturity": 4385,
"rate": 0.03986864130635106
},
{
"tenor": "15Y",
"maturity": 5480,
"rate": 0.039884006370912016
},
{
"tenor": "20Y",
"maturity": 7307,
"rate": 0.03940855790321542
},
{
"tenor": "25Y",
"maturity": 9134,
"rate": 0.03804951829543225
},
{
"tenor": "30Y",
"maturity": 10961,
"rate": 0.036573592658114495
},
{
"tenor": "40Y",
"maturity": 14612,
"rate": 0.03329402953514909
},
{
"tenor": "50Y",
"maturity": 18264,
"rate": 0.029736924965687982
}
]
}df_curva_sol = pd.DataFrame(curva_sol["values"])df_curva_sol.columns = ['tenor', 'plazo', 'tasa']
zcc_sol = zcc_from_df(df_curva_sol, tasa)Curvas para Sensibilidad
Para calcular sensibilidad a la curva cero cupón, se define qué vértice de la curva se quiere desplazar y el monto en puntos básicos del desplazamiento.
vertice = 15
bp = 1Se construyen las curvas con ese vértice 1 punto básico más arriba y 1 punto básico más abajo. Para esto se define una función auxiliar.
def curvas_sens(
df: pd.DataFrame,
tasa: qcf.QCInterestRate,
vertice: int,
bp: float
) -> tuple[qcf.ZeroCouponCurve, qcf.ZeroCouponCurve]:
bp /= 10_000
lvec = qcf.long_vec()
vec_sens_up = qcf.double_vec()
vec_sens_down = qcf.double_vec()
for t in df.itertuples():
lvec.append(int(t.plazo))
if t.Index == vertice:
vec_sens_up.append(t.tasa + bp)
vec_sens_down.append(t.tasa - bp)
else:
vec_sens_up.append(t.tasa)
vec_sens_down.append(t.tasa)
zcc_sens_up = qcf.QCCurve(lvec, vec_sens_up)
lin_sens_up = qcf.QCLinearInterpolator(zcc_sens_up)
zz_sens_up = qcf.ZeroCouponCurve(lin_sens_up, tasa)
zcc_sens_down = qcf.QCCurve(lvec, vec_sens_down)
lin_sens_down = qcf.QCLinearInterpolator(zcc_sens_down)
zz_sens_down = qcf.ZeroCouponCurve(lin_sens_down, tasa)
return zz_sens_up, zz_sens_downzcc_clp_up, zcc_clp_down = curvas_sens(curva_clp, tasa, vertice, bp)
zcc_usd_up, zcc_usd_down = curvas_sens(curva_usd, tasa, vertice, bp)
zcc_clf_up, zcc_clf_down = curvas_sens(curva_clf, tasa, vertice, bp)FixedRateCashflow Leg
Se da de alta una pata fija:
rp = qcf.RecPay.RECEIVE
fecha_inicio = qcf.QCDate(13, 6, 2024)
fecha_final = qcf.QCDate(13, 6, 2026)
bus_adj_rule = qcf.BusyAdjRules.MODFOLLOW
periodicidad = qcf.Tenor('12M')
periodo_irregular = qcf.StubPeriod.SHORTFRONT
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
nominal = 1_000_000.0
amort_es_flujo = True
valor_tasa_fija = 0.048864
valor_tasa_fija = 0.047134
tasa_cupon = qcf.QCInterestRate(
valor_tasa_fija,
qcf.QCAct360(),
qcf.QCLinearWf()
)
moneda = qcf.QCUSD()
es_bono = False
fixed_rate_leg = qcf.LegFactory.build_bullet_fixed_rate_leg(
rp,
fecha_inicio,
fecha_final,
bus_adj_rule,
periodicidad,
periodo_irregular,
calendario,
lag_pago,
nominal,
amort_es_flujo,
tasa_cupon,
moneda,
es_bono
)aux.leg_as_dataframe(fixed_rate_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_pago | nocional | amortizacion | interes | amort_es_flujo | flujo | moneda_nocional | valor_tasa | tipo_tasa | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2024-06-13 | 2025-06-13 | 2025-06-13 | 1,000,000.00 | 0.00 | 47,788.64 | True | 47,788.64 | USD | 4.7134% | LinAct360 |
| 1 | 2025-06-13 | 2026-06-15 | 2026-06-15 | 1,000,000.00 | 1,000,000.00 | 48,050.49 | True | 1,048,050.49 | USD | 4.7134% | LinAct360 |
Se calcula ahora el valor presente:
vp_fija = pv.pv(fecha_val:=qcf.QCDate(11, 6, 2024), fixed_rate_leg, zcc_sofr)
dias = fecha_val.day_diff(qcf.QCDate(13, 6, 2024))
print(f"Días: {dias}")
print(f"Valor presente de la pata fija es: {vp_fija / zcc_sofr.get_discount_factor_at(dias) :,.0f}")Días: 2
Valor presente de la pata fija es: 999,784Al calcular el valor presente, también se calculan las derivadas del valor presente respecto a cada uno de los vértices de la curva.
der = pv.get_derivatives()Con esas derivadas, se puede calcular la sensibilidad a la curva cupón cero a un movimiento de 1 punto básico.
i = 0
total = 0
for d in der:
total += d * bp / 10_000
print(f"Sensibilidad en {i}: {d * bp / 10_000:0,.0f}")
i += 1
print(f"Sensibilidad total: {total:,.0f}")Sensibilidad en 0: 0
Sensibilidad en 1: 0
Sensibilidad en 2: 0
Sensibilidad en 3: 0
Sensibilidad en 4: 0
Sensibilidad en 5: 0
Sensibilidad en 6: 0
Sensibilidad en 7: 0
Sensibilidad en 8: 0
Sensibilidad en 9: 0
Sensibilidad en 10: 0
Sensibilidad en 11: 0
Sensibilidad en 12: 0
Sensibilidad en 13: -5
Sensibilidad en 14: 0
Sensibilidad en 15: -191
Sensibilidad en 16: -1
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad en 19: 0
Sensibilidad en 20: 0
Sensibilidad en 21: 0
Sensibilidad en 22: 0
Sensibilidad en 23: 0
Sensibilidad en 24: 0
Sensibilidad en 25: 0
Sensibilidad en 26: 0
Sensibilidad en 27: 0
Sensibilidad en 28: 0
Sensibilidad total: -196Se puede verificar la sensibilidad por diferencias finitas.
Se calcula el valor presente con las curvas desplazadas.
vp_fija_sens_up = pv.pv(fecha_hoy, fixed_rate_leg, zcc_usd_up)
vp_fija_sens_down = pv.pv(fecha_hoy, fixed_rate_leg, zcc_usd_down)
print(f"Valor presente up de la pata fija es: {vp_fija_sens_up:,.4f}")
print(f"Valor presente down de la pata fija es: {vp_fija_sens_down:,.4f}")Valor presente up de la pata fija es: 1,056,008.6812
Valor presente down de la pata fija es: 1,056,012.1915Finalmente, se calcula la sensibilidad (usando la aproximación central por diferencias finitas).
print(f"Sensibilidad por diferencias finitas: {(vp_fija_sens_up - vp_fija_sens_down) / 2:,.0f}")Sensibilidad por diferencias finitas: -2OvernightIndex Leg
Se da de alta una pata OvernightIndex.
rp = qcf.RecPay.RECEIVE
fecha_inicio = qcf.QCDate(10, 1, 2019)
fecha_final = qcf.QCDate(10, 1, 2022)
bus_adj_rule = qcf.BusyAdjRules.FOLLOW
fix_adj_rule = qcf.BusyAdjRules.PREVIOUS
dates_for_eq_rate = qcf.DatesForEquivalentRate.ACCRUAL
periodicidad_pago = qcf.Tenor('6M')
periodo_irregular_pago = qcf.StubPeriod.SHORTFRONT
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
nominal = 38_000_000_000.0
amort_es_flujo = True
spread = .0
gearing = 1.0
overnight_index_leg = qcf.LegFactory.build_bullet_overnight_index_leg(
rec_pay=rp,
start_date=fecha_inicio,
end_date=fecha_final,
bus_adj_rule=bus_adj_rule,
fix_adj_rule=fix_adj_rule,
settlement_periodicity=periodicidad_pago,
settlement_stub_period=periodo_irregular_pago,
settlement_calendar=calendario,
fixing_calendar=calendario,
settlement_lag=lag_pago,
initial_notional=nominal,
amort_is_cashflow=amort_es_flujo,
spread=spread,
gearing=gearing,
interest_rate=qcf.QCInterestRate(0.0, qcf.QCAct360(), qcf.QCLinearWf()),
index_name='ICPCLP',
eq_rate_decimal_places=4,
notional_currency=qcf.QCCLP(),
dates_for_eq_rate=dates_for_eq_rate,
sett_lag_behaviour=qcf.SettLagBehaviour.DONT_MOVE,
)aux.leg_as_dataframe(overnight_index_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_inicial_indice | fecha_final_indice | fecha_pago | nocional | amortizacion | amort_es_flujo | moneda_nocional | nombre_indice | valor_indice_inicial | valor_indice_final | valor_tasa | tipo_tasa | interes | flujo | spread | gearing | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019-01-10 | 2019-07-10 | 2019-01-10 | 2019-07-10 | 2019-07-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 1.00 | 1.00 | 0.0000% | LinAct360 | 0.00 | 0.00 | 0.0000% | 1.00 |
| 1 | 2019-07-10 | 2020-01-10 | 2019-07-10 | 2020-01-10 | 2020-01-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 1.00 | 1.00 | 0.0000% | LinAct360 | 0.00 | 0.00 | 0.0000% | 1.00 |
| 2 | 2020-01-10 | 2020-07-10 | 2020-01-10 | 2020-07-10 | 2020-07-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 1.00 | 1.00 | 0.0000% | LinAct360 | 0.00 | 0.00 | 0.0000% | 1.00 |
| 3 | 2020-07-10 | 2021-01-11 | 2020-07-10 | 2021-01-11 | 2021-01-11 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 1.00 | 1.00 | 0.0000% | LinAct360 | 0.00 | 0.00 | 0.0000% | 1.00 |
| 4 | 2021-01-11 | 2021-07-12 | 2021-01-11 | 2021-07-12 | 2021-07-12 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 1.00 | 1.00 | 0.0000% | LinAct360 | 0.00 | 0.00 | 0.0000% | 1.00 |
| 5 | 2021-07-12 | 2022-01-10 | 2021-07-12 | 2022-01-10 | 2022-01-10 | 38,000,000,000.00 | 38,000,000,000.00 | True | CLP | ICPCLP | 1.00 | 1.00 | 0.0000% | LinAct360 | 0.00 | 38,000,000,000.00 | 0.0000% | 1.00 |
Notar que al dar de alta un Leg con OvernightIndexCashflow, los valores futuros de los índeces son los default (=1.0). Por lo tanto, el primer paso para valorizar estos cashflows, es calcular los valores forward de los índices.
Se da de alta un objeto de tipo ForwardRates.
fwd_rates = qcf.ForwardRates()Se calculan los índices forward.
icp_val = 18_890.34 # icp a la fecha de proceso
fecha_hoy = qcf.QCDate(8, 1, 2019)
index = 0
for i in range(overnight_index_leg.size()):
cashflow = overnight_index_leg.get_cashflow_at(i)
if cashflow.get_start_date() <= fecha_hoy <= cashflow.get_end_date():
index = i
icp_fecha_inicio_cupon_vigente = 18_376.69
overnight_index_leg.get_cashflow_at(index).set_start_date_index(icp_fecha_inicio_cupon_vigente)
fwd_rates.set_rates_overnight_index_leg(fecha_hoy, icp_val, overnight_index_leg, zcc_clp)aux.leg_as_dataframe(overnight_index_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_inicial_indice | fecha_final_indice | fecha_pago | nocional | amortizacion | amort_es_flujo | moneda_nocional | nombre_indice | valor_indice_inicial | valor_indice_final | valor_tasa | tipo_tasa | interes | flujo | spread | gearing | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019-01-10 | 2019-07-10 | 2019-01-10 | 2019-07-10 | 2019-07-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 18,892.15 | 19,015.28 | 1.3000% | LinAct360 | 248,372,222.00 | 248,372,222.00 | 0.0000% | 1.00 |
| 1 | 2019-07-10 | 2020-01-10 | 2019-07-10 | 2020-01-10 | 2020-01-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 19,015.28 | 19,138.86 | 1.2700% | LinAct360 | 246,662,222.00 | 246,662,222.00 | 0.0000% | 1.00 |
| 2 | 2020-01-10 | 2020-07-10 | 2020-01-10 | 2020-07-10 | 2020-07-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 19,138.86 | 19,264.34 | 1.3000% | LinAct360 | 249,744,444.00 | 249,744,444.00 | 0.0000% | 1.00 |
| 3 | 2020-07-10 | 2021-01-11 | 2020-07-10 | 2021-01-11 | 2021-01-11 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 19,264.34 | 19,388.80 | 1.2600% | LinAct360 | 246,050,000.00 | 246,050,000.00 | 0.0000% | 1.00 |
| 4 | 2021-01-11 | 2021-07-12 | 2021-01-11 | 2021-07-12 | 2021-07-12 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 19,388.80 | 19,550.81 | 1.6500% | LinAct360 | 316,983,333.00 | 316,983,333.00 | 0.0000% | 1.00 |
| 5 | 2021-07-12 | 2022-01-10 | 2021-07-12 | 2022-01-10 | 2022-01-10 | 38,000,000,000.00 | 38,000,000,000.00 | True | CLP | ICPCLP | 19,550.81 | 19,728.77 | 1.8000% | LinAct360 | 345,800,000.00 | 38,345,800,000.00 | 0.0000% | 1.00 |
Con esto, podemos calcular el valor presente.
vp_on_index_leg = pv.pv(fecha_hoy, overnight_index_leg, zcc_clp)
print(f"Valor presente pata ON Index Leg: {vp_on_index_leg:,.0f}")Valor presente pata ON Index Leg: 37,996,356,295print(f"{nominal * zcc_clp.get_discount_factor_at(2):,.0f}")37,996,356,295También en este caso es posible calcular la sensibilidad a la curva de descuento.
der = pv.get_derivatives()
i = 0
for d in der:
print(f"Sensibilidad en {i:}: {d * bp / 10_000:0,.0f}")
i += 1
sens_disc = [d * bp / 10_000 for d in der]
print()
print("Sensibilidad de descuento: {0:,.0f} CLP".format(sum(sens_disc)))Sensibilidad en 0: 0
Sensibilidad en 1: 0
Sensibilidad en 2: -670
Sensibilidad en 3: -11,665
Sensibilidad en 4: -545
Sensibilidad en 5: -24,764
Sensibilidad en 6: -35,947
Sensibilidad en 7: -116,962
Sensibilidad en 8: -11,053,193
Sensibilidad en 9: 0
Sensibilidad en 10: 0
Sensibilidad en 11: 0
Sensibilidad en 12: 0
Sensibilidad en 13: 0
Sensibilidad en 14: 0
Sensibilidad en 15: 0
Sensibilidad en 16: 0
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad de descuento: -11,243,745 CLPLa estructura es la misma que para una pata fija, lo que indica que se debe también incluir la sensibilidad a la curva de proyección.
result = []
for i in range(overnight_index_leg.size()):
cshflw = overnight_index_leg.get_cashflow_at(i)
amt_der = cshflw.get_amount_derivatives()
df = zcc_clp.get_discount_factor_at(fecha_hoy.day_diff(cshflw.get_settlement_date()))
amt_der = [a * bp / 10_000 * df for a in amt_der]
if len(amt_der) > 0:
result.append(np.array(amt_der))
total = result[0] * 0
for r in result:
total += r
for i in range(len(total)):
print("Sensibilidad en {0:}: {1:0,.0f}".format(i, total[i]))
print()
print("Sensibilidad de proyección: {0:,.0f} CLP".format(sum(total)))Sensibilidad en 0: -13,880
Sensibilidad en 1: -6,940
Sensibilidad en 2: 670
Sensibilidad en 3: 11,665
Sensibilidad en 4: 545
Sensibilidad en 5: 24,764
Sensibilidad en 6: 35,947
Sensibilidad en 7: 116,962
Sensibilidad en 8: 11,053,193
Sensibilidad en 9: 0
Sensibilidad en 10: 0
Sensibilidad en 11: 0
Sensibilidad en 12: 0
Sensibilidad en 13: 0
Sensibilidad en 14: 0
Sensibilidad en 15: 0
Sensibilidad en 16: 0
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad de proyección: 11,222,925 CLPComo se espera de una pata OvernightIndex (con lag de pago igual a 0 y spread igual a 0), ambas sensibilidades se cancelan.
Se verifica la sensibilidad de proyección por diferencias finitas:
fwd_rates.set_rates_overnight_index_leg(fecha_hoy, icp_val, overnight_index_leg, zcc_clp_up)
vp_on_index_leg_up = pv.pv(fecha_hoy, overnight_index_leg, zcc_clp)
fwd_rates.set_rates_overnight_index_leg(fecha_hoy, icp_val, overnight_index_leg, zcc_clp_down)
vp_on_index_leg_down = pv.pv(fecha_hoy, overnight_index_leg, zcc_clp)
print(f"Sensibilidad en vértice {vertice}: {(vp_on_index_leg_up - vp_on_index_leg_down) / 2:,.0f} CLP")Sensibilidad en vértice 15: 0 CLPIborCashflow Leg
Se da de alta una pata de tipo IborCashflow.
rp = qcf.RecPay.RECEIVE
fecha_inicio = qcf.QCDate(12, 11, 2019)
fecha_final = qcf.QCDate(12, 5, 2021)
bus_adj_rule = qcf.BusyAdjRules.MODFOLLOW
periodicidad_pago = qcf.Tenor('6M')
periodo_irregular_pago = qcf.StubPeriod.NO
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
periodicidad_fijacion = qcf.Tenor('6M')
periodo_irregular_fijacion = qcf.StubPeriod.NO
# vamos a usar el mismo calendario para pago y fijaciones
lag_de_fijacion = 2
# Definición del índice
codigo = 'TERMSOFR6M'
lin_act360 = qcf.QCInterestRate(.0, qcf.QCAct360(), qcf.QCLinearWf())
fixing_lag = qcf.Tenor('2d')
tenor = qcf.Tenor('6m')
fixing_calendar = calendario
settlement_calendar = calendario
usd = qcf.QCUSD()
termsofr_6m = qcf.InterestRateIndex(
codigo,
lin_act360,
fixing_lag,
tenor,
fixing_calendar,
settlement_calendar,
usd
)
# Fin índice
nominal = 20_000_000.0
amort_es_flujo = True
moneda = usd
spread = .02
gearing = 1.0
ibor_leg = qcf.LegFactory.build_bullet_ibor_leg(
rp,
fecha_inicio,
fecha_final,
bus_adj_rule,
periodicidad_pago,
periodo_irregular_pago,
calendario,
lag_pago,
periodicidad_fijacion,
periodo_irregular_fijacion,
calendario,
lag_de_fijacion,
termsofr_6m,
nominal,
amort_es_flujo,
moneda,
spread,
gearing
)aux.leg_as_dataframe(ibor_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_fixing | fecha_pago | nocional | amortizacion | interes | amort_es_flujo | flujo | moneda_nocional | codigo_indice_tasa | valor_tasa | spread | gearing | tipo_tasa | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019-11-12 | 2020-05-12 | 2019-11-08 | 2020-05-12 | 20,000,000.00 | 0.00 | 202,222.22 | True | 202,222.22 | USD | TERMSOFR6M | 0.0000% | 2.0000% | 1.00 | LinAct360 |
| 1 | 2020-05-12 | 2020-11-12 | 2020-05-08 | 2020-11-12 | 20,000,000.00 | 0.00 | 204,444.44 | True | 204,444.44 | USD | TERMSOFR6M | 0.0000% | 2.0000% | 1.00 | LinAct360 |
| 2 | 2020-11-12 | 2021-05-12 | 2020-11-10 | 2021-05-12 | 20,000,000.00 | 20,000,000.00 | 201,111.11 | True | 20,201,111.11 | USD | TERMSOFR6M | 0.0000% | 2.0000% | 1.00 | LinAct360 |
valor_termsofr_6m = 0.02
fecha_hoy = qcf.QCDate(8, 5, 2020)
fwd_rates.set_rates_ibor_leg1(fecha_hoy, ibor_leg, zcc_usd)aux.leg_as_dataframe(ibor_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_fixing | fecha_pago | nocional | amortizacion | interes | amort_es_flujo | flujo | moneda_nocional | codigo_indice_tasa | valor_tasa | spread | gearing | tipo_tasa | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019-11-12 | 2020-05-12 | 2019-11-08 | 2020-05-12 | 20,000,000.00 | 0.00 | 202,222.22 | True | 202,222.22 | USD | TERMSOFR6M | 0.0000% | 2.0000% | 1.00 | LinAct360 |
| 1 | 2020-05-12 | 2020-11-12 | 2020-05-08 | 2020-11-12 | 20,000,000.00 | 0.00 | 381,670.26 | True | 381,670.26 | USD | TERMSOFR6M | 1.7337% | 2.0000% | 1.00 | LinAct360 |
| 2 | 2020-11-12 | 2021-05-12 | 2020-11-10 | 2021-05-12 | 20,000,000.00 | 20,000,000.00 | 360,756.76 | True | 20,360,756.76 | USD | TERMSOFR6M | 1.5876% | 2.0000% | 1.00 | LinAct360 |
cci = ibor_leg.get_current_cashflow_index(fecha_hoy)ibor_leg.get_cashflow_at(cci).set_interest_rate_value(valor_termsofr_6m)termsofr_6m.get_rate().get_value()0.02aux.leg_as_dataframe(ibor_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_fixing | fecha_pago | nocional | amortizacion | interes | amort_es_flujo | flujo | moneda_nocional | codigo_indice_tasa | valor_tasa | spread | gearing | tipo_tasa | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019-11-12 | 2020-05-12 | 2019-11-08 | 2020-05-12 | 20,000,000.00 | 0.00 | 404,444.44 | True | 404,444.44 | USD | TERMSOFR6M | 2.0000% | 2.0000% | 1.00 | LinAct360 |
| 1 | 2020-05-12 | 2020-11-12 | 2020-05-08 | 2020-11-12 | 20,000,000.00 | 0.00 | 381,670.26 | True | 381,670.26 | USD | TERMSOFR6M | 1.7337% | 2.0000% | 1.00 | LinAct360 |
| 2 | 2020-11-12 | 2021-05-12 | 2020-11-10 | 2021-05-12 | 20,000,000.00 | 20,000,000.00 | 360,756.76 | True | 20,360,756.76 | USD | TERMSOFR6M | 1.5876% | 2.0000% | 1.00 | LinAct360 |
ibor_leg.get_cashflow_at(cci).accrued_interest(fecha_hoy)395555.55555555347qcf.QCDate(12, 11, 2019).day_diff(fecha_hoy) * (valor_termsofr_6m * gearing + spread) / 360 * nominal395555.55555555556ibor_leg.get_cashflow_at(cci).get_interest_rate_value()0.02ibor_leg.get_cashflow_at(cci).get_spread()0.02termsofr_6m.get_rate().get_value()0.02Se verifica la tasa forward del segundo cashflow.
which_cashflow = 1
d1 = fecha_hoy.day_diff(ibor_leg.get_cashflow_at(which_cashflow).get_start_date())
d2 = fecha_hoy.day_diff(ibor_leg.get_cashflow_at(which_cashflow).get_end_date())
print(f"d1: {d1:,.0f}")
print(f"d2: {d2:,.0f}")
crv = zcc_usd
w1 = 1 / crv.get_discount_factor_at(d1)
w2 = 1 / crv.get_discount_factor_at(d2)
print(f"Factor forward: {w2 / w1:.4%}")
print(f"Tasa forward: {(w2 / w1 - 1) * 360 / (d2 - d1):.4%}")
print(f"Curve method {crv.get_forward_rate_with_rate(termsofr_6m.get_rate(), d1, d2):.4%}")d1: 4
d2: 188
Factor forward: 100.8861%
Tasa forward: 1.7337%
Curve method 1.7337%Cálculo de valor presente.
vp_ibor = pv.pv(fecha_hoy, ibor_leg, zcc_usd)
print(f"Valor presente pata IBOR: {vp_ibor:,.0f}")Valor presente pata IBOR: 20,802,232Derivadas del valor presente.
der = pv.get_derivatives()
i = 0
for d in der:
print(f"Sensibilidad en {i}: {d * bp / 10_000:0,.0f}")
i += 1
print()
print(f"Sensibilidad de descuento: {sum(der) * bp / 10_000:,.0f} USD")Sensibilidad en 0: 0
Sensibilidad en 1: -0
Sensibilidad en 2: 0
Sensibilidad en 3: 0
Sensibilidad en 4: 0
Sensibilidad en 5: 0
Sensibilidad en 6: 0
Sensibilidad en 7: 0
Sensibilidad en 8: 0
Sensibilidad en 9: -16
Sensibilidad en 10: -4
Sensibilidad en 11: 0
Sensibilidad en 12: 0
Sensibilidad en 13: 0
Sensibilidad en 14: 0
Sensibilidad en 15: -2,002
Sensibilidad en 16: -22
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad en 19: 0
Sensibilidad en 20: 0
Sensibilidad en 21: 0
Sensibilidad en 22: 0
Sensibilidad en 23: 0
Sensibilidad en 24: 0
Sensibilidad en 25: 0
Sensibilidad en 26: 0
Sensibilidad en 27: 0
Sensibilidad de descuento: -2,044 USDSe verifica la sensibilidad de descuento por diferencias finitas.
vp_ibor_up = pv.pv(fecha_hoy, ibor_leg, zcc_usd_up)
print(f"Valor presente up pata IBOR: {vp_ibor_up:,.0f}")
vp_ibor_down = pv.pv(fecha_hoy, ibor_leg, zcc_usd_down)
print(f"Valor presente down pata IBOR: {vp_ibor_down:,.0f}")
print(f"Sensibilidad de descuento en el vértice {vertice}: {(vp_ibor_up - vp_ibor_down) / 2:,.0f}")Valor presente up pata IBOR: 20,800,231
Valor presente down pata IBOR: 20,804,234
Sensibilidad de descuento en el vértice 15: -2,002Se calcula también la sensibilidad a la curva de proyección.
result = []
for i in range(ibor_leg.size()):
cshflw = ibor_leg.get_cashflow_at(i)
df = zcc_usd.get_discount_factor_at(fecha_hoy.day_diff(cshflw.get_settlement_date()))
amt_der = cshflw.get_amount_derivatives()
if len(amt_der) > 0:
amt_der = [a * bp / 10_000 * df for a in amt_der]
result.append(np.array(amt_der))
total = result[0] * 0
for r in result:
total += r
for i in range(len(total)):
print(f"Sensibilidad en {i}: {total[i]:0,.0f}")
print()
print(f"Sensibilidad de proyección: {sum(total):,.0f} USD")Sensibilidad en 0: 0
Sensibilidad en 1: -22
Sensibilidad en 2: 0
Sensibilidad en 3: 0
Sensibilidad en 4: 0
Sensibilidad en 5: 0
Sensibilidad en 6: 0
Sensibilidad en 7: 0
Sensibilidad en 8: 0
Sensibilidad en 9: 7
Sensibilidad en 10: 2
Sensibilidad en 11: 0
Sensibilidad en 12: 0
Sensibilidad en 13: 0
Sensibilidad en 14: 0
Sensibilidad en 15: 1,982
Sensibilidad en 16: 22
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad en 19: 0
Sensibilidad en 20: 0
Sensibilidad en 21: 0
Sensibilidad en 22: 0
Sensibilidad en 23: 0
Sensibilidad en 24: 0
Sensibilidad en 25: 0
Sensibilidad en 26: 0
Sensibilidad en 27: 0
Sensibilidad de proyección: 1,991 USDSe verifica la sensibilidad de proyección por diferencias finitas.
fwd_rates.set_rates_ibor_leg1(fecha_hoy, ibor_leg, zcc_usd_up)
vp_ibor_up = pv.pv(fecha_hoy, ibor_leg, zcc_usd)
print(f"Valor presente up pata IBOR: {vp_ibor_up:,.0f}")
fwd_rates.set_rates_ibor_leg1(fecha_hoy, ibor_leg, zcc_usd_down)
vp_ibor_down = pv.pv(fecha_hoy, ibor_leg, zcc_usd)
print(f"Valor presente down pata IBOR: {vp_ibor_down:,.0f}")
print(f"Sensibilidad de proyección en el vértice {vertice}: {(vp_ibor_up - vp_ibor_down) / 2:,.0f}")Valor presente up pata IBOR: 20,804,214
Valor presente down pata IBOR: 20,800,251
Sensibilidad de proyección en el vértice 15: 1,982IcpClfCashflow Leg
Se da de alta una pata de tipo IcpClfCashflow.
rp = qcf.RecPay.RECEIVE
fecha_inicio = qcf.QCDate(31, 5, 2018)
fecha_final = qcf.QCDate(31, 3, 2021)
bus_adj_rule = qcf.BusyAdjRules.FOLLOW
periodicidad_pago = qcf.Tenor('6M')
periodo_irregular_pago = qcf.StubPeriod.SHORTFRONT
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
nominal = 300_000.0
amort_es_flujo = True
spread = .0
gearing = 1.0
icp_clf_leg = qcf.LegFactory.build_bullet_icp_clf_leg(
rp,
fecha_inicio,
fecha_final,
bus_adj_rule,
periodicidad_pago,
periodo_irregular_pago,
calendario,
lag_pago,
nominal,
amort_es_flujo,
spread,
gearing
)aux.leg_as_dataframe(icp_clf_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_pago | nocional | amortizacion | amort_es_flujo | flujo | moneda_nocional | icp_inicial | icp_final | uf_inicial | uf_final | valor_tasa | interes | spread | gearing | tipo_tasa | flujo_en_clp | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2018-05-31 | 2018-10-01 | 2018-10-01 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 1 | 2018-10-01 | 2019-04-01 | 2019-04-01 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 2 | 2019-04-01 | 2019-09-30 | 2019-09-30 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 3 | 2019-09-30 | 2020-03-31 | 2020-03-31 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 4 | 2020-03-31 | 2020-09-30 | 2020-09-30 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 5 | 2020-09-30 | 2021-03-31 | 2021-03-31 | 300,000.00 | 300,000.00 | True | 300,000.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 10,500,000,000.00 |
icp_hoy = 18_882.07
uf_hoy = 28_440.19
fwd_rates.set_rates_icp_clf_leg(fecha_hoy, icp_hoy, uf_hoy, icp_clf_leg, zcc_clp, zcc_clp, zcc_clf)
cshflw = icp_clf_leg.get_cashflow_at(3)
cshflw.set_start_date_uf(28_080.26)
cshflw.set_start_date_icp(18_786.13)aux.leg_as_dataframe(icp_clf_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_pago | nocional | amortizacion | amort_es_flujo | flujo | moneda_nocional | icp_inicial | icp_final | uf_inicial | uf_final | valor_tasa | interes | spread | gearing | tipo_tasa | flujo_en_clp | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2018-05-31 | 2018-10-01 | 2018-10-01 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 1 | 2018-10-01 | 2019-04-01 | 2019-04-01 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 2 | 2019-04-01 | 2019-09-30 | 2019-09-30 | 300,000.00 | 0.00 | True | 0.00 | CLF | 10,000.00 | 10,000.00 | 35,000.00 | 35,000.00 | 0.0000% | 0.00 | 0.0000% | 1.00 | LinAct360 | 0.00 |
| 3 | 2019-09-30 | 2020-03-31 | 2020-03-31 | 300,000.00 | 0.00 | True | -171,885.95 | CLF | 18,786.13 | 10,000.00 | 28,080.26 | 35,000.00 | -112.7121% | -171,885.95 | 0.0000% | 1.00 | LinAct360 | -6,016,008,337.00 |
| 4 | 2020-03-31 | 2020-09-30 | 2020-09-30 | 300,000.00 | 0.00 | True | 391,681.30 | CLF | 10,000.00 | 18,986.62 | 35,000.00 | 28,822.76 | 256.8402% | 391,681.30 | 0.0000% | 1.00 | LinAct360 | 11,289,337,594.00 |
| 5 | 2020-09-30 | 2021-03-31 | 2021-03-31 | 300,000.00 | 300,000.00 | True | 297,808.27 | CLF | 18,986.62 | 19,102.24 | 28,822.76 | 29,211.03 | -1.4451% | -2,191.73 | 0.0000% | 1.00 | LinAct360 | 8,699,285,743.00 |
vp_icp_clf = pv.pv(fecha_hoy, icp_clf_leg, zcc_clf)
print(f"Valor presente en UF: {vp_icp_clf:,.2f}")
print(f"Valor presente en CLP: {vp_icp_clf * uf_hoy:,.0f}")Valor presente en UF: 697,118.18
Valor presente en CLP: 19,826,173,500Sensibilidad de Descuento
der = pv.get_derivatives()
i = 0
for d in der:
print(f"Sensibilidad en {i}: {d * bp / 10_000:0,.2f}")
i += 1
print()
print(f"Sensibilidad de descuento: {sum(der) * bp / 10_000:,.2f} CLF")Sensibilidad en 0: 0.00
Sensibilidad en 1: 0.00
Sensibilidad en 2: 0.00
Sensibilidad en 3: 0.00
Sensibilidad en 4: 0.00
Sensibilidad en 5: -5.41
Sensibilidad en 6: -10.27
Sensibilidad en 7: 0.00
Sensibilidad en 8: 0.00
Sensibilidad en 9: 0.00
Sensibilidad en 10: 0.00
Sensibilidad en 11: -11.85
Sensibilidad en 12: -15.24
Sensibilidad en 13: 0.00
Sensibilidad en 14: 0.00
Sensibilidad en 15: 0.00
Sensibilidad en 16: 0.00
Sensibilidad en 17: 0.00
Sensibilidad en 18: 0.00
Sensibilidad en 19: 0.00
Sensibilidad en 20: 0.00
Sensibilidad en 21: 0.00
Sensibilidad en 22: 0.00
Sensibilidad en 23: 0.00
Sensibilidad en 24: 0.00
Sensibilidad en 25: 0.00
Sensibilidad en 26: 0.00
Sensibilidad en 27: 0.00
Sensibilidad en 28: 0.00
Sensibilidad en 29: 0.00
Sensibilidad en 30: 0.00
Sensibilidad en 31: 0.00
Sensibilidad en 32: 0.00
Sensibilidad de descuento: -42.77 CLFSensibilidad de Proyección
result = []
for i in range(icp_clf_leg.size()):
cshflw = icp_clf_leg.get_cashflow_at(i)
df = zcc_clf.get_discount_factor_at(fecha_hoy.day_diff(cshflw.date()))
amt_der = cshflw.get_amount_ufclf_derivatives()
if len(amt_der) > 0:
amt_der = [a * bp / 10_000 * df for a in amt_der]
result.append(np.array(amt_der))
total = result[0] * 0
for r in result:
total += r
for i in range(len(total)):
print(f"Sensibilidad en {i}: {total[i]:0,.2f}")Sensibilidad en 0: 0.00
Sensibilidad en 1: 0.00
Sensibilidad en 2: 0.00
Sensibilidad en 3: 0.00
Sensibilidad en 4: 0.00
Sensibilidad en 5: 5.41
Sensibilidad en 6: 10.27
Sensibilidad en 7: 0.00
Sensibilidad en 8: 0.00
Sensibilidad en 9: 0.00
Sensibilidad en 10: 0.00
Sensibilidad en 11: 11.85
Sensibilidad en 12: 15.24
Sensibilidad en 13: 0.00
Sensibilidad en 14: 0.00
Sensibilidad en 15: 0.00
Sensibilidad en 16: 0.00
Sensibilidad en 17: 0.00
Sensibilidad en 18: 0.00
Sensibilidad en 19: 0.00
Sensibilidad en 20: 0.00
Sensibilidad en 21: 0.00
Sensibilidad en 22: 0.00
Sensibilidad en 23: 0.00
Sensibilidad en 24: 0.00
Sensibilidad en 25: 0.00
Sensibilidad en 26: 0.00
Sensibilidad en 27: 0.00
Sensibilidad en 28: 0.00
Sensibilidad en 29: 0.00
Sensibilidad en 30: 0.00
Sensibilidad en 31: 0.00
Sensibilidad en 32: 0.00CompoundedOvernightRate Leg
Se da de alta una pata de tipo CompoundedOvernightRate.
rp = qcf.RecPay.PAY
fecha_inicio = qcf.QCDate(13, 6, 2024)
fecha_final = qcf.QCDate(13, 12, 2025)
bus_adj_rule = qcf.BusyAdjRules.MODFOLLOW
periodicidad_pago = qcf.Tenor('12M')
periodo_irregular_pago = qcf.StubPeriod.SHORTFRONT
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
lookback = 0
######################################################################
# Definición del índice
codigo = 'OISTEST'
lin_act360 = qcf.QCInterestRate(.0, qcf.QCAct360(), qcf.QCLinearWf())
fixing_lag = qcf.Tenor('0d')
tenor = qcf.Tenor('1d')
fixing_calendar = calendario
settlement_calendar = calendario
usd = qcf.QCUSD()
oistest = qcf.InterestRateIndex(
codigo,
lin_act360,
fixing_lag,
tenor,
fixing_calendar,
settlement_calendar,
usd)
# Fin índice
######################################################################
nominal = 1_000_000.0
amort_es_flujo = True
moneda = usd
spread = .0
gearing = 1.0
cor_leg = qcf.LegFactory.build_bullet_compounded_overnight_rate_leg_2(
rp,
fecha_inicio,
fecha_final,
bus_adj_rule,
periodicidad_pago,
periodo_irregular_pago,
calendario,
lag_pago,
calendario,
oistest,
nominal,
amort_es_flujo,
usd,
spread,
gearing,
qcf.QCInterestRate(0.0, qcf.QCAct360(), qcf.QCLinearWf()),
10,
lookback,
0
)Valor Presente
ts = qcf.time_series()
fwd_rates.set_rates_compounded_overnight_leg2(
fecha_inicio,
cor_leg,
zcc_sofr,
ts
)aux.leg_as_dataframe(cor_leg).style.format(format_dict)| fecha_inicial | fecha_final | fecha_pago | nocional | amortizacion | interes | amort_es_flujo | flujo | moneda_nocional | codigo_indice_tasa | tipo_tasa | valor_tasa | spread | gearing | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2024-06-13 | 2024-12-13 | 2024-12-13 | -1,000,000.00 | 0.00 | -27,002.51 | True | -27,002.51 | USD | OISTEST | LinAct360 | 5.3120% | 0.0000% | 1.00 |
| 1 | 2024-12-13 | 2025-12-15 | 2025-12-15 | -1,000,000.00 | -1,000,000.00 | -47,599.76 | True | -1,047,599.76 | USD | OISTEST | LinAct360 | 4.6692% | 0.0000% | 1.00 |
print(f'Valor presente: {pv.pv(fecha_inicio, cor_leg, zcc_sofr):,.0f}')Valor presente: -1,000,000Sensibilidad de Proyección
proj_sens_by_cashflow = np.array([np.array(
np.array(cor_leg.get_cashflow_at(i).get_amount_derivatives()) *
zcc_usd.get_discount_factor_at(fecha_hoy.day_diff(cor_leg.get_cashflow_at(i).get_settlement_date())) * bp / 10_000)
for i in range(cor_leg.size())])
proj_sens = np.sum(proj_sens_by_cashflow, axis=0)
for i, s in enumerate(proj_sens):
print(f"Sensibilidad en {i}: {s:0,.2f}")Sensibilidad en 0: 0.00
Sensibilidad en 1: 0.00
Sensibilidad en 2: 0.00
Sensibilidad en 3: 0.00
Sensibilidad en 4: 0.00
Sensibilidad en 5: 0.00
Sensibilidad en 6: 0.00
Sensibilidad en 7: 0.00
Sensibilidad en 8: 0.01
Sensibilidad en 9: 0.07
Sensibilidad en 10: 0.00
Sensibilidad en 11: 0.00
Sensibilidad en 12: 0.00
Sensibilidad en 13: 0.00
Sensibilidad en 14: -143.95
Sensibilidad en 15: 0.00
Sensibilidad en 16: 0.00
Sensibilidad en 17: 0.00
Sensibilidad en 18: 0.00
Sensibilidad en 19: 0.00
Sensibilidad en 20: 0.00
Sensibilidad en 21: 0.00
Sensibilidad en 22: 0.00
Sensibilidad en 23: 0.00
Sensibilidad en 24: 0.00
Sensibilidad en 25: 0.00
Sensibilidad en 26: 0.00
Sensibilidad en 27: 0.00
Sensibilidad en 28: 0.00Verifica sensibilidad de proyección.
fwd_rates.set_rates_compounded_overnight_leg2(fecha_hoy, cor_leg, zcc_usd_up, ts)
vp_cor_up = pv.pv(fecha_hoy, cor_leg, zcc_usd)
print(f"Valor presente up pata CompoundedOvernightRate: {vp_cor_up:,.0f}")
fwd_rates.set_rates_compounded_overnight_leg2(fecha_hoy, cor_leg, zcc_usd_down, ts)
vp_cor_down = pv.pv(fecha_hoy, cor_leg, zcc_usd)
print(f"Valor presente down pata CompoundedOvernightRate: {vp_cor_down:,.0f}")
print(f"Sensibilidad de proyección en el vértice {vertice}: {(vp_cor_up - vp_cor_down) / 2:,.2f}")Valor presente up pata CompoundedOvernightRate: -936,739
Valor presente down pata CompoundedOvernightRate: -936,739
Sensibilidad de proyección en el vértice 15: 0.00Sensibilidad de Descuento
disc_der = np.array(pv.get_derivatives()) * bp / 10_000
for i, s in enumerate(disc_der):
print(f"Sensibilidad en {i}: {s:0,.2f}")Sensibilidad en 0: 0.00
Sensibilidad en 1: 0.00
Sensibilidad en 2: 0.00
Sensibilidad en 3: 0.00
Sensibilidad en 4: 0.00
Sensibilidad en 5: 0.00
Sensibilidad en 6: 0.00
Sensibilidad en 7: 0.00
Sensibilidad en 8: 0.00
Sensibilidad en 9: 0.00
Sensibilidad en 10: 0.00
Sensibilidad en 11: 0.00
Sensibilidad en 12: 0.00
Sensibilidad en 13: 0.00
Sensibilidad en 14: 0.00
Sensibilidad en 15: 0.00
Sensibilidad en 16: 0.00
Sensibilidad en 17: 0.00
Sensibilidad en 18: 0.00
Sensibilidad en 19: 1.49
Sensibilidad en 20: 366.30
Sensibilidad en 21: 156.75
Sensibilidad en 22: 0.00
Sensibilidad en 23: 0.00
Sensibilidad en 24: 0.00
Sensibilidad en 25: 0.00
Sensibilidad en 26: 0.00
Sensibilidad en 27: 0.00Verifica la sensibilidad de descuento.
fwd_rates.set_rates_compounded_overnight_leg2(fecha_hoy, cor_leg, zcc_usd, ts)
vp_cor_up = pv.pv(fecha_hoy, cor_leg, zcc_usd_up)
print(f"Valor presente up pata CompoundedOvernightRate: {vp_cor_up:,.2f}")
fwd_rates.set_rates_compounded_overnight_leg2(fecha_hoy, cor_leg, zcc_usd, ts)
vp_cor_down = pv.pv(fecha_hoy, cor_leg, zcc_usd_down)
print(f"Valor presente down pata CompoundedOvernightRate: {vp_cor_down:,.2f}")
print(f"Sensibilidad de descuento en el vértice {vertice}: {(vp_cor_up - vp_cor_down) / 2:,.2f}")Valor presente up pata CompoundedOvernightRate: -936,738.63
Valor presente down pata CompoundedOvernightRate: -936,738.63
Sensibilidad de descuento en el vértice 15: 0.00