lballabio / QuantLib

The QuantLib C++ library
http://quantlib.org
Other
5.19k stars 1.77k forks source link

Pricing options with holidays #2035

Open bgladwyn-pm opened 1 month ago

bgladwyn-pm commented 1 month ago

Hi, I'm trying to use Quant lib to price FX options. For some dates, when the settlement date is 2 days after the evaluation, and delivery two days after the expiry I get perfect agreement between bloomberg OVML and quant lib. However, if there is a weekened/holiday between expiry/delivery or evaluation/settlement then there is no longer agreement. How do I get this behavior in quantlib so I can accurately price options?

`import QuantLib as ql import numpy as np

evaluationDate = ql.Date(13, 2, 2018) settlementDate = evaluationDate + ql.Period(2, ql.Days) # T+2 = Date(15, Feb, 2018) expirationDate = ql.Date(13, 2, 2019) # Date(15, Feb, 2019) deliveryDate = expirationDate + ql.Period(2, ql.Days) # Date(19, Feb, 2019) numberofdays=expirationDate-settlementDate print(numberofdays)

Parameters

S = 100 K = 105 f = 0.05 # Foreign rate (EUR in EURUSD) r = 0.02 # Domestic rate (USD in EURUSD) vol = 0.2

calendar = ql.UnitedStates(ql.UnitedStates.NYSE) dayCounter = ql.Actual365Fixed() exerciseType = ql.Exercise.European result = 4.6205 tol = 1e-3 # tolerance optionType = ql.Option.Call compounding = ql.Compounded compoundingFrequency = ql.Annual

Set the evaluation date

ql.Settings.instance().evaluationDate = evaluationDate

Option data

exercise = ql.EuropeanExercise(expirationDate) underlyingH = ql.QuoteHandle(ql.SimpleQuote(S))

rTS = ql.YieldTermStructureHandle(ql.FlatForward(evaluationDate, r365/360, dayCounter, compounding, compoundingFrequency)) fTS = ql.YieldTermStructureHandle(ql.FlatForward(evaluationDate, f365/360, dayCounter, compounding, compoundingFrequency)) flatVolTS = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(evaluationDate, calendar, vol, dayCounter))

print(f'Fwd matching bloomberg {1.30(1+r365/360)/(1+f365/360)}') print(f'Forward rate {1.30(1+r)/(1+f)}')

payoff = ql.PlainVanillaPayoff(optionType, K) process = ql.GarmanKohlagenProcess(underlyingH, fTS, rTS, flatVolTS)

option = ql.VanillaOption(payoff, exercise) engine = ql.AnalyticEuropeanEngine(process) option.setPricingEngine(engine)

Calculate option price

calculated = option.NPV()

Print results

expected = 4.613072 error=(calculated-expected)/expected print(f"Calculated value = {calculated:.5f}, Expected value = {expected:.5f}, Error = {error*100:.8f}%")`

The output matches Bloomberg: 363 Fwd matching bloomberg 1.2623661599471252 Forward rate 1.262857142857143 Calculated value = 4.61307, Expected value = 4.61307, Error = 0.00000739%

But shifting the expiry to 02/15/19 gives: 365 Fwd matching bloomberg 1.2623661599471252 Forward rate 1.262857142857143 Calculated value = 4.62657, Expected value = 4.62016, Error = 0.13883435% which no longer matches.

Is there a way to consider the correct dates in this calculation? Thanks for the help! :)

image image

boring-cyborg[bot] commented 1 month ago

Thanks for posting! It might take a while before we look at your issue, so don't worry if there seems to be no feedback. We'll get to it.