Puede consultar el archivo de Excel: excel_para_pyhton.xlsm
Puede ver la Hoja1 del documento de Excel.
Supongamos un bono con las siguientes características:
Se pide:
pip install numpy-financial
Requirement already satisfied: numpy-financial in /usr/local/lib/python3.10/dist-packages (1.0.0) Requirement already satisfied: numpy>=1.15 in /usr/local/lib/python3.10/dist-packages (from numpy-financial) (1.23.5)
import numpy_financial as npf
# DATOS DEL BONO
nominal = 1000
cupon = 0.1
n = 5 # años
r = 0.08
# FLUJOS DE CAJA
flujos = [0]
for i in range(1, n+1):
if i < n:
flujos.append(cupon * nominal)
elif i == n:
flujos.append(cupon * nominal + nominal)
print("Flujos de caja: ", flujos)
# PRECIO DEL BONO
precio = 0
for i in range(1, n+1):
precio += flujos[i] / (1+r)**i
print("Precio del bono:", precio)
bono = flujos[:]
bono[0] = -precio
# COMPROBACIÓN TIR = r
tir = npf.irr(bono)
print(f"La TIR del bono es: {tir:.5%}") # Internal rate of return
Flujos de caja: [0, 100.0, 100.0, 100.0, 100.0, 1100.0] Precio del bono: 1079.8542007415613 La TIR del bono es: 8.00000%
Puede ver la Hoja2 del documento de Excel.
Supongamos un bono con las siguientes características:
Se pide:
import numpy_financial as npf
# DATOS
nominal = 1000
cupon = 0.1
n = 5 # años
r01 = 0.02
r02 = 0.04
r03 = 0.06
r04 = 0.08
r05 = 0.10
etti = [r01,r02,r03,r04,r05]
# FLUJOS DE CAJA
flujos = [0]
for i in range(1, n+1):
if i < n:
flujos.append(cupon * nominal)
elif i == n:
flujos.append(cupon * nominal + nominal)
print("Flujos de caja: ", flujos)
# PRECIO DEL BONO CON LA ETTI
precio = 0
for i in range(1, n+1):
precio += flujos[i] / (1+etti[i-1])**i
print("Precio del bono:", precio)
bono = flujos[:]
bono[0] = -precio
# CALCULAR LA TIR DEL BONO TIR = r
tir = npf.irr(bono)
print(f"La TIR del bono es: {tir:.6%}") # Internal rate of return
Flujos de caja: [0, 100.0, 100.0, 100.0, 100.0, 1100.0] Precio del bono: 1030.9732059359958 La TIR del bono es: 9.199575%
Puede ver la Hoja3 del documento de Excel.
Supongamos dos bonos A y B, ambos a 5 años con las siguientes características:
Se pide:
import numpy_financial as npf
# DATOS BONO A
nominalA = 10_000
cuponA = 0.02
n = 5 # años, para A y B
# DATOS BONO B
nominalB = 4_000
cuponB = 0.25
# ETTI
etti =[.02,.04,.06,.08,.10]
# FLUJOS DE CAJA DE LOS BONOS
def flujos(nominal, cupon, n):
cf = [0] # array con el cash flow
for i in range(1, n+1):
if i < n:
cf.append(cupon * nominal)
elif i == n:
cf.append(cupon * nominal + nominal)
return cf
# Flujos de caja de los Bonos A y B
flujosA = flujos(nominalA, cuponA, n)
flujosB = flujos(nominalB, cuponB, n)
# PRECIO DE UN BONO CON LA ETTI
def precio_etti(flujos, etti):
precio = 0
for i in range(1, n+1):
precio += flujos[i] * (1+etti[i-1])**-i
return precio
# Precios de los Bonos A y B
precioA = precio_etti(flujosA, etti)
precioB = precio_etti(flujosB, etti)
print(f"El precio del bono A es {precioA}")
print(f"El precio del bono B es {precioB}")
# Flujos de caja de los bonos A y B
bonoA = flujosA[:]
bonoA[0] = -precioA
bonoB = flujosB[:]
bonoB[0] = -precioB
# CREACIÓN DEL BONO SINTÉTICO C
# m es el número de bonos que se han de comprar o vender de uno de los bonos
m = max(cuponA*nominalA, cuponB*nominalB) / min(cuponB*nominalB, cuponA*nominalA)
# con los datos de ejemplo m = 1000 / 200 = 5
# Flujos de caja del bono C
bonoC = [0]*(n+1)
for i in range(0, n+1):
bonoC[i] = m * bonoA[i] - bonoB[i]
print(f"Flujos de caja del bono C: {bonoC}")
# Calcular la TIR de los bonos
tirA = npf.irr(bonoA)
tirB = npf.irr(bonoB)
tirC = npf.irr(bonoC)
print(f"La TIR del bono A es: {tirA:.6%}")
print(f"La TIR del bono B es: {tirB:.6%}")
print(f"La TIR del bono C es: {tirC:.6%}")
El precio del bono A es 7029.316996345231 El precio del bono B es 6584.2041210050265 Flujos de caja del bono C: [-28562.38086072113, 0.0, 0.0, 0.0, 0.0, 46000.0] La TIR del bono A es: 9.795736% La TIR del bono B es: 8.573790% La TIR del bono C es: 10.000000%
Puede ver la Hoja4 del documento de Excel.
La importancia de trabajar con bonos cupón cero.
La TIR es la Tasa Interna de Rentabilidad, la palabra 'Interna' indica que si la operación financiera analizada se mezcla con otras operaciones la rentabilidad prometida por la TIR se podría alterar.
Una operación de inversión que tenga flujos de caja intermedios, por ejemplo un bono cupón explícito, puede no proporcionar a su propietario la rentabilidad que promete la TIR del bono si el inversor no se preocupa de reinvertir los flujos de caja intermedios hasta el final de la operación.
Supongamos un bono cupón explícito con las siguientes características:
Se pide:
import numpy_financial as npf
# DATOS
nominal = 10_000
cupon = .1
n = 10 # años
precio = 10_000
# Flujos de caja del bono
cf = [0]*(n+1)
for i in range(1,n+1):
cf[i] = cupon * nominal
cf[n] += nominal
cf[0] = -precio
print(f"La TIR del bono es: {npf.irr(cf):.2%}")
# Montante de la Reinversión
def montante(cf,r):
m = 0 # montante
for i in range(1, n+1):
m += cf[i]*(1+r)**(n-i)
return m
tasas_reinversion = [0,.1,.2]
for t in tasas_reinversion:
m = montante(cf,t)
print()
print(f"El montante reinvirtiendo al tanto del {t:.0%} es {m:,.2f} €")
print(f"La rentabilidad del inversor reinvirtiendo al tanto {t:.0%} es {(m/precio)**(1/n)-1:.2%}")
La TIR del bono es: 10.00% El montante reinvirtiendo al tanto del 0% es 20,000.00 € La rentabilidad del inversor reinvirtiendo al tanto 0% es 7.18% El montante reinvirtiendo al tanto del 10% es 25,937.42 € La rentabilidad del inversor reinvirtiendo al tanto 10% es 10.00% El montante reinvirtiendo al tanto del 20% es 35,958.68 € La rentabilidad del inversor reinvirtiendo al tanto 20% es 13.65%
Puede ver la Hoja5 del documento de Excel.
Comparemos dos inversiones A y B, ambas de duración 1 año y que proporcionan ambas un 10% anual, con una inversión de un millón de euros cada una. La inversión A es cierta, esto supone que existe un 100% de probabilidad de que el flujo de caja final sea 1.100.000 €. La inversión B, tiene una probabilidad del 1% de que el montante final sea m1, y una probabilidad del 99% de que el montante final sea m2.
Se pide:
import numpy_financial as npf
# DATOS
c = 1_000_000 # capital inicial
r = .1 # rentabilidad 10% anual
ma = c*(1+r) # Montante A: el montante de la inversión A es 1.100.000
p = .01 # probabilidad de que suceda una de las ramas de la inverión B
q = 1-p # probabilidad complementaria de la otra rama de B, se cumple que p+q=1
# Cálculo de mb2
# El montante de la inversión B es mb y tiene dos componentes mb1 + mb2 = mb
def mb_2(ma,p,mb1):
return (ma - p * mb1) / q
# Resultados
print("CASO 1")
mb1 = 500_000 # primer caso que nos piden
mb2 = mb_2(ma,p,mb1)
print(f"Si el montante alcanzado con mb1 es {mb1:,} entonces el montante mb2 es {mb2:,.2f}")
print(f"La TIR de la inversión B1 es: {npf.irr([-c,mb1]):.1%}")
print(f"La TIR de la inversión B2 es: {npf.irr([-c,mb2]):.2%}")
print(f"La TIR de la inversión A es: {npf.irr([-c, ma]):.1%}") # comprobar que ambas TIR son del 10%
print(f"La TIR de la inversión B es: {npf.irr([-c, p*mb1+q*mb2]):.1%}") # comprobar que ambas TIR son del 10%
print()
print("CASO 2")
mb1 = 0 # segundo caso que nos piden
mb2 = mb_2(ma,p,mb1)
print(f"Si el montante alcanzado con mb1 es {mb1} entonces el montante mb2 es {mb2:,.2f}")
print(f"La TIR de la inversión B1 es: {npf.irr([-c,mb1]):.1%}")
print(f"La TIR de la inversión B2 es: {npf.irr([-c,mb2]):.2%}")
print(f"La TIR de la inversión A es: {npf.irr([-c, ma]):.1%}") # comprobar que ambas TIR son del 10%
print(f"La TIR de la inversión B es: {npf.irr([-c, p*mb1+q*mb2]):.1%}") # comprobar que ambas TIR son del 10%
CASO 1 Si el montante alcanzado con mb1 es 500,000 entonces el montante mb2 es 1,106,060.61 La TIR de la inversión B1 es: -50.0% La TIR de la inversión B2 es: 10.61% La TIR de la inversión A es: 10.0% La TIR de la inversión B es: 10.0% CASO 2 Si el montante alcanzado con mb1 es 0 entonces el montante mb2 es 1,111,111.11 La TIR de la inversión B1 es: nan% La TIR de la inversión B2 es: 11.11% La TIR de la inversión A es: 10.0% La TIR de la inversión B es: 10.0%
Puede ver la Hoja6 del documento de Excel.
En el mercado cotizan los bonos A y B.
Se pide:
import numpy_financial as npf
from random import randint, seed
seed()
# DATOS
tirA = .1
cuponB = 4_400
nominalB = 10_000
precioB = 14_000
nominalA = randint(1,10000)
print(f"El nominal elegido para el bono A es {nominalA}")
# FLUJOS DE CAJA bonos A y B
bonoB = [-precioB, cuponB, cuponB+nominalB]
bonoA = [-nominalA/(1+tirA), nominalA, 0] # añadimos un flujo 0 en t=2 por comodidad, para luego restar flujos
m = bonoB[1]/bonoA[1]
print(f"El multiplicador m es {m}")
bonoC = [bonoB[0]-m*bonoA[0], bonoB[1]-m*bonoA[1], bonoB[2]-m*bonoA[2]]
print("Bono C: ", bonoC)
# TIR del bono C
tirC = npf.irr(bonoC)
print(f"La TIR del bono C es: {tirC:.6%}")
# Creación de la ETTI
etti = [tirA, tirC] # la tirC si pertenece a la ETTI por ser un bono cupón cero, pero la tirB no pertenece
# COMPROBAR PRECIOS CON LA ETTI
def precio_etti(flujos, etti):
precio = 0
for i in range(1, len(flujos)):
precio += flujos[i] * (1+etti[i-1])**-i
return precio
# Precios de los Bonos A, B y C
precioA = precio_etti(bonoA, etti)
precioB = precio_etti(bonoB, etti)
precioC = precio_etti(bonoC, etti)
print(f"La diferencia de precios en el bono A es {precioA+bonoA[0]}")
print(f"La diferencia de precios en el bono B es {precioB+bonoB[0]}")
print(f"La diferencia de precios en el bono C es {precioC+bonoC[0]}")
El nominal elegido para el bono A es 517 El multiplicador m es 8.51063829787234 Bono C: [-10000.0, 0.0, 14400.0] La TIR del bono C es: 20.000000% La diferencia de precios en el bono A es 5.684341886080802e-14 La diferencia de precios en el bono B es -3.637978807091713e-12 La diferencia de precios en el bono C es -3.637978807091713e-12
Puede ver la Hoja7 del documento de Excel.
En el mercado cotizan los bonos A , B y C.
Se pide:
Supongamos conocidos los flujos de caja de los bonos A, B y C incluidos sus precios.
Se pide:
import numpy_financial as npf
##### FASE 1 #####
# Flujos de caja de los bonos
bonoA = [0,1000,0,0] # inicialmente el flujo de caja inicial es cero
bonoB = [0,90,1090,0]
bonoC = [0,500,500,1500]
# Calcular el precio de los bonos dada la ETTI
etti = [.1, .2, .3]
def precio_etti(flujos, etti):
precio = 0
for i in range(1, len(flujos)):
precio += flujos[i] * (1+etti[i-1])**-i
return precio
precioA = precio_etti(bonoA, etti)
precioB = precio_etti(bonoB, etti)
precioC = precio_etti(bonoC, etti)
print(f"El precio del bono A es {precioA:,.2f} €")
print(f"El precio del bono B es {precioB:,.2f} €")
print(f"El precio del bono C es {precioC:,.2f} €")
print()
# Calcular la TIR de los bonos
bonoA[0] = -precioA
bonoB[0] = -precioB
bonoC[0] = -precioC
tirA = npf.irr(bonoA)
tirB = npf.irr(bonoB)
tirC = npf.irr(bonoC)
print(f"La TIR del bono A es: {tirA:.2%}")
print(f"La TIR del bono B es: {tirB:.2%}")
print(f"La TIR del bono C es: {tirC:.2%}")
print()
##### FASE 2 #####
etti_calculada = [0,0,0] # inicializamos el array
etti_calculada[0] = tirA # la ETTI del año 1 es la TIR del bono A por ser un bono cupón cero a un año
def calcula_etti(bono, etti_calculada):
n = [i for i, e in enumerate(bono) if e != 0][-1]
ultimo_flujo = bono[n]
precio = -bono[0]
fcid = 0 # inicializamos los flujos de caja intermedios descontados
for t in range(1,n): # recorre los flujos de caja intermedios
fcid += bono[t] / (1+etti[t-1])**t
return (ultimo_flujo / (precio - fcid))**(1/n)-1
r02 = calcula_etti(bonoB, etti_calculada) # calculamos r02 conocido el bono B y r01
etti_calculada[1] = r02
r03 = calcula_etti(bonoC, etti_calculada) # calculamos r03 conocido el bono C, r01 y r02
etti_calculada[2] = r03
# Imprime la etti_calculada
for i in range(len(etti_calculada)):
print(f"La ETTI del año {i+1} es {etti_calculada[i]:.2%}")
El precio del bono A es 909.09 € El precio del bono B es 838.76 € El precio del bono C es 1,484.52 € La TIR del bono A es: 10.00% La TIR del bono B es: 19.49% La TIR del bono C es: 25.13% La ETTI del año 1 es 10.00% La ETTI del año 2 es 20.00% La ETTI del año 3 es 30.00%
# Para calcular n
bonoA = [0,1000,0,0] # n = 1, vector = [1]
bonoB = [0,90,1090,0] # n = 2, vector = [1, 2]
bonoC = [0,500,500,1500] # n = 3, vector = [1, 2, 3]
bono = bonoA
vector = [i for i, e in enumerate(bono) if e != 0] # list comprhension
print(vector[-1])
1
Puede consultar el archivo de Excel: arbitraje_con_bonos.xlsx
Puede ver la Hoja1 del documento de Excel.
Supongamos dos fruterías donde venden el mismo tipo de manzana.
Operamos bajo el supuesto de que en cada tienda podemos comprar o vender las manzanas.
Estrategia de arbitraje:
La estrategia consiste en comprar el activo infravalorado y vender el activo sobrevalorado.
De esta forma estaríamos ganando la diferencia.
Supongamos que el caso anterior de compraventa no se produce simultáneamente en el tiempo y que necesitamos financiar nuestra compra durante un tiempo.
precioA = 100
precioB = 160
n = 1 # duración en años
Co = precioA # Capital inicial prestado
r = .1 # tipo de interés anual
Cn = Co*(1+r)**n
flujos_caja = [Co-precioA, round(precioB-Cn,8)]
print(f"Los flujos de caja de la operación en los diferentes años son: {flujos_caja}")
Los flujos de caja de la operación en los diferentes años son: [0, 50.0]
Puede ver la Hoja2 del documento de Excel.
Disponemos de dos bonos que maduran a dos años, ambos de cupón explícito y con diferente duración de Macaulay (duration).
Se pide:
pip install numpy-financial
Requirement already satisfied: numpy-financial in /usr/local/lib/python3.10/dist-packages (1.0.0) Requirement already satisfied: numpy>=1.15 in /usr/local/lib/python3.10/dist-packages (from numpy-financial) (1.23.5)
import numpy_financial as npf
import numpy as np # la librería numpy nos permitirá trabajar con ndarrays
# DATOS
bonoA = [0, 10, 1010]
bonoB = [0, 200, 1200]
etti = [.05, .2]
# PRECIO DE UN BONO CON LA ETTI
def precio_etti(flujos, etti):
precio = 0
for i in range(1, len(bonoA)):
precio += flujos[i] * (1+etti[i-1])**-i
return precio
# Precios de los Bonos A y B
precioA = precio_etti(bonoA, etti)
precioB = precio_etti(bonoB, etti)
print(f"El precio del bono A es {precioA}")
print(f"El precio del bono B es {precioB}")
# Flujos de caja de los bonos A y B
bonoA[0] = -precioA
bonoB[0] = -precioB
# Calcular la TIR de los bonos
tirA = npf.irr(bonoA)
tirB = npf.irr(bonoB)
print(f"La TIR del bono A es: {tirA:.4%}")
print(f"La TIR del bono B es: {tirB:.4%}")
# Calcular la duración de Macaulay
def duracion(bono):
tir =npf.irr(bono)
precio = 0
numerador = 0
for i in range(1, len(bonoA)):
precio += bono[i] * (1+tir)**-i
numerador += i * bono[i] * (1+tir)**-i
return numerador / precio
print(f"La duration del bono A es {duracion(bonoA)}")
print(f"La duration del bono B es {duracion(bonoB)}")
# Nuevo precio del bono A en el mercado desequilibrado
precioAprima = npf.npv(tirB, bonoA) + precioA # sumamos el antiguo precio de A ya que va en la posición [0] del array con signo negativo
print(f"El nuevo precio del bono A en el mercado desequilibrado es {precioAprima}")
bonoAprima =[-precioAprima, bonoA[1], bonoA[2]]
# Bono C = B - 20A'
# bonoC = bonoB - 20 * bonoAprima # esto da error, necesitamos trabajar con ndarrays
bonoB_arr = np.array(bonoB)
bonoAprima_arr =np.array(bonoAprima)
bonoC_arr = bonoB_arr - 20 * bonoAprima_arr
print(f"El bono C es {bonoC_arr}")
# Ajustamos con un préstamo
prestamo = np.array([bonoC_arr[2] / (1+etti[1])**2, 0, -bonoC_arr[2]])
print(f"El préstamo es {prestamo}")
# Cartera Total
total = bonoC_arr + prestamo
print(f"Cartera resultante del arbitraje {total}")
El precio del bono A es 710.9126984126985 El precio del bono B es 1023.809523809524 La TIR del bono A es: 19.8989% La TIR del bono B es: 18.4704% La duration del bono A es 1.988268094271526 La duration del bono B es 1.8351074725250018 El nuevo precio del bono A en el mercado desequilibrado es 728.0583383461574 El bono C es [ 13537.35724311 0. -19000. ] El préstamo es [-13194.44444444 0. 19000. ] Cartera resultante del arbitraje [342.91279867 0. 0. ]
En el mercado están disponibles los siguientes bonos:
Se pide:
import numpy_financial as npf
import numpy as np
############## MÉTODO 1 ##############
# DATOS
bonoA = np.array([-933, 1000, 0])
bonoB = np.array([-98, 7, 107]) # ver los flujos de caja del archivo Excel
# Bono C
bonoC = np.array([0,0,0]) # inicializamos el ndarray
bonoC = bonoA[0]*bonoB - bonoB[0]*bonoA
print(f"Bono C: {bonoC}")
# Forward r12
r12 = npf.irr(bonoC)
print(f"MÉTODO 1: el forward r12 es {r12:.4%}")
############## MÉTODO 2 ##############
# Bono D
bonoD = np.array([0,0,0]) # inicializamos el ndarray
bonoD = bonoA[1]*bonoB - bonoB[1]*bonoA
print(f"Bono D: {bonoD}")
# ETTI a dos años r02
r02 = npf.irr(bonoD)
print(f"La ETTI a dos años es r02 = {r02:.4%}")
# TIR del Bono A (r01)
r01 = npf.irr(bonoA)
print(f"La TIR del bono A es r01 = {r01:.4%}")
############## COMPROBACIÓN ##############
# Comprobación r12 = r12_bis
r12_bis = (1+r02)**2 / (1+r01) -1
print(f"MÉTODO 2: el forward r12 es {r12_bis:.4%}")
print(f"La diferencia de r12 por ambos métodos es {abs(r12 - r12_bis)}")
Bono C: [ 0 91469 -99831] MÉTODO 1: el forward r12 es 9.1419% Bono D: [-91469 0 107000] La ETTI a dos años es r02 = 8.1571% La TIR del bono A es r01 = 7.1811% MÉTODO 2: el forward r12 es 9.1419% La diferencia de r12 por ambos métodos es 0.0
En un mercado de renta fija cotizan los siguienes bonos:
Se pide:
import numpy_financial as npf
import numpy as np
# DATOS
bonoA = np.array([-100, 110, 0, 0])
bonoB = np.array([-500, 0, 500*1.09**2, 0]) # la TIR del bono B es del 9%
bonoC = np.array([-1000/1.08**3, 0, 0, 1000])
print(f"Bono A: {bonoA}")
print(f"Bono B: {bonoB}")
print(f"Bono C: {bonoC}")
############## MÉTODO 1 ##############
bonoD = np.ndarray([0,0,0,0])
bonoD = -bonoC[0] * bonoB + bonoB[0] * bonoC
print(f"Bono D: {bonoD}")
tirD = npf.irr(bonoD)
print(f"Método 1: el forward r23 es {tirD:.5%}")
############## MÉTODO 2 ##############
r23 = (1+0.08)**3 / (1+0.09)**2 -1
print(f"Método 2: el forward r23 es {r23:.5%}")
Bono A: [-100 110 0 0] Bono B: [-500. 0. 594.05 0. ] Bono C: [-793.83224102 0. 0. 1000. ] Bono D: [ 0. 0. 471576.04277803 -500000. ] Método 1: el forward r23 es 6.02744% Método 2: el forward r23 es 6.02744%