Estoy utilizando Quantlib en Python para fijar el precio de una opción FX. Estoy comparando el resultado con Bloomberg, para asegurarme de que el código está funcionando correctamente.
También quiero calcular todas las griegas, y eventualmente utilizarlas en una expansión de Taylor de las pérdidas y ganancias (como por ejemplo: P&L de la opción de compra con cobertura delta )
La opción que estoy tratando de valorar, tiene el siguiente precio en Bloomberg:
Es un ejemplo estilizado.
El código que utilizo es el siguiente:
import QuantLib as ql
Spot = 1.1
Strike = 1.101
Sigma = 10/100
Ccy1Rate = 5/100
Ccy2Rate = 10/100
OptionType = ql.Option.Call
#Option dates in quantlib objects
EvaluationDate = ql.Date(3, 1,2022)
SettlementDate = ql.Date(5, 1, 2022) #Evaluation +2
ExpiryDate = ql.Date(10, 1, 2022) #Evaluation + term which is 1 week
DeliveryDate = ql.Date(12, 1, 2022) #Expiry +2
NumberOfDaysBetween = ExpiryDate - EvaluationDate
#print(NumberOfDaysBetween)
#Generate continuous interest rates
EurRate = Ccy1Rate
UsdRate = Ccy2Rate
#Create QuoteHandle objects. Easily to adapt later on.
#You can only access SimpleQuote objects. When you use setvalue, you can change it.
#These global variables will then be used in pricing the option.
#Everything will be adaptable except for the strike.
SpotGlobal = ql.SimpleQuote(Spot)
SpotHandle = ql.QuoteHandle(SpotGlobal)
VolGlobal = ql.SimpleQuote(Sigma)
VolHandle = ql.QuoteHandle(VolGlobal)
UsdRateGlobal = ql.SimpleQuote(UsdRate)
UsdRateHandle = ql.QuoteHandle(UsdRateGlobal)
EurRateGlobal = ql.SimpleQuote(EurRate)
EurRateHandle = ql.QuoteHandle(EurRateGlobal)
#Settings such as calendar, evaluationdate; daycount
Calendar = ql.UnitedStates()
ql.Settings.instance().evaluationDate = EvaluationDate
DayCountRate = ql.Actual360()
DayCountVolatility = ql.ActualActual()
#Create rate curves, vol surface and GK process
RiskFreeRateEUR = ql.YieldTermStructureHandle(ql.FlatForward(0, Calendar, EurRateHandle, DayCountRate))
RiskFreeRateUSD = ql.YieldTermStructureHandle(ql.FlatForward(0, Calendar, UsdRate, DayCountRate))
Volatility = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(0, Calendar, VolHandle, DayCountVolatility))
GKProcess = ql.GarmanKohlagenProcess(SpotHandle, RiskFreeRateEUR, RiskFreeRateUSD, Volatility)
#Generate option
Payoff = ql.PlainVanillaPayoff(OptionType, Strike)
Exercise = ql.EuropeanExercise(ExpiryDate)
Option = ql.VanillaOption(Payoff, Exercise)
Option.setPricingEngine(ql.AnalyticEuropeanEngine(GKProcess))
BsPrice = Option.NPV()
ql.Settings.instance().evaluationDate = EvaluationDate
print("Premium is:", Option.NPV()*1000000/Spot)
print("Gamma is:", Option.gamma()*1000000*Spot/100)
print("Vega is:", Option.vega()*1000000*(1/100)/Spot)
print("Theta is:", Option.theta()*1000000*(1/365)/Spot)
print("Delta is:", Option.delta()*1000000)
Lo que da la siguiente salida:
Premium is: 5550.960519027888
Gamma is: 287777.2550015351
Vega is: 551.9015849344515
Theta is: -462.68771985750703
Delta is: 504102.4957777005
Coincide muy bien con Bloomberg, excepto con Theta. Intenté dividir por 255 (días laborables) en lugar de por 365, pero también está mal.
Me pregunto cuál es la respuesta correcta, ya que es necesaria para encontrar la expansión de Taylor P&L.
2 votos
Theta en BBG no es la fórmula de forma cerrada, sino un bache (un día menos hasta el vencimiento) porque así lo prefieren la mayoría de los usuarios.
0 votos
Gracias, y ya que estamos: ¿tienes idea de por qué el Delta tampoco coincide?
2 votos
Puede comprobarlo rápidamente fijando el precio un viernes, en cuyo caso la theta es aproximadamente 3 veces la del día anterior porque se adelanta al siguiente día de negociación. La diferencia en la delta se debe a que una es con prima incluida y la otra no. En OVML tienes un ajuste para esto. 504102 - 5550 = delta bbg
0 votos
La explicación del Delta es correcta. Sin embargo, para el theta encuentro -462,6877 (como en el ejemplo) y luego -493,8955 para el día siguiente y -534,006358 para el día siguiente. Por lo tanto, creo que ya tengo la solución para este problema. ¿Es correcto dividir por (1/365)? ¿Quizás sea necesario Sqrt(T) en alguna parte?
2 votos
Theta es todo lo demás igual. Por lo tanto, es necesario anular manualmente todas las entradas, también la delantera, lo que significa que no debe fijar ambos tipos. Si su objetivo principal es utilizar la lógica y los valores de Bloomberg Pricer, hay una API para OVML, y puede cargar grandes cantidades de operaciones en MARS para obtener una valoración completa, incluyendo todas las griegas en cualquier día que desee y no necesita obtener ningún dato de mercado, que es la parte más difícil en la fijación de precios de opciones vainilla.