Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions quantlib/_instrument.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cdef extern from 'ql/instrument.hpp' namespace 'QuantLib':
Instrument()
bool isExpired()
Real NPV() except +
Real errorEstimate() except +
Date& valuationDate() except +
void setPricingEngine(shared_ptr[PricingEngine]&)
T result[T](const string& tag)
5 changes: 5 additions & 0 deletions quantlib/instrument.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ cdef class Instrument(Observable):
def __get__(self):
return self._thisptr.get().NPV()

@property
def error_estimate(self) -> Real:
"""error estimate on the NPV when available"""
return self._thisptr.get().errorEstimate()

property npv:
""" Shortcut to the net_present_value property. """
def __get__(self):
Expand Down
5 changes: 5 additions & 0 deletions quantlib/methods/montecarlo/_mctraits.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cdef extern from 'ql/methods/montecarlo/mctraits.hpp' namespace 'QuantLib' nogil:
cdef cppclass MultiVariate:
pass
cdef cppclass SingleVariate:
pass
2 changes: 1 addition & 1 deletion quantlib/pricingengines/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .vanilla.vanilla import AnalyticDividendEuropeanEngine
from .vanilla.vanilla import FdHestonHullWhiteVanillaEngine
from .vanilla.fdblackscholesvanillaengine import FdBlackScholesVanillaEngine

from .vanilla.mchestonhullwhiteengine import MCHestonHullWhiteEngine
from .swaption.jamshidian_swaption_engine import JamshidianSwaptionEngine
from .swaption.black_swaption_engine import (
BlackSwaptionEngine, BachelierSwaptionEngine)
Expand Down
9 changes: 5 additions & 4 deletions quantlib/pricingengines/vanilla/_mceuropeanhestonengine.pxd
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
include '../../types.pxi'

from quantlib.types cimport BigNatural, Real, Size
from libcpp cimport bool
from quantlib.processes._heston_process cimport HestonProcess
from quantlib.pricingengines._pricing_engine cimport PricingEngine
from quantlib.handle cimport shared_ptr
from ._mcvanillaengine cimport MCVanillaEngine
from quantlib.methods.montecarlo._mctraits cimport MultiVariate

#needed to prevent a Forward declaration error
cdef extern from 'ql/exercise.hpp':
pass

cdef extern from 'ql/pricingengines/vanilla/mceuropeanhestonengine.hpp' namespace 'QuantLib':
cdef cppclass MCEuropeanHestonEngine[RNG=*,S=*,P=*](PricingEngine):
cdef extern from 'ql/pricingengines/vanilla/mceuropeanhestonengine.hpp' namespace 'QuantLib' nogil:
cdef cppclass MCEuropeanHestonEngine[RNG=*,S=*,P=*](MCVanillaEngine[MultiVariate, RNG, S]):
MCEuropeanHestonEngine(shared_ptr[HestonProcess]& sp,
Size timeSteps,
Size timeStepsPerYear,
Expand Down
31 changes: 31 additions & 0 deletions quantlib/pricingengines/vanilla/_mchestonhullwhiteengine.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from quantlib.types cimport BigNatural, Real, Size
from libcpp cimport bool
from quantlib.processes._hybrid_heston_hullwhite_process cimport HybridHestonHullWhiteProcess
from quantlib.pricingengines._pricing_engine cimport PricingEngine
from quantlib.handle cimport shared_ptr
from ._mcvanillaengine cimport MCVanillaEngine
from quantlib.methods.montecarlo._mctraits cimport MultiVariate

cdef extern from 'ql/pricingengines/vanilla/mchestonhullwhiteengine.hpp' namespace 'QuantLib' nogil:
cdef cppclass MCHestonHullWhiteEngine[RNG=*,S=*](MCVanillaEngine[MultiVariate, RNG, S]):
MCHestonHullWhiteEngine(shared_ptr[HybridHestonHullWhiteProcess]& sp,
Size timeSteps,
Size timeStepsPerYear,
bool antitheticVariate,
bool controlVariate,
Size requiredSamples,
Real requiredTolerance,
Size maxSamples,
BigNatural seed) except +
cdef cppclass MakeMCHestonHullWhiteEngine[RNG=*,S=*]:
MakeMCHestonHullWhiteEngine(shared_ptr[HybridHestonHullWhiteProcess])
MakeMCHestonHullWhiteEngine& withSteps(Size steps)
MakeMCHestonHullWhiteEngine& withStepsPerYear(Size steps)
MakeMCHestonHullWhiteEngine& withAntitheticVariate(bool b = true)
MakeMCHestonHullWhiteEngine& withControlVariate(bool b = true)
MakeMCHestonHullWhiteEngine& withSamples(Size samples)
MakeMCHestonHullWhiteEngine& withAbsoluteTolerance(Real tolerance)
MakeMCHestonHullWhiteEngine& withMaxSamples(Size samples)
MakeMCHestonHullWhiteEngine& withSeed(BigNatural seed)
# conversion to pricing engine
shared_ptr[PricingEngine] operator() const
18 changes: 4 additions & 14 deletions quantlib/pricingengines/vanilla/_mcvanillaengine.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,21 @@
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
"""

include '../../types.pxi'

from libcpp.vector cimport vector
from libcpp.string cimport string
from quantlib.types cimport BigNatural, Real, Size
from libcpp cimport bool

from quantlib.handle cimport shared_ptr
cimport quantlib.processes._stochastic_process as _sp
cimport quantlib._stochastic_process as _sp
from quantlib.pricingengines._pricing_engine cimport PricingEngine


cdef extern from 'ql/math/randomnumbers/rngtraits.hpp' namespace 'QuantLib':
cdef extern from 'ql/math/randomnumbers/rngtraits.hpp' namespace 'QuantLib' nogil:
cdef cppclass PseudoRandom:
pass
cdef cppclass LowDiscrepancy:
pass

cdef extern from 'ql/methods/montecarlo/mctraits.hpp' namespace 'QuantLib':
cdef cppclass MultiVariate:
pass
cdef cppclass SingleVariate:
pass

cdef extern from 'ql/pricingengines/vanilla/mcvanillaengine.hpp' namespace 'QuantLib':
cdef extern from 'ql/pricingengines/vanilla/mcvanillaengine.hpp' namespace 'QuantLib' nogil:
cdef cppclass MCVanillaEngine[MC, RNG, S=*, INST=*](PricingEngine):
MCVanillaEngine(shared_ptr[_sp.StochasticProcess]& sp,
Size timeSteps,
Expand Down
10 changes: 5 additions & 5 deletions quantlib/pricingengines/vanilla/mceuropeanhestonengine.pyx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from quantlib.types cimport BigNatural, Integer, Real, Size
from libcpp cimport bool
from quantlib.handle cimport shared_ptr, static_pointer_cast
from quantlib._defines cimport QL_MAX_INTEGER
from quantlib.handle cimport static_pointer_cast
from quantlib.utilities.null cimport Null
from quantlib.processes.heston_process cimport HestonProcess
cimport quantlib.processes._heston_process as _hp
from .mcvanillaengine cimport MCVanillaEngine
from . cimport _mceuropeanhestonengine as _mceh

cdef class MCEuropeanHestonEngine(MCVanillaEngine):

def __init__(self, HestonProcess process, Size time_steps=Null[Integer](),
Size steps_per_year=Null[Integer](), bool antithetic_variate=True,
def __init__(self, HestonProcess process, Size time_steps=Null[Size](),
Size steps_per_year=Null[Size](), bool antithetic_variate=True,
Size required_samples=Null[Integer](), Real required_tolerance=Null[Real](),
Size max_samples=Null[Integer](),
Size max_samples=QL_MAX_INTEGER,
BigNatural seed=0):
self._thisptr.reset(
new _mceh.MCEuropeanHestonEngine(
Expand Down
4 changes: 4 additions & 0 deletions quantlib/pricingengines/vanilla/mchestonhullwhiteengine.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .mcvanillaengine cimport MCVanillaEngine

cdef class MCHestonHullWhiteEngine(MCVanillaEngine):
pass
31 changes: 31 additions & 0 deletions quantlib/pricingengines/vanilla/mchestonhullwhiteengine.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from quantlib.types cimport BigNatural, Integer, Real, Size
from libcpp cimport bool
from quantlib.handle cimport static_pointer_cast
from quantlib.utilities.null cimport Null
from quantlib.processes.hybrid_heston_hullwhite_process cimport HybridHestonHullWhiteProcess
cimport quantlib.processes._hybrid_heston_hullwhite_process as _hhwp
from quantlib._defines cimport QL_MAX_INTEGER
from . cimport _mchestonhullwhiteengine as _mchhw

cdef class MCHestonHullWhiteEngine(MCVanillaEngine):
def __init__(self, HybridHestonHullWhiteProcess sp,
Size time_steps=Null[Size](),
Size time_steps_per_year=Null[Size](),
bool antithetic_variate=True,
bool control_variate=True,
Size required_samples=Null[Size](),
Real required_tolerance=Null[Real](),
Size max_samples=QL_MAX_INTEGER,
BigNatural seed=0):
self._thisptr.reset(
new _mchhw.MCHestonHullWhiteEngine(
static_pointer_cast[_hhwp.HybridHestonHullWhiteProcess](sp._thisptr),
time_steps,
time_steps_per_year,
antithetic_variate,
control_variate,
required_samples,
required_tolerance,
max_samples,
seed)
)
17 changes: 17 additions & 0 deletions quantlib/processes/_hybrid_heston_hullwhite_process.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from quantlib.types cimport Real
from quantlib.handle cimport shared_ptr
from quantlib._stochastic_process cimport StochasticProcess
from ._heston_process cimport HestonProcess
from ._hullwhite_process cimport HullWhiteForwardProcess

cdef extern from 'ql/processes/hybridhestonhullwhiteprocess.hpp' namespace 'QuantLib' nogil:

cdef cppclass HybridHestonHullWhiteProcess(StochasticProcess):
enum Discretization:
Euler
BSMHullWhite
HybridHestonHullWhiteProcess(
shared_ptr[HestonProcess]& heston_process,
shared_ptr[HullWhiteForwardProcess]& hullWhiteProcess,
Real corrEquityShortRate,
HybridHestonHullWhiteProcess.Discretization discretization) # = BSMHullWhite
3 changes: 2 additions & 1 deletion quantlib/processes/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .black_scholes_process import BlackScholesMertonProcess
from .bates_process import BatesProcess
from .heston_process import HestonProcess
from .hullwhite_process import HullWhiteProcess
from .hullwhite_process import HullWhiteProcess, HullWhiteForwardProcess
from .hybrid_heston_hullwhite_process import HybridHestonHullWhiteProcess
2 changes: 1 addition & 1 deletion quantlib/processes/hullwhite_process.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ from .forwardmeasureprocess cimport ForwardMeasureProcess1D
cdef class HullWhiteProcess(StochasticProcess1D):
pass

cdef class ForwardHullWhiteProcess(ForwardMeasureProcess1D):
cdef class HullWhiteForwardProcess(ForwardMeasureProcess1D):
pass
9 changes: 9 additions & 0 deletions quantlib/processes/hybrid_heston_hullwhite_process.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from quantlib.stochastic_process cimport StochasticProcess

cdef extern from "ql/processes/hybridhestonhullwhiteprocess.hpp" namespace "QuantLib::HybridHestonHullWhiteProcess" nogil:
cpdef enum class Discretization:
Euler
BSMHullWhite

cdef class HybridHestonHullWhiteProcess(StochasticProcess):
pass
23 changes: 23 additions & 0 deletions quantlib/processes/hybrid_heston_hullwhite_process.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from quantlib.types cimport Real
from quantlib.handle cimport static_pointer_cast
from .hullwhite_process cimport HullWhiteForwardProcess
from .heston_process cimport HestonProcess
from . cimport _hullwhite_process as _hw
from . cimport _heston_process as _hp
from . cimport _hybrid_heston_hullwhite_process as _hhhwp

cdef class HybridHestonHullWhiteProcess(StochasticProcess):
Euler = Discretization.Euler
BSMHullWhite = Discretization.BSMHullWhite

def __init__(self,
HestonProcess heston_process,
HullWhiteForwardProcess hull_white_process,
Real corr_equity_short_rate,
Discretization discretization=Discretization.BSMHullWhite):
self._thisptr.reset(
new _hhhwp.HybridHestonHullWhiteProcess(static_pointer_cast[_hp.HestonProcess](heston_process._thisptr),
static_pointer_cast[_hw.HullWhiteForwardProcess](hull_white_process._thisptr),
corr_equity_short_rate,
discretization)
)
76 changes: 60 additions & 16 deletions test/test_hybridhestonhullwhite_process.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from __future__ import division
from __future__ import print_function

import unittest

import numpy as np
Expand All @@ -15,12 +12,14 @@
from quantlib.instruments.vanillaoption import VanillaOption

from quantlib.time.api import (today, Years, Actual365Fixed,
Period, May, Date,
Period, May, Date, Actual360,
NullCalendar)

from quantlib.processes.api import (BlackScholesMertonProcess,
HestonProcess,
HullWhiteProcess)
HullWhiteProcess,
HullWhiteForwardProcess,
HybridHestonHullWhiteProcess)

from quantlib.models.equity.heston_model import (
HestonModel)
Expand All @@ -33,7 +32,8 @@
AnalyticBSMHullWhiteEngine,
AnalyticHestonEngine,
AnalyticHestonHullWhiteEngine,
FdHestonHullWhiteVanillaEngine)
FdHestonHullWhiteVanillaEngine,
MCHestonHullWhiteEngine)

from quantlib.quotes import SimpleQuote

Expand All @@ -43,6 +43,7 @@

from .utilities import flat_rate

from math import sin, exp

class HybridHestonHullWhiteProcessTestCase(unittest.TestCase):

Expand Down Expand Up @@ -95,8 +96,8 @@ def setUp(self):
self.dates = dates

def test_bsm_hw(self):
print("Testing European option pricing for a BSM process" +
" with one-factor Hull-White model...")
"""Testing European option pricing for a BSM process
with one-factor Hull-White model"""

dc = Actual365Fixed()
todays_date = today()
Expand Down Expand Up @@ -247,12 +248,8 @@ def test_compare_bsm_bsmhw_hestonhw(self):
self.assertAlmostEqual(npv_bsm, npv_hestonhw, delta=tol)

def test_compare_BsmHW_HestonHW(self):
"""
From Quantlib test suite
"""

print("Comparing European option pricing for a BSM " +
"process with one-factor Hull-White model...")
"""Comparing European option pricing for a BSM
process with one-factor Hull-White model"""

dc = Actual365Fixed()

Expand Down Expand Up @@ -355,8 +352,8 @@ def test_zanette(self):
# constant yield and div curves

dates = [todays_date + Period(i, Years) for i in range(3)]
rates = [0.04 for i in range(3)]
divRates = [0.03 for i in range(3)]
rates = [0.04] * 3
divRates = [0.03] * 3
r_ts = HandleYieldTermStructure(ZeroCurve(dates, rates, dc))
q_ts = HandleYieldTermStructure(ZeroCurve(dates, divRates, dc))

Expand Down Expand Up @@ -420,3 +417,50 @@ def price_cal(rho, tGrid):
expected_price = [11.38, ] * 4 + [12.79, ] * 4 + [14.06, ] * 4

np.testing.assert_almost_equal(calc_price, expected_price, 2)

def test_mc_vanilla_pricing(self):
"""Testing Monte-Carlo vanilla option pricing"""
dc = Actual360()
todays_date = today()
settings = Settings()
settings.evaluation_date = todays_date
dates = [todays_date + Period(i, Years) for i in range(41)]
rates = [0.03 + 0.0003 * exp(sin(i / 4.0)) for i in range(41)]
div_rates = [0.02 + 0.0001 * exp(sin(i / 5.0)) for i in range(41)]
maturity = todays_date + Period(20, Years)

s0 = SimpleQuote(100)
r_ts = HandleYieldTermStructure(ZeroCurve(dates, rates, dc))
q_ts = HandleYieldTermStructure(ZeroCurve(dates, div_rates, dc))
vol = SimpleQuote(0.25)
vol_ts = BlackConstantVol(todays_date, NullCalendar(), vol, dc)
bsm_process= BlackScholesMertonProcess(s0, q_ts, r_ts, vol_ts)
heston_process = HestonProcess(r_ts, q_ts, s0, 0.0625, 0.5, 0.0625, 1e-5, 0.3)
hw_process = HullWhiteForwardProcess(r_ts, 0.01, 0.01)
hw_process.forward_measure_time = dc.year_fraction(todays_date, maturity)

tol = 0.05
corr = [-0.9, -0.5, 0.0, 0.5, 0.9]
strike = [100.0]
exercise = EuropeanExercise(maturity)
for rho in corr:
for s in strike:
joint_process = HybridHestonHullWhiteProcess(heston_process, hw_process, rho)
payoff = PlainVanillaPayoff(OptionType.Put, s)
option_heston_hw = VanillaOption(payoff, exercise)
engine = MCHestonHullWhiteEngine(joint_process,
time_steps=1,
required_tolerance=tol,
seed=42)
option_heston_hw.set_pricing_engine(engine)
hw_model = HullWhite(r_ts, hw_process.a, hw_process.sigma)
option_BsmHW = VanillaOption(payoff, exercise)
option_BsmHW.set_pricing_engine(AnalyticBSMHullWhiteEngine(rho, bsm_process, hw_model))
calculated = option_heston_hw.npv
error = option_heston_hw.error_estimate
expected = option_BsmHW.npv
print(abs(calculated - expected), error)
if rho == 0:
self.assertTrue(abs(calculated - expected) < tol)
else:
self.assertTrue(abs(calculated - expected) < 1.2 * error)