1
$\begingroup$

I am trying to find price of Continuous Geometric Average Asian Option using Finite Difference methodology in QuantLib Python. I am unable to do so. However, I am able to find price of the same option using closed form solution. Here is the code:

import QuantLib as ql
today = ql.Settings.instance().evaluationDate

averageType = ql.Average.Geometric
option_type = ql.Option.Call

strike = 100.0
exerciseDate = ql.TARGET().advance(today, 90, ql.Days)

payoff = ql.PlainVanillaPayoff(option_type, strike)
exercise = ql.EuropeanExercise(exerciseDate)
option = ql.ContinuousAveragingAsianOption(averageType, payoff, exercise)

initialValue = ql.QuoteHandle(ql.SimpleQuote(100))
sigma = 0.3
riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.03, ql.Actual365Fixed()))
volTS = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(today, ql.NullCalendar(), sigma, ql.Actual365Fixed()))
stochProcess = ql.BlackScholesProcess(initialValue, riskFreeTS, volTS)

engine = ql.AnalyticContinuousGeometricAveragePriceAsianEngine(stochProcess)

option.setPricingEngine(engine)
price = option.NPV()

print(f"Option price: {price}")

Any help/advice would be greatly appreciated!!

$\endgroup$

2 Answers 2

3
$\begingroup$

QuantLib does have an FD pricing engine for asian options ql.FdBlackScholesAsianEngine(stochProcess, tGrid=100, xGrid=100, aGrid=50), but I've just discovered it only prices Discrete, Arithmetic payoffs!

Moving from Continuous to Discrete (documented here) doesn't change the price of the option much, if you pass in something like asianFixingDates = [ql.TARGET().advance(today, x, ql.Days) for x in range(1,91)] which samples every day. Of course, this is a bit unrealistic, but it's good that we recover the continuous price from the analytic pricer in that limit (I get 4.187 vs. 4.184 from the original code when I make this change).

Unfortunately, running the FD pricer on this option gives me this error: RuntimeError: Arithmetic averaging supported only

Moving to an arithmetic averaging option does impact pricing significantly. However, in case that is of any use to you, I've included the changes required to your code at the bottom of this answer (changing the averaging to ql.Average.Arithmetic, and using a discrete option)

As an alternative if you need a numerical solver, you might consider ql.MCDiscreteGeometricAPEngine (documented here) which uses Monte Carlo instead to price the geometric option. You'll still need to price the discrete averaging option, but the price comes out very close to the analytic solution using something like this:

rng = "lowdiscrepancy" # could use "pseudorandom"
numPaths = 100000

engine = ql.MCDiscreteGeometricAPEngine(stochProcess, rng, requiredSamples=numPaths)

option.setPricingEngine(engine)
price = option.NPV()

print(f"Option price: {price}")

Price a discrete-averaging arithmetic asian using FD in QL:

import QuantLib as ql
today = ql.Settings.instance().evaluationDate

averageType = ql.Average.Arithmetic
option_type = ql.Option.Call

strike = 100.0
exerciseDate = ql.TARGET().advance(today, 90, ql.Days)

pastFixings = 0 # Empty because this is a new contract
asianFixingDates = [ql.TARGET().advance(today, x, ql.Days) for x in range(1,91)]

payoff = ql.PlainVanillaPayoff(option_type, strike)
exercise = ql.EuropeanExercise(exerciseDate)
option = ql.DiscreteAveragingAsianOption(averageType, 0.0, pastFixings, asianFixingDates, payoff, exercise)

initialValue = ql.QuoteHandle(ql.SimpleQuote(100))
sigma = 0.3
riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.03, ql.Actual365Fixed()))
volTS = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(today, ql.NullCalendar(), sigma, ql.Actual365Fixed()))
stochProcess = ql.BlackScholesProcess(initialValue, riskFreeTS, volTS)

engine = ql.FdBlackScholesAsianEngine(stochProcess, tGrid=100, xGrid=100, aGrid=50)

option.setPricingEngine(engine)
price = option.NPV()

print(f"Option price: {price}")
$\endgroup$
1
  • $\begingroup$ Yep. That's the issue with QuantLib that FdBlackScholesAsianEngine prices Discrete American Payoffs. Thanks StackG for the valuable response! $\endgroup$
    – Desi_Quant
    Sep 3, 2020 at 4:37
1
$\begingroup$

Using MC Simulation, if I am trying to price Geometric Average Asian Option by running the following code:

import QuantLib as ql
today = ql.Settings.instance().evaluationDate

averageType = ql.Average.Geometric
option_type = ql.Option.Call

strike = 100.0
exerciseDate = ql.TARGET().advance(today, 90, ql.Days)

pastFixings = 0 # Empty because this is a new contract
asianFixingDates = [ql.TARGET().advance(today, x, ql.Days) for x in range(1,91)]

payoff = ql.PlainVanillaPayoff(option_type, strike)
exercise = ql.EuropeanExercise(exerciseDate)
option = ql.DiscreteAveragingAsianOption(averageType, 0.0, pastFixings, asianFixingDates, payoff, exercise)

initialValue = ql.QuoteHandle(ql.SimpleQuote(100))
sigma = 0.3
riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.03, ql.Actual365Fixed()))
volTS = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(today, ql.NullCalendar(), sigma, ql.Actual365Fixed()))
process = ql.BlackScholesProcess(initialValue, riskFreeTS, volTS)
steps = 2

rng = "lowdiscrepancy" 
numPaths = 500000
engine = ql.MCDiscreteGeometricAPEngine(process, rng, steps, requiredSamples=numPaths)

option.setPricingEngine(engine)
price = option.NPV()

print(f"Option price: {price}")

I am getting the following error:

runfile('C:/Users/nitin.kapai/Documents/Exam_v1/QuantLib Code/Asian_Discrete_Geometric_MC_Simulation_QuantLib.py', wdir='C:/Users/nitin.kapai/Documents/Exam_v1/QuantLib Code')
Traceback (most recent call last):

  File "C:\Users\nitin.kapai\Documents\Exam_v1\QuantLib Code\Asian_Discrete_Geometric_MC_Simulation_QuantLib.py", line 34, in <module>
    engine = ql.MCDiscreteGeometricAPEngine(process, rng, steps, requiredSamples=numPaths)

  File "C:\Users\nitin.kapai\Anaconda3\lib\site-packages\QuantLib\QuantLib.py", line 12996, in MCDiscreteGeometricAPEngine
    seed)

  File "C:\Users\nitin.kapai\Anaconda3\lib\site-packages\QuantLib\QuantLib.py", line 12968, in __init__
    _QuantLib.MCLDDiscreteGeometricAPEngine_swiginit(self, _QuantLib.new_MCLDDiscreteGeometricAPEngine(*args, **kwargs))

TypeError: in method 'new_MCLDDiscreteGeometricAPEngine', argument 2 of type 'bool'
```
$\endgroup$
3
  • $\begingroup$ Two bugs... 1) you've defined a geometric average option so you need a 1.0 in the running sum instead of a 0.0. 2) Take the steps argument out of the ql.MCDiscreteGeometricAPEngine. When I do these two things the code runs successfully and gives me 4.210 $\endgroup$
    – StackG
    Sep 3, 2020 at 7:14
  • $\begingroup$ Thanks again StackG!!.I have lately started working on pricing exotics using different numerical techniques. Just trying to learn as much as I can. $\endgroup$
    – Desi_Quant
    Sep 3, 2020 at 8:07
  • $\begingroup$ You're welcome, good luck with your studies! $\endgroup$
    – StackG
    Sep 3, 2020 at 9:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.