Guía/Cashflows

Para ejecutar todos los ejemplos se debe importar la librería. Se sugiere utilizar siempre el alias qcf.

In [1]:
pythoncopy
import qcfinancial as qcf
import aux_functions as aux
import pandas as pd
In [2]:
pythoncopy
qcf.id()
Out[2]:
'version: 1.11.3, build: 2026-06-29 16:00'

Simple Cashflow

Un objeto de tipo SimpleCashflow representa un flujo de caja cierto en una fecha y moneda. Para instanciar un objeto de este tipo se requiere:

  • QCDate: fecha del flujo
  • float: monto del flujo
  • QCCurrency: moneda del flujo

Veamos un ejemplo. Primero se define los parámetros.

In [3]:
pythoncopy
fecha1 = qcf.QCDate(24, 8, 2024)
monto = 1_000_000
clp = qcf.QCCLP()

Luego, se instancia el objeto.

In [4]:
pythoncopy
simple_cashflow = qcf.SimpleCashflow(
    fecha1, 
    monto, 
    clp,
)

Métodos amount, ccy y date

Permiten obtener el monto, la moneda y la fecha del flujo.

In [5]:
pythoncopy
print(f"Monto: {simple_cashflow.amount():,.0f}")
Out[5]:
Monto: 1,000,000
In [6]:
pythoncopy
print(f"Moneda: {simple_cashflow.ccy()}")
Out[6]:
Moneda: CLP
In [7]:
pythoncopy
print(f"Fecha: {simple_cashflow.date()}")
Out[7]:
Fecha: 2024-08-24

Método is_expired

Todos los cashflows tienen el método de conveniencia is_expired que sirve para indicar si un flujo ya ocurrió o está vencido.

In [8]:
pythoncopy
simple_cashflow.is_expired(qcf.QCDate("2024-09-01"))
Out[8]:
True

Notar que no se considera vencido si la fecha de referencia es menor o igual a la fecha de vencimiento.

In [9]:
pythoncopy
simple_cashflow.is_expired(fecha1)
Out[9]:
False
In [10]:
pythoncopy
simple_cashflow.is_expired(qcf.QCDate("2024-01-01"))
Out[10]:
False

Función show

In [11]:
pythoncopy

Esta función envuelve de forma cómoda todo el flujo en un objeto tuple. La función show está sobrecargada y admite todos los tipos de flujo de qcfinancial.

In [12]:
pythoncopy
qcf.show(simple_cashflow)
Out[12]:
('2024-08-24', 1000000.0, 'CLP')

Función get_column_names

Para nombrar los elementos de la tupla anterior, se puede utilizar la función get_column_names.

In [13]:
pythoncopy
qcf.get_column_names('SimpleCashflow')
Out[13]:
('fecha_pago', 'monto', 'moneda')

Con ella podemos, por ejemplo, estructurar aún más el output de show utilizando un pandas.DataFrame.

In [14]:
pythoncopy
df = pd.DataFrame([qcf.show(simple_cashflow)])
df.columns = list(qcf.get_column_names('SimpleCashflow'))
df.style.format({'monto':'{:,.0f}'})
Out[14]:
  fecha_pago monto moneda
0 2024-08-24 1,000,000 CLP

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [15]:
pythoncopy
simple_cashflow.record()
Out[15]:
{'amort_is_cashflow': True,
 'amortization': 1000000.0,
 'cashflow': 1000000.0,
 'discount_factor': 0.0,
 'end_date': '2024-08-24',
 'notional': 1000000.0,
 'notional_currency': 'CLP',
 'present_value': 0.0,
 'type_of_cashflow': 'simple'}

Simple Multicurrency Cashflow

Un objeto de tipo SimpleMultiCurrencyCashflow representa un flujo de caja cierto en una fecha y moneda, el cual, sin embargo, se liquidará en una segunda moneda utilizando el valor de un índice de tipo de cambio a una fecha futura. Para dar de alta uno de estos objetos se requiere:

  • QCDate: fecha final
  • float: monto nominal
  • QCCurrency: la moneda del monto nominal
  • QCDate: la fecha de publicación del FXRateIndex que se usará en la conversión
  • QCCurrency: la moneda final del flujo
  • FXRateIndex: el índice de tipo de cambio a utilizar
  • float: el valor del índice de tipo de cambio

Ejemplo 1

Se da de alta los objetos que el constructor del índice de tipo de cambio necesita.

In [16]:
pythoncopy
# moneda
usd = qcf.QCUSD()

# tipo de cambio
usdclp = qcf.FXRate(usd, clp)

# tenor
_1d = qcf.Tenor('1D')
In [17]:
pythoncopy
# calendario Santiago
scl = qcf.CalendarFactory.build(qcf.QCDate("2024-01-01"), 10, [qcf.BusinessCalendarId.CLSA])

# dólar observado
usdclp_obs = qcf.FXRateIndex(usdclp, "USDOBS", _1d, _1d, scl)

# valor del índice
fx_rate_value = 900.0

Se da de alta el cashflow.

In [18]:
pythoncopy
simple_mccy_cashflow = qcf.SimpleMultiCurrencyCashflow(
    fecha1, 
    monto, 
    clp,  # nominal en CLP
    fecha1, 
    usd,  # pago en USD
    usdclp_obs, 
    fx_rate_value
)

Este objeto hereda de SimpleCashflow y por lo tanto, los métodos amount, date y ccy siguen disponibles.

In [19]:
pythoncopy
print(f"Monto: {simple_mccy_cashflow.amount():,.2f}")
print(f"Fecha: {simple_mccy_cashflow.date()}")
print(f"Moneda: {simple_mccy_cashflow.ccy()}")
Out[19]:
Monto: 1,000,000.00
Fecha: 2024-08-24
Moneda: CLP

Para obtener el monto en la moneda de pago y la moneda de pago están los métodos settlement_amount y settlement_ccy.

In [20]:
pythoncopy
print(f"Monto de pago: {simple_mccy_cashflow.settlement_amount():,.0f}")
Out[20]:
Monto de pago: 1,111
In [21]:
pythoncopy
print(f"Moneda de pago: {simple_mccy_cashflow.settlement_ccy()}")
Out[21]:
Moneda de pago: USD

El valor del índice de tipo de cambio se puede alterar con el método set_fx_rate_index_value.

In [22]:
pythoncopy
simple_mccy_cashflow.set_fx_rate_index_value(800.00)
print(f"Flujo: {simple_mccy_cashflow.settlement_amount():,.2f} {simple_mccy_cashflow.settlement_ccy()}")
Out[22]:
Flujo: 1,250.00 USD

Ejemplo 2

Aquí, las monedas están invertidas respecto al caso anterior. El valor del índice se usa de la forma correcta.

In [23]:
pythoncopy
simple_mccy_cashflow = qcf.SimpleMultiCurrencyCashflow(
    fecha1, 
    1_000_000, 
    usd,  # nominal en USD
    fecha1, 
    clp,  # pago en CLP
    usdclp_obs, 
    fx_rate_value,
)

Se repite los cálculos del ejemplo anterior.

In [24]:
pythoncopy
print(f"Fecha: {simple_mccy_cashflow.date()}")
print(f"Nominal: {simple_mccy_cashflow.nominal():,.2f}")
print(f"Moneda nominal: {simple_mccy_cashflow.ccy().get_iso_code()}")
print(f"Flujo: {simple_mccy_cashflow.settlement_amount():,.0f}")
print(f"Moneda flujo: {simple_mccy_cashflow.settlement_ccy().get_iso_code()}")
Out[24]:
Fecha: 2024-08-24
Nominal: 1,000,000.00
Moneda nominal: USD
Flujo: 900,000,000
Moneda flujo: CLP

Ejemplo 3

En este caso, hay inconsistencia entre las monedas y el tipo de cambio del índice y vemos que se produce un error.

In [25]:
pythoncopy
eur = qcf.QCEUR()
try:
    simple_mccy_cashflow = qcf.SimpleMultiCurrencyCashflow(
        fecha1,
        1_000_000,
        usd,         # nominal en USD
        fecha1,
        eur,         # pago en EUR
        usdclp_obs,  # ¡usamos el dólar observado!
        fx_rate_value,
    )
except ValueError as e:
    print("Error:", e)
Out[25]:
Error: Fx Rate Index provided is not compatible with nominal and settlement currency. 

Funciones show y get_columns

In [26]:
pythoncopy
qcf.show(simple_mccy_cashflow)
Out[26]:
('2024-08-24',
 1000000.0,
 'USD',
 '2024-08-24',
 'CLP',
 'USDOBS',
 900.0,
 900000000.0)
In [27]:
pythoncopy
qcf.get_column_names('SimpleMultiCurrencyCashflow')
Out[27]:
('fecha_pago',
 'monto_nominal',
 'moneda_nominal',
 'fecha_fixing_fx',
 'moneda_pago',
 'codigo_indice_fx',
 'valor_indice_fx',
 'monto_moneda_pago')

Nuevamente, usando estas funciones podemos visualizar el flujo utilizando un pandas.DataFrame.

In [28]:
pythoncopy
df = pd.DataFrame([qcf.show(simple_mccy_cashflow)])
df.columns = list(qcf.get_column_names('SimpleMultiCurrencyCashflow'))
df.style.format({
    'monto_nominal':'{:,.0f}',
    'monto_moneda_pago':'{:,.0f}',
    'valor_indice_fx':'{:,.2f}'
})
Out[28]:
  fecha_pago monto_nominal moneda_nominal fecha_fixing_fx moneda_pago codigo_indice_fx valor_indice_fx monto_moneda_pago
0 2024-08-24 1,000,000 USD 2024-08-24 CLP USDOBS 900.00 900,000,000

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [29]:
pythoncopy
simple_mccy_cashflow.record()
Out[29]:
{'amort_is_cashflow': True,
 'amortization': 1000000.0,
 'cashflow': 1000000.0,
 'discount_factor': 0.0,
 'end_date': '2024-08-24',
 'fx_fixing_date': '24-08-2024',
 'fx_rate_index': 'USDOBS',
 'fx_rate_index_value': 900.0,
 'notional': 1000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'settlement_currency': 'CLP',
 'type_of_cashflow': 'simple_multi_currency'}

Fixed Rate Cashflow

Un objeto de tipo FixedRateCashflow representa un flujo de caja calculado a partir de la aplicación de una tasa prefijada, entre dos fechas prefijadas a un nominal prefijado. Para dar de alta uno de estos objetos se requiere:

  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de pago
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • QCInterestRate: la tasa de interés a aplicar (su valor y convenciones)
  • QCCurrency: moneda del nominal y del flujo de caja

Construcción

Se define los parámetros requeridos.

In [30]:
pythoncopy
fecha_inicio = qcf.QCDate(20, 5, 2024)
fecha_final = qcf.QCDate(20, 5, 2025)
fecha_pago = qcf.QCDate(22, 5, 2025)
nominal = 1_000_000_000.0
amortizacion = 100_000_000.0
amort_is_cashflow = False

Se define la yearFraction, wealthFunction el valor de la tasa y finalmente la tasa de interés.

In [31]:
pythoncopy
act360 = qcf.QCAct360()
lin_wf = qcf.QCLinearWf()
valor_tasa_fija = .1
tasa = qcf.QCInterestRate(valor_tasa_fija, act360, lin_wf)

Se instancia el cashflow.

In [32]:
pythoncopy
fixed_rate_cashflow = qcf.FixedRateCashflow(
    fecha_inicio, 
    fecha_final, 
    fecha_pago, 
    nominal, 
    amortizacion, 
    amort_is_cashflow, 
    tasa, 
    clp,
)

Getters

In [33]:
pythoncopy
print(f"Tipo de flujo: {fixed_rate_cashflow.get_type()}")
print(f"Fecha Inicio: {fixed_rate_cashflow.get_start_date()}")
print(f"Fecha Final: {fixed_rate_cashflow.get_end_date()}")
print(f"Fecha Pago: {fixed_rate_cashflow.get_settlement_date()}")
print(f"Moneda: {fixed_rate_cashflow.ccy()}") # __str__ devuelve el ISO Code de la moneda
print(f"Nominal: {fixed_rate_cashflow.get_nominal():,.0f}")
print(f"Amortización: {fixed_rate_cashflow.get_amortization():,.0f}")
print(f"Tasa de interés: {fixed_rate_cashflow.get_rate()}")
Out[33]:
Tipo de flujo: FixedRateCashflow
Fecha Inicio: 2024-05-20
Fecha Final: 2025-05-20
Fecha Pago: 2025-05-22
Moneda: CLP
Nominal: 1,000,000,000
Amortización: 100,000,000
Tasa de interés: 0.100000 Act360 Lin

Para obtener el valor de la tasa fija, se usa además un getter de QCInterestRate.

In [34]:
pythoncopy
print(f"Tasa de interés: {fixed_rate_cashflow.get_rate().get_value():.2%}")
Out[34]:
Tasa de interés: 10.00%

Setters

Cambiar el nominal.

In [35]:
pythoncopy
nuevo_nominal = 2_000_000_000.0
fixed_rate_cashflow.set_nominal(nuevo_nominal)
print(f"Nuevo nominal: {fixed_rate_cashflow.get_nominal():,.0f}")
Out[35]:
Nuevo nominal: 2,000,000,000

Cambiar la amortización.

In [36]:
pythoncopy
nueva_amortizacion = 200_000_000.0
fixed_rate_cashflow.set_amortization(nueva_amortizacion)
print(f"Nueva amortización: {fixed_rate_cashflow.get_amortization():,.0f}")
Out[36]:
Nueva amortización: 200,000,000

Cambiar el valor de la tasa.

In [37]:
pythoncopy
fixed_rate_cashflow.set_rate_value(new_rate_value:=.12)
print(f"Nuevo valor de la tasa: {fixed_rate_cashflow.get_rate().get_value():.2%}")
Out[37]:
Nuevo valor de la tasa: 12.00%

Cálculos

Se devuelve el valor de la tasa fija al original.

In [38]:
pythoncopy
fixed_rate_cashflow.set_rate_value(valor_tasa_fija)

Método amount.

In [39]:
pythoncopy
print(f"Flujo Total: {fixed_rate_cashflow.amount():,.0f}")
Out[39]:
Flujo Total: 202,777,778

Vemos que el flujo incluye los intereses, pero no la amortización (amort_is_cashflow = False). Podemos verificar a mano este resultado.

In [40]:
pythoncopy
dias_devengo = fecha_inicio.day_diff(fecha_final)
print(f"Días de devengo: {dias_devengo}")
print(f"Check: {fixed_rate_cashflow.get_nominal() * valor_tasa_fija * dias_devengo / 360:,.0f}")
Out[40]:
Días de devengo: 365
Check: 202,777,778

Método accrued_interest. Calcula el interés devengado a una cierta fecha.

In [41]:
pythoncopy
fecha_intermedia = qcf.QCDate(15, 1, 2025)
print(f"Interés Devengado: {fixed_rate_cashflow.accrued_interest(fecha_intermedia):,.0f}")
dias_devengo = fecha_inicio.day_diff(fecha_intermedia)
print(f"Días de devengo: {dias_devengo}")
print(f"Check: {fixed_rate_cashflow.get_nominal() * valor_tasa_fija * dias_devengo / 360.0:,.0f}")
Out[41]:
Interés Devengado: 133,333,333
Días de devengo: 240
Check: 133,333,333

Con este método, utilizando la fecha final del flujo se puede obtener el interés total.

In [42]:
pythoncopy
print(f"Interés total: al {fixed_rate_cashflow.accrued_interest(fixed_rate_cashflow.get_end_date()):,.0f}")
Out[42]:
Interés total: al 202,777,778

Función show

In [43]:
pythoncopy
print(qcf.show(fixed_rate_cashflow))
Out[43]:
('2024-05-20', '2025-05-20', '2025-05-22', 2000000000.0, 200000000.0, 202777777.77777794, False, 202777777.77777794, 'CLP', 0.1, 'LinAct360')

Función get_column_names

In [44]:
pythoncopy
qcf.get_column_names(fixed_rate_cashflow.get_type())
Out[44]:
('fecha_inicial',
 'fecha_final',
 'fecha_pago',
 'nocional',
 'amortizacion',
 'interes',
 'amort_es_flujo',
 'flujo',
 'moneda_nocional',
 'valor_tasa',
 'tipo_tasa')

Nuevamente, juntando las últimas dos funciones se obtiene una representación tabular del flujo usando pandas.DataFrame.

In [45]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(fixed_rate_cashflow)], 
    columns=qcf.get_column_names(fixed_rate_cashflow.get_type())
)
df.style.format({
    'nominal':'{:,.0f}', 
    'amortizacion':'{:,.0f}', 
    'interes':'{:,.0f}', 
    'flujo':'{:,.0f}',
    'valor_tasa':'{:.2%}'
})
Out[45]:
  fecha_inicial fecha_final fecha_pago nocional amortizacion interes amort_es_flujo flujo moneda_nocional valor_tasa tipo_tasa
0 2024-05-20 2025-05-20 2025-05-22 2000000000.000000 200,000,000 202,777,778 False 202,777,778 CLP 10.00% LinAct360

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [46]:
pythoncopy
fixed_rate_cashflow.record()
Out[46]:
{'amort_is_cashflow': False,
 'amortization': 200000000.0,
 'cashflow': 202777777.77777794,
 'discount_factor': 0.0,
 'end_date': '2025-05-20',
 'interest': 202777777.77777794,
 'notional': 2000000000.0,
 'notional_currency': 'CLP',
 'present_value': 0.0,
 'rate_value': 0.1,
 'settlement_date': '2025-05-22',
 'start_date': '2024-05-20',
 'type_of_cashflow': 'fixed_rate',
 'type_of_rate': '0.100000 Act360 Lin'}

Fixed Rate Multi Currency Cashflow

Un objeto de tipo FixedRateMultiCurrencyCashflow representa un flujo de caja a tasa fija (FixedRateCashflow) que se liquidará en una moneda distinta de la moneda del nominal utilizando el valor a una cierta fecha de un índice de tipo de cambio prefijado. Para dar de alta uno de estos objetos se requiere:

  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de pago
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • QCInterestRate: la tasa de interés a aplicar (su valor y convenciones)
  • QCCurrency: moneda del nominal
  • QCDate: fecha de fijación del índice de tipo de cambio
  • QCCurrency: moneda en la que se liquida el flujo
  • FXRateIndex: índice de tipo de cambio a utilizar
  • float: valor del índice de tipo de cambio

Construcción

Cuando coinciden, se usan los mismos parámetros que en el ejemplo anterior y agregamos los nuevos parámetros requeridos.

In [47]:
pythoncopy
fecha_fijacion = fecha_final
usd = qcf.QCUSD()
indice = usdclp_obs
valor_indice = 900.0
nominal = 1_000_000.0
amort = 100_000.0
amort_is_cashflow = True

Se instancia el objeto.

In [48]:
pythoncopy
fixed_rate_mccy_cashflow = qcf.FixedRateMultiCurrencyCashflow(
    fecha_inicio,
    fecha_final,
    fecha_pago,
    nominal,
    amort,
    amort_is_cashflow,
    tasa,
    usd,
    fecha_fijacion,
    clp,
    indice,
    valor_indice,
)

Getters

La clase FixedRateMultiCurrencyCashflow es una subclase de FixedRateCashflow y por lo tanto, hereda todos sus métodos y variables.

In [49]:
pythoncopy
print(f"Tipo de flujo: {fixed_rate_mccy_cashflow.get_type()}")
print(f"Fecha Inicio: {fixed_rate_mccy_cashflow.get_start_date()}")
print(f"Fecha Final: {fixed_rate_mccy_cashflow.get_end_date()}")
print(f"Fecha Pago: {fixed_rate_mccy_cashflow.get_settlement_date()}")
print(f"Fecha Fijación Índice FX: {fixed_rate_mccy_cashflow.get_fx_fixing_date()}")
print(f"Moneda del Nominal: {fixed_rate_mccy_cashflow.ccy()}")
print(f"Nominal: {fixed_rate_mccy_cashflow.get_nominal():,.0f}")
print(f"Amortización: {fixed_rate_mccy_cashflow.get_amortization():,.0f}")
print(f"Moneda de Liquidación: {fixed_rate_mccy_cashflow.settlement_currency()}")

# Aquí se usa un getter de QCInterestRate
print(f"Tasa de interés: {fixed_rate_mccy_cashflow.get_rate().get_value():.2%}")
Out[49]:
Tipo de flujo: FixedRateMultiCurrencyCashflow
Fecha Inicio: 2024-05-20
Fecha Final: 2025-05-20
Fecha Pago: 2025-05-22
Fecha Fijación Índice FX: 2025-05-20
Moneda del Nominal: USD
Nominal: 1,000,000
Amortización: 100,000
Moneda de Liquidación: CLP
Tasa de interés: 10.00%

Setters

Nuevo nominal.

In [50]:
pythoncopy
nuevo_nominal = 100.0
fixed_rate_mccy_cashflow.set_nominal(nuevo_nominal)
print(f"Nuevo nominal: {fixed_rate_mccy_cashflow.get_nominal():,.1f}")
Out[50]:
Nuevo nominal: 100.0

Nueva amortización.

In [51]:
pythoncopy
nueva_amortizacion = 10.0
fixed_rate_mccy_cashflow.set_amortization(nueva_amortizacion)
print(f"Nueva amortización: {fixed_rate_mccy_cashflow.get_amortization():,.1f}")
Out[51]:
Nueva amortización: 10.0

Cambiar el valor de la tasa.

In [52]:
pythoncopy
fixed_rate_mccy_cashflow.set_rate_value(new_rate_value)
print(f"Nuevo valor de la tasa: {fixed_rate_mccy_cashflow.get_rate().get_value():.2%}")
Out[52]:
Nuevo valor de la tasa: 12.00%

Cálculos

Se vuelve al nominal, amortización y valor de la tasa iniciales.

In [53]:
pythoncopy
fixed_rate_mccy_cashflow.set_nominal(nominal)
fixed_rate_mccy_cashflow.set_amortization(amort)
fixed_rate_mccy_cashflow.set_rate_value(valor_tasa_fija)

Método amount. En este caso sí se incluye la amortización (amort_is_cashflow = True).

In [54]:
pythoncopy
print(f"Flujo Total: {fixed_rate_mccy_cashflow.amount():,.2f}")
dias_devengo = fecha_inicio.day_diff(fecha_final)
print(
    f"Check: {fixed_rate_mccy_cashflow.get_nominal() * valor_tasa_fija * dias_devengo / 360 + fixed_rate_mccy_cashflow.get_amortization():,.2f}"
)
Out[54]:
Flujo Total: 201,388.89
Check: 201,388.89

Método accrued_interest.

In [55]:
pythoncopy
fecha_intermedia = qcf.QCDate(15, 1, 2025)
print(f"Interés Devengado: {(interes_devengado:=fixed_rate_mccy_cashflow.accrued_interest(fecha_intermedia)):,.02f}")
print(f"Check: {fixed_rate_mccy_cashflow.get_nominal() * valor_tasa_fija * fecha_inicio.day_diff(fecha_intermedia) / 360.0:,.02f}")
Out[55]:
Interés Devengado: 66,666.67
Check: 66,666.67

Método accrued_interest_in_sett_currency. Retorna el interés devengado en moneda de pago. Para esto requiere de un objeto de tipo time_series que contenga el valor del índice a la fecha de devengo solicitada.

In [56]:
pythoncopy
ts = qcf.time_series()
ts[fecha_inicio] = 900
ts[fecha_intermedia] = 950
In [57]:
pythoncopy
print(f"Interés devengado en moneda de pago: {fixed_rate_mccy_cashflow.accrued_interest_in_sett_ccy(fecha_intermedia, ts):,.2f}")
print(f"Check: {interes_devengado * ts[fecha_intermedia]:,.2f}")
Out[57]:
Interés devengado en moneda de pago: 63,333,333.33
Check: 63,333,333.33

Funciones show y get_column_names

Juntando estas dos funciones se obtiene una representación tabular del flujo usando pandas.DataFrame.

In [58]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(fixed_rate_mccy_cashflow)], 
    columns=qcf.get_column_names(fixed_rate_mccy_cashflow.get_type())
)
df.style.format({
    'nominal':'{:,.2f}', 
    'amortizacion':'{:,.2f}', 
    'amortizacion_moneda_pago':'{:,.0f}', 
    'interes':'{:,.2f}', 
    'interes_moneda_pago':'{:,.0f}',
    'flujo':'{:,.2f}',
    'valor_tasa':'{:.2%}',
    'valor_indice_fx':'{:.2f}'
})
Out[58]:
  fecha_inicial fecha_final fecha_pago nocional amortizacion interes amort_es_flujo flujo moneda_nocional valor_tasa tipo_tasa fecha_fixing_fx moneda_pago indice_fx valor_indice_fx amortizacion_moneda_pago interes_moneda_pago
0 2024-05-20 2025-05-20 2025-05-22 1000000.000000 100,000.00 101,388.89 True 201,388.89 USD 10.00% LinAct360 2025-05-20 CLP USDOBS 900.00 90,000,000 91,250,000

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [59]:
pythoncopy
fixed_rate_mccy_cashflow.record()
Out[59]:
{'amort_is_cashflow': True,
 'amort_sett_currency': 90000000.0,
 'amortization': 100000.0,
 'cashflow': 201388.888888889,
 'discount_factor': 0.0,
 'end_date': '2025-05-20',
 'fx_fixing_date': '20-05-2025',
 'fx_rate_index': 'USDOBS',
 'fx_rate_index_value': 900.0,
 'interest': 101388.88888888898,
 'interest_sett_currency': 91250000.00000007,
 'notional': 1000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.1,
 'settlement_currency': 'CLP',
 'settlement_date': '2025-05-22',
 'start_date': '2024-05-20',
 'type_of_cashflow': 'fixed_rate_multi_currency',
 'type_of_rate': '0.100000 Act360 Lin'}

Ibor Cashflow

Un objeto de tipo IborCashflow representa un flujo de caja calculado a partir de la aplicación de una tasa flotante fijada en una cierta fecha (TERMSOFR, Euribor, ...) , entre dos fechas prefijadas a un nominal prefijado. Para dar de alta uno de estos objetos se requiere:

  • InterestRateIndex: el índice de tasa de interés prefijado
  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de fijación del índice de tasa de interés
  • QCDate: fecha de pago
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • QCCurrency: moneda del nominal y del flujo de caja
  • float: spread aditivo a aplicar a la fijación del índice
  • float: spread multiplicativo o gearing a aplicar a la fijación del índice

Para dar de alta un InterestRateIndex se requiere:

  • str: código del índice
  • QCInterestRate: un objeto tasa de interés que contenga las convenciones del índice
  • Tenor: el lag de inicio del índice respecto a la fecha de fixing (por ejemplo 2d para Libor USD)
  • Tenor: el tenor del índice (3M por ejemplo para Libor USD 3M)
  • QCBusinessCalendar: el calendario de fixing
  • QCBusinessCalendar: el calendario de pago
  • QCCurrency: la moneda a la que corresponde el índice (por ejemplo EUR para EURIBOR 3M)

Construcción

Se define primero el índice de tasa de interés.

In [60]:
pythoncopy
codigo = "TERMSOFR6M"
lin_act360 = qcf.QCInterestRate(0.0, act360, lin_wf)
fixing_lag = qcf.Tenor("2D")
tenor = qcf.Tenor("6M")
fixing_calendar = qcf.CalendarFactory.build(qcf.QCDate("2024-01-01"), 10, [qcf.BusinessCalendarId.USNY])
settlement_calendar = fixing_calendar
term_sofr_6m = qcf.InterestRateIndex(
    codigo, 
    lin_act360, 
    fixing_lag, 
    tenor, 
    fixing_calendar, 
    settlement_calendar, 
    usd
)

Getters del objeto InterestRateIndex.

In [61]:
pythoncopy
print(f"Code: {term_sofr_6m.get_code()}")
print(f"Tenor: {term_sofr_6m.get_tenor()}")
print(f"Tasa: {term_sofr_6m.get_rate()}")
Out[61]:
Code: TERMSOFR6M
Tenor: 6M
Tasa: 0.000000 Act360 Lin

Para fijar el valor del índice en una fecha en particular.

In [62]:
pythoncopy
term_sofr_6m.set_rate_value(0.01)
print(f"Fixing Tasa: {term_sofr_6m.get_rate().get_value():.2%}")
Out[62]:
Fixing Tasa: 1.00%
In [63]:
pythoncopy
fecha_fixing = qcf.QCDate(20, 9, 2018)
print(f"Fecha Inicio: {term_sofr_6m.get_start_date(fecha_fixing)}")
print(f"Fecha Final: {term_sofr_6m.get_end_date(fecha_fixing)}")
Out[63]:
Fecha Inicio: 2018-09-24
Fecha Final: 2019-03-25

Con esto, veamos un ejemplo de construcción y uso de un IborCashflow.

In [64]:
pythoncopy
fecha_inicio = qcf.QCDate(20, 9, 2018)
fecha_final = qcf.QCDate(20, 9, 2019)
fecha_pago = qcf.QCDate(23, 9, 2019)
fecha_fixing = qcf.QCDate(18, 9, 2018)
nominal = 1_000_000.0
amort = 100_000.0
amort_is_cashflow = True
spread = 0.0
gearing = 1.0

ibor_cashflow = qcf.IborCashflow(
    term_sofr_6m,
    fecha_inicio,
    fecha_final,
    fecha_fixing,
    fecha_pago,
    nominal,
    amort,
    amort_is_cashflow,
    usd,
    spread,
    gearing,
)

Getters

In [65]:
pythoncopy
print(f"Tipo Cashflow:\t{ibor_cashflow.get_type()}")
print(f"Fecha Fixing:\t{ibor_cashflow.get_fixing_date()}")
print(f"Fecha Inicio:\t{ibor_cashflow.get_start_date()}")
print(f"Fecha Final:\t{ibor_cashflow.get_end_date()}")
print(f"Fecha Pago:\t{ibor_cashflow.get_settlement_date()}")
print(f"Nominal:\t{ibor_cashflow.get_nominal():,.0f}")
print(f"Amortización:\t{ibor_cashflow.get_amortization():,.0f}")
print(f"Moneda:\t\t{ibor_cashflow.ccy()}")
print(f"Valor Tasa:\t{ibor_cashflow.get_interest_rate_value():.2%}")
print(f"Valor Spread:\t{ibor_cashflow.get_spread():.2%}")
print(f"Valor Gearing:\t{ibor_cashflow.get_gearing():.2f}")
Out[65]:
Tipo Cashflow:	IborCashflow
Fecha Fixing:	2018-09-18
Fecha Inicio:	2018-09-20
Fecha Final:	2019-09-20
Fecha Pago:	2019-09-23
Nominal:	1,000,000
Amortización:	100,000
Moneda:		USD
Valor Tasa:	1.00%
Valor Spread:	0.00%
Valor Gearing:	1.00

Setters

Nuevo nominal.

In [66]:
pythoncopy
nuevo_nominal = 2_000_000.0
ibor_cashflow.set_nominal(nuevo_nominal)
print(f"Nominal: {ibor_cashflow.get_nominal():,.0f}")
Out[66]:
Nominal: 2,000,000

Nueva amortización.

In [67]:
pythoncopy
nueva_amortizacion = 200_000.0
ibor_cashflow.set_amortization(nueva_amortizacion)
print(f"Amortización: {ibor_cashflow.get_amortization():,.0f}")
Out[67]:
Amortización: 200,000

Cambia el valor del índice.

In [68]:
pythoncopy
ibor_cashflow.set_interest_rate_value(nuevo_valor_indice:=.02)
print(f"Valor Tasa: {ibor_cashflow.get_interest_rate_value():.2%}")
Out[68]:
Valor Tasa: 2.00%

Cambia el valor del spread.

In [69]:
pythoncopy
ibor_cashflow.set_spread(.01)
print(f"Valor Spread: {ibor_cashflow.get_spread():.2%}")
Out[69]:
Valor Spread: 1.00%

Cambia el valor del gearing.

In [70]:
pythoncopy
ibor_cashflow.set_gearing(1.2)
print(f"Valor Spread: {ibor_cashflow.get_gearing():.2}")
Out[70]:
Valor Spread: 1.2

Cálculos

Método amount. Retorna el flujo total incluyendo la amortización si corresponde.

In [71]:
pythoncopy
print(f"Flujo: {ibor_cashflow.amount():,.2f}")
Out[71]:
Flujo: 268,944.44

Método accrued_interest. Retorna el interés devengado a una cierta fecha.

In [72]:
pythoncopy
fecha_devengo = qcf.QCDate(20, 7, 2019)
print(f"Interés Devengado al {fecha_devengo}: {ibor_cashflow.accrued_interest(fecha_devengo):,.2f}")

# La tasa es el valor del índice más el spread
tasa = ibor_cashflow.get_interest_rate_value() * ibor_cashflow.get_gearing() + ibor_cashflow.get_spread()

check = tasa * fecha_inicio.day_diff(fecha_devengo) / 360.0 * ibor_cashflow.get_nominal()
print(f"Check: {check:,.2f}")
Out[72]:
Interés Devengado al 2019-07-20: 57,233.33
Check: 57,233.33

Funciones show y get_column_names

Al igual que en casos anteriores, juntando estas dos funciones se obtiene una representación tabular del flujo usando pandas.DataFrame.

In [73]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(ibor_cashflow)], 
    columns=qcf.get_column_names(ibor_cashflow.get_type())
)
df.style.format({
    'nominal':'{:,.2f}', 
    'amortizacion':'{:,.2f}', 
    'interes':'{:,.2f}', 
    'flujo':'{:,.2f}',
    'valor_tasa':'{:.2%}',
    'spread':'{:.2%}',
    'gearing':'{:.2f}',
})
Out[73]:
  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 2018-09-20 2019-09-20 2018-09-18 2019-09-23 2000000.000000 200,000.00 40,555.56 True 268,944.44 USD TERMSOFR6M 2.00% 1.00% 1.20 LinAct360

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [74]:
pythoncopy
ibor_cashflow.record()
Out[74]:
{'amort_is_cashflow': True,
 'amortization': 200000.0,
 'cashflow': 240555.55555555568,
 'discount_factor': 0.0,
 'end_date': '2019-09-20',
 'fixing_date': '2018-09-18',
 'gearing': 1.2,
 'interest': 40555.55555555568,
 'interest_rate_index': 'TERMSOFR6M',
 'notional': 2000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.02,
 'settlement_date': '2019-09-23',
 'spread': 0.01,
 'start_date': '2018-09-20',
 'type_of_cashflow': 'ibor',
 'type_of_rate': '0.020000 Act360 Lin'}

Ibor Multi Currency Cashflow

Un objeto de tipo IborMultiCurrencyCashflow representa un flujo de caja a tasa variable (IborCashflow) que se liquidará en una moneda distinta de la moneda del nominal utilizando el valor a una cierta fecha de un índice de tipo de cambio prefijado. Para dar de alta uno de estos objetos se requiere:

  • InterestRateIndex: el índice de tasa de interés prefijado
  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de fijación del índice de tasa de interés
  • QCDate: fecha de pago
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • QCCurrency: moneda del nominal y del flujo de caja
  • float: spread aditivo a aplicar a la fijación del índice
  • float: spread multiplicativo o gearing a aplicar a la fijación del índice
  • QCDate: fecha de publicación del índice de tipo de cambio
  • QCCurrency: moneda en la que se liquida el flujo
  • FXRateIndex: índice de tipo de cambio a utilizar
  • float: valor del índice de tipo de cambio

Construcción

Parámetros iniciales, sólo agregamos los parámetros relacionados al MultiCurrency y fijamos el valor del índice de tasa de interés en 1.00%.

In [75]:
pythoncopy
valor_indice_fx = 900.0
fecha_publicacion = qcf.QCDate(20, 9, 2019)
term_sofr_6m.set_rate_value(0.01)

Se da de alta el objeto.

In [76]:
pythoncopy
ibor_mccy_cashflow = qcf.IborMultiCurrencyCashflow(
    term_sofr_6m,
    fecha_inicio,
    fecha_final,
    fecha_fixing,
    fecha_pago,
    nominal,
    amort,
    amort_is_cashflow,
    usd,
    spread,
    gearing,
    fecha_publicacion,
    clp,
    indice,
    valor_indice,
)

Getters

El objeto IborMultiCurrencyCashflow es una subclase de IborCashflow y por lo tanto, hereda todos sus métodos y variables.

In [77]:
pythoncopy
print(f"Tipo Cashflow:\t {ibor_mccy_cashflow.get_type()}")
print(f"Fecha Fixing:\t {ibor_mccy_cashflow.get_fixing_date()}")
print(f"Fecha Inicio:\t {ibor_mccy_cashflow.get_start_date()}")
print(f"Fecha Final:\t {ibor_mccy_cashflow.get_end_date()}")
print(f"Fecha Pago:\t {ibor_mccy_cashflow.get_settlement_date()}")
print(f"Nominal:\t {ibor_mccy_cashflow.get_nominal():,.0f}")
print(f"Amortización:\t {ibor_mccy_cashflow.get_amortization():,.0f}")
print(f"Moneda:\t\t {ibor_mccy_cashflow.ccy()}")
print(f"Valor Tasa:\t {ibor_mccy_cashflow.get_interest_rate_value():.2%}")
print(f"Valor Spread:\t {ibor_mccy_cashflow.get_spread():.2%}")
print(f"Valor Gearing:\t {ibor_mccy_cashflow.get_gearing():.2f}")
Out[77]:
Tipo Cashflow:	 IborMultiCurrencyCashflow
Fecha Fixing:	 2018-09-18
Fecha Inicio:	 2018-09-20
Fecha Final:	 2019-09-20
Fecha Pago:	 2019-09-23
Nominal:	 1,000,000
Amortización:	 100,000
Moneda:		 USD
Valor Tasa:	 1.00%
Valor Spread:	 0.00%
Valor Gearing:	 1.00

Adicionalmente tenemos:

In [78]:
pythoncopy
print(f"Fecha Fixing FX: {ibor_mccy_cashflow.get_fx_fixing_date()}")
print(f"Valor Índice FX: {ibor_mccy_cashflow.get_fx_rate_index_value():,.2f}")
Out[78]:
Fecha Fixing FX: 2019-09-20
Valor Índice FX: 900.00

Setters

Nuevo nominal.

In [79]:
pythoncopy
nuevo_nominal = 2_000_000.0
ibor_mccy_cashflow.set_nominal(nuevo_nominal)
print(f"Nominal: {ibor_mccy_cashflow.get_nominal():,.0f}")
Out[79]:
Nominal: 2,000,000

Nueva amortización.

In [80]:
pythoncopy
nueva_amortizacion = 200_000.0
ibor_mccy_cashflow.set_amortization(nueva_amortizacion)
print(f"Amortización: {ibor_mccy_cashflow.get_amortization():,.0f}")
Out[80]:
Amortización: 200,000

Cambia el valor del índice de tasa de interés.

In [81]:
pythoncopy
ibor_mccy_cashflow.set_interest_rate_value(nuevo_valor_indice:=.02)
print(f"Valor Tasa: {ibor_mccy_cashflow.get_interest_rate_value():.2%}")
Out[81]:
Valor Tasa: 2.00%

Nuevo valor para el índice FX.

In [82]:
pythoncopy
ibor_mccy_cashflow.set_fx_rate_index_value(950.0)
print(f"Valor Índice FX: {ibor_mccy_cashflow.get_fx_rate_index_value():,.2f}")
Out[82]:
Valor Índice FX: 950.00

Cálculos

Método amount. Retorna el flujo total incluyendo la amortización si corresponde.

In [83]:
pythoncopy
print(f"Flujo: {ibor_mccy_cashflow.amount():,.2f}")
check = ibor_mccy_cashflow.get_nominal() * fecha_inicio.day_diff(fecha_final) / 360.0 * (
    ibor_mccy_cashflow.get_interest_rate_value() * ibor_mccy_cashflow.get_gearing() + spread) + ibor_mccy_cashflow.get_amortization()
print(f"Check: {check:,.2f}")
Out[83]:
Flujo: 240,555.56
Check: 240,555.56

Método accrued_interest. Retorna el interés devengado a una cierta fecha.

In [84]:
pythoncopy
fecha_devengo = qcf.QCDate(20, 7, 2019)
print(f"Interés Devengado al {fecha_devengo}: {ibor_mccy_cashflow.accrued_interest(fecha_devengo):,.2f}")

tasa = ibor_mccy_cashflow.get_interest_rate_value() * ibor_mccy_cashflow.get_gearing() + ibor_mccy_cashflow.get_spread()

check = tasa * fecha_inicio.day_diff(fecha_devengo) / 360.0 * ibor_cashflow.get_nominal()
print(f"Check: {check:,.2f}")
Out[84]:
Interés Devengado al 2019-07-20: 33,666.67
Check: 33,666.67

Se agrega el método accrued_interest_in_sett_ccy que retorna el interés devengado en moneda de pago. Para utilizarlo se requiere un objeto de tipo time_series.

In [85]:
pythoncopy
ts[fecha_inicio] = 800.0
ts[fecha_devengo] = 950.0
In [86]:
pythoncopy
print(f"Interés devengado en moneda de pago: {ibor_mccy_cashflow.accrued_interest_in_sett_ccy(fecha_devengo, ts):,.0f}")
print(f"Check: {check * 950.0:,.0f}")
Out[86]:
Interés devengado en moneda de pago: 31,983,333
Check: 31,983,333

Funciones show y get_column_names

Al igual que en casos anteriores, juntando estas dos funciones se obtiene una representación tabular del flujo usando pandas.DataFrame.

In [87]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(ibor_mccy_cashflow)], 
    columns=qcf.get_column_names(ibor_mccy_cashflow.get_type())
)
df.style.format({
    'nocional':'{:,.2f}', 
    'amortizacion':'{:,.2f}', 
    'amortizacion_moneda_pago':'{:,.2f}', 
    'interes':'{:,.2f}',
    'interes_moneda_pago':'{:,.2f}',
    'flujo':'{:,.2f}',
    'flujo_moneda_pago':'{:,.2f}',
    'valor_tasa':'{:.2%}',
    'spread':'{:.2%}',
    'valor_indice_fx':'{:,.2f}',
})
Out[87]:
  fecha_inicial fecha_final fecha_fixing fecha_pago nocional amortizacion interes amort_es_flujo flujo moneda_nocional codigo_indice_tasa spread gearing valor_tasa tipo_tasa fecha_fixing_fx moneda_pago codigo_indice_fx valor_indice_fx amortizacion_moneda_pago interes_moneda_pago flujo_moneda_pago
0 2018-09-20 2019-09-20 2018-09-18 2019-09-23 2,000,000.00 200,000.00 40,555.56 True 240,555.56 USD TERMSOFR6M 0.00% 1.000000 2.00% LinAct360 2019-09-20 CLP USDOBS 950.00 190,000,000.00 38,527,777.78 228,527,777.78

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [88]:
pythoncopy
ibor_mccy_cashflow.record()
Out[88]:
{'amort_is_cashflow': True,
 'amort_sett_currency': 190000000.0,
 'amortization': 200000.0,
 'cashflow': 240555.55555555568,
 'discount_factor': 0.0,
 'end_date': '2019-09-20',
 'fixing_date': '2018-09-18',
 'fx_fixing_date': '20-09-2019',
 'fx_rate_index': 'USDOBS',
 'fx_rate_index_value': 950.0,
 'gearing': 1.0,
 'interest': 40555.55555555568,
 'interest_rate_index': 'TERMSOFR6M',
 'interest_sett_currency': 38527777.777777895,
 'notional': 2000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.02,
 'settlement_currency': 'CLP',
 'settlement_date': '2019-09-23',
 'spread': 0.0,
 'start_date': '2018-09-20',
 'type_of_cashflow': 'ibor_multi_currency',
 'type_of_rate': '0.020000 Act360 Lin'}

Overnight Index Cashflow

Un objeto de tipo OvernightIndexCashflow representa un flujo de caja del tipo de la pata flotante de un swap ICPCLP (cámara promedio) de Chile usando cualquier tipo de índice similar (por ejemplo SOFRINDX) y cualquier moneda. Adicionalmente, permite definir en forma independiente a start_date y end_date las fechas inicial y final utilizadas para los valores del índice. Esto puede resultar útil cuando una de estas operaciones se utiliza para cubrir créditos o bonos a tasa fija y tiene, por lo tanto, vencimientos en fechas inhábiles. Para dar de alta uno de estos objetos se requiere:

  • QCDate: fecha inicio devengo (para la aplicación de la tasa)
  • QCDate: fecha final devengo (para la aplicación de la tasa)
  • QCDate: fecha inicio índice (para el valor del índice)
  • QCDate: fecha final índice (para el valor del índice)
  • QCDate: fecha de pago
  • QCCurrency: moneda del nocional
  • float: nocional (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • float: spread aditivo a aplicar a la fijación de la tasa equivalente (TNA en el caso de un ICPCLP)
  • float: spread multiplicativo o gearing a aplicar a la fijación de la tasa equivalente
  • QCInterestRate: con este objeto se especifica en qué convención se calcula la tasa equivalente
  • string: nombre del índice overnight a utilizar
  • unsigned int: número de decimales a utilizar para determinar la tasa equivalente
  • DatesForEquivalentRate: enum que indica qué fechas se utilizan en el cálculo de la tasa equivalente.

Construcción

Alta de parámetros iniciales.

In [89]:
pythoncopy
# Fecha inicio de devengo es un domingo
fecha_inicio_devengo = qcf.QCDate(20, 6, 2025)

# Fecha final de devengo es sábado
fecha_final_devengo = qcf.QCDate(20, 12, 2025)

# Estas fechas deben corresponder a días hábiles
# En ambos casos vamos a la fecha hábil siguiente
fecha_inicio_indice = qcf.QCDate(23, 6, 2025)
fecha_final_indice = qcf.QCDate(22, 12, 2025)

# La fecha de pago es el lunes siguiente
fecha_pago = qcf.QCDate(22, 12, 2025)

# Se usan las fechas de inicio y fin de devengo para
# el cálculo de la tasa equivalente
dates_for_eq_rate = qcf.DatesForEquivalentRate.ACCRUAL

moneda_nocional = qcf.QCUSD()
nocional = 4_933_575_383
amort = 191_378_608
amort_es_flujo = True
spread = 0.0
gearing = 1.0
tasa = qcf.QCInterestRate(0.0, qcf.QCAct360(), qcf.QCLinearWf())
nombre_indice = 'INDICE'
num_decimales = 4 # decimales como número <-> 6 decimales como porcentaje

Se da de alta el objeto.

In [90]:
pythoncopy
overnight_index_cashflow = qcf.OvernightIndexCashflow(
    fecha_inicio_devengo,
    fecha_final_devengo,
    fecha_inicio_indice,
    fecha_final_indice,
    fecha_pago,
    moneda_nocional,
    nocional,
    amort,
    amort_es_flujo,
    spread,
    gearing,
    tasa,
    nombre_indice,
    num_decimales,
    dates_for_eq_rate,
)

Getters

In [91]:
pythoncopy
print(f"Tipo Cashflow:\t\t\t{overnight_index_cashflow.get_type()}")
print(f"Nominal:\t\t\t{overnight_index_cashflow.get_nominal():,.0f}")
print(f"Amortización:\t\t\t{overnight_index_cashflow.get_amortization():,.0f}")
print(f"Fecha Inicio Devengo:\t\t{overnight_index_cashflow.get_start_date()}")
print(f"Fecha Final Devengo:\t\t{overnight_index_cashflow.get_end_date()}")
print(f"Fecha Inicio Índice:\t\t{overnight_index_cashflow.get_index_start_date()}")
print(f"Fecha Final Índice:\t\t{overnight_index_cashflow.get_index_end_date()}")
print(f"Fecha Pago:\t\t\t{overnight_index_cashflow.get_settlement_date()}")
print(f"Valor Índice Fecha Inicio:\t{overnight_index_cashflow.get_start_date_index():,.8f}")
print(f"Valor Índice Fecha Final:\t{overnight_index_cashflow.get_end_date_index():,.8f}")
print(f"Valor Tasa Eq:\t\t\t{overnight_index_cashflow.get_rate_value():.4%}")
print(f"Tipo de Tasa:\t\t\t{overnight_index_cashflow.get_type_of_rate()}")
print(f"Moneda:\t\t\t\t{overnight_index_cashflow.ccy()}")
Out[91]:
Tipo Cashflow:			OvernightIndexCashflow
Nominal:			4,933,575,383
Amortización:			191,378,608
Fecha Inicio Devengo:		2025-06-20
Fecha Final Devengo:		2025-12-20
Fecha Inicio Índice:		2025-06-23
Fecha Final Índice:		2025-12-22
Fecha Pago:			2025-12-22
Valor Índice Fecha Inicio:	1.00000000
Valor Índice Fecha Final:	1.00000000
Valor Tasa Eq:			0.0000%
Tipo de Tasa:			LinAct360
Moneda:				USD

Setters

Se fijan los valores inicial y final del índice.

In [92]:
pythoncopy
valor_indice_inicio = 25_387.13
overnight_index_cashflow.set_start_date_index(valor_indice_inicio)
print(f"Valor Índice Fecha Inicio: {overnight_index_cashflow.get_start_date_index():,.2f}")
Out[92]:
Valor Índice Fecha Inicio: 25,387.13
In [93]:
pythoncopy
valor_indice_final = 26_009.81   # valor_indice_inicio * (1 + .051234 * 7 / 360.0)
overnight_index_cashflow.set_end_date_index(valor_indice_final)
print(f"Valor Índice Fecha Final: {overnight_index_cashflow.get_end_date_index():,.2f}")
Out[93]:
Valor Índice Fecha Final: 26,009.81

Decimales para el cálculo de la tasa equivalente.

In [94]:
pythoncopy
decimales_para_tasa_eq = 4
overnight_index_cashflow.set_eq_rate_decimal_places(decimales_para_tasa_eq)
print(f"Nueva Tasa Eq: {overnight_index_cashflow.get_rate_value():.4%}")
Out[94]:
Nueva Tasa Eq: 4.8300%
In [95]:
pythoncopy
decimales_para_tasa_eq = 6
overnight_index_cashflow.set_eq_rate_decimal_places(decimales_para_tasa_eq)
print(f"Nueva Tasa Eq: {overnight_index_cashflow.get_rate_value():.4%}")
Out[95]:
Nueva Tasa Eq: 4.8251%

Nuevo nocional.

In [96]:
pythoncopy
new_notional = 123_456
overnight_index_cashflow.set_nominal(new_notional)
print(f"Nuevo Nocional: {overnight_index_cashflow.get_nominal():,.2f}")
Out[96]:
Nuevo Nocional: 123,456.00

Nueva amortización.

In [97]:
pythoncopy
new_amortization = 100_000
overnight_index_cashflow.set_amortization(new_amortization)
print(f"Nueva Amortización: {overnight_index_cashflow.get_amortization():,.2f}")
Out[97]:
Nueva Amortización: 100,000.00

Cálculos

Tasa equivalente del período.

In [98]:
pythoncopy
decimales_para_tasa_eq = 4
overnight_index_cashflow.set_eq_rate_decimal_places(decimales_para_tasa_eq)
print(f"Valor Tasa Equivalente Todo el Período: {overnight_index_cashflow.get_rate_value():.4%}")
check = round((
    valor_indice_final / valor_indice_inicio - 1
) * 360.0 / fecha_inicio_devengo.day_diff(fecha_final_devengo), decimales_para_tasa_eq)
print(f"Check: {check:.4%}")
Out[98]:
Valor Tasa Equivalente Todo el Período: 4.8300%
Check: 4.8300%

Se cambian las fechas utilizadas para el cálculo de la tasa equivalente.

In [99]:
pythoncopy
overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.INDEX)
print(f"Valor Tasa Equivalente Todo el Período: {overnight_index_cashflow.get_rate_value():.4%}")
check = round((
    valor_indice_final / valor_indice_inicio - 1
) * 360.0 / fecha_inicio_indice.day_diff(fecha_final_indice), decimales_para_tasa_eq)
print(f"Check: {check:.4%}")
Out[99]:
Valor Tasa Equivalente Todo el Período: 4.8500%
Check: 4.8500%

Método accrued_interest. Este método tiene dos sobrecargas. En la primera, el valor del índice se entrega explícitamente. En el ejemplo se utilizan las dos opciones para el parámetro dates_for_eq_rate.

In [100]:
pythoncopy
# Volvemos el nocional y la amortización al valor inicial
overnight_index_cashflow.set_nominal(nocional)
overnight_index_cashflow.set_amortization(amort)
In [101]:
pythoncopy
fecha_devengo = qcf.QCDate(17, 11, 2023)
valor_indice_devengo = 10_000.0 * (1 + .051234 * 4 / 360)
In [102]:
pythoncopy
overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.ACCRUAL)
print(f"Interés devengado (dates_for_eq_rate = ACCRUAL): {overnight_index_cashflow.accrued_interest(fecha_devengo, valor_indice_devengo): ,.4f}")
tasa = round((
    valor_indice_devengo / valor_indice_inicio - 1
) * 360.0 / fecha_inicio_devengo.day_diff(fecha_devengo), decimales_para_tasa_eq)
print(f"Check: {nocional * fecha_inicio_devengo.day_diff(fecha_devengo) * tasa / 360.0:,.4f}\n")
Out[102]:
Interés devengado (dates_for_eq_rate = ACCRUAL):  0.0000
Check: -2,989,025,831.9170
In [103]:
pythoncopy
overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.INDEX)
print(f"Interés devengado (dates_for_eq_rate = INDEX): {overnight_index_cashflow.accrued_interest(fecha_final_devengo,fecha_final_indice, valor_indice_final): ,.4f}")
tasa = round((
    valor_indice_final / valor_indice_inicio - 1
) * 360.0 / fecha_inicio_indice.day_diff(fecha_final_indice), decimales_para_tasa_eq)
print(f"Check: {nocional * fecha_inicio_devengo.day_diff(fecha_final_devengo) * tasa / 360.0:,.4f}")
Out[103]:
Interés devengado (dates_for_eq_rate = INDEX):  121,633,189.7550
Check: 121,633,189.7550
In [104]:
pythoncopy
overnight_index_cashflow.settlement_amount()
Out[104]:
313011797.76

La segunda sobrecarga de accrued_interest permite empaquetar el valor del índice a fecha de devengo en un objeto de tipo time_series.

In [105]:
pythoncopy
data = qcf.time_series()
data[fecha_inicio_indice] = valor_indice_inicio
data[fecha_final_indice] = valor_indice_final

overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.ACCRUAL)
In [106]:
pythoncopy
# El segundo parámetro de accrued_interest es ahora data
print(f"Interés devengado (dates_for_eq_rate = ACCRUAL): {overnight_index_cashflow.accrued_interest(fecha_final_indice, data): ,.4f}")

# En este caso, en los dias para el cálculo de la tasa equivalente
# coinciden con los días de devengo, aunque cualquiera de las dos
# fechas de devengo sea inhábil.
dias_calculo_tasa_eq = fecha_inicio_devengo.day_diff(fecha_devengo)
tasa = (valor_indice_devengo / valor_indice_inicio - 1) * 360.0 / dias_calculo_tasa_eq
tasa = round(tasa, decimales_para_tasa_eq)

dias_devengo = dias_calculo_tasa_eq
print(f"Check: {nocional * tasa * dias_devengo / 360.0:,.4f}\n")

overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.INDEX)
Out[106]:
Interés devengado (dates_for_eq_rate = ACCRUAL):  120,934,266.5758
Check: -2,989,025,831.9170
In [107]:
pythoncopy
# El segundo parámetro de accrued_interest es ahora data
print(f"Interés devengado (dates_for_eq_rate = INDEX): {
overnight_index_cashflow.accrued_interest(
    fecha_final_devengo,
    fecha_final_indice,
    valor_indice_final,
): ,.4f}")

# En este caso, en los días para el cálculo de la tasa equivalente 
# se utiliza las fecha de inicio del índice.
dias_calculo_tasa_eq = fecha_inicio_indice.day_diff(fecha_final_indice)
print(f"Días cálculo tasa equivalente: {dias_calculo_tasa_eq}")
tasa = (valor_indice_final / valor_indice_inicio - 1) * 360.0 / dias_calculo_tasa_eq
tasa = round(tasa, decimales_para_tasa_eq)
print(f"Valor tasa: {tasa:.4%}")

# Para el devengo siempre se utiliza, los días de devengo
dias_devengo = fecha_final_devengo.day_diff(fecha_inicio_devengo)
print(f"Check: {nocional * tasa * dias_devengo / 360.0:,.4f}\n")
Out[107]:
Interés devengado (dates_for_eq_rate = INDEX):  121,633,189.7550
Días cálculo tasa equivalente: 182
Valor tasa: 4.8500%
Check: -121,633,189.7550

Método amount

In [108]:
pythoncopy
overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.ACCRUAL)
print(f"Amount (ACCRUAL): {overnight_index_cashflow.amount():,.4f}")
dias_calculo_tasa_eq = fecha_inicio_devengo.day_diff(fecha_final_devengo)
tasa = (valor_indice_final / valor_indice_inicio - 1) * 360.0 / dias_calculo_tasa_eq
dias_devengo = dias_calculo_tasa_eq
print(f"Check: {nocional * (1 + tasa * dias_devengo / 360.0):,.4f}\n")
Out[108]:
Amount (ACCRUAL): 312,386,328.0332
Check: 5,054,583,103.0332
In [109]:
pythoncopy
overnight_index_cashflow.set_dates_for_eq_rate(qcf.DatesForEquivalentRate.INDEX)
print(f"Amount (INDEX): {overnight_index_cashflow.amount():,.4f}")
dias_calculo_tasa_eq = fecha_inicio_indice.day_diff(fecha_final_indice)
tasa = (valor_indice_final / valor_indice_inicio - 1) * 360.0 / dias_calculo_tasa_eq
print(f"Check: {nocional * (1 + tasa * dias_devengo / 360.0):,.4f}\n")
Out[109]:
Amount (INDEX): 313,051,205.6158
Check: 5,055,247,980.6158

Funciones show y get_column_names

Al igual que en casos anteriores, juntando estas dos funciones se obtiene una representación tabular del flujo usando pandas.DataFrame.

In [110]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(overnight_index_cashflow)], 
    columns=qcf.get_column_names(overnight_index_cashflow.get_type())
)
df.style.format({
    'nocional':'{:,.2f}', 
    'amortizacion':'{:,.2f}', 
    'amortizacion_moneda_pago':'{:,.2f}', 
    'interes':'{:,.2f}',
    'interes_moneda_pago':'{:,.2f}',
    'flujo':'{:,.2f}',
    'flujo_moneda_pago':'{:,.2f}',
    'valor_tasa_equivalente':'{:.6%}',
    'spread':'{:.2%}',
    'valor_indice_inicial':'{:,.2f}',
    'valor_indice_final':'{:,.2f}',
})
Out[110]:
  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 2025-06-20 2025-12-20 2025-06-23 2025-12-22 2025-12-22 4,933,575,383.00 191,378,608.00 True USD INDICE 25,387.13 26,009.81 0.048500 LinAct360 121,633,189.76 313,011,797.76 0.00% 1.000000

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [111]:
pythoncopy
overnight_index_cashflow.record()
Out[111]:
{'amort_is_cashflow': True,
 'amortization': 191378608.0,
 'cashflow': 314516538.24686074,
 'discount_factor': 0.0,
 'end_date': '2025-12-20',
 'end_date_index': 26009.81,
 'gearing': 1.0,
 'index_end_date': '2025-12-22',
 'index_start_date': '2025-06-23',
 'interest': 123137930.24686071,
 'interest_rate_index': 'INDICE',
 'notional': 4933575383.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.0485,
 'settlement_date': '2025-12-22',
 'spread': 0.0,
 'start_date': '2025-06-20',
 'start_date_index': 25387.13,
 'type_of_cashflow': 'overnight_index',
 'type_of_rate': 'LinAct360'}

Overnight Index Multi Currency Cashflow

Un objeto de tipo OvernightIndexMultiCurrencyCashflow hereda de OvernightIndexCashflow y representa un flujo de caja del tipo de la pata flotante de un swap ICPCLP (cámara promedio) de Chile usando cualquier tipo de índice similar (por ejemplo SOFRINDX), cualquier moneda de nocional, pero con flujos de caja en una moneda distinta a la del nocional, por ejemplo un ICPCLP con contraparte en USA que compensa en USD. Al heredar de OvernightIndexCashflow, también permite definir en forma independiente a start_date y end_date las fechas inicial y final utilizadas para los valores del índice. Para dar de alta uno de estos objetos se requiere:

  • QCDate: fecha inicio devengo (para la aplicación de la tasa)
  • QCDate: fecha final devengo (para la aplicación de la tasa)
  • QCDate: fecha inicio índice (para el valor del índice)
  • QCDate: fecha final índice (para el valor del índice)
  • QCDate: fecha de pago
  • QCCurrency: moneda del nocional
  • float: nocional (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • float: spread aditivo a aplicar a la fijación de la tasa equivalente (TNA en el caso de un ICPCLP)
  • float: spread multiplicativo o gearing a aplicar a la fijación de la tasa equivalente
  • QCInterestRate: con este objeto se especifica en qué convención se calcula la tasa equivalente
  • string: nombre del índice overnight a utilizar
  • unsigned int: número de decimales a utilizar para determinar la tasa equivalente
  • DatesForEquivalentRate: enum que indica qué fechas se utilizan en el cálculo de la tasa equivalente.

Hasta acá son los mismos argumentos necesarios para construir un OvernightIndexCashflow. Se añaden los siguientes argumentos:

  • QCDate: fecha de fixing del índice de tipo de cambio. Esta fecha se refiere a la fecha de publicación del índice, no a la fecha de fixing en sentido financiero.
  • QCCurrency: moneda de pago de los flujos de caja
  • FXRateIndex: índice de tipo de cambio utilizado para la conversión de los flujos a moneda de pago

Ejemplo

In [112]:
pythoncopy
fecha_inicio_devengo = qcf.QCDate(13, 11, 2023)

# Notar que la fecha final de devengo es sábado
fecha_final_devengo = qcf.QCDate(18, 11, 2023)

fecha_inicio_indice = qcf.QCDate(13, 11, 2023)

# Notar que la fecha final de índice es el viernes
fecha_final_indice = qcf.QCDate(17, 11, 2023)

# La fecha de pago es el lunes siguiente
fecha_pago = qcf.QCDate(20, 11, 2023)

moneda_nocional = qcf.QCUSD()
nocional = 10_000_000.0
amort = 1_000_000.0
amort_es_flujo = True
spread = 0.0
gearing = 1.0
tasa = qcf.QCInterestRate(0.0, qcf.QCAct360(), qcf.QCLinearWf())
nombre_indice = 'INDICE'
num_decimales = 8
dates_for_eq_rate = qcf.DatesForEquivalentRate.ACCRUAL

Parámetros para la parte MultiCurrency.

In [113]:
pythoncopy
fecha_fixing_fx_index = fecha_final_devengo
moneda_pago = qcf.QCCLP()
indice_fx = usdclp_obs
In [114]:
pythoncopy
overnight_index_mccy_cashflow = qcf.OvernightIndexMultiCurrencyCashflow(
    fecha_inicio_devengo,
    fecha_final_devengo,
    fecha_inicio_indice,
    fecha_final_indice,
    fecha_pago,
    moneda_nocional,
    nocional,
    amort,
    amort_es_flujo,
    spread,
    gearing,
    tasa,
    nombre_indice,
    num_decimales,
    dates_for_eq_rate,
    fecha_fixing_fx_index,
    moneda_pago,
    indice_fx,
)

Nuevos Getters

In [115]:
pythoncopy
print(f"Tipo de flujo: {overnight_index_mccy_cashflow.get_type()}")
Out[115]:
Tipo de flujo: OvernightIndexMultiCurrencyCashflow

Este getter retorna todo el objeto FXRateIndex.

In [116]:
pythoncopy
overnight_index_mccy_cashflow.get_fx_rate_index()
Out[116]:
<qcfinancial.FXRateIndex at 0x113ad4730>
In [117]:
pythoncopy
print(f"Código del índice FX: {overnight_index_mccy_cashflow.get_fx_rate_index_code()}")
Out[117]:
Código del índice FX: USDOBS
In [118]:
pythoncopy
print(f"Valor del índice FX: {overnight_index_mccy_cashflow.get_fx_rate_index_value():,.2f}")
Out[118]:
Valor del índice FX: 1.00

Nuevos Cálculos

Primero se fijan los valores del índice overnight.

In [119]:
pythoncopy
valor_indice_inicio = 1.0
valor_indice_final = 1 + .1234 * 4 / 360 # Suponemos un valor constante de 12.34% por 4 días del índice
In [120]:
pythoncopy
overnight_index_mccy_cashflow.set_start_date_index(valor_indice_inicio)
overnight_index_mccy_cashflow.set_end_date_index(valor_indice_final)
In [121]:
pythoncopy
print(f"Amount: {overnight_index_mccy_cashflow.amount():,.2f}")
Out[121]:
Amount: 1,013,711.11
In [122]:
pythoncopy
print(f"Interés en moneda de pago: {overnight_index_mccy_cashflow.settlement_ccy_interest():,.0f}")
Out[122]:
Interés en moneda de pago: 13,711
In [123]:
pythoncopy
print(f"Amortización en moneda de pago {overnight_index_mccy_cashflow.settlement_ccy_amortization():,.0f}")
Out[123]:
Amortización en moneda de pago 1,000,000

Funciones show y get_column_names

Se envuelve el resultado de la función show en un pd.DataFrame.

In [124]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(overnight_index_mccy_cashflow),],
    columns=qcf.get_column_names("OvernightIndexMultiCurrencyCashflow", "")
)
df.style.format(aux.format_dict)
Out[124]:
  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 moneda_pago indice_fx fecha_fixing_fx valor_indice_fx interes_moneda_pago amortizacion_moneda_pago flujo_moneda_pago
0 2023-11-13 2023-11-18 2023-11-13 2023-11-17 2023-11-20 10,000,000.00 1,000,000.00 True USD INDICE 1.000000 1.001371 9.8720% LinAct360 13,711.00 1,013,711.00 0.0000% 1.00 CLP USDOBS 2023-11-18 1.00 13,711.00 1,000,000.00 1,013,711.00

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [125]:
pythoncopy
overnight_index_mccy_cashflow.record()
Out[125]:
{'amort_is_cashflow': True,
 'amort_sett_currency': 1000000.0,
 'amortization': 1000000.0,
 'cashflow': 1013711.1111111122,
 'discount_factor': 0.0,
 'end_date': '2023-11-18',
 'end_date_index': 1.0013711111111112,
 'fx_fixing_date': '18-11-2023',
 'fx_rate_index': 'USDOBS',
 'fx_rate_index_value': 1.0,
 'gearing': 1.0,
 'index_end_date': '2023-11-17',
 'index_start_date': '2023-11-13',
 'interest': 13711.111111112117,
 'interest_rate_index': 'INDICE',
 'interest_sett_currency': 13711.0,
 'notional': 10000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.09872,
 'settlement_currency': 'CLP',
 'settlement_date': '2023-11-20',
 'spread': 0.0,
 'start_date': '2023-11-13',
 'start_date_index': 1.0,
 'type_of_cashflow': 'overnight_index_multi_currency',
 'type_of_rate': 'LinAct360'}

Nuevo Setter

Valor del índice de tipo de cambio.

In [126]:
pythoncopy
overnight_index_mccy_cashflow.set_fx_rate_index_value(900.0)

Ver el efecto en las últimas dos columnas.

In [127]:
pythoncopy
pd.DataFrame(
    [qcf.show(overnight_index_mccy_cashflow),],
    columns=qcf.get_column_names("OvernightIndexMultiCurrencyCashflow", "")
).style.format(aux.format_dict)
Out[127]:
  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 moneda_pago indice_fx fecha_fixing_fx valor_indice_fx interes_moneda_pago amortizacion_moneda_pago flujo_moneda_pago
0 2023-11-13 2023-11-18 2023-11-13 2023-11-17 2023-11-20 10,000,000.00 1,000,000.00 True USD INDICE 1.000000 1.001371 9.8720% LinAct360 911,339,999.00 912,339,999.00 0.0000% 1.00 CLP USDOBS 2023-11-18 900.00 12,339,999.00 900,000,000.00 912,339,999.00

Compounded Overnight Rate Cashflow 2

Un objeto de tipo CompoundedOvernightRateCashflow2 representa un flujo de caja calculado como un cupón de la pata flotante de un OIS sobre cualquier índice overnight (SOFR, FF, ESTR). Para dar de alta uno de estos objetos se requiere:

  • InterestRateIndex: el índice de tasa de interés prefijado
  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de pago
  • DateList: fechas de fixing del índice
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • QCCurrency: moneda del nocional de la operación
  • float: spread aditivo a aplicar a la fijación de la tasa equivalente
  • float: spread multiplicativo o gearing a aplicar a la fijación de la tasa equivalente
  • QCInterestRate: permite especificar cuál es la convención de cálculo de la tasa equivalente
  • unsigned int: número de decimales a usar en el cálculo de la tasa equivalente. Por ejemplo para 0.12345678% este valor debe ser 10.
  • unsigned int: número de días de lookback
  • unsigned int: número de días de lockout

El efecto de lookback y lockout aún no está implementado.

Constructor

Se da de alta un índice overnight ficiticio de test.

In [128]:
pythoncopy
codigo = "OITEST"
lin_act360 = qcf.QCInterestRate(0.0, act360, lin_wf)
fixing_lag = qcf.Tenor("0d")
tenor = qcf.Tenor("1d")
fixing_calendar = aux.get_business_calendar("US", range(2024, 2035))
settlement_calendar = fixing_calendar
oitest = qcf.InterestRateIndex(
    codigo, 
    lin_act360, 
    fixing_lag, 
    tenor, 
    fixing_calendar, 
    settlement_calendar, 
    usd
)

Al dar de alta directamente este cashflow es necesario entregar explícitamente las fechas de fijación. En el capítulo relacionado a construcción de qcf.Leg veremos cómo este proceso se simplifica.

In [129]:
pythoncopy
fixing_dates = qcf.DateList()
In [130]:
pythoncopy
fixing_dates.append(qcf.QCDate(27, 12, 2021))
fixing_dates.append(qcf.QCDate(28, 12, 2021))
fixing_dates.append(qcf.QCDate(29, 12, 2021))
fixing_dates.append(qcf.QCDate(30, 12, 2021))

Damos de alta las variables que faltan.

In [131]:
pythoncopy
fecha_inicio = qcf.QCDate(27, 12, 2021)
fecha_final = qcf.QCDate(31, 12, 2021)
fecha_pago = qcf.QCDate(2, 1, 2022)
nocional = 10_000_000.0
amortizacion = 100_000.0
amort_is_cashflow = True
notional_currency = qcf.QCUSD()
spread = 0.001
gearing = 1.5
interest_rate = qcf.QCInterestRate(0.0, qcf.QCAct360(), qcf.QCLinearWf())
eq_rate_decimal_places = 8

Se da de alta el cashflow.

In [132]:
pythoncopy
cor_cashflow_2 = qcf.CompoundedOvernightRateCashflow2(
    oitest,
    fecha_inicio,
    fecha_final,
    fecha_pago,
    fixing_dates,
    nocional,
    amortizacion,
    amort_is_cashflow,
    notional_currency,
    spread,
    gearing,
    interest_rate,
    eq_rate_decimal_places,
    lookback := 0,
    lockout := 0,
)

Getters

In [133]:
pythoncopy
print(f"Type of cashflow:\t\t{cor_cashflow_2.get_type()}")
print(f"Fecha Inicio:\t\t\t{cor_cashflow_2.get_start_date()}")
print(f"Fecha Final:\t\t\t{cor_cashflow_2.get_end_date()}")
print(f"Fecha Pago:\t\t\t{cor_cashflow_2.get_settlement_date()}")
print(f"Nocional:\t\t\t{cor_cashflow_2.get_nominal():,.0f}")
print(f"Amortización:\t\t\t{cor_cashflow_2.get_amortization():,.0f}")
print(f"Moneda del nocional:\t\t{cor_cashflow_2.ccy()}")
print(f"Spread:\t\t\t\t{cor_cashflow_2.get_spread():.2%}")
print(f"Gearing:\t\t\t{cor_cashflow_2.get_gearing():.2f}")
print(f"Número de decimales de tasa:\t{cor_cashflow_2.get_eq_rate_decimal_places()}")
Out[133]:
Type of cashflow:		CompoundedOvernightRateCashflow2
Fecha Inicio:			2021-12-27
Fecha Final:			2021-12-31
Fecha Pago:			2022-01-02
Nocional:			10,000,000
Amortización:			100,000
Moneda del nocional:		USD
Spread:				0.10%
Gearing:			1.50
Número de decimales de tasa:	8
In [134]:
pythoncopy
print("Fechas de fijación:")
for d in cor_cashflow_2.get_fixing_dates():
    print(d)
Out[134]:
Fechas de fijación:
2021-12-27
2021-12-28
2021-12-29
2021-12-30

Setters

In [135]:
pythoncopy
cor_cashflow_2.set_notional(1_000)
print(f"Nocional: {cor_cashflow_2.get_nominal():,.0f}")
Out[135]:
Nocional: 1,000
In [136]:
pythoncopy
cor_cashflow_2.set_amortization(0)
print(f"Amortización: {cor_cashflow_2.get_amortization()}")
Out[136]:
Amortización: 0.0

Se reversa el ejemplo.

In [137]:
pythoncopy
cor_cashflow_2.set_notional(nocional)
cor_cashflow_2.set_amortization(amortizacion)

Cálculos

El accrued_fixing corresponde a la fijación de tasa equivalente en una fecha anterior a la fecha final del cashflow. Para el cálculo de accrued_fixing se requiere un objeto de tipo TimeSeries que contenga los datos históricos del índice overnight.

In [138]:
pythoncopy
ts = qcf.time_series()
In [139]:
pythoncopy
ts[qcf.QCDate(27, 12, 2021)] = 0.01
ts[qcf.QCDate(28, 12, 2021)] = 0.02
ts[qcf.QCDate(29, 12, 2021)] = 0.03
ts[qcf.QCDate(30, 12, 2021)] = 0.04
In [140]:
pythoncopy
print(f"Accrued fixing: {cor_cashflow_2.accrued_fixing(qcf.QCDate(29, 12, 2021), ts):.6%}")
Out[140]:
Accrued fixing: 1.500028%
In [141]:
pythoncopy
check = ((1 + 0.01 / 360) * (1 + 0.02 / 360.0) - 1) * 360 / 2.0
print(f"Check: {check:.6%}")
Out[141]:
Check: 1.500028%

El accrued_interest corresponde a los intereses devengados en una fecha anterior a la fecha final del cashflow. Para el cálculo de accrued_interest también se requiere un objeto de tipo TimeSeries que contenga los datos históricos del índice overnight.

In [142]:
pythoncopy
print(f"Accrued interest: {cor_cashflow_2.accrued_interest(qcf.QCDate(29, 12, 2021), ts):,.2f}")
Out[142]:
Accrued interest: 1,305.58
In [143]:
pythoncopy
check = (
    cor_cashflow_2.get_nominal()
    * (cor_cashflow_2.accrued_fixing(qcf.QCDate(29, 12, 2021), ts) * gearing + spread) * 2 / 360.0
)
print(f"Check: {check:,.2f}")
Out[143]:
Check: 1,305.58

Para que el método amount retorne el resultado correcto, es necesario ejecutar primero el método fixings que realiza la fijación de todas las tasas overnight necesarias.

In [144]:
pythoncopy
fixing = cor_cashflow_2.fixing(ts)
print(f"Fixing: {fixing:.6%}")
Out[144]:
Fixing: 2.500243%

De esa forma.

In [145]:
pythoncopy
print(f"Amount: {cor_cashflow_2.amount():,.2f}")
Out[145]:
Amount: 104,278.18
In [146]:
pythoncopy
check = ((1 + 0.01 / 360) * (1 + 0.02 / 360.0) * (1 + 0.03 / 360.0) * (1 + 0.04 / 360.0) - 1) * 360 / 4.0
print(f"Check: {nocional * (check * gearing + spread) * 4 / 360 + amortizacion:,.2f}")
Out[146]:
Check: 104,278.18

Se puede calcular el interés que depende del spread.

In [147]:
pythoncopy
print(f"Interés del spread: {cor_cashflow_2.interest_from_spread():,.2f}")
Out[147]:
Interés del spread: 111.11
In [148]:
pythoncopy
print(f"Check: {nocional * spread * 4 / 360:,.2f}")
Out[148]:
Check: 111.11

Funciones show y get_column_names

In [149]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(cor_cashflow_2)], 
    columns=qcf.get_column_names("CompoundedOvernightRateCashflow2", "")
)
df.style.format(aux.format_dict)
Out[149]:
  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 2021-12-27 2021-12-31 2022-01-02 10,000,000.00 100,000.00 4,278.18 True 104,278.18 USD OITEST LinAct360 2.5002% 0.1000% 1.50

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [150]:
pythoncopy
cor_cashflow_2.record()
Out[150]:
{'amort_is_cashflow': True,
 'amortization': 100000.0,
 'cashflow': 104278.18288644576,
 'discount_factor': 0.0,
 'end_date': '2021-12-31',
 'gearing': 1.5,
 'interest': 4278.182886445769,
 'interest_rate_index': 'OITEST',
 'notional': 10000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.025002430652003582,
 'settlement_date': '2022-01-02',
 'spread': 0.001,
 'start_date': '2021-12-27',
 'type_of_cashflow': 'overnight_index',
 'type_of_rate': 'LinAct360'}

Compounded Overnight Rate Multi Currency Cashflow 2

Un objeto de tipo CompoundedOvernightRateMultiCurrencyCashflow2 representa un flujo de caja calculado como un cupón de la pata flotante de un OIS sobre cualquier índice overnight (SOFR, FF, EONIA) con la característica adicional de liquidar sus flujos en una moneda distina a la del nocional. Para dar de alta uno de estos objetos se requiere:

  • InterestRateIndex: el índice de tasa de interés prefijado
  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de pago
  • DateList: fechas de fixing del índice
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • QCCurrency: moneda del nocional de la operación
  • float: spread aditivo a aplicar a la fijación de la tasa equivalente
  • float: spread multiplicativo o gearing a aplicar a la fijación de la tasa equivalente
  • QCInterestRate: permite especificar cuál es la convención de cálculo de la tasa equivalente
  • unsigned int: número de decimales a usar en el cálculo de la tasa equivalente. Por ejemplo para 0.12345678% este valor debe ser 10.
  • unsigned int: número de días de lookback
  • unsigned int: número de días de lockout

Estos argumentos son los heredados de un CompoundedOvernightRateCashflow. También en este caso, el efecto de lookback y lockout aún no está implementado.

A estos argumentos se debe agregar:

  • QCDate: fecha de fixing del índice de tipo de cambio. Esta fecha se refiere a la fecha de publicación del índice, no a la fecha de fixing en sentido financiero.
  • QCCurrency: moneda de pago de los flujos de caja
  • FXRateIndex: índice de tipo de cambio utilizado para la conversión de los flujos a moneda de pago

Constructor

Se da de alta un índice overnight ficiticio de test.

In [151]:
pythoncopy
codigo = "OITEST"
lin_act360 = qcf.QCInterestRate(0.0, act360, lin_wf)
fixing_lag = qcf.Tenor("0d")
tenor = qcf.Tenor("1d")
fixing_calendar = scl
settlement_calendar = scl
oitest = qcf.InterestRateIndex(
    codigo, 
    lin_act360, 
    fixing_lag, 
    tenor, 
    fixing_calendar, 
    settlement_calendar, 
    usd
)

Al dar de alta directamente este cashflow es necesario entregar explícitamente las fechas de fijación. En el capítulo relacionado a construcción de qcf.Leg veremos cómo este proceso se simplifica.

In [152]:
pythoncopy
fixing_dates = qcf.DateList()
In [153]:
pythoncopy
fixing_dates.append(qcf.QCDate(27, 12, 2021))
fixing_dates.append(qcf.QCDate(28, 12, 2021))
fixing_dates.append(qcf.QCDate(29, 12, 2021))
fixing_dates.append(qcf.QCDate(30, 12, 2021))

Damos de alta las variables que faltan.

In [154]:
pythoncopy
fecha_inicio = qcf.QCDate(27, 12, 2021)
fecha_final = qcf.QCDate(31, 12, 2021)
fecha_pago = qcf.QCDate(2, 1, 2022)
nocional = 10_000_000.0
amortizacion = 100_000.0
amort_is_cashflow = True
notional_currency = qcf.QCUSD()
spread = 0.001
gearing = 1.5
interest_rate = qcf.QCInterestRate(0.0, qcf.QCAct360(), qcf.QCLinearWf())
eq_rate_decimal_places = 8
# ---------------------Para el MultiCurrency ----------------------------
fecha_fixing_fx = fecha_pago
moneda_pago = qcf.QCCLP()

Se da de alta el objeto.

In [155]:
pythoncopy
cor_cashflow_mccy_2 = qcf.CompoundedOvernightRateMultiCurrencyCashflow2(
    oitest,
    fecha_inicio,
    fecha_final,
    fecha_pago,
    fixing_dates,
    nocional,
    amortizacion,
    amort_is_cashflow,
    notional_currency,
    spread,
    gearing,
    interest_rate,
    eq_rate_decimal_places,
    lookback,
    lockout,
    fecha_fixing_fx,
    moneda_pago,
    usdclp_obs,
)

Nuevos Getters

In [156]:
pythoncopy
print(f"Fx Rate Index: {cor_cashflow_mccy_2.get_fx_rate_index()}")
print(f"Fx Rate Index Value: {cor_cashflow_mccy_2.get_fx_rate_index_value()}")
print(f"Fx Rate Index Code: {cor_cashflow_mccy_2.get_fx_rate_index_code()}")
print(f"Fx Rate Index Fxing Date: {cor_cashflow_mccy_2.get_fx_rate_index_fixing_date()}")
Out[156]:
Fx Rate Index: <qcfinancial.FXRateIndex object at 0x113ad4730>
Fx Rate Index Value: 1.0
Fx Rate Index Code: USDOBS
Fx Rate Index Fxing Date: 2022-01-02

Nuevo Setter

In [157]:
pythoncopy
cor_cashflow_mccy_2.set_fx_rate_index_value(900.0)
print(f"Fx Rate Index Value: {cor_cashflow_mccy_2.get_fx_rate_index_value()}")
Out[157]:
Fx Rate Index Value: 900.0

Nuevos Cálculos

In [158]:
pythoncopy
print(f"Interest: {cor_cashflow_mccy_2.interest(ts):,.2f}")
Out[158]:
Interest: 4,278.18
In [159]:
pythoncopy
print(f"To settlement currency: {cor_cashflow_mccy_2.to_settlement_currency(cor_cashflow_mccy_2.interest(ts)):,.0f}")
Out[159]:
To settlement currency: 3,850,364

Funciones show y get_column_names

In [160]:
pythoncopy
df = pd.DataFrame(
    [qcf.show(cor_cashflow_mccy_2)],
    columns=qcf.get_column_names("CompoundedOvernightRateMultiCurrencyCashflow2", "")
)
df.style.format(aux.format_dict)
Out[160]:
  fecha_inicial fecha_final fecha_pago nocional amortizacion interes amort_es_flujo flujo moneda_nocional codigo_indice_tasa tipo_tasa spread gearing valor_tasa moneda_pago fx_rate_index fecha_fixing_fx valor_indice_fx interes_moneda_pago amortizacion_moneda_pago flujo_moneda_pago
0 2021-12-27 2021-12-31 2022-01-02 10,000,000.00 100,000.00 4,278.18 True 104,278.18 USD OITEST LinAct360 0.1000% 1.50 2.5002% CLP USDOBS 2022-01-02 900.00 3,850,364.60 90,000,000.00 93,850,364.50

Método record

Retorna un dict con los datos del flujo de caja. Si éste ha sido valorizado, retorna también el valor presente y el factor de descuento utilizado.

In [161]:
pythoncopy
cor_cashflow_mccy_2.record()
Out[161]:
{'amort_is_cashflow': True,
 'amort_sett_currency': 90000000.0,
 'amortization': 100000.0,
 'cashflow': 104278.18288644576,
 'discount_factor': 0.0,
 'end_date': '2021-12-31',
 'fx_fixing_date': '02-01-2022',
 'fx_rate_index': 'USDOBS',
 'fx_rate_index_value': 900.0,
 'gearing': 1.5,
 'interest': 4278.182886445769,
 'interest_rate_index': 'OITEST',
 'interest_sett_currency': 3850364.597801192,
 'notional': 10000000.0,
 'notional_currency': 'USD',
 'present_value': 0.0,
 'rate_value': 0.025002430652003582,
 'settlement_currency': 'CLP',
 'settlement_date': '2022-01-02',
 'spread': 0.001,
 'start_date': '2021-12-27',
 'type_of_cashflow': 'overnight_index_multi_currency',
 'type_of_rate': 'LinAct360'}

Icp Clf Cashflow

Un objeto de tipo IcpClfCashflow representa un flujo de caja calculado como un cupón de la pata flotante de un swap ICP (cámara promedio) en UF de Chile. Para dar de alta uno de estos objetos se requiere:

  • QCDate: fecha inicio (para la aplicación de la tasa)
  • QCDate: fecha final (para la aplicación de la tasa)
  • QCDate: fecha de pago
  • float: nominal (monto al que se le aplica la tasa)
  • float: amortización (eventual flujo de caja que corresponde a una porción del nominal)
  • bool: indica si la amortización anterior es un flujo de caja o sólo una disminución de nominal
  • float: spread aditivo a aplicar a la fijación de la TRA
  • float: spread multiplicativo o gearing a aplicar a la fijación de la TRA
  • vector<float>: objeto double_vec (en Python) que contiene ICP Inicio, ICP Final, UF Inicio, UF Final (se debe respetar el orden)

Recordar que TRA significa Tasa Real Anual y se determina utilizando los valores del índice ICP y los valores de la UF en la fecha de inicio y fecha final del IcpClfCashflow.

Constructor

Parámetros iniciales.

In [162]:
pythoncopy
fecha_inicio = qcf.QCDate(20, 9, 2018)
fecha_final = qcf.QCDate(20, 9, 2019)
fecha_pago = qcf.QCDate(23, 9, 2019)
nominal = 300_000.0
amort = 100_000.0
spread = 0.0
gearing = 1.0

icp_uf = qcf.double_vec()
# Los primeros dos valores corresponden a icp_inicial e icp_final.
# Los segundos dos valores corresponden a uf_inicial y uf_final
icp_uf.append(10_000.0)
icp_uf.append(10_250.0)
icp_uf.append(35_000.0)
icp_uf.append(35_500.0)

Alta del objeto.

In [163]:
pythoncopy
icp_clf_cashflow = qcf.IcpClfCashflow(
    fecha_inicio, 
    fecha_final, 
    fecha_pago, 
    nominal, 
    amort, 
    True, 
    spread, 
    gearing, 
    icp_uf
)

Getters

In [164]:
pythoncopy
print("Fecha Inicio:", icp_clf_cashflow.get_start_date())
print("Fecha Final:", icp_clf_cashflow.get_end_date())

print(f"ICP Fecha Inicio: {icp_clf_cashflow.get_start_date_icp():,.2f}")
print(f"ICP Fecha Final: {icp_clf_cashflow.get_end_date_icp():,.2f}")

print(f"UF Fecha Inicio: {icp_clf_cashflow.get_start_date_uf():,.2f}")
print(f"UF Fecha Final: {icp_clf_cashflow.get_end_date_uf():,.2f}")

print(f"Valor TRA Todo el Período: {icp_clf_cashflow.get_rate_value():.4%}")
tna = icp_clf_cashflow.get_tna(fecha_final, 10_250.0)
dias = fecha_inicio.day_diff(fecha_final)
tra = ((1 + tna * dias / 360.0) * icp_uf[2]/ icp_uf[3] - 1) * 360.0 / dias
print(f"Check TRA: {round(tra, 6):.4%}")

print(f"Nominal: {icp_clf_cashflow.get_nominal():,.0f}")
print(f"Amortización: {icp_clf_cashflow.get_amortization():,.0f}")
print("Tipo de Tasa:", icp_clf_cashflow.get_type_of_rate())
print("Moneda:", icp_clf_cashflow.ccy())
Out[164]:
Fecha Inicio: 2018-09-20
Fecha Final: 2019-09-20
ICP Fecha Inicio: 10,000.00
ICP Fecha Final: 10,250.00
UF Fecha Inicio: 35,000.00
UF Fecha Final: 35,500.00
Valor TRA Todo el Período: 1.0461%
Check TRA: 1.0461%
Nominal: 300,000
Amortización: 100,000
Tipo de Tasa: LinAct360
Moneda: CLF

Setters

In [165]:
pythoncopy
decimales_para_tra = 8
icp_clf_cashflow.set_tra_decimal_places(decimales_para_tra)
print(f"Nueva TRA: {icp_clf_cashflow.get_rate_value():.6%}")

nuevo_nominal = 100_000.0
icp_clf_cashflow.set_nominal(nuevo_nominal)
print(f"Nuevo Nominal: {icp_clf_cashflow.get_nominal():,.0f}")

nueva_amortizacion = 10_000.0
icp_clf_cashflow.set_amortization(nueva_amortizacion)
print(f"Nueva Amortización: {icp_clf_cashflow.get_amortization():,.0f}")

nuevo_icp_inicio = 20_000.0
icp_clf_cashflow.set_start_date_icp(nuevo_icp_inicio)
print(f"Nuevo ICP Inicio: {icp_clf_cashflow.get_start_date_icp():,.2f}")

nuevo_icp_final = 20_500.0
icp_clf_cashflow.set_end_date_icp(nuevo_icp_final)
print(f"Nuevo ICP Final: {icp_clf_cashflow.get_end_date_icp():,.2f}")
print(f"Check TNA Final: {icp_clf_cashflow.get_tna(fecha_final, nuevo_icp_final):.6%}")
Out[165]:
Nueva TRA: 1.046054%
Nuevo Nominal: 100,000
Nueva Amortización: 10,000
Nuevo ICP Inicio: 20,000.00
Nuevo ICP Final: 20,500.00
Check TNA Final: 2.470000%

Funciones show y get_column_names

In [166]:
pythoncopy
df = pd.DataFrame([qcf.show(icp_clf_cashflow)], columns=qcf.get_column_names("IcpClfCashflow", ""))
df.style.format(aux.format_dict)
Out[166]:
  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-09-20 2019-09-20 2019-09-23 100,000.00 10,000.00 True 11,060.58 CLF 20,000.00 20,500.00 35,000.00 35,500.00 1.0461% 1,060.58 0.0000% 1.00 LinAct360 392,650,680.00