4 votos

Usando QuantLib Python para valorar opciones de FX utilizando volatilidad estocástica

Me gustaría usar QuantLib (y en particular el envoltorio de python) para valorar opciones de divisas utilizando el modelo de Heston. Gracias a http://gouthamanbalaraman.com y todos los artículos que contiene: en particular http://gouthamanbalaraman.com/blog/valuing-european-option-heston-model-quantLib.html para valorar opciones de acciones a través de heston y al revisar el código C++, he podido valorar opciones de divisas utilizando el modelo GKM. Agradecería incluso una pista sobre la área correcta del código C++ ya que incluso con esto, debería ser capaz de deducir las llamadas correctas en python.

Podría ser que no hay una solución en forma cerrada y uno necesite recurrir a Monte Carlo, aunque el artículo : https://arxiv.org/pdf/1010.1617.pdf parece sugerir lo contrario (en particular la página 5).

¡Saludos!

0 votos

David ¿Cómo configuras estos números en flat forward? flat_ts = ql.YieldTermStructureHandle(ql.FlatForward(2, ql.NullCalendar(), 0.015, ql.Actual365NoLeap())) dividend_ts = ql.YieldTermStructureHandle(ql.FlatForward(2, ql.NullCalendar(), -0.0065, ql.Actual365NoLeap())) Sé que es la comilla, pero ¿cómo eliges la comilla correcta para cada par de divisas? Gracias.

3voto

Chris Mc Puntos 31

El HestonModelHelper en QuantLib espera un valor de spot, strike y BlackVol.

En teoría, podrías convertir el strike de tus opciones FX (que normalmente se cotizan en términos de Delta) en un strike absoluto (Consulta este post para más detalles), y luego calibrar el modelo como si los instrumentos fueran opciones sobre una acción donde la tasa extranjera sería el dividendo.

He preparado un ejemplo rápido usando opciones de 6M en el EURUSD (obviamente debería mejorarse porque el ajuste no es especialmente bueno...).

import QuantLib as ql
import pandas as pd

flat_ts = ql.YieldTermStructureHandle(
    ql.FlatForward(2, ql.NullCalendar(), 0.015, ql.Actual365NoLeap())
)
dividend_ts = ql.YieldTermStructureHandle(
    ql.FlatForward(2, ql.NullCalendar(), -0.0065, ql.Actual365NoLeap())
)
spot = 1.08417

# parámetros ficticios
v0 = 0.01; kappa = 0.2; theta = 0.02; rho = -0.75; sigma = 0.5;

process = ql.HestonProcess(flat_ts, dividend_ts, 
                           ql.QuoteHandle(ql.SimpleQuote(spot)), 
                           v0, kappa, theta, sigma, rho)
model = ql.HestonModel(process)
engine = ql.AnalyticHestonEngine(model)

heston_helpers = []

data = [
    [1.0953, 4.89],
    [1.111, 4.97],
    [1.1233, 5.12],
    [1.1404, 5.39],
    [1.1533, 5.595],
    [1.1745, 5.923]
]

tenor = ql.Period('6M')
for strike, vol in data:
    helper = ql.HestonModelHelper(tenor, ql.TARGET(), spot,
                                  strike, ql.QuoteHandle(ql.SimpleQuote(vol / 100)), flat_ts, dividend_ts )
    helper.setPricingEngine(engine)
    heston_helpers.append(helper)

lm = ql.LevenbergMarquardt(1e-8, 1e-8, 1e-8)
model.calibrate(heston_helpers, lm,  ql.EndCriteria(500, 50, 1.0e-8,1.0e-8, 1.0e-8))
theta, kappa, sigma, rho, v0 = model.params()

print(f"theta = {theta:.4f}, kappa = {kappa:.4f}, sigma = {sigma:.4f}, rho = {rho:.4f}, v0 = {v0:.4f}")

avg = 0.0

summary = []
for i, opt in enumerate(heston_helpers):
    err = (opt.modelValue()/opt.marketValue() - 1.0)
    summary.append((
        data[i][0], opt.marketValue(), 
        opt.modelValue(), 
        100.0*(opt.modelValue()/opt.marketValue() - 1.0)))
    avg += abs(err)
avg = avg*100.0/len(heston_helpers)

print("Error Abs Promedio (%): %5.3f" % (avg))
df = pd.DataFrame(
    summary,
    columns=["Strikes", "Valor de Mercado", "Valor del Modelo", "Error Relativo (%)"],
    index=['']*len(summary))

introducir descripción de la imagen aquí

df.set_index('Strikes')[['Valor de Mercado', 'Valor del Modelo']].plot(marker='o')

introducir descripción de la imagen aquí

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