|
1 | | -from __future__ import division |
2 | | -from __future__ import print_function |
3 | | - |
4 | 1 | import unittest |
5 | 2 |
|
6 | 3 | import numpy as np |
|
15 | 12 | from quantlib.instruments.vanillaoption import VanillaOption |
16 | 13 |
|
17 | 14 | from quantlib.time.api import (today, Years, Actual365Fixed, |
18 | | - Period, May, Date, |
| 15 | + Period, May, Date, Actual360, |
19 | 16 | NullCalendar) |
20 | 17 |
|
21 | 18 | from quantlib.processes.api import (BlackScholesMertonProcess, |
22 | 19 | HestonProcess, |
23 | | - HullWhiteProcess) |
| 20 | + HullWhiteProcess, |
| 21 | + HullWhiteForwardProcess, |
| 22 | + HybridHestonHullWhiteProcess) |
24 | 23 |
|
25 | 24 | from quantlib.models.equity.heston_model import ( |
26 | 25 | HestonModel) |
|
33 | 32 | AnalyticBSMHullWhiteEngine, |
34 | 33 | AnalyticHestonEngine, |
35 | 34 | AnalyticHestonHullWhiteEngine, |
36 | | - FdHestonHullWhiteVanillaEngine) |
| 35 | + FdHestonHullWhiteVanillaEngine, |
| 36 | + MCHestonHullWhiteEngine) |
37 | 37 |
|
38 | 38 | from quantlib.quotes import SimpleQuote |
39 | 39 |
|
|
43 | 43 |
|
44 | 44 | from .utilities import flat_rate |
45 | 45 |
|
| 46 | +from math import sin, exp |
46 | 47 |
|
47 | 48 | class HybridHestonHullWhiteProcessTestCase(unittest.TestCase): |
48 | 49 |
|
@@ -95,8 +96,8 @@ def setUp(self): |
95 | 96 | self.dates = dates |
96 | 97 |
|
97 | 98 | def test_bsm_hw(self): |
98 | | - print("Testing European option pricing for a BSM process" + |
99 | | - " with one-factor Hull-White model...") |
| 99 | + """Testing European option pricing for a BSM process |
| 100 | + with one-factor Hull-White model""" |
100 | 101 |
|
101 | 102 | dc = Actual365Fixed() |
102 | 103 | todays_date = today() |
@@ -247,12 +248,8 @@ def test_compare_bsm_bsmhw_hestonhw(self): |
247 | 248 | self.assertAlmostEqual(npv_bsm, npv_hestonhw, delta=tol) |
248 | 249 |
|
249 | 250 | def test_compare_BsmHW_HestonHW(self): |
250 | | - """ |
251 | | - From Quantlib test suite |
252 | | - """ |
253 | | - |
254 | | - print("Comparing European option pricing for a BSM " + |
255 | | - "process with one-factor Hull-White model...") |
| 251 | + """Comparing European option pricing for a BSM |
| 252 | + process with one-factor Hull-White model""" |
256 | 253 |
|
257 | 254 | dc = Actual365Fixed() |
258 | 255 |
|
@@ -355,8 +352,8 @@ def test_zanette(self): |
355 | 352 | # constant yield and div curves |
356 | 353 |
|
357 | 354 | dates = [todays_date + Period(i, Years) for i in range(3)] |
358 | | - rates = [0.04 for i in range(3)] |
359 | | - divRates = [0.03 for i in range(3)] |
| 355 | + rates = [0.04] * 3 |
| 356 | + divRates = [0.03] * 3 |
360 | 357 | r_ts = HandleYieldTermStructure(ZeroCurve(dates, rates, dc)) |
361 | 358 | q_ts = HandleYieldTermStructure(ZeroCurve(dates, divRates, dc)) |
362 | 359 |
|
@@ -420,3 +417,50 @@ def price_cal(rho, tGrid): |
420 | 417 | expected_price = [11.38, ] * 4 + [12.79, ] * 4 + [14.06, ] * 4 |
421 | 418 |
|
422 | 419 | np.testing.assert_almost_equal(calc_price, expected_price, 2) |
| 420 | + |
| 421 | + def test_mc_vanilla_pricing(self): |
| 422 | + """Testing Monte-Carlo vanilla option pricing""" |
| 423 | + dc = Actual360() |
| 424 | + todays_date = today() |
| 425 | + settings = Settings() |
| 426 | + settings.evaluation_date = todays_date |
| 427 | + dates = [todays_date + Period(i, Years) for i in range(41)] |
| 428 | + rates = [0.03 + 0.0003 * exp(sin(i / 4.0)) for i in range(41)] |
| 429 | + div_rates = [0.02 + 0.0001 * exp(sin(i / 5.0)) for i in range(41)] |
| 430 | + maturity = todays_date + Period(20, Years) |
| 431 | + |
| 432 | + s0 = SimpleQuote(100) |
| 433 | + r_ts = HandleYieldTermStructure(ZeroCurve(dates, rates, dc)) |
| 434 | + q_ts = HandleYieldTermStructure(ZeroCurve(dates, div_rates, dc)) |
| 435 | + vol = SimpleQuote(0.25) |
| 436 | + vol_ts = BlackConstantVol(todays_date, NullCalendar(), vol, dc) |
| 437 | + bsm_process= BlackScholesMertonProcess(s0, q_ts, r_ts, vol_ts) |
| 438 | + heston_process = HestonProcess(r_ts, q_ts, s0, 0.0625, 0.5, 0.0625, 1e-5, 0.3) |
| 439 | + hw_process = HullWhiteForwardProcess(r_ts, 0.01, 0.01) |
| 440 | + hw_process.forward_measure_time = dc.year_fraction(todays_date, maturity) |
| 441 | + |
| 442 | + tol = 0.05 |
| 443 | + corr = [-0.9, -0.5, 0.0, 0.5, 0.9] |
| 444 | + strike = [100.0] |
| 445 | + exercise = EuropeanExercise(maturity) |
| 446 | + for rho in corr: |
| 447 | + for s in strike: |
| 448 | + joint_process = HybridHestonHullWhiteProcess(heston_process, hw_process, rho) |
| 449 | + payoff = PlainVanillaPayoff(OptionType.Put, s) |
| 450 | + option_heston_hw = VanillaOption(payoff, exercise) |
| 451 | + engine = MCHestonHullWhiteEngine(joint_process, |
| 452 | + time_steps=1, |
| 453 | + required_tolerance=tol, |
| 454 | + seed=42) |
| 455 | + option_heston_hw.set_pricing_engine(engine) |
| 456 | + hw_model = HullWhite(r_ts, hw_process.a, hw_process.sigma) |
| 457 | + option_BsmHW = VanillaOption(payoff, exercise) |
| 458 | + option_BsmHW.set_pricing_engine(AnalyticBSMHullWhiteEngine(rho, bsm_process, hw_model)) |
| 459 | + calculated = option_heston_hw.npv |
| 460 | + error = option_heston_hw.error_estimate |
| 461 | + expected = option_BsmHW.npv |
| 462 | + print(abs(calculated - expected), error) |
| 463 | + if rho == 0: |
| 464 | + self.assertTrue(abs(calculated - expected) < tol) |
| 465 | + else: |
| 466 | + self.assertTrue(abs(calculated - expected) < 1.2 * error) |
0 commit comments