4 votos

Valoración de los préstamos estructurados en QuantLib

Intento averiguar si es posible valorar los productos estructurados, sobre todo los préstamos, en cantidad. La idea es construir una clase de bonos con diferentes flujos de efectivo. Por ejemplo, un préstamo podría tener cupones que sólo paguen intereses, podría ser sólo amortizable o incluso los cupones pueden aumentar la cantidad nominal.

También, para propósitos de FTP, uno podría estar interesado en el rendimiento que devuelve el valor nominal del préstamo. ¿Hay una función nativa para eso?

¿Es posible hacer esto con la versión actual en Python o en C++?

Para ser más precisos, imagina estos dos ejemplos:

Primero, tengo un préstamo que se emite con un valor nominal de 2000 y tiene dos amortizaciones de 1000: enter image description here

He tenido éxito construyendo los flujos de efectivo con la clase de bonos con un simple código:

def bond_from_table(df,notional, outlay_date, day_counter, r=0.03):
    eval_date = ql.Settings.instance().evaluationDate
    coupons = []
    redemptions = []
    redem = 0
    for i in range(df.shape[0]):
        start_date = df.at[i,'Start']
        end_date = df.at[i,'End']
        notional -= redem        
        redem = df.at[i,'Capital']
        redemptions.append(ql.Redemption(redem,end_date))    
    coupons.append(ql.FixedRateCoupon(end_date,notional,r,day_counter,start_date,end_date))    

    leg = ql.Leg(coupons)
    loan = ql.Bond(0,calendar,eval_date,leg)
    return loan

Como parece que la clase de bonos construye las redenciones de los nominales que se están pagando. Lo bueno es que usando la clase de bonos podemos acceder a todas las funciones de los bonos y otras cosas como la capacidad de rescate y así sucesivamente.

¡Pero! si trato de construir algo como esto:

enter image description here

Si después del pago inicial de 2000 al cliente, se pagan otros 400 en las próximas fechas (por eso el signo -) -al final, se recibe el total del préstamo- me daría un error diciendo que el nominal está aumentando, lo cual es cierto. Supongo que puede haber otra ruta para esto, pero creo que perderé las funcionalidades de la clase de bonos.

¿Alguna idea? Gracias de antemano,

1 votos

FTP = Precios de Transferencia de Fondos ?

0 votos

Sí, a efectos de ALM

1 votos

Podría ser útil que enumeraras las características de los préstamos estructurados que quieres utilizar para proyectar los flujos de caja. Por ejemplo, recuerdo un préstamo indonesio en el que en cada reajuste el emisor tenía la opción de pagar libor a 3 meses + diferencial en 3 meses, o libor a 6 meses + diferencial y reajuste en 6 meses. Eso no es lo que querías decir, ¿verdad?

3voto

Brad Tutterow Puntos 5628

Como has comprobado, el Bond asume que está comprando un bono; es decir, que está pagando un nocional por adelantado y que le será devuelto en una solución o en una serie de pagos amortizables. El supuesto se codifica aquí y podrías intentar eliminarlo y volver a compilar, pero no te lo aconsejo. Puede ser que otras partes del código dependan de que esa comprobación haya pasado con éxito.

Lo que se podría hacer en su lugar es tomar ambos conjuntos de flujos de caja ( coupons y redemptions ), los junta, los ordena y considera el tramo resultante como su préstamo. Probablemente no le dará toda la funcionalidad del bono (por ejemplo, el recálculo perezoso cuando algo cambia) pero podrá analizarlos utilizando los métodos del CashFlows clase. Esto le dará el VAN, el BPS, el rendimiento y medidas como la duración, así como alguna información estática. Desafortunadamente, no le permitirá acceder a características como la capacidad de compra, que están codificadas dentro de alguna clase específica de bonos.

0 votos

Hola Luigi, gracias por la respuesta. He hecho lo que mencionas (eliminar el supuesto y recompilar la librería) y parece que todo funciona correctamente, incluso el nocional se va incrementando a medida que pasa el tiempo. ¿Podrías indicarme un ejemplo de lo que podría estar en conflicto con eso? Además, ¿qué opinas de crear una clase para este tipo de instrumentos en la librería base, merecería la pena?

1 votos

Estaba pensando en la normalización del precio al nocional actual, pero debería funcionar, así que más que nada estaba siendo prudente :) Un instrumento separado tendría sentido, sin embargo. De esta manera podríamos hacer diferentes supuestos explícitamente.

0 votos

Gracias de nuevo, le echaré un vistazo a ver cómo se puede implementar. Una última pregunta, normalmente en los préstamos uno se interesa por el tipo de cupón que devuelve cierto VAN, en lugar de la TIR habitual. Creo que una forma fácil de hacer este cálculo es iterar sobre el tipo de cupón y crear una instancia de la clase bono/préstamo cada vez hasta alcanzar el VAN, pero me parece un poco sucio. Estuve mirando la clase InterestRate y el atributo rate es privado y aunque lo cambie no creo que eso actualice los flujos de caja del bono. ¿Hay alguna clase que permita hacer algo así?

1voto

Chris Mc Puntos 31

Para añadir una posible solución a tu último comentario: "normalmente en los préstamos uno se interesa por el tipo de cupón que devuelve cierto VAN, en lugar de la habitual TIR"...

En python podrías extender la clase Bond con tu propia función para obtener el cupón con álgebra matricial.

$$PV_{Loan} = PV_{amortizaitons} + PV_{coupons}$$

Descomponer el PV del cupón por vectores:

$$PV_{Loan} = PV_{amortizaitons} + \big[ notionals \times dcf \times coupon \times dfs \big]$$

Así que básicamente puedes resolver el cupón por:

$$coupon = (PV_{Loan} - PV_{amortizaitons}) / \big[ notionals \times dcf \times dfs\big]$$

Este es un ejemplo que probablemente podría mejorarse, pero espero que te hagas una idea...

crv = ql.FlatForward(ql.Date(16,9,2019), 0.025, ql.Actual360())

fixedRate = 0.03
notionals = [2000] * 9 + [1000] * 10

class Loan(ql.AmortizingFixedRateBond):
    def couponRate(self, pv):
        notional_flows = np.array([cf.amount() for cf in self.redemptions()])
        notional_dfs = np.array([crv.discount(cf.date()) for cf in self.redemptions()])
        notional_pv =  pv - (notional_flows * notional_dfs).sum()
        dates = list(schedule)
        dcf = np.array([ql.Actual360().yearFraction(d0, d1) for d0,d1 in zip(dates[:-1], dates[1:])])
        dfs = np.array([crv.discount(dt) for dt in dates[1:]])
        return notional_pv / (dcf * dfs * notionals).sum()

schedule = ql.MakeSchedule(ql.Date(16,9,2019), ql.Date(30,4,2021), ql.Period('1m'), firstDate=ql.Date(31,10,2019), endOfMonth=True)       
loan = Loan(2, notionals, schedule, [fixedRate], ql.Actual360(), ql.ModifiedFollowing)

loan.couponRate(2000)

0 votos

Gracias David, es una buena idea, pero creo -no estoy seguro- que el método atmRate() hace lo mismo.

0 votos

¡Tienes razón! ql.BondFunctions.atmRate(bond, crv, date, cleanPrice) haría lo mismo. Inicialmente pensé que sólo resolvería para par. Entonces, ¿qué es exactamente lo que estabas buscando que Luigi respondió "No, no hay"

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