<jupyter_start><jupyter_text><NAME> En este notebook aprenderemos a valorizar un portfolio de papeles en distintos escenarios, tanto a fecha de hoy como en una fecha futura  Librerías<jupyter_code>from datetime import date
import datetime
import bisect
import pandas as pd
import numpy as np

import autograd.numpy as agnp
from autograd import grad

import my_functions as mf<jupyter_output><empty_output><jupyter_text>Data de Bonos Vamos a utilizar la misma base de datos de la sesión anterior.<jupyter_code>bonos = pd.read_excel('data/bonos_empresa_carga_inicial.xlsx')<jupyter_output><empty_output><jupyter_text>Al hacer el append, se regenera el índice. Con el valor por default `ignore_index=True` se matienen los índices originales de ambos `DataFrame`.<jupyter_code>bonos = bonos.append(pd.read_excel('data/bonos_estado_carga_inicial.xlsx'), ignore_index=True)
tablas_desarrollo = pd.read_csv('data/tablas_desarrollo.csv')
tablas_desarrollo.head()<jupyter_output><empty_output><jupyter_text>Valorización de un Portfolio Vamos a definir un portfolio de bonos con el cual realizar los cálculos. La posición repetida en BTP0470930 es para considerar la práctica usual en Chile de considerar *palo a palo* las transacciones y no el precio promedio de toda la posición.<jupyter_code>portfolio = [
    {
        'nemotecnico': 'BTP0470930',
        'monto': 1000000000,
        'fecha_compra': date(2021, 3, 18),
        'tir_compra': .0321,
        'monto_compra': 1124753559,
    },
    {
        'nemotecnico': 'BTP0470930',
        'monto': 2000000000,
        'fecha_compra': date(2021, 2, 23),
        'tir_compra': .0293,
        'monto_compra': 2340842895,
    },
    {
        'nemotecnico': 'BTU0200335',
        'monto': 300000,
        'fecha_compra': date(2021, 7, 7),
        'tir_compra': .0225,
        'monto_compra': 8732178169,
    },
    {
        'nemotecnico': 'BBNS-W0414',
        'monto': 100000,
        'fecha_compra': date(2020, 9, 3),
        'tir_compra': -.0009,
        'monto_compra': 3657255336,
    },
    {
        'nemotecnico': 'BSTDW11218',
        'monto': 300000,
        'fecha_compra': date(2020, 11, 13),
        'tir_compra': -.0045,
        'monto_compra': 9533881736,
    },
    {
        'nemotecnico': 'BCCA-F0919',
        'monto': 1000000000,
        'fecha_compra': date(2021, 3, 24),
        'tir_compra': .0721 ,
        'monto_compra': 1013970102,
    }
]<jupyter_output><empty_output><jupyter_text>La fecha de valorización.<jupyter_code>fecha_valor = date(2021, 8, 23)<jupyter_output><empty_output><jupyter_text>Y los datos de mercado correspondientes.<jupyter_code>ufs = {fecha_valor: 29850.56}
mkt = {
    'BTP0470930': {
        'tir_mcdo': .01,
        'base': .0028,
        'spread': 0.0072
    },
    'BTU0200335': {
        'tir_mcdo': .0196,
        'base': .0196,
        'spread': 0.0
    },
    'BBNS-W0414': {
        'tir_mcdo': .0016,
        'base': -.0046,
        'spread': 0.0062
    },
    'BSTDW11218': {
        'tir_mcdo': .01,
        'base': .0028,
        'spread': 0.0072
    },
    'BCCA-F0919': {
        'tir_mcdo': .09,
        'base': .0326,
        'spread': 0.0574
    }
}
def get_valorizador_with_data(bonos, tablas_desarrollo, ufs):
    def wrapper(fecha_valor, nemotecnico, tir, monto):
        return mf.valorizador_rf(
            fecha_valor,
            nemotecnico,
            tir,
            monto,
            bonos,
            tablas_desarrollo,
            ufs
        )
    return wrapper
valorizador = get_valorizador_with_data(bonos, tablas_desarrollo, ufs)
def valor_portfolio(fecha_val, escenario, valorizador):
    return [
        valorizador(fecha_valor, nemo, escenario[nemo]['tir_mcdo'], 100) for nemo in mkt
    ]
result = valor_portfolio(fecha_valor, mkt, valorizador)
result<jupyter_output><empty_output><jupyter_text>Vamos a procesar un poco este resultado para que resulte más legible y contenga también la información del portfolio.<jupyter_code>def genera_informe(resultado_valorizacion, portfolio, escenario):
    """
    """
    df_valor = pd.DataFrame.from_dict(resultado_valorizacion)
    df_portfolio = pd.DataFrame.from_dict(portfolio)
    df_portfolio['tir_mcdo'] = df_portfolio.apply(
        lambda row: escenario[row['nemotecnico']]['tir_mcdo'], axis=1)
    df_portfolio = df_portfolio.merge(
        df_valor[['nemotecnico', 'valor_pago', 'precio', 'duracion', 'convexidad']])
    df_portfolio['valor_pago'] = df_portfolio['valor_pago'] * df_portfolio['monto'] / 100.0
    return df_portfolio
df_portfolio = genera_informe(result, portfolio, mkt)
df_portfolio.style.format({
    'tir_compra': '{:.4%}',
    'tir_mcdo': '{:.4%}',
    'precio': '{:.4%}',
    'monto': '{:,.0f}',
    'monto_compra': '{:,.0f}',
    'valor_pago': '{:,.0f}',
    'duracion': '{:,.2f}',
    'convexidad': '{:,.2f}',
})<jupyter_output><empty_output><jupyter_text>El valor total del portfolio es:<jupyter_code>print(f"Valor total: {df_portfolio['valor_pago'].sum(): ,.0f}")<jupyter_output>Valor total:  26,483,106,005<jupyter_text>Definir Escenarios de Valorización Supongamos que queremos saber cuánto cambia el valor del portfolio si las tasas base suben N puntos básicos. Vamos a definir una función que dado un número puntos básicos y un escenario base de valorización construya el escenario deseado.<jupyter_code>def escenario_tasa_base(n, escenario_base):
    return {
        k: {
        'tir_mcdo': round(escenario_base[k]['tir_mcdo'] + n / 10000., 6),
        'base': round(escenario_base[k]['base'] + n / 10000., 6),
        'spread': escenario_base[k]['spread']
        } for k in escenario_base
    }
mkt_10pb_mas_base = escenario_tasa_base(10, mkt)
mkt_10pb_mas_base<jupyter_output><empty_output><jupyter_text>Podemos generar un informe de valorización con este escenario:<jupyter_code>nuevo_valor = valor_portfolio(fecha_valor, mkt_10pb_mas_base, valorizador)
df_portfolio_10pb_mas_base = genera_informe(
    nuevo_valor,
    portfolio,
    mkt_10pb_mas_base
)
df_portfolio_10pb_mas_base.style.format({
    'tir_compra': '{:.4%}',
    'tir_mcdo': '{:.4%}',
    'precio': '{:.4%}',
    'monto': '{:,.0f}',
    'monto_compra': '{:,.0f}',
    'valor_pago': '{:,.0f}',
    'duracion': '{:,.2f}',
    'convexidad': '{:,.2f}',
})
print(f"Valor total +10 pb en base: {df_portfolio_10pb_mas_base['valor_pago'].sum(): ,.0f}")<jupyter_output>Valor total +10 pb en base:  26,305,116,373<jupyter_text>Ejercicio Hacer un escenario que mueva sólo el spread.<jupyter_code>def escenario_spread(n, escenario_base):
    def is_gov(nemo):
        return nemo[0:3] in ['BTP', 'BTU', 'BCU', 'BCP']

    def tir(nemo):
        return (round(escenario_base[nemo]['tir_mcdo'] + n / 10000., 6) if
                not is_gov(nemo) else escenario_base[nemo]['tir_mcdo'])

    def spread(nemo):
        return (round(escenario_base[nemo]['spread'] + n / 10000., 6) if
                not is_gov(nemo) else escenario_base[nemo]['spread'])

    return {
        k: {
            'tir_mcdo': tir(k),
            'base': escenario_base[k]['base'],
            'spread': spread(k),
        } for k in escenario_base
    }
mkt_10pb_mas_spread = escenario_spread(10, mkt)
mkt_10pb_mas_spread
nuevo_valor_2 = valor_portfolio(fecha_valor, mkt_10pb_mas_spread, valorizador)
df_portfolio_10pb_spread = genera_informe(
    nuevo_valor_2,
    portfolio,
    mkt_10pb_mas_spread
)
df_portfolio_10pb_spread.style.format({
    'tir_compra': '{:.4%}',
    'tir_mcdo': '{:.4%}',
    'precio': '{:.4%}',
    'monto': '{:,.0f}',
    'monto_compra': '{:,.0f}',
    'valor_pago': '{:,.0f}',
    'duracion': '{:,.2f}',
    'convexidad': '{:,.2f}',
})
print(f"Valor total +10 pb en spread: {df_portfolio_10pb_spread['valor_pago'].sum(): ,.0f}")<jupyter_output>Valor total +10 pb en spread:  26,439,774,989
{'fraction_non_alphanumeric': 0.1416998842741417, 'fraction_numerical': 0.05747717628905748, 'mean_word_length': 3.591499409681228, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 3, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '157431', 'license': 'MIT', 'n_tokens_mistral': 3342, 'n_tokens_neox': 3023, 'n_words': 590, 'path': '03_02_b_renta_fija.ipynb', 'repo_name': 'adiaz-efaa/intro-fin-python', 'size': '7787'}