1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
"""
Copyright (C) 2019 Klaus Spanderen
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
<quantlib-dev@lists.sf.net>. The license is also available online at
<https://www.quantlib.org/license.shtml>.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
"""
import math
import unittest
import QuantLib as ql
class SabrTest(unittest.TestCase):
def testHagenFormula(self):
""" Testing Hagen et al. formula """
today = ql.Date(9,1,2019)
dc = ql.Actual365Fixed()
maturityDate = today + ql.Period(6, ql.Months)
maturityTime = dc.yearFraction(today, maturityDate)
alpha = 0.35
beta = 0.85
nu = 0.75
rho = 0.85
f0 = 100.0
strike = 110.0
sabrVol = ql.sabrVolatility(strike, f0, maturityTime, alpha, beta, nu, rho)
self.assertAlmostEqual(sabrVol, 0.205953, 6,
msg="Unable to reproduce Hagen et al. SABR volatility")
flochKennedyVol = ql.sabrFlochKennedyVolatility(
strike, f0, maturityTime, alpha, beta, nu, rho)
self.assertAlmostEqual(flochKennedyVol, 0.205447, 6,
msg="Unable to reproduce Le Floc'h-Kennedy SABR volatility")
def testPdeSolver(self):
""" Testing BENCHOP-SLV SABR example value """
today = ql.Date(8, 1, 2019)
dc = ql.Actual365Fixed()
maturityDate = today + ql.Period(10 * 365, ql.Days)
maturityTime = dc.yearFraction(today, maturityDate)
f0 = 0.07
alpha = 0.4
nu = 0.8
beta = 0.5
rho = -0.6
strike = f0 * math.exp(-0.1 * math.sqrt(maturityTime))
rTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.0, dc))
# see https://ir.cwi.nl/pub/28249
expected = 0.052450313614407
option = ql.VanillaOption(
ql.PlainVanillaPayoff(ql.Option.Call, strike),
ql.EuropeanExercise(maturityDate))
option.setPricingEngine(ql.FdSabrVanillaEngine(f0, alpha, beta, nu, rho, rTS, 30, 800, 30, 1, 0.8))
calculated = option.NPV()
self.assertAlmostEqual(calculated, expected, 4,
msg="Unable to reproduce Le Floc'h-Kennedy SABR volatility")
def testSabrPdeVsCevPdeVsAnalyticCev(self):
""" Testing SABR PDE vs CEV PDE vs Analytic CEV """
today = ql.Date(1, 3, 2019)
dc = ql.Actual365Fixed()
maturityDate = today + ql.Period(12, ql.Months)
f0 = 1.2
alpha = 0.35
beta = 0.9
nu = 1e-3
rho = 0.25
strike = 1.1
rTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.05, dc))
option = ql.VanillaOption(
ql.PlainVanillaPayoff(ql.Option.Call, strike),
ql.EuropeanExercise(maturityDate))
option.setPricingEngine(ql.FdSabrVanillaEngine(f0, alpha, beta, nu, rho, rTS, 30, 400, 3))
fdSabrNPV = option.NPV()
option.setPricingEngine(ql.FdCEVVanillaEngine(f0, alpha, beta, rTS, 30, 400))
fdCevNPV = option.NPV()
option.setPricingEngine(ql.AnalyticCEVEngine(f0, alpha, beta, rTS))
analyticCevNPV = option.NPV()
self.assertAlmostEqual(fdSabrNPV, analyticCevNPV, 4,
msg="Unable to match PDE SABR value with analytic CEV value")
self.assertAlmostEqual(fdCevNPV, analyticCevNPV, 4,
msg="Unable to match PDE CEV value with analytic CEV value")
if __name__ == '__main__':
print("testing QuantLib", ql.__version__)
unittest.main(verbosity=2)
|