Renta Fija Local: Valorización#

En este notebook construiremos un valorizador de renta fija local similar al de la Bolsa de Comercio o RiskAmerica. Dado un nemotécnico y su TIR de mercado, obtendremos el precio, valor par, duración, convexidad y monto a pagar. Adicionalmente, con este valorizador podremos construir una funcionalidad adicional, valorizar un forward de renta fija.

Librerías#

from datetime import date
import pandas as pd
import numpy as np

from autograd import grad

import my_functions as fn

Data#

bonos = pd.read_excel('data/bonos_empresa_carga_inicial.xlsx')
bonos = bonos.append(pd.read_excel('data/bonos_estado_carga_inicial.xlsx'))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_2098/3609385080.py in ?()
----> 1 bonos = bonos.append(pd.read_excel('data/bonos_estado_carga_inicial.xlsx'))

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/pandas/core/generic.py in ?(self, name)
   6295             and name not in self._accessors
   6296             and self._info_axis._can_hold_identifiers_and_holds_name(name)
   6297         ):
   6298             return self[name]
-> 6299         return object.__getattribute__(self, name)

AttributeError: 'DataFrame' object has no attribute 'append'
tablas_desarrollo = pd.read_csv('data/tablas_desarrollo.csv')
tablas_desarrollo
nemotecnico numero_cupon fecha_vcto_cupon interes amortizacion saldo_insoluto
0 BC18-A0719 1 2019-10-31 0.98534 0.00 100.00
1 BC18-A0719 2 2020-01-31 0.98534 0.00 100.00
2 BC18-A0719 3 2020-04-30 0.98534 0.00 100.00
3 BC18-A0719 4 2020-07-31 0.98534 4.65 100.00
4 BC18-A0719 5 2020-10-31 0.93952 0.00 95.35
... ... ... ... ... ... ...
3277320 BWATT-Q 18 2028-09-01 1.93130 0.00 100.00
3277321 BWATT-Q 19 2029-03-01 1.93130 0.00 100.00
3277322 BWATT-Q 20 2029-09-01 1.93130 100.00 100.00
3277323 XERO 0 0001-01-01 0.00000 0.00 0.00
3277324 ZERO 0 0001-01-01 0.00000 0.00 0.00

3277325 rows × 6 columns

Valorizador#

Nos gustaría poder reciclar las funciones que calculan el valor par y el valor presente, pero, nos damos cuenta, que las últimas versiones se han adaptado mucho al formato de la data disponible y ahora tenemos que hacer algunos cambios.

Vamos a definir funciones valor_presente y valor_par que representen de la forma más general posible ambos cálculos. De esta manera, el formato particular de la data disponible se transforma en un problema totalmente separado del proceso de cálculo en sí.

Función valor_presente#

def valor_presente(fecha_valor, fechas, flujos, tasa):
    """
    Calcula el valor presente de un conjunto de flujos utilizando una única tasa de descuento.
    
    Parameters
    ----------
    
    fecha_valor: datetime.date
        Fecha a la cual se quiere obtener el valor presente.
        
    fechas: List[datetime.date]
        Fechas de pago de los flujos.
        
    flujos: List[float]
        Flujos a traer a valor presente. Deben corresponder a las fechas en el parámetro `fechas`.
        Los flujos cuyas fechas sean iguales o previas a `fecha_valor` no serán incluidos en el cálculo.
        
    tasa: float
        Tasa de descuento a utilizar. Debe estar en convención Com Act/365.
        
    Returns
    -------
    
    Un `float` que corresponde al valor presente de los flujos.
    """
    result = 0.0
    for fec, fl in zip(fechas, flujos):
        p = (fec - fecha_valor).days
        if p > 0:
            result += fl * (1 + tasa)**(-p / 365.0)
    return result

Realicemos una prueba con la data que tenemos disponible en este notebook. Utilizaremos el bono BBNS-W0414.

tabla = tablas_desarrollo[tablas_desarrollo.nemotecnico == 'BBNS-W0414']
tabla
nemotecnico numero_cupon fecha_vcto_cupon interes amortizacion saldo_insoluto
57683 BBNS-W0414 1 2014-10-01 1.5 0.0 100.0
57684 BBNS-W0414 2 2015-04-01 1.5 0.0 100.0
57685 BBNS-W0414 3 2015-10-01 1.5 0.0 100.0
57686 BBNS-W0414 4 2016-04-01 1.5 0.0 100.0
57687 BBNS-W0414 5 2016-10-01 1.5 0.0 100.0
57688 BBNS-W0414 6 2017-04-01 1.5 0.0 100.0
57689 BBNS-W0414 7 2017-10-01 1.5 0.0 100.0
57690 BBNS-W0414 8 2018-04-01 1.5 0.0 100.0
57691 BBNS-W0414 9 2018-10-01 1.5 0.0 100.0
57692 BBNS-W0414 10 2019-04-01 1.5 0.0 100.0
57693 BBNS-W0414 11 2019-10-01 1.5 0.0 100.0
57694 BBNS-W0414 12 2020-04-01 1.5 0.0 100.0
57695 BBNS-W0414 13 2020-10-01 1.5 0.0 100.0
57696 BBNS-W0414 14 2021-04-01 1.5 0.0 100.0
57697 BBNS-W0414 15 2021-10-01 1.5 0.0 100.0
57698 BBNS-W0414 16 2022-04-01 1.5 0.0 100.0
57699 BBNS-W0414 17 2022-10-01 1.5 0.0 100.0
57700 BBNS-W0414 18 2023-04-01 1.5 0.0 100.0
57701 BBNS-W0414 19 2023-10-01 1.5 0.0 100.0
57702 BBNS-W0414 20 2024-04-01 1.5 100.0 100.0

Vemos que los flujos corresponden a la suma de las columnas interes y amortizacion y las fechas están en la columna fecha_vcto_cupon.

flujos = tabla['interes'] + tabla['amortizacion']

El tipo de la variables flujos es:

type(flujos)
pandas.core.series.Series

Para tener un np.array con los valores aplicaremos:

flujos.values
array([  1.5,   1.5,   1.5,   1.5,   1.5,   1.5,   1.5,   1.5,   1.5,
         1.5,   1.5,   1.5,   1.5,   1.5,   1.5,   1.5,   1.5,   1.5,
         1.5, 101.5])

Obtenemos un numpy.array que es un tipo similar a una List, pero optimizado para cálculos.

Para las fechas aplicamos un procedimiento similar:

fechas = tabla['fecha_vcto_cupon'].values
fechas
array(['2014-10-01', '2015-04-01', '2015-10-01', '2016-04-01',
       '2016-10-01', '2017-04-01', '2017-10-01', '2018-04-01',
       '2018-10-01', '2019-04-01', '2019-10-01', '2020-04-01',
       '2020-10-01', '2021-04-01', '2021-10-01', '2022-04-01',
       '2022-10-01', '2023-04-01', '2023-10-01', '2024-04-01'],
      dtype=object)

Vemos que tenemos que obtenemos un numpy.array de str, pero queremos datetime.date.

import datetime
fechas = np.asarray([datetime.datetime.strptime(f, "%Y-%m-%d").date() for f in fechas])
fechas
array([datetime.date(2014, 10, 1), datetime.date(2015, 4, 1),
       datetime.date(2015, 10, 1), datetime.date(2016, 4, 1),
       datetime.date(2016, 10, 1), datetime.date(2017, 4, 1),
       datetime.date(2017, 10, 1), datetime.date(2018, 4, 1),
       datetime.date(2018, 10, 1), datetime.date(2019, 4, 1),
       datetime.date(2019, 10, 1), datetime.date(2020, 4, 1),
       datetime.date(2020, 10, 1), datetime.date(2021, 4, 1),
       datetime.date(2021, 10, 1), datetime.date(2022, 4, 1),
       datetime.date(2022, 10, 1), datetime.date(2023, 4, 1),
       datetime.date(2023, 10, 1), datetime.date(2024, 4, 1)],
      dtype=object)

Ahora aplicamos la función:

valor_presente(date(2014,4,1), fechas, flujos, .0302)
99.9997566755515

Función valor_par#

Para el cálculo del valor_par necesitamos la fecha valor, las fechas iniciales de los cupones y la tera.

def valor_par(fecha_valor, fechas_iniciales, saldos_insolutos, tera):
    """
    Calcula el valor par en base 100 para un bono de renta fija local.
    
    Parameters
    ----------
    
    fecha_valor: datetime.date
        Fecha a la cual se quiere obtener el valor par.
        
    fechas_iniciales: List[datetime.date]
        Fechas de inicio de devengo de los flujos del bono.
        
    saldos_insolutos: List[float]
        Saldos insolutos de cada cupón del bono.
        
    tera: float
        Tasa efectiva real anual del bono según las convenciones de la Bolsa de Comercio de Santiago.
        
    Returns
    -------
    
    Un `float` que corresponde al valor par del bono.
    """
    fecha = fn.find_le(fechas_iniciales, fecha_valor)
    i = fechas_iniciales.index(fecha)
    saldo = saldos_insolutos[i]
    d = (fecha_valor - fecha).days
    return saldo * (1 + tera)**(d / 365.0)

Probemos. La data que ahora estamos utilizando no está en el formato astuto de RiskAmerica que agrega un cupón 0 a la tabla de desarrollo. Vamos a tomar las fechas de la tabla de desarrollo y luego vamos a insertar al inicio la fecha de emisión desde la tabla de características.

Para este ejemplo vamos a usar el nemotécnico BCCA-F0919.

fechas_iniciales = tablas_desarrollo[tablas_desarrollo.nemotecnico == 'BCCA-F0919']['fecha_vcto_cupon'].values
fecha_emision = bonos[bonos.nemotecnico == 'BCCA-F0919']['fecha_emision'].iloc[0]
np.insert(fechas_iniciales, 0, [str(fecha_emision)])
array(['2019-09-27 00:00:00', '2019-12-27', '2020-03-27', '2020-06-27',
       '2020-09-27', '2020-12-27', '2021-03-27', '2021-06-27',
       '2021-09-27', '2021-12-27', '2022-03-27', '2022-06-27',
       '2022-09-27', '2022-12-27', '2023-03-27', '2023-06-27',
       '2023-09-27', '2023-12-27', '2024-03-27'], dtype=object)
fechas_iniciales = [datetime.datetime.strptime(f, "%Y-%m-%d").date() for f in fechas_iniciales]
fechas_iniciales
[datetime.date(2019, 12, 27),
 datetime.date(2020, 3, 27),
 datetime.date(2020, 6, 27),
 datetime.date(2020, 9, 27),
 datetime.date(2020, 12, 27),
 datetime.date(2021, 3, 27),
 datetime.date(2021, 6, 27),
 datetime.date(2021, 9, 27),
 datetime.date(2021, 12, 27),
 datetime.date(2022, 3, 27),
 datetime.date(2022, 6, 27),
 datetime.date(2022, 9, 27),
 datetime.date(2022, 12, 27),
 datetime.date(2023, 3, 27),
 datetime.date(2023, 6, 27),
 datetime.date(2023, 9, 27),
 datetime.date(2023, 12, 27),
 datetime.date(2024, 3, 27)]
saldos_insolutos = list(tablas_desarrollo[tablas_desarrollo.nemotecnico == 'BCCA-F0919']['saldo_insoluto'].values)
tera = bonos[bonos.nemotecnico == 'BCCA-F0919']['tasa_descuento'].iloc[0] / 100.0
tera
0.08092
valor_par(date(2019, 12, 31), fechas_iniciales, saldos_insolutos, tera)
100.08531037450017

Primera Versión#

Con las definiciones anteriores estamos en condiciones de construir el valorizador, al menos la funcionalidad relacionada con el valor presente y valor par.

Vamos a definir una función que aproveche la funcionalidad definida y abstraiga los detalles relacionados con la obtención de la data específica de un bono.

IMPORTANTE: Esta función captura desde el contexto global los DataFrame bonos y tablas, evitando así tener que pasar estos argumentos a cada llamada de la función. Al finalizar veremos una manera más elegante y explícita de lograr el mismo efecto.

def valorizador_rf(fecha_valor, nemotecnico, tir, monto):
    """
    Valoriza un instrumento de renta fija local a una fecha, a partir de su nemotécnico y la tir de mercado.
    
    Arguments
    ---------
    
    nemotecnico: str
        Es un nemotécnico válido de renta fija local.
        
    fecha_valor: datetime.date
        Fecha a la cual se requiere el cálculo.
        
    tir: float
        Tir de mercado del bono.
        
    monto: float
        Monto total del bono.
        
    Returns
    -------
    
    El precio, el valor par, el valor presente y el valor de pago del bono.
    
    """
    # Busca las características del bono
    caracteristicas = bonos[bonos.nemotecnico == nemotecnico]
    
    # Busca la tabla de desarrollo
    tabla = tablas_desarrollo[tablas_desarrollo.nemotecnico == nemotecnico]

    
    # Establece el valor de la TERA,
    tera = round(caracteristicas['tasa_descuento'].iloc[0] / 100.0, 6)
    
    # la fecha de inicio del primer cupón,
    primera_fecha = caracteristicas['fecha_emision'].iloc[0]
    primera_fecha = datetime.datetime.strptime(str(primera_fecha)[0:10], "%Y-%m-%d").date()
    
    # las fechas de pago de cupón
    fechas_pago = tabla['fecha_vcto_cupon'].values
    fechas_pago = [datetime.datetime.strptime(f, "%Y-%m-%d").date() for f in fechas_pago]
    
    # y los cupones.
    flujos = (tabla['interes'] + tabla['amortizacion']).values
    
    # Se calcula el valor presente
    vpresente = valor_presente(fecha_valor, fechas_pago, flujos, tir)
    
    # Se establecen las fechas de inicio de los cupones.
    np.insert(fechas_pago, 0, [primera_fecha])
    
    # Se establecen los saldos insolutos
    saldos_insolutos = list(tabla['saldo_insoluto'])
    
    # Se calcula el valor par.
    vpar = valor_par(fecha_valor, fechas_pago, saldos_insolutos, tera)
    
    # Se calcula el precio a 4 decimales
    precio = round(vpresente / vpar, 6)
    
    # Se calcula el valor a pagar en unidades monetarias.
    valor_pago = precio * vpar * monto / 100.0
    
    return {
        'precio': precio,
        'valor_par': vpar,
        'valor_presente': vpresente,
        'valor_pago': valor_pago
    }
valorizador_rf(date(2021, 8, 13), 'BWATT-Q', 0.0673, 1000)
{'precio': 0.832537,
 'valor_par': 101.74312935262013,
 'valor_presente': 84.70490433903439,
 'valor_pago': 847.049196818423}

Duración y Convexidad#

¿Qué es la duración de un bono? Típicamente la primera respuesta es el promedio ponderado por el valor presente de los flujos de los plazos residuales de los flujos. Una segunda forma, más robusta y que se extiende a otros contextos es pensar en la duración como la derivada primera del valor del bono respecto a la TIR de mercado.

\[V\left(TIR\right)=\sum_{i=1}^N C_i\cdot \exp\left(-TIR\cdot yf_i\right)\]
\[\frac{dV}{dTIR}=-\sum_{i=1}^N yf_i\cdot C_i\cdot \exp\left(-TIR\cdot yf_i\right)=-D\cdot V\left(TIR\right)\]

En esta convención de tasa, la duración es, a menos del valor presente del bono y el signo, exactamente igual a la derivada del valor del bono. Si usamos la convención de la TIR de mercado de la renta fija local tenemos:

\[V\left(TIR\right)=\sum_{i=1}^N C_i\cdot\left(1+TIR\right)^{-yf_i}\]
\[\frac{dV}{dTIR}=-\frac{\sum_{i=1}^N yf_i\cdot C_i\cdot\left(1+TIR\right)^{-yf_i}}{1+TIR}=-D_M\cdot V\left(TIR\right)\]

Vemos que, en esta convención de tasa, la derivada del valor del bono coincide con la duración modificada.

Para la convexidad se puede hacer un razonamiento del todo análogo al anterior:

\[\frac{d^2V}{dTIR^2}=\frac{\sum_{i=1}^N yf_i\cdot \left(yf_i+1\right)\cdot C_i\cdot\left(1+TIR\right)^{-yf_i}}{\left(1+TIR\right)^2}\]

Podríamos utilizar las últimas fórmulas y escribir funciones que calculen la duración y convexidad de un bono, pero vamos a adoptar un acercamiento al problema más extendible a situaciones futuras de mayor compeljidad.

Vamos a utilizar la librería autograd que es capaz de calcular la derivada de una función. No es una aproximación por diferencias finitas, autograd calcula la derivada del código Python que define la función.

dvdtir = grad(valor_presente, 3) # Derivada de la función valor_presente respecto a la TIR (3 variable)
type(dvdtir)
function
dur = -(1 + .0002) * dvdtir(date(2021,8,13), fechas, flujos, .0002)
dur /= valor_presente(date(2021,8,13), fechas, flujos, .0002)
dur
2.5322376795337695
d2vdtir2 = grad(dvdtir, 3)
conv = d2vdtir2(date(2021,8,13), fechas, flujos, .0002)
conv /= valor_presente(date(2021,8,13), fechas, flujos, .0002) * (1 + .0002)**2
conv
9.116173104913129

Dado lo anterior, podemos hacer una segunda versión de la función valorizador_rf que retorne también la duración y la convexidad.

def valorizador_rf(fecha_valor, nemotecnico, tir, monto):
    """
    Valoriza un instrumento de renta fija local a una fecha, a partir de su nemotécnico y la tir de mercado.

    Arguments
    ---------

    nemotecnico: str
        Es un nemotécnico válido de renta fija local.

    fecha_valor: datetime.date
        Fecha a la cual se requiere el cálculo.

    tir: float
        Tir de mercado del bono.

    monto: float
        Monto total del bono.

    Returns
    -------

    El precio, el valor par, el valor presente, el valor de pago, la duración y la convexidad del bono.

    """
    # Busca las características del bono
    caracteristicas = bonos[bonos.nemotecnico == nemotecnico]

    # Busca la tabla de desarrollo
    tabla = tablas_desarrollo[tablas_desarrollo.nemotecnico == nemotecnico]

    # Establece el valor de la TERA,
    tera = round(caracteristicas['tasa_descuento'].iloc[0] / 100.0, 6)

    # la fecha de inicio del primer cupón,
    primera_fecha = caracteristicas['fecha_emision'].iloc[0]
    primera_fecha = datetime.datetime.strptime(
        str(primera_fecha)[0:10], "%Y-%m-%d").date()

    # las fechas de pago de cupón
    fechas_pago = tabla['fecha_vcto_cupon'].values
    fechas_pago = [datetime.datetime.strptime(
        f, "%Y-%m-%d").date() for f in fechas_pago]

    # y los cupones.
    flujos = (tabla['interes'] + tabla['amortizacion']).values

    # Se calcula el valor presente
    vpresente = valor_presente(fecha_valor, fechas_pago, flujos, tir)

    # Se calcula la duración.
    dvdtir = grad(valor_presente, 3)
    dur = -(1 + tir) * dvdtir(fecha_valor,
                              fechas_pago, flujos, tir) / vpresente

    # Se calcula la convexidad
    d2dvdtir2 = grad(dvdtir, 3)
    conv = d2dvdtir2(fecha_valor, fechas_pago, flujos, tir)
    conv /= vpresente

    # Se establecen las fechas de inicio de los cupones.
    np.insert(fechas_pago, 0, [primera_fecha])
    
    # Se establecen los saldos insolutos
    saldos_insolutos = list(tabla['saldo_insoluto'])

    # Se calcula el valor par.
    vpar = valor_par(fecha_valor, fechas_pago, saldos_insolutos, tera)

    # Se calcula el precio a 4 decimales
    precio = round(vpresente / vpar, 6)

    # Se calcula el valor a pagar en unidades monetarias.
    valor_pago = precio * vpar * monto / 100.0

    return {
        'precio': precio,
        'valor_par': vpar,
        'valor_presente': vpresente,
        'valor_pago': valor_pago,
        'duracion': dur,
        'convexidad': conv
    }

Probemos con los mismos valores anteriores.

valorizador_rf(date(2021, 8, 13), 'BWATT-Q', 0.0673, 1000000)
{'precio': 0.832537,
 'valor_par': 101.74312935262013,
 'valor_presente': 84.70490433903439,
 'valor_pago': 847049.196818423,
 'duracion': 6.732718905495722,
 'convexidad': 50.83936352905741}

Segunda Versión#

Sin captura global de la data, pero con la misma usabilidad.

def valorizador_rf(fecha_valor, nemotecnico, tir, monto, bonos, tablas_desarrollo):
    """
    Valoriza un instrumento de renta fija local a una fecha, a partir de su nemotécnico y la tir de mercado.
    
    Arguments
    ---------
    
    nemotecnico: str
        Es un nemotécnico válido de renta fija local.
        
    fecha_valor: datetime.date
        Fecha a la cual se requiere el cálculo.
        
    tir: float
        Tir de mercado del bono.
        
    monto: float
        Monto total del bono.
        
    bonos: pandas.DataFrame
        Contiene la información de cabecera de los bonos. En particular tiene las columnas `nemotecnico`,
        `tasa_descuento` que representa la TERA del bono y `fecha_emision`.
        
    tablas_desarrollo: pandas.DataFrame
        Contiene las columnas `nemotecnico`, `interes`, `amortizacion` y `fecha_vcto_cupon`.
        
    Returns
    -------
    
    El precio, el valor par, el valor presente, el valor de pago, la duración y la convexidad del bono.
    
    """
    # Busca las características del bono
    caracteristicas = bonos[bonos.nemotecnico == nemotecnico]
    
    # Busca la tabla de desarrollo
    tabla = tablas_desarrollo[tablas_desarrollo.nemotecnico == nemotecnico]

    
    # Establece el valor de la TERA,
    tera = round(caracteristicas['tasa_descuento'].iloc[0] / 100.0, 6)
    
    # la fecha de inicio del primer cupón,
    primera_fecha = caracteristicas['fecha_emision'].iloc[0]
    primera_fecha = datetime.datetime.strptime(str(primera_fecha)[0:10], "%Y-%m-%d").date()
    
    # las fechas de pago de cupón
    fechas_pago = tabla['fecha_vcto_cupon'].values
    fechas_pago = [datetime.datetime.strptime(f, "%Y-%m-%d").date() for f in fechas_pago]
    
    # y los cupones.
    flujos = (tabla['interes'] + tabla['amortizacion']).values
    
    # Se calcula el valor presente
    vpresente = valor_presente(fecha_valor, fechas_pago, flujos, tir)
    
    # Se calcula la duración.
    dvdtir = grad(valor_presente, 3)
    dur = -(1 + tir) * dvdtir(fecha_valor, fechas_pago, flujos, tir) / vpresente
    
    # Se calcula la convexidad
    d2dvdtir2 = grad(dvdtir, 3)
    conv = d2dvdtir2(fecha_valor, fechas_pago, flujos, tir)
    conv /= vpresente 
    
    # Se establecen las fechas de inicio de los cupones.
    np.insert(fechas_pago, 0, [primera_fecha])
    
    # Se establecen los saldos insolutos
    saldos_insolutos = list(tabla['saldo_insoluto'])
    
    # Se calcula el valor par.
    vpar = valor_par(fecha_valor, fechas_pago, saldos_insolutos, tera)
    
    # Se calcula el precio a 4 decimales
    precio = round(vpresente / vpar, 6)
    
    # Se calcula el valor a pagar en unidades monetarias.
    valor_pago = precio * vpar * monto / 100.0
    
    return {
        'precio': precio,
        'valor_par': vpar,
        'valor_presente': vpresente,
        'valor_pago': valor_pago,
        'duracion': dur,
        'convexidad': conv
    }

Est función hace explícita la dependencia de la data relacionada con los bonos y permite, por lo tanto, que sea reutilizable en otros contextos. Sin embargo, el código se hace más verboso si tenemos que pasar los DataFrames con la data cada vez que llamamos la función. Sin embargo, esto último se puede solucionar con la siguiente captura.

import this
def get_valorizador_with_data(bonos, tablas_desarrollo):
    def wrapper(fecha_valor, nemotecnico, tir, monto):
        return valorizador_rf(
            fecha_valor,
            nemotecnico,
            tir, monto,
            bonos,
            tablas_desarrollo
        )
    return wrapper
val_rf = get_valorizador_with_data(bonos, tablas_desarrollo)
val_rf(date(2021, 8, 13), 'BWATT-Q', 0.0673, 1000000)
{'precio': 0.832537,
 'valor_par': 101.74312935262013,
 'valor_presente': 84.70490433903439,
 'valor_pago': 847049.196818423,
 'duracion': 6.732718905495722,
 'convexidad': 50.83936352905741}

Un Ejemplo Típico#

Como fijar el valor de una variable de una función.

def suma(a, b):
    return a + b
def suma_x(x):
    def wrapper(a):
        return suma(a, x)
    return wrapper
suma2 = suma_x(2)
suma2(10)
12