29 ext::shared_ptr<AnalyticHestonEngine>&
engine_;
30 Real logK_, phiRightLimit_;
32 std::complex<Real> i_, adj_;
34 P12Integrand(ext::shared_ptr<AnalyticHestonEngine>& engine,
38 Real phiRightLimit = 100) :
engine_(engine), logK_(logK),
39 phiRightLimit_(phiRightLimit), tenor_(tenor), i_(
std::complex<
Real>(0.0, 1.0)) {
43 adj_ = std::complex<Real>(0.0, -1.0);
45 adj_ = std::complex<Real>(0.0, 0.0);
51 Real phiDash = (0.5+1e-8+0.5*phi) * phiRightLimit_;
52 return 0.5*phiRightLimit_*std::real((std::exp(-phiDash*logK_*i_) / (phiDash*i_)) *
engine_->chF(phiDash+adj_, tenor_));
57 class P12HatIntegrand {
59 Time tenor_, resetTime_;
62 Real logK_, phiRightLimit_, nuRightLimit_;
63 const AnalyticHestonForwardEuropeanEngine*
const parent_;
64 GaussLegendreIntegration innerIntegrator_;
66 P12HatIntegrand(
Time tenor,
71 const AnalyticHestonForwardEuropeanEngine*
const parent,
73 Real nuRightLimit) : tenor_(tenor), resetTime_(resetTime),
74 s0_(s0), P1_(P1), logK_(logK), phiRightLimit_(phiRightLimit),
75 nuRightLimit_(nuRightLimit), parent_(parent), innerIntegrator_(128) {}
79 Real nuDash = nuRightLimit_ * (0.5 *
nu + 0.5 + 1e-8);
82 ext::shared_ptr<AnalyticHestonEngine> engine = parent_->forwardChF(s0_, nuDash);
83 P12Integrand pIntegrand(engine, logK_, tenor_, P1_, phiRightLimit_);
84 Real p1Integral = innerIntegrator_(pIntegrand);
87 Real propagator = parent_->propagator(resetTime_, nuDash);
90 return propagator * (0.5 + p1Integral/
M_PI);
96 ext::shared_ptr<HestonProcess> process,
Size integrationOrder)
97 : process_(
std::move(process)), integrationOrder_(integrationOrder), outerIntegrator_(128) {
107 "Very low values (<~10%) for Heston Vol-of-Vol cause numerical issues" \
108 "in this implementation of the propagator function, try using" \
109 "MCForwardEuropeanHestonEngine Monte-Carlo engine instead");
124 "not an European option");
127 ext::shared_ptr<PlainVanillaPayoff>
payoff =
128 ext::dynamic_pointer_cast<PlainVanillaPayoff>(this->
arguments_.payoff);
129 QL_REQUIRE(payoff,
"non plain vanilla payoff given");
133 Time tenor = expiryTime - resetTime;
141 Real expiryRatio = expiryDcf / expiryDividendDiscount;
142 Real resetRatio = resetDcf / resetDividendDiscount;
144 QL_REQUIRE(resetTime >= 0.0,
"Reset Date cannot be in the past");
145 QL_REQUIRE(expiryTime >= 0.0,
"Expiry Date cannot be in the past");
148 Real phiRightLimit = 100.0;
149 Real nuRightLimit = std::max(2.0, 10.0 * (1+std::max(0.0,
rho_)) *
sigma_ * std::sqrt(resetTime * std::max(
v0_,
theta_)));
153 std::pair<Real, Real> P1HatP2Hat;
154 if (resetTime <= 1e-3) {
156 P1HatP2Hat =
calculateP1P2(tenor, tempQuote, moneyness *
s0_->value(), expiryRatio, phiRightLimit);
158 P1HatP2Hat =
calculateP1P2Hat(tenor, resetTime, moneyness, expiryRatio/resetRatio, phiRightLimit, nuRightLimit);
163 Real F =
s0_->value() / expiryRatio;
164 switch (
payoff->optionType()){
166 value = expiryDcf * (
F*P1HatP2Hat.first - moneyness*
s0_->value()*P1HatP2Hat.second/resetRatio);
169 value = expiryDcf * (moneyness*
s0_->value()*(1-P1HatP2Hat.second)/resetRatio -
F*(1-P1HatP2Hat.first));
172 QL_FAIL(
"unknown option type");
198 Real nuRightLimit)
const {
204 Real logMoneyness = std::log(moneyness*ratio);
206 P12HatIntegrand p1HatIntegrand(tenor, resetTime, unitQuote, logMoneyness,
true,
this, phiRightLimit, nuRightLimit);
207 P12HatIntegrand p2HatIntegrand(tenor, resetTime, unitQuote, logMoneyness,
false,
this, phiRightLimit, nuRightLimit);
212 std::pair<Real, Real> P1HatP2Hat(p1HatIntegral, p2HatIntegral);
219 Real varReset)
const {
220 Real B, Lambda, term1, term2, term3;
226 term1 = std::exp(-0.5*(B * varReset + Lambda)) * B / 2;
227 term2 = std::pow(B * varReset / Lambda, 0.5*(
R_/2 - 1));
230 return term1 * term2 * term3;
235 Real varReset)
const {
239 ext::shared_ptr<HestonProcess> hestonProcess(
new
243 ext::shared_ptr<HestonModel> hestonModel(
new HestonModel(hestonProcess));
245 ext::shared_ptr<AnalyticHestonEngine> analyticHestonEngine(
249 return analyticHestonEngine;
257 Real phiRightLimit)
const {
259 ext::shared_ptr<AnalyticHestonEngine> engine =
forwardChF(St,
v0_);
260 Real logK = std::log(K*ratio/St->value());
264 P12Integrand p1Integrand(engine, logK, tenor,
true, phiRightLimit);
265 P12Integrand p2Integrand(engine, logK, tenor,
false, phiRightLimit);
267 Real p1Integral = integrator(p1Integrand);
268 Real p2Integral = integrator(p2Integrand);
270 std::pair<Real, Real> P1P2(0.5 + p1Integral/
M_PI, 0.5 + p2Integral/
M_PI);
analytic heston engine for forward-starting european options
ext::shared_ptr< PricingEngine > engine_
analytic Heston-model engine based on Fourier transform
AnalyticHestonForwardEuropeanEngine(ext::shared_ptr< HestonProcess > process, Size integrationOrder=144)
ext::shared_ptr< HestonProcess > process_
GaussLegendreIntegration outerIntegrator_
void calculate() const override
std::pair< Real, Real > calculateP1P2(Time t, Handle< Quote > &St, Real K, Real ratio, Real phiRightLimit=100) const
ext::shared_ptr< AnalyticHestonEngine > forwardChF(Handle< Quote > &spotReset, Real varReset) const
std::pair< Real, Real > calculateP1P2Hat(Time tenor, Time resetTime, Real K, Real ratio, Real phiRightLimit=100, Real nuRightLimit=2.0) const
Handle< YieldTermStructure > dividendYield_
Real propagator(Time resetTime, Real varReset) const
Handle< YieldTermStructure > riskFreeRate_
Gauss-Legendre integration.
VanillaOption::results results_
ForwardOptionArguments< VanillaOption::arguments > arguments_
Shared handle to an observable.
Heston model for the stochastic volatility of an asset.
Square-root stochastic-volatility Heston process.
std::map< std::string, ext::any > additionalResults
market element returning a stored value
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Real Time
continuous quantity with 1-year units
std::size_t Size
size of a container
ext::shared_ptr< QuantLib::Payoff > payoff
Real modifiedBesselFunction_i(Real nu, Real x)