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))
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.