QuantLib
QuantLib copied to clipboard
Pricing options with holidays
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! :)
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.
This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.
Hmm—the option is not using settlementDate or deliveryDate for pricing, so there should probably be some correction for that. The option discounts to the evaluation date, and I guess we should discount to the settlement date instead, so something like
calculated /= rTS.discount(settlementDate)
should account for that. In the same way, we're discounting the payoff from the expiration date, not the delivery date, so we should also add
calculated *= rTS.discount(deliveryDate)/rTS.discount(expirationDate)
However, doing that makes the result only slightly better. There might be some other difference—for instance, I see in the second screenshot that r is no longer exactly 5%; is it possible that using that updated rate helps?
This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.
This issue was automatically closed because it has been stalled for two weeks with no further activity.