Valorización y Sensibilidad¶
Configuración¶
Para ejecutar todos los ejemplos se debe importar la librería. Se sugiere utilizar siempre el alias qcf
.
import qcfinancial as qcf
qcf.id()
'version: 0.10.0, build: 2024-06-09 10:20'
Librerías adicionales.
import aux_functions as aux # Aquí se guardó la funcion leg_as_dataframe del notebook 3
from math import exp, log
import pandas as pd
import numpy as np
pd.options.display.max_columns=300
Para 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]
1000
Se obtiene la tasa en una posición de la curva.
curva.get_values_at(0)[1]
0.025
Se 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]
100
curva.get_values_at(0)[1]
0.026
Se 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%
Derivadas del factor de descuento respecto a los vértices de la curva.
[zcc.wf_derivative_at(i) for i in range(zcc.get_length())]
[0.21822330167566764, 0.6234951476447647, 0.0]
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%
Derivadas del factor de capitalización de la tasa forward.
[zcc.fwd_wf_derivative_at(i) for i in range(zcc.get_length())]
[0.16508763600814624, 0.0, 0.0]
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,000
Se 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.77
Se verifica a mano el resultado.
plazo = fecha_hoy.day_diff(fecha_vcto)
print("Plazo:", plazo)
Plazo: 348
tasa_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.77
Renta 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 = True
Se 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.110516628864033351
Con 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,111
Con 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.55938455
Curvas 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)
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 = 1
Se 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_down
zcc_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(31, 1, 2024)
fecha_final = qcf.QCDate(31, 1, 2025)
bus_adj_rule = qcf.BusyAdjRules.MODFOLLOW
periodicidad = qcf.Tenor('6M')
periodo_irregular = qcf.StubPeriod.SHORTFRONT
calendario = qcf.BusinessCalendar(fecha_inicio, 20)
lag_pago = 0
nominal = 20_000_000.0
amort_es_flujo = True
valor_tasa_fija = .01774
tasa_cupon = qcf.QCInterestRate(
valor_tasa_fija,
qcf.QC30360(),
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 | nominal | amortizacion | interes | amort_es_flujo | flujo | moneda | valor_tasa | tipo_tasa | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2024-01-31 | 2024-07-31 | 2024-07-31 | 20,000,000.00 | 0.00 | 177,400.00 | True | 177,400.00 | USD | 1.7740% | Lin30360 |
1 | 2024-07-31 | 2025-01-31 | 2025-01-31 | 20,000,000.00 | 20,000,000.00 | 177,400.00 | True | 20,177,400.00 | USD | 1.7740% | Lin30360 |
Se calcula ahora el valor presente:
vp_fija = pv.pv(fecha_hoy, fixed_rate_leg, zcc_usd)
print(f"Valor presente de la pata fija es: {vp_fija:,.0f}")
Valor presente de la pata fija es: 20,017,705
Al 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: -9
Sensibilidad en 10: 0
Sensibilidad en 11: 0
Sensibilidad en 12: 0
Sensibilidad en 13: 0
Sensibilidad en 14: -62
Sensibilidad en 15: -1,927
Sensibilidad en 16: 0
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 total: -1,998
Se 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: 20,015,777.5553
Valor presente down de la pata fija es: 20,019,632.4437
Finalmente, 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: -1,927
OvernightIndex Leg¶
Se da de alta una pata OvernightIndex.
rp = qcf.RecPay.RECEIVE
fecha_inicio = qcf.QCDate(10,1,2019)
fecha_final = qcf.QCDate(10,7,2029)
bus_adj_rule = qcf.BusyAdjRules.FOLLOW
fix_adj_rule = qcf.BusyAdjRules.PREVIOUS
dates_for_eq_rate = qcf.DatesForEquivalentRate.ACCRUAL
periodicidad_pago = qcf.Tenor('2Y')
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,
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=2,
notional_currency=qcf.QCCLP(),
dates_for_eq_rate=dates_for_eq_rate,
)
aux.leg_as_dataframe(overnight_index_leg).style.format(format_dict)
fecha_inicial_devengo | fecha_final_devengo | 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_equivalente | 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 | 2021-07-12 | 2019-07-10 | 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 |
2 | 2021-07-12 | 2023-07-10 | 2021-07-12 | 2023-07-10 | 2023-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 | 2023-07-10 | 2025-07-10 | 2023-07-10 | 2025-07-10 | 2025-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 |
4 | 2025-07-10 | 2027-07-12 | 2025-07-10 | 2027-07-12 | 2027-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 | 2027-07-12 | 2029-07-10 | 2027-07-12 | 2029-07-10 | 2029-07-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(5, 3, 2020)
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_devengo | fecha_final_devengo | 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_equivalente | 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 | 2021-07-12 | 2019-07-10 | 2021-07-12 | 2021-07-12 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 18,376.69 | 19,226.28 | 2.0000% | LinAct360 | 1,547,444,444.44 | 1,547,444,444.44 | 0.0000% | 1.00 |
2 | 2021-07-12 | 2023-07-10 | 2021-07-12 | 2023-07-10 | 2023-07-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 19,226.28 | 19,877.30 | 2.0000% | LinAct360 | 1,536,888,888.89 | 1,536,888,888.89 | 0.0000% | 1.00 |
3 | 2023-07-10 | 2025-07-10 | 2023-07-10 | 2025-07-10 | 2025-07-10 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 19,877.30 | 21,118.61 | 3.0000% | LinAct360 | 2,314,833,333.33 | 2,314,833,333.33 | 0.0000% | 1.00 |
4 | 2025-07-10 | 2027-07-12 | 2025-07-10 | 2027-07-12 | 2027-07-12 | 38,000,000,000.00 | 0.00 | True | CLP | ICPCLP | 21,118.61 | 22,978.68 | 4.0000% | LinAct360 | 3,090,666,666.67 | 3,090,666,666.67 | 0.0000% | 1.00 |
5 | 2027-07-12 | 2029-07-10 | 2027-07-12 | 2029-07-10 | 2029-07-10 | 38,000,000,000.00 | 38,000,000,000.00 | True | CLP | ICPCLP | 22,978.68 | 25,238.13 | 5.0000% | LinAct360 | 3,847,500,000.00 | 41,847,500,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: 39,062,144,488
Tambié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: 0
Sensibilidad en 3: 0
Sensibilidad en 4: 0
Sensibilidad en 5: -74,910
Sensibilidad en 6: -158,708
Sensibilidad en 7: 0
Sensibilidad en 8: -271,813
Sensibilidad en 9: -137,584
Sensibilidad en 10: -753,029
Sensibilidad en 11: -382,738
Sensibilidad en 12: -1,332,749
Sensibilidad en 13: -691,260
Sensibilidad en 14: -19,372,761
Sensibilidad en 15: -9,846,486
Sensibilidad en 16: 0
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad de descuento: -33,022,037 CLP
La 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: 0
Sensibilidad en 1: 0
Sensibilidad en 2: 0
Sensibilidad en 3: 0
Sensibilidad en 4: 0
Sensibilidad en 5: 74,910
Sensibilidad en 6: 158,708
Sensibilidad en 7: 0
Sensibilidad en 8: 271,813
Sensibilidad en 9: 137,584
Sensibilidad en 10: 753,029
Sensibilidad en 11: 382,738
Sensibilidad en 12: 1,332,749
Sensibilidad en 13: 691,260
Sensibilidad en 14: 19,372,761
Sensibilidad en 15: 9,846,486
Sensibilidad en 16: 0
Sensibilidad en 17: 0
Sensibilidad en 18: 0
Sensibilidad de proyección: 33,022,037 CLP
Como 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: 9,846,486 CLP
IborCashflow 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 = 'TERSOFR6M'
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 = .0
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 | nominal | amortizacion | interes | amort_es_flujo | flujo | moneda | 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 | 0.00 | True | 0.00 | USD | TERSOFR6M | 0.0000% | 0.0000% | 1.00 | LinAct360 |
1 | 2020-05-12 | 2020-11-12 | 2020-05-08 | 2020-11-12 | 20,000,000.00 | 0.00 | 0.00 | True | 0.00 | USD | TERSOFR6M | 0.0000% | 0.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 | 0.00 | True | 20,000,000.00 | USD | TERSOFR6M | 0.0000% | 0.0000% | 1.00 | LinAct360 |
valor_termsofr_6m = 0.02
fecha_hoy = qcf.QCDate(25, 2, 2020)
ibor_leg.get_cashflow_at(0).set_interest_rate_value(valor_termsofr_6m)
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 | nominal | amortizacion | interes | amort_es_flujo | flujo | moneda | 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 | TERSOFR6M | 2.0000% | 0.0000% | 1.00 | LinAct360 |
1 | 2020-05-12 | 2020-11-12 | 2020-05-08 | 2020-11-12 | 20,000,000.00 | 0.00 | 169,878.64 | True | 169,878.64 | USD | TERSOFR6M | 1.6619% | 0.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 | 155,899.59 | True | 20,155,899.59 | USD | TERSOFR6M | 1.5504% | 0.0000% | 1.00 | LinAct360 |
Se 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: 77
d2: 261
Factor forward: 100.8494%
Tasa forward: 1.6619%
Curve method 1.6619%
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,126,209
Derivadas 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: -2
Sensibilidad en 6: -2
Sensibilidad en 7: 0
Sensibilidad en 8: 0
Sensibilidad en 9: 0
Sensibilidad en 10: 0
Sensibilidad en 11: -6
Sensibilidad en 12: -6
Sensibilidad en 13: 0
Sensibilidad en 14: 0
Sensibilidad en 15: -1,407
Sensibilidad en 16: -986
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,409 USD
Se 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,124,803
Valor presente down pata IBOR: 20,127,616
Sensibilidad de descuento en el vértice 15: -1,407
Se 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: 0
Sensibilidad en 2: 0
Sensibilidad en 3: 0
Sensibilidad en 4: 0
Sensibilidad en 5: -190
Sensibilidad en 6: -231
Sensibilidad en 7: 0
Sensibilidad en 8: 0
Sensibilidad en 9: 0
Sensibilidad en 10: 0
Sensibilidad en 11: 6
Sensibilidad en 12: 6
Sensibilidad en 13: 0
Sensibilidad en 14: 0
Sensibilidad en 15: 1,407
Sensibilidad en 16: 986
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,984 USD
Se 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,127,616
Valor presente down pata IBOR: 20,124,803
Sensibilidad de proyección en el vértice 15: 1,407
IcpClfCashflow 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 | nominal | amortizacion | amort_es_flujo | flujo | moneda | icp_inicial | icp_final | uf_inicial | uf_final | valor_tasa | interes | spread | gearing | tipo_tasa | flujo_en_clp | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2018-05-31 | 2018-10-31 | 2018-10-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 |
1 | 2018-10-31 | 2019-04-30 | 2019-04-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 |
2 | 2019-04-30 | 2019-10-31 | 2019-10-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 |
3 | 2019-10-31 | 2020-04-30 | 2020-04-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 |
4 | 2020-04-30 | 2020-11-02 | 2020-11-02 | 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-11-02 | 2021-04-30 | 2021-04-30 | 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 | nominal | amortizacion | amort_es_flujo | flujo | moneda | icp_inicial | icp_final | uf_inicial | uf_final | valor_tasa | interes | spread | gearing | tipo_tasa | flujo_en_clp | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2018-05-31 | 2018-10-31 | 2018-10-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 |
1 | 2018-10-31 | 2019-04-30 | 2019-04-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 |
2 | 2019-04-30 | 2019-10-31 | 2019-10-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 |
3 | 2019-10-31 | 2020-04-30 | 2020-04-30 | 300,000.00 | 0.00 | True | -3,401.28 | CLF | 18,786.13 | 18,935.12 | 28,080.26 | 28,627.71 | -2.2426% | -3,401.28 | 0.0000% | 1.00 | LinAct360 | -97,370,764.00 |
4 | 2020-04-30 | 2020-11-02 | 2020-11-02 | 300,000.00 | 0.00 | True | -2,589.43 | CLF | 18,935.12 | 19,050.65 | 28,627.71 | 29,053.02 | -1.6706% | -2,589.43 | 0.0000% | 1.00 | LinAct360 | -75,230,762.00 |
5 | 2020-11-02 | 2021-04-30 | 2021-04-30 | 300,000.00 | 300,000.00 | True | 298,044.72 | CLF | 19,050.65 | 19,173.77 | 29,053.02 | 29,432.64 | -1.3108% | -1,955.28 | 0.0000% | 1.00 | LinAct360 | 8,772,243,383.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: 297,715.99
Valor presente en CLP: 8,467,099,423
Sensibilidad 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.06
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.17
Sensibilidad en 10: 0.01
Sensibilidad en 11: 0.00
Sensibilidad en 12: 0.00
Sensibilidad en 13: 0.00
Sensibilidad en 14: 0.00
Sensibilidad en 15: -33.62
Sensibilidad en 16: -2.17
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: -35.54 CLF
Sensibilidad 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.06
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.17
Sensibilidad en 10: -0.01
Sensibilidad en 11: 0.00
Sensibilidad en 12: 0.00
Sensibilidad en 13: 0.00
Sensibilidad en 14: 0.00
Sensibilidad en 15: 33.62
Sensibilidad en 16: 2.17
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
CompoundedOvernightRate Leg¶
Se da de alta una pata de tipo CompoundedOvernightRate.
rp = qcf.RecPay.PAY
fecha_inicio = qcf.QCDate(31, 3, 2020)
fecha_final = qcf.QCDate(31, 1, 2023)
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 = 2
######################################################################
# 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 = 5_500_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_hoy,
cor_leg,
zcc_usd,
ts
)
aux.leg_as_dataframe(cor_leg).style.format(format_dict)
fecha_inicial | fecha_final | fecha_pago | nominal | amortizacion | interes | amort_es_flujo | flujo | moneda | codigo_indice_tasa | tipo_tasa | valor_tasa | spread | gearing | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2020-03-31 | 2021-01-29 | 2021-01-29 | -5,500,000.00 | 0.00 | -0.00 | True | -77,568.07 | USD | OISTEST | LinAct360 | 1.6701% | 0.0000% | 1.00 |
1 | 2021-01-29 | 2022-01-31 | 2022-01-31 | -5,500,000.00 | 0.00 | -0.00 | True | -84,818.39 | USD | OISTEST | LinAct360 | 1.5127% | 0.0000% | 1.00 |
2 | 2022-01-31 | 2023-01-31 | 2023-01-31 | -5,500,000.00 | -5,500,000.00 | -0.00 | True | -5,585,601.26 | USD | OISTEST | LinAct360 | 1.5351% | 0.0000% | 1.00 |
print(f'Valor presente: {pv.pv(fecha_hoy, cor_leg, zcc_usd):,.0f}')
Valor presente: -5,491,175
Sensibilidad 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: 45.39
Sensibilidad en 5: 7.26
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: -6.21
Sensibilidad en 15: -0.89
Sensibilidad en 16: -2.18
Sensibilidad en 17: -120.89
Sensibilidad en 18: -1,457.54
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
Verifica 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: -5,491,175
Valor presente down pata CompoundedOvernightRate: -5,491,174
Sensibilidad de proyección en el vértice 15: -0.89
Sensibilidad 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: 6.20
Sensibilidad en 15: 0.89
Sensibilidad en 16: 2.19
Sensibilidad en 17: 120.90
Sensibilidad en 18: 1,457.54
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
Verifica 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: -5,491,174
Valor presente down pata CompoundedOvernightRate: -5,491,175
Sensibilidad de descuento en el vértice 15: 0.89