1 votos

Macaulay o duración modificada en Python

Me preguntaba si hay alguna función en Python que calcule la duración de Macaulay o modificada cuando el tiempo hasta el vencimiento no es un número entero, por ejemplo cuando el tiempo hasta el vencimiento es de 1514 días y necesitas una respuesta precisa para la duración de Macaulay o modificada.

Tal vez alguien pueda compartir su código.

Gracias

1 votos

Algunas personas aquí usan Quantlib Python. Es muy general y, por lo tanto, algo complejo, pero puede proporcionar cálculos exactos según los estándares de la industria.

1voto

Chris Mc Puntos 31

No hay una función interna en python para obtener la duración (como en Excel por ejemplo), aunque no es tan difícil de programar. Básicamente necesitas días contados, tasas y factores de descuento.

Como mencionó noob2, es posible obtenerlo con QuantLib aunque hay una curva de aprendizaje hasta que te sientas cómodo construyendo los objetos necesarios.

Aquí hay un ejemplo simple que podría ayudarte a empezar:

import QuantLib as ql

días = 1514
cupón = 0.028
yld = 0.000054

inicio = ql.Date().todaysDate()
madurez = inicio + ql.Period(días, ql.Days)

bono = ql.FixedRateBond(2, ql.TARGET(), 1000, inicio, madurez, ql.Period('1Y'), [cupón], ql.ActualActual())
tasa = ql.InterestRate(yld, ql.ActualActual(), ql.Compounded, ql.Annual)
duracion_simple = ql.BondFunctions.duration(bono, tasa, ql.Duration.Simple)
duracion_modificada = ql.BondFunctions.duration(bono, tasa, ql.Duration.Modified)
duracion_macaulay = ql.BondFunctions.duration(bono, tasa, ql.Duration.Macaulay)
print(duracion_macaulay, duracion_modificada)

Lo cual mostraría: 3.9742030045989956 3.9739884092248974

O dependiendo de cuán precisa quieras ser, podrías definir tu propia función en python:

cupón = 0.028
yld = 0.000054

def duraciones(c, y, m, n):
    duración_macaulay = ((1+y) / (m*y)) - ( (1 + y + n*(c-y)) / ((m*c* ((1+y)**n - 1)) + m*y) )
    duración_modificada = duración_macaulay / (1 + y)
    return duración_macaulay, duración_modificada

print( duraciones(cupón, yld, 1, días/365) )

Lo cual mostraría: (3.98414223634245, 3.983927104278819)

0 votos

Entonces, por ejemplo, si tengo un bono que vence en 1514 días, Valor nominal = 1000, Rendimiento = 0,0054%, Tasa de cupón = 2,8%, Cupones pagados anualmente, Precio = 1092.50. ¿Cómo debería verse el código para calcular la duración modificada? Lo siento por preguntar, pero realmente no estudié programación, pero estoy intentando calcular la Duración modificada para mi tesis, porque Excel no calcula la Duración modificada con rendimiento negativo. ¡Gracias :)

0 votos

¿Estás seguro de que el rendimiento es correcto? Porque según los detalles de ese bono y el precio, el rendimiento debería ser negativo.

0 votos

El ejemplo que estoy tratando de resolver es: settlment= 2016-03-30 maturity=2020-05-22 rate=2.8% pr=1092.5034 redemption=1000 frequency=1 Cuando utilizo la función de Excel YIELD obtengo respuesta 0.005369 Cuando utilizo la función MDURATION en el mismo bono obtengo una respuesta 3.874379 Pero cuando uso tu código e ingreso los mismos números obtengo la misma respuesta de duración modificada que usaste antes, aproximadamente 3.97. Lo cual es aproximadamente 0.1 diferente de la respuesta que obtengo de Excel. Tal vez sepas por qué estas dos respuestas son diferentes. Gracias de nuevo, solo tratando de entender cómo calcular la duración con rendimiento negativo.

0voto

Peter Puntos 216

Parte 1

Esto no es exactamente lo que pediste, pero es un paso intermedio para llegar allí. Es lo suficientemente fácil armar una función simple que calcula la duración de Macaulay de un conjunto de flujos de efectivo, tomando como inputs la tasa de vp, los montos de los flujos de efectivo y los períodos, no las fechas, solo los períodos, es decir, período 1, 2, 3, etc.

A continuación, tengo un ejemplo de un bono que paga 6% dos veces al año (es decir, 3% cada 6 meses) y que cotiza al par.

Si pasa los períodos en años, es decir, el primer semestre es el período 0.5, entonces la tasa debe ser anualizada, por lo que el 6% se convierte en 6.09%. Si pasa el período en semestres, de modo que el primer semestre sea el período 1, la tasa debe ser del 3%, y el resultado final debe dividirse por dos porque queremos la duración como una medida promedio ponderada del tiempo en años.

def mac_duration(periods, cash, pv_rate):

    periods= np.float64(periods)
    cash = np.float64(cash)
    pv = np.zeros(cash.size)

    for i in range(cash.size):
        pv[i] = cash[i] / ( 1 + pv_rate)** periods[i]
    sum_pv = np.sum(pv)

    return np.dot(pv/sum_pv, periods)

def mod_duration(periods, cash, my_rate):
    return mac_duration(periods, cash, my_rate) / (1 + my_rate)    

cash = [30,30,30,30,30,1030]
rate = 6e-2
rate_sem = rate/2
rate_annual = (1 + rate_sem)**2 - 1
periods_sem = np.arange(1,7)
periods_years = periods_sem / 2

mac_dur_ann = mac_duration( periods_years, cash, rate_annual)
mac_dur_sem = mac_duration( periods_sem, cash, rate_sem) / 2

Parte 2

Ahora todo lo que necesitas es una función que calcule el conteo de días entre fechas, convierta eso en fracciones de año y pase el resultado a la función anterior.

Notarás que el resultado es ligeramente diferente ahora que estamos usando act/365, porque los pagos ya no suceden exactamente a la mitad del año (el 1 de julio está a 181 días después del 1 de enero, y 181 != 365/2). Si lo calculas en un bono que paga solo una vez al año (ver cash2), obtienes el mismo resultado, si no hay cálculo en un año bisiesto.

Además, ahora necesitas especificar desde qué día comenzar a contar los días. Con la otra función, esto era implícito en los períodos, es decir, el período 1 era, bueno, 1 período desde el punto de inicio.

def mac_duration_dates(dates, cash, pv_rate, day_0, day_count = 365):

    yearfrac = ([ (d - day_0).days / day_count for d in dates])
    out = mac_duration( yearfrac, cash, pv_rate )
    return out

day_0 = pd.to_datetime(date(2013,1,1))
df = pd.DataFrame() 
df['month'] = np.arange(6,42,6)
df['dates'] = df.apply(lambda x: day_0 + pd.DateOffset(months = x['month']), axis = 1)
mac_dur_with_dates = mac_duration_dates(df['dates'], cash, rate_annual, day_0 = day_0, day_count = 365)

cash2 = [0,60,0,60,0,1060]
mac_dur2 = mac_duration( periods_years, cash2, 6e-2)
mac_dur_with_dates2 = mac_duration_dates(df['dates'], cash2, 6e-2, day_0 = day_0, day_count = 365)
print( np.isclose(mac_dur_with_dates2, mac_dur2))

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X