QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
analytichestonengine.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2004, 2005, 2008 Klaus Spanderen
5 Copyright (C) 2007 StatPro Italia srl
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
26#include <ql/functional.hpp>
27#include <ql/instruments/payoffs.hpp>
28#include <ql/math/integrals/discreteintegrals.hpp>
29#include <ql/math/integrals/exponentialintegrals.hpp>
30#include <ql/math/integrals/gausslobattointegral.hpp>
31#include <ql/math/integrals/kronrodintegral.hpp>
32#include <ql/math/integrals/simpsonintegral.hpp>
33#include <ql/math/integrals/trapezoidintegral.hpp>
34#include <ql/math/solvers1d/brent.hpp>
35#include <ql/math/functional.hpp>
36#include <ql/pricingengines/blackcalculator.hpp>
37#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
38#include <utility>
39
40#if defined(QL_PATCH_MSVC)
41#pragma warning(disable: 4180)
42#endif
43
44namespace QuantLib {
45
46 namespace {
47
48 class integrand1 {
49 private:
50 const Real c_inf_;
51 const ext::function<Real(Real)> f_;
52 public:
53 integrand1(Real c_inf, ext::function<Real(Real)> f) : c_inf_(c_inf), f_(std::move(f)) {}
54 Real operator()(Real x) const {
55 if ((1.0-x)*c_inf_ > QL_EPSILON)
56 return f_(-std::log(0.5-0.5*x)/c_inf_)/((1.0-x)*c_inf_);
57 else
58 return 0.0;
59 }
60 };
61
62 class integrand2 {
63 private:
64 const Real c_inf_;
65 const ext::function<Real(Real)> f_;
66 public:
67 integrand2(Real c_inf, ext::function<Real(Real)> f) : c_inf_(c_inf), f_(std::move(f)) {}
68 Real operator()(Real x) const {
69 if (x*c_inf_ > QL_EPSILON) {
70 return f_(-std::log(x)/c_inf_)/(x*c_inf_);
71 } else {
72 return 0.0;
73 }
74 }
75 };
76
77 class integrand3 {
78 private:
79 const integrand2 int_;
80 public:
81 integrand3(Real c_inf, const ext::function<Real(Real)>& f)
82 : int_(c_inf, f) {}
83
84 Real operator()(Real x) const { return int_(1.0-x); }
85 };
86
87 class u_Max {
88 public:
89 u_Max(Real c_inf, Real epsilon) : c_inf_(c_inf), logEpsilon_(std::log(epsilon)) {}
90
91 Real operator()(Real u) const {
92 ++evaluations_;
93 return c_inf_*u + std::log(u) + logEpsilon_;
94 }
95
96 Size evaluations() const { return evaluations_; }
97
98 private:
99 const Real c_inf_, logEpsilon_;
100 mutable Size evaluations_ = 0;
101 };
102
103
104 class uHat_Max {
105 public:
106 uHat_Max(Real v0T2, Real epsilon) : v0T2_(v0T2), logEpsilon_(std::log(epsilon)) {}
107
108 Real operator()(Real u) const {
109 ++evaluations_;
110 return v0T2_*u*u + std::log(u) + logEpsilon_;
111 }
112
113 Size evaluations() const { return evaluations_; }
114
115 private:
116 const Real v0T2_, logEpsilon_;
117 mutable Size evaluations_ = 0;
118 };
119 }
120
121 // helper class for integration
122 class AnalyticHestonEngine::Fj_Helper {
123 public:
124 Fj_Helper(const VanillaOption::arguments& arguments,
125 const ext::shared_ptr<HestonModel>& model,
126 const AnalyticHestonEngine* engine,
127 ComplexLogFormula cpxLog,
128 Time term,
129 Real ratio,
130 Size j);
131
132 Fj_Helper(Real kappa,
133 Real theta,
134 Real sigma,
135 Real v0,
136 Real s0,
137 Real rho,
138 const AnalyticHestonEngine* engine,
139 ComplexLogFormula cpxLog,
140 Time term,
141 Real strike,
142 Real ratio,
143 Size j);
144
145 Fj_Helper(Real kappa,
146 Real theta,
147 Real sigma,
148 Real v0,
149 Real s0,
150 Real rho,
151 ComplexLogFormula cpxLog,
152 Time term,
153 Real strike,
154 Real ratio,
155 Size j);
156
157 Real operator()(Real phi) const;
158
159 private:
160 const Size j_;
161 // const VanillaOption::arguments& arg_;
162 const Real kappa_, theta_, sigma_, v0_;
163 const ComplexLogFormula cpxLog_;
164
165 // helper variables
166 const Time term_;
167 const Real x_, sx_, dd_;
168 const Real sigma2_, rsigma_;
169 const Real t0_;
170
171 // log branch counter
172 mutable int b_; // log branch counter
173 mutable Real g_km1_; // imag part of last log value
174
175 const AnalyticHestonEngine* const engine_;
176 };
177
178
179 AnalyticHestonEngine::Fj_Helper::Fj_Helper(
180 const VanillaOption::arguments& arguments,
181 const ext::shared_ptr<HestonModel>& model,
182 const AnalyticHestonEngine* const engine,
183 ComplexLogFormula cpxLog,
184 Time term, Real ratio, Size j)
185 : j_ (j), //arg_(arguments),
186 kappa_(model->kappa()), theta_(model->theta()),
187 sigma_(model->sigma()), v0_(model->v0()),
188 cpxLog_(cpxLog), term_(term),
189 x_(std::log(model->process()->s0()->value())),
190 sx_(std::log(ext::dynamic_pointer_cast<StrikedTypePayoff>
191 (arguments.payoff)->strike())),
192 dd_(x_-std::log(ratio)),
193 sigma2_(sigma_*sigma_),
194 rsigma_(model->rho()*sigma_),
195 t0_(kappa_ - ((j_== 1)? model->rho()*sigma_ : Real(0))),
196 b_(0), g_km1_(0),
197 engine_(engine)
198 {
199 }
200
201 AnalyticHestonEngine::Fj_Helper::Fj_Helper(Real kappa, Real theta,
202 Real sigma, Real v0, Real s0, Real rho,
203 const AnalyticHestonEngine* const engine,
204 ComplexLogFormula cpxLog,
205 Time term,
206 Real strike,
207 Real ratio,
208 Size j)
209 :
210 j_(j),
211 kappa_(kappa),
212 theta_(theta),
213 sigma_(sigma),
214 v0_(v0),
215 cpxLog_(cpxLog),
216 term_(term),
217 x_(std::log(s0)),
218 sx_(std::log(strike)),
219 dd_(x_-std::log(ratio)),
220 sigma2_(sigma_*sigma_),
221 rsigma_(rho*sigma_),
222 t0_(kappa - ((j== 1)? rho*sigma : Real(0))),
223 b_(0),
224 g_km1_(0),
225 engine_(engine)
226 {
227 }
228
229 AnalyticHestonEngine::Fj_Helper::Fj_Helper(Real kappa,
230 Real theta,
231 Real sigma,
232 Real v0,
233 Real s0,
234 Real rho,
235 ComplexLogFormula cpxLog,
236 Time term,
237 Real strike,
238 Real ratio,
239 Size j)
240 : j_(j), kappa_(kappa), theta_(theta), sigma_(sigma), v0_(v0), cpxLog_(cpxLog), term_(term),
241 x_(std::log(s0)), sx_(std::log(strike)), dd_(x_ - std::log(ratio)), sigma2_(sigma_ * sigma_),
242 rsigma_(rho * sigma_), t0_(kappa - ((j == 1) ? rho * sigma : Real(0))), b_(0), g_km1_(0),
243 engine_(nullptr) {}
244
245
246 Real AnalyticHestonEngine::Fj_Helper::operator()(Real phi) const
247 {
248 const Real rpsig(rsigma_*phi);
249
250 const std::complex<Real> t1 = t0_+std::complex<Real>(0, -rpsig);
251 const std::complex<Real> d =
252 std::sqrt(t1*t1 - sigma2_*phi
253 *std::complex<Real>(-phi, (j_== 1)? 1 : -1));
254 const std::complex<Real> ex = std::exp(-d*term_);
255 const std::complex<Real> addOnTerm =
256 engine_ != nullptr ? engine_->addOnTerm(phi, term_, j_) : Real(0.0);
257
258 if (cpxLog_ == Gatheral) {
259 if (phi != 0.0) {
260 if (sigma_ > 1e-5) {
261 const std::complex<Real> p = (t1-d)/(t1+d);
262 const std::complex<Real> g
263 = std::log((1.0 - p*ex)/(1.0 - p));
264
265 return
266 std::exp(v0_*(t1-d)*(1.0-ex)/(sigma2_*(1.0-ex*p))
267 + (kappa_*theta_)/sigma2_*((t1-d)*term_-2.0*g)
268 + std::complex<Real>(0.0, phi*(dd_-sx_))
269 + addOnTerm
270 ).imag()/phi;
271 }
272 else {
273 const std::complex<Real> td = phi/(2.0*t1)
274 *std::complex<Real>(-phi, (j_== 1)? 1 : -1);
275 const std::complex<Real> p = td*sigma2_/(t1+d);
276 const std::complex<Real> g = p*(1.0-ex);
277
278 return
279 std::exp(v0_*td*(1.0-ex)/(1.0-p*ex)
280 + (kappa_*theta_)*(td*term_-2.0*g/sigma2_)
281 + std::complex<Real>(0.0, phi*(dd_-sx_))
282 + addOnTerm
283 ).imag()/phi;
284 }
285 }
286 else {
287 // use l'Hospital's rule to get lim_{phi->0}
288 if (j_ == 1) {
289 const Real kmr = rsigma_-kappa_;
290 if (std::fabs(kmr) > 1e-7) {
291 return dd_-sx_
292 + (std::exp(kmr*term_)*kappa_*theta_
293 -kappa_*theta_*(kmr*term_+1.0) ) / (2*kmr*kmr)
294 - v0_*(1.0-std::exp(kmr*term_)) / (2.0*kmr);
295 }
296 else
297 // \kappa = \rho * \sigma
298 return dd_-sx_ + 0.25*kappa_*theta_*term_*term_
299 + 0.5*v0_*term_;
300 }
301 else {
302 return dd_-sx_
303 - (std::exp(-kappa_*term_)*kappa_*theta_
304 +kappa_*theta_*(kappa_*term_-1.0))/(2*kappa_*kappa_)
305 - v0_*(1.0-std::exp(-kappa_*term_))/(2*kappa_);
306 }
307 }
308 }
309 else if (cpxLog_ == BranchCorrection) {
310 const std::complex<Real> p = (t1+d)/(t1-d);
311
312 // next term: g = std::log((1.0 - p*std::exp(d*term_))/(1.0 - p))
313 std::complex<Real> g;
314
315 // the exp of the following expression is needed.
316 const std::complex<Real> e = std::log(p)+d*term_;
317
318 // does it fit to the machine precision?
319 if (std::exp(-e.real()) > QL_EPSILON) {
320 g = std::log((1.0 - p/ex)/(1.0 - p));
321 } else {
322 // use a "big phi" approximation
323 g = d*term_ + std::log(p/(p - 1.0));
324
325 if (g.imag() > M_PI || g.imag() <= -M_PI) {
326 // get back to principal branch of the complex logarithm
327 Real im = std::fmod(g.imag(), 2*M_PI);
328 if (im > M_PI)
329 im -= 2*M_PI;
330 else if (im <= -M_PI)
331 im += 2*M_PI;
332
333 g = std::complex<Real>(g.real(), im);
334 }
335 }
336
337 // be careful here as we have to use a log branch correction
338 // to deal with the discontinuities of the complex logarithm.
339 // the principal branch is not always the correct one.
340 // (s. A. Sepp, chapter 4)
341 // remark: there is still the change that we miss a branch
342 // if the order of the integration is not high enough.
343 const Real tmp = g.imag() - g_km1_;
344 if (tmp <= -M_PI)
345 ++b_;
346 else if (tmp > M_PI)
347 --b_;
348
349 g_km1_ = g.imag();
350 g += std::complex<Real>(0, 2*b_*M_PI);
351
352 return std::exp(v0_*(t1+d)*(ex-1.0)/(sigma2_*(ex-p))
353 + (kappa_*theta_)/sigma2_*((t1+d)*term_-2.0*g)
354 + std::complex<Real>(0,phi*(dd_-sx_))
355 + addOnTerm
356 ).imag()/phi;
357 }
358 else {
359 QL_FAIL("unknown complex logarithm formula");
360 }
361 }
362
363
364 AnalyticHestonEngine::AP_Helper::AP_Helper(
365 Time term, Real fwd, Real strike, ComplexLogFormula cpxLog,
366 const AnalyticHestonEngine* const enginePtr)
367 : term_(term),
368 fwd_(fwd),
369 strike_(strike),
370 freq_(std::log(fwd/strike)),
371 cpxLog_(cpxLog),
372 enginePtr_(enginePtr) {
373 QL_REQUIRE(enginePtr != nullptr, "pricing engine required");
374
375 const Real v0 = enginePtr->model_->v0();
376 const Real kappa = enginePtr->model_->kappa();
377 const Real theta = enginePtr->model_->theta();
378 const Real sigma = enginePtr->model_->sigma();
379 const Real rho = enginePtr->model_->rho();
380
381 switch(cpxLog_) {
383 vAvg_ = (1-std::exp(-kappa*term))*(v0 - theta)
384 /(kappa*term) + theta;
385 break;
387 vAvg_ = -8.0*std::log(enginePtr->chF(
388 std::complex<Real>(0, -0.5), term).real())/term;
389 break;
390 case AsymptoticChF:
391 phi_ = -(v0+term*kappa*theta)/sigma
392 * std::complex<Real>(std::sqrt(1-rho*rho), rho);
393
394 psi_ = std::complex<Real>(
395 (kappa- 0.5*rho*sigma)*(v0 + term*kappa*theta)
396 + kappa*theta*std::log(4*(1-rho*rho)),
397 - ((0.5*rho*rho*sigma - kappa*rho)/std::sqrt(1-rho*rho)
398 *(v0 + kappa*theta*term)
399 - 2*kappa*theta*std::atan(rho/std::sqrt(1-rho*rho))))
400 /(sigma*sigma);
401 break;
402 default:
403 QL_FAIL("unknown control variate");
404 }
405 }
406
408 QL_REQUIRE( enginePtr_->addOnTerm(u, term_, 1)
409 == std::complex<Real>(0.0)
410 && enginePtr_->addOnTerm(u, term_, 2)
411 == std::complex<Real>(0.0),
412 "only Heston model is supported");
413
414 const std::complex<Real> z(u, -0.5);
415
416 std::complex<Real> phiBS;
417
418 switch (cpxLog_) {
421 phiBS = std::exp(
422 -0.5*vAvg_*term_*(z*z + std::complex<Real>(-z.imag(), z.real())));
423 break;
424 case AsymptoticChF:
425 phiBS = std::exp(u*phi_ + psi_);
426 break;
427 default:
428 QL_FAIL("unknown control variate");
429 }
430
431 return (std::exp(std::complex<Real>(0.0, u*freq_))
432 * (phiBS - enginePtr_->chF(z, term_)) / (u*u + 0.25)).real();
433 }
434
437 return BlackCalculator(
438 Option::Call, strike_, fwd_, std::sqrt(vAvg_*term_))
439 .value();
440 }
441 else if (cpxLog_ == AsymptoticChF) {
442 const std::complex<Real> phiFreq(phi_.real(), phi_.imag() + freq_);
443
444 using namespace ExponentialIntegral;
445 return fwd_ - std::sqrt(strike_*fwd_)/M_PI*
446 (std::exp(psi_)*(
447 -2.0*Ci(-0.5*phiFreq)*std::sin(0.5*phiFreq)
448 +std::cos(0.5*phiFreq)*(M_PI+2.0*Si(0.5*phiFreq)))).real();
449 }
450 else
451 QL_FAIL("unknown control variate");
452 }
453
454 std::complex<Real> AnalyticHestonEngine::chF(
455 const std::complex<Real>& z, Time t) const {
456
457 const Real kappa = model_->kappa();
458 const Real sigma = model_->sigma();
459 const Real theta = model_->theta();
460 const Real rho = model_->rho();
461 const Real v0 = model_->v0();
462
463 const Real sigma2 = sigma*sigma;
464
465 if (sigma > 1e-4) {
466 const std::complex<Real> g
467 = kappa + rho*sigma*std::complex<Real>(z.imag(), -z.real());
468
469 const std::complex<Real> D = std::sqrt(
470 g*g + (z*z + std::complex<Real>(-z.imag(), z.real()))*sigma2);
471
472 const std::complex<Real> G = (g-D)/(g+D);
473
474 return std::exp(v0/sigma2*(1.0-std::exp(-D*t))/(1.0-G*std::exp(-D*t))
475 *(g-D) + kappa*theta/sigma2*((g-D)*t
476 -2.0*std::log((1.0-G*std::exp(-D*t))/(1.0-G))));
477 }
478 else {
479 const Real kt = kappa*t;
480 const Real ekt = std::exp(kt);
481 const Real e2kt = std::exp(2*kt);
482 const Real rho2 = rho*rho;
483 const std::complex<Real> zpi = z + std::complex<Real>(0.0, 1.0);
484
485 return std::exp(-(((theta - v0 + ekt*((-1 + kt)*theta + v0))
486 *z*zpi)/ekt)/(2.*kappa))
487 + (std::exp(-(kt) - ((theta - v0 + ekt
488 *((-1 + kt)*theta + v0))*z*zpi)
489 /(2.*ekt*kappa))*rho*(2*theta + kt*theta -
490 v0 - kt*v0 + ekt*((-2 + kt)*theta + v0))
491 *(1.0 - std::complex<Real>(-z.imag(),z.real()))*z*z)
492 /(2.*kappa*kappa)*sigma
493 + (std::exp(-2*kt - ((theta - v0 + ekt
494 *((-1 + kt)*theta + v0))*z*zpi)/(2.*ekt*kappa))*z*z*zpi
495 *(-2*rho2*squared(2*theta + kt*theta - v0 -
496 kt*v0 + ekt*((-2 + kt)*theta + v0))
497 *z*z*zpi + 2*kappa*v0*(-zpi
498 + e2kt*(zpi + 4*rho2*z) - 2*ekt*(2*rho2*z
499 + kt*(zpi + rho2*(2 + kt)*z))) + kappa*theta*(zpi + e2kt
500 *(-5.0*zpi - 24*rho2*z+ 2*kt*(zpi + 4*rho2*z)) +
501 4*ekt*(zpi + 6*rho2*z + kt*(zpi + rho2*(4 + kt)*z)))))
502 /(16.*squared(squared(kappa)))*sigma2;
503 }
504 }
505
506 std::complex<Real> AnalyticHestonEngine::lnChF(
507 const std::complex<Real>& z, Time T) const {
508 return std::log(chF(z, T));
509 }
510
512 const ext::shared_ptr<HestonModel>& model,
513 Size integrationOrder)
516 VanillaOption::results>(model),
517 evaluations_(0),
520 Integration::gaussLaguerre(integrationOrder))),
522 }
523
525 const ext::shared_ptr<HestonModel>& model,
526 Real relTolerance, Size maxEvaluations)
529 VanillaOption::results>(model),
530 evaluations_(0),
531 cpxLog_(Gatheral),
532 integration_(new Integration(Integration::gaussLobatto(
533 relTolerance, Null<Real>(), maxEvaluations))),
534 andersenPiterbargEpsilon_(Null<Real>()) {
535 }
536
538 const ext::shared_ptr<HestonModel>& model,
539 ComplexLogFormula cpxLog,
540 const Integration& integration,
541 const Real andersenPiterbargEpsilon)
544 VanillaOption::results>(model),
545 evaluations_(0),
546 cpxLog_(cpxLog),
547 integration_(new Integration(integration)),
548 andersenPiterbargEpsilon_(andersenPiterbargEpsilon) {
549 QL_REQUIRE( cpxLog_ != BranchCorrection
550 || !integration.isAdaptiveIntegration(),
551 "Branch correction does not work in conjunction "
552 "with adaptive integration methods");
553 }
554
557 Time t, Real v0, Real kappa, Real theta, Real sigma, Real rho) {
558
559 if (t > 0.1 && (v0+t*kappa*theta)/sigma*std::sqrt(1-rho*rho) < 0.055) {
560 return AsymptoticChF;
561 }
562 else {
564 }
565 }
566
567
569 return evaluations_;
570 }
571
573 Real dividendDiscount,
574 Real spotPrice,
575 Real strikePrice,
576 Real term,
577 Real kappa, Real theta, Real sigma, Real v0, Real rho,
578 const TypePayoff& type,
579 const Integration& integration,
580 const ComplexLogFormula cpxLog,
581 const AnalyticHestonEngine* const enginePtr,
582 Real& value,
583 Size& evaluations) {
584
585 const Real ratio = riskFreeDiscount/dividendDiscount;
586
587 evaluations = 0;
588
589 switch(cpxLog) {
590 case Gatheral:
591 case BranchCorrection: {
592 const Real c_inf = std::min(0.2, std::max(0.0001,
593 std::sqrt(1.0-rho*rho)/sigma))*(v0 + kappa*theta*term);
594
595 const Real p1 = integration.calculate(c_inf,
596 Fj_Helper(kappa, theta, sigma, v0, spotPrice, rho, enginePtr,
597 cpxLog, term, strikePrice, ratio, 1))/M_PI;
598 evaluations += integration.numberOfEvaluations();
599
600 const Real p2 = integration.calculate(c_inf,
601 Fj_Helper(kappa, theta, sigma, v0, spotPrice, rho, enginePtr,
602 cpxLog, term, strikePrice, ratio, 2))/M_PI;
603 evaluations += integration.numberOfEvaluations();
604
605 switch (type.optionType())
606 {
607 case Option::Call:
608 value = spotPrice*dividendDiscount*(p1+0.5)
609 - strikePrice*riskFreeDiscount*(p2+0.5);
610 break;
611 case Option::Put:
612 value = spotPrice*dividendDiscount*(p1-0.5)
613 - strikePrice*riskFreeDiscount*(p2-0.5);
614 break;
615 default:
616 QL_FAIL("unknown option type");
617 }
618 }
619 break;
622 case AsymptoticChF:
623 case OptimalCV: {
624 const Real c_inf =
625 std::sqrt(1.0-rho*rho)*(v0 + kappa*theta*term)/sigma;
626
627 const Real fwdPrice = spotPrice / ratio;
628
629 const Real epsilon = enginePtr->andersenPiterbargEpsilon_
630 *M_PI/(std::sqrt(strikePrice*fwdPrice)*riskFreeDiscount);
631
632 const ext::function<Real()> uM = [&](){
633 return Integration::andersenPiterbargIntegrationLimit(c_inf, epsilon, v0, term);
634 };
635
636 AP_Helper cvHelper(term, fwdPrice, strikePrice,
637 (cpxLog == OptimalCV)
638 ? optimalControlVariate(term, v0, kappa, theta, sigma, rho)
639 : cpxLog,
640 enginePtr
641 );
642
643 const Real cvValue = cvHelper.controlVariateValue();
644
645 const Real h_cv = integration.calculate(c_inf, cvHelper, uM)
646 * std::sqrt(strikePrice * fwdPrice)/M_PI;
647 evaluations += integration.numberOfEvaluations();
648
649 switch (type.optionType())
650 {
651 case Option::Call:
652 value = (cvValue + h_cv)*riskFreeDiscount;
653 break;
654 case Option::Put:
655 value = (cvValue + h_cv - (fwdPrice - strikePrice))*riskFreeDiscount;
656 break;
657 default:
658 QL_FAIL("unknown option type");
659 }
660 }
661 break;
662
663 default:
664 QL_FAIL("unknown complex log formula");
665 }
666 }
667
669 {
670 // this is a european option pricer
671 QL_REQUIRE(arguments_.exercise->type() == Exercise::European,
672 "not an European option");
673
674 // plain vanilla
675 ext::shared_ptr<PlainVanillaPayoff> payoff =
676 ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
677 QL_REQUIRE(payoff, "non plain vanilla payoff given");
678
679 const ext::shared_ptr<HestonProcess>& process = model_->process();
680
681 const Real riskFreeDiscount = process->riskFreeRate()->discount(
682 arguments_.exercise->lastDate());
683 const Real dividendDiscount = process->dividendYield()->discount(
684 arguments_.exercise->lastDate());
685
686 const Real spotPrice = process->s0()->value();
687 QL_REQUIRE(spotPrice > 0.0, "negative or null underlying given");
688
689 const Real strikePrice = payoff->strike();
690 const Real term = process->time(arguments_.exercise->lastDate());
691
692 doCalculation(riskFreeDiscount,
693 dividendDiscount,
694 spotPrice,
695 strikePrice,
696 term,
697 model_->kappa(),
698 model_->theta(),
699 model_->sigma(),
700 model_->v0(),
701 model_->rho(),
702 *payoff,
704 cpxLog_,
705 this,
706 results_.value,
708 }
709
710
712 ext::shared_ptr<Integrator> integrator)
713 : intAlgo_(intAlgo), integrator_(std::move(integrator)) {}
714
716 Algorithm intAlgo, ext::shared_ptr<GaussianQuadrature> gaussianQuadrature)
717 : intAlgo_(intAlgo), gaussianQuadrature_(std::move(gaussianQuadrature)) {}
718
721 Real absTolerance,
722 Size maxEvaluations) {
723 return Integration(GaussLobatto,
724 ext::shared_ptr<Integrator>(
725 new GaussLobattoIntegral(maxEvaluations,
726 absTolerance,
727 relTolerance,
728 false)));
729 }
730
733 Size maxEvaluations) {
734 return Integration(GaussKronrod,
735 ext::shared_ptr<Integrator>(
736 new GaussKronrodAdaptive(absTolerance,
737 maxEvaluations)));
738 }
739
742 Size maxEvaluations) {
743 return Integration(Simpson,
744 ext::shared_ptr<Integrator>(
745 new SimpsonIntegral(absTolerance,
746 maxEvaluations)));
747 }
748
751 Size maxEvaluations) {
752 return Integration(Trapezoid,
753 ext::shared_ptr<Integrator>(
754 new TrapezoidIntegral<Default>(absTolerance,
755 maxEvaluations)));
756 }
757
760 QL_REQUIRE(intOrder <= 192, "maximum integraton order (192) exceeded");
761 return Integration(GaussLaguerre,
762 ext::shared_ptr<GaussianQuadrature>(
763 new GaussLaguerreIntegration(intOrder)));
764 }
765
768 return Integration(GaussLegendre,
769 ext::shared_ptr<GaussianQuadrature>(
770 new GaussLegendreIntegration(intOrder)));
771 }
772
775 return Integration(GaussChebyshev,
776 ext::shared_ptr<GaussianQuadrature>(
777 new GaussChebyshevIntegration(intOrder)));
778 }
779
782 return Integration(GaussChebyshev2nd,
783 ext::shared_ptr<GaussianQuadrature>(
784 new GaussChebyshev2ndIntegration(intOrder)));
785 }
786
789 return Integration(
790 DiscreteSimpson, ext::shared_ptr<Integrator>(
791 new DiscreteSimpsonIntegrator(evaluations)));
792 }
793
796 return Integration(
797 DiscreteTrapezoid, ext::shared_ptr<Integrator>(
798 new DiscreteTrapezoidIntegrator(evaluations)));
799 }
800
802 if (integrator_ != nullptr) {
803 return integrator_->numberOfEvaluations();
804 } else if (gaussianQuadrature_ != nullptr) {
805 return gaussianQuadrature_->order();
806 } else {
807 QL_FAIL("neither Integrator nor GaussianQuadrature given");
808 }
809 }
810
812 return intAlgo_ == GaussLobatto
813 || intAlgo_ == GaussKronrod
814 || intAlgo_ == Simpson
815 || intAlgo_ == Trapezoid;
816 }
817
819 Real c_inf,
820 const ext::function<Real(Real)>& f,
821 const ext::function<Real()>& maxBound) const {
822
823 Real retVal;
824
825 switch(intAlgo_) {
826 case GaussLaguerre:
827 retVal = (*gaussianQuadrature_)(f);
828 break;
829 case GaussLegendre:
830 case GaussChebyshev:
831 case GaussChebyshev2nd:
832 retVal = (*gaussianQuadrature_)(integrand1(c_inf, f));
833 break;
834 case Simpson:
835 case Trapezoid:
836 case GaussLobatto:
837 case GaussKronrod:
838 if (maxBound && maxBound() != Null<Real>())
839 retVal = (*integrator_)(f, 0.0, maxBound());
840 else
841 retVal = (*integrator_)(integrand2(c_inf, f), 0.0, 1.0);
842 break;
843 case DiscreteTrapezoid:
844 case DiscreteSimpson:
845 if (maxBound && maxBound() != Null<Real>())
846 retVal = (*integrator_)(f, 0.0, maxBound());
847 else
848 retVal = (*integrator_)(integrand3(c_inf, f), 0.0, 1.0);
849 break;
850 default:
851 QL_FAIL("unknwon integration algorithm");
852 }
853
854 return retVal;
855 }
856
858 Real c_inf,
859 const ext::function<Real(Real)>& f,
860 Real maxBound) const {
861
863 c_inf, f, [=](){ return maxBound; });
864 }
865
867 Real c_inf, Real epsilon, Real v0, Real t) {
868
869 const Real uMaxGuess = -std::log(epsilon)/c_inf;
870 const Real uMaxStep = 0.1*uMaxGuess;
871
872 const Real uMax = Brent().solve(u_Max(c_inf, epsilon),
873 QL_EPSILON*uMaxGuess, uMaxGuess, uMaxStep);
874
875 const Real uHatMax = Brent().solve(uHat_Max(0.5*v0*t, epsilon),
876 QL_EPSILON*std::sqrt(uMaxGuess),
877 std::sqrt(uMaxGuess), 0.1*std::sqrt(uMaxGuess));
878
879 return std::max(uMax, uHatMax);
880 }
881}
static Real andersenPiterbargIntegrationLimit(Real c_inf, Real epsilon, Real v0, Real t)
Integration(Algorithm intAlgo, ext::shared_ptr< GaussianQuadrature > quadrature)
static Integration discreteSimpson(Size evaluation=1000)
static Integration gaussChebyshev2nd(Size integrationOrder=128)
static Integration simpson(Real absTolerance, Size maxEvaluations=1000)
static Integration gaussLegendre(Size integrationOrder=128)
static Integration gaussChebyshev(Size integrationOrder=128)
static Integration gaussLobatto(Real relTolerance, Real absTolerance, Size maxEvaluations=1000)
Real calculate(Real c_inf, const ext::function< Real(Real)> &f, const ext::function< Real()> &maxBound={}) const
static Integration trapezoid(Real absTolerance, Size maxEvaluations=1000)
static Integration gaussLaguerre(Size integrationOrder=128)
static Integration gaussKronrod(Real absTolerance, Size maxEvaluations=1000)
static Integration discreteTrapezoid(Size evaluation=1000)
analytic Heston-model engine based on Fourier transform
std::complex< Real > lnChF(const std::complex< Real > &z, Time t) const
static void doCalculation(Real riskFreeDiscount, Real dividendDiscount, Real spotPrice, Real strikePrice, Real term, Real kappa, Real theta, Real sigma, Real v0, Real rho, const TypePayoff &type, const Integration &integration, ComplexLogFormula cpxLog, const AnalyticHestonEngine *enginePtr, Real &value, Size &evaluations)
AnalyticHestonEngine(const ext::shared_ptr< HestonModel > &model, Real relTolerance, Size maxEvaluations)
const ext::shared_ptr< Integration > integration_
std::complex< Real > chF(const std::complex< Real > &z, Time t) const
static ComplexLogFormula optimalControlVariate(Time t, Real v0, Real kappa, Real theta, Real sigma, Real rho)
Black 1976 calculator class.
Brent 1-D solver
Definition: brent.hpp:37
Gauss-Chebyshev integration (second kind)
Integral of a 1-dimensional function using the Gauss-Kronrod methods.
generalized Gauss-Laguerre integration
Integral of a one-dimensional function.
Base class for some pricing engine on a particular model.
Heston model for the stochastic volatility of an asset.
Definition: hestonmodel.hpp:42
template class providing a null value for a given type.
Definition: null.hpp:76
Integral of a one-dimensional function.
Real solve(const F &f, Real accuracy, Real guess, Real step) const
Definition: solver1d.hpp:84
Integral of a one-dimensional function.
Intermediate class for put/call payoffs.
Definition: payoffs.hpp:49
Option::Type optionType() const
Definition: payoffs.hpp:51
Vanilla option (no discrete dividends, no barriers) on a single asset.
#define QL_EPSILON
Definition: qldefines.hpp:178
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
T squared(T x)
Definition: functional.hpp:37
STL namespace.