Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/pricingengines/vanilla/jumpdiffusionengine.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2004 Ferdinando Ametrano
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
 <https://www.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
21
#include <ql/exercise.hpp>
22
#include <ql/math/distributions/poissondistribution.hpp>
23
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
24
#include <ql/pricingengines/vanilla/jumpdiffusionengine.hpp>
25
#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
26
#include <ql/termstructures/yield/flatforward.hpp>
27
#include <ql/utilities/dataformatters.hpp>
28
#include <utility>
29
30
namespace QuantLib {
31
32
    JumpDiffusionEngine::JumpDiffusionEngine(ext::shared_ptr<Merton76Process> process,
33
                                             Real relativeAccuracy,
34
                                             Size maxIterations)
35
0
    : process_(std::move(process)), relativeAccuracy_(relativeAccuracy),
36
0
      maxIterations_(maxIterations) {
37
0
        registerWith(process_);
38
0
    }
39
40
41
0
    void JumpDiffusionEngine::calculate() const {
42
43
0
        Real jumpSquareVol = process_->logJumpVolatility()->value()
44
0
            * process_->logJumpVolatility()->value();
45
0
        Real muPlusHalfSquareVol = process_->logMeanJump()->value()
46
0
            + 0.5*jumpSquareVol;
47
        // mean jump size
48
0
        Real k = std::exp(muPlusHalfSquareVol) - 1.0;
49
0
        Real lambda = (k+1.0) * process_->jumpIntensity()->value();
50
51
0
        ext::shared_ptr<StrikedTypePayoff> payoff =
52
0
            ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
53
0
        QL_REQUIRE(payoff, "non-striked payoff given");
54
55
0
        Real variance =
56
0
            process_->blackVolatility()->blackVariance(
57
0
                                              arguments_.exercise->lastDate(),
58
0
                                              payoff->strike());
59
60
0
        DayCounter voldc = process_->blackVolatility()->dayCounter();
61
0
        Calendar volcal = process_->blackVolatility()->calendar();
62
0
        Date volRefDate = process_->blackVolatility()->referenceDate();
63
0
        Time t = voldc.yearFraction(volRefDate,
64
0
                                    arguments_.exercise->lastDate());
65
0
        Rate riskFreeRate = -std::log(process_->riskFreeRate()->discount(
66
0
                                          arguments_.exercise->lastDate()))/t;
67
0
        Date rateRefDate = process_->riskFreeRate()->referenceDate();
68
69
0
        PoissonDistribution p(lambda*t);
70
71
0
        Handle<Quote> stateVariable = process_->stateVariable();
72
0
        Handle<YieldTermStructure> dividendTS = process_->dividendYield();
73
0
        RelinkableHandle<YieldTermStructure> riskFreeTS(
74
0
                                                   *process_->riskFreeRate());
75
0
        RelinkableHandle<BlackVolTermStructure> volTS(
76
0
                                                *process_->blackVolatility());
77
78
0
        ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess(
79
0
                 new GeneralizedBlackScholesProcess(stateVariable, dividendTS,
80
0
                                                    riskFreeTS, volTS));
81
82
0
        AnalyticEuropeanEngine baseEngine(bsProcess);
83
84
0
        auto* baseArguments = dynamic_cast<VanillaOption::arguments*>(baseEngine.getArguments());
85
86
0
        baseArguments->payoff   = arguments_.payoff;
87
0
        baseArguments->exercise = arguments_.exercise;
88
89
0
        baseArguments->validate();
90
91
0
        const auto* baseResults =
92
0
            dynamic_cast<const VanillaOption::results*>(baseEngine.getResults());
93
94
0
        results_.value       = 0.0;
95
0
        results_.delta       = 0.0;
96
0
        results_.gamma       = 0.0;
97
0
        results_.theta       = 0.0;
98
0
        results_.vega        = 0.0;
99
0
        results_.rho         = 0.0;
100
0
        results_.dividendRho = 0.0;
101
102
0
        Real r, v, weight, lastContribution = 1.0;
103
0
        Size i;
104
0
        Real theta_correction;
105
        // Haug arbitrary criterium is:
106
        //for (i=0; i<11; i++) {
107
0
        for (i=0;  (lastContribution>relativeAccuracy_ && i<maxIterations_) 
108
0
                 || i < Size(lambda*t); i++) {
109
110
            // constant vol/rate assumption. It should be relaxed
111
0
            v = std::sqrt((variance + i*jumpSquareVol)/t);
112
0
            r = riskFreeRate - process_->jumpIntensity()->value()*k
113
0
                + i*muPlusHalfSquareVol/t;
114
0
            riskFreeTS.linkTo(ext::shared_ptr<YieldTermStructure>(new
115
0
                FlatForward(rateRefDate, r, voldc)));
116
0
            volTS.linkTo(ext::shared_ptr<BlackVolTermStructure>(new
117
0
                BlackConstantVol(rateRefDate, volcal, v, voldc)));
118
119
0
            baseArguments->validate();
120
0
            baseEngine.calculate();
121
122
0
            weight = p(Size(i));
123
0
            results_.value       += weight * baseResults->value;
124
0
            results_.delta       += weight * baseResults->delta;
125
0
            results_.gamma       += weight * baseResults->gamma;
126
0
            results_.vega        += weight * (std::sqrt(variance/t)/v)*
127
0
                                                           baseResults->vega;
128
            // theta modified
129
0
            theta_correction = baseResults->vega*((i*jumpSquareVol)/
130
0
                                                  (2.0*v*t*t)) +
131
0
                baseResults->rho*i*muPlusHalfSquareVol/(t*t);
132
0
            results_.theta += weight *(baseResults->theta + theta_correction +
133
0
                                  lambda*baseResults->value);
134
0
            if(i != 0){
135
0
                 results_.theta -= (p(Size(i-1))*lambda* baseResults->value);
136
0
            }
137
            //end theta calculation
138
0
            results_.rho         += weight * baseResults->rho;
139
0
            results_.dividendRho += weight * baseResults->dividendRho;
140
141
0
            lastContribution = std::fabs(baseResults->value /
142
0
                (std::fabs(results_.value)>QL_EPSILON ? results_.value : 1.0));
143
144
0
            lastContribution = std::max<Real>(lastContribution,
145
0
                std::fabs(baseResults->delta /
146
0
               (std::fabs(results_.delta)>QL_EPSILON ? results_.delta : 1.0)));
147
148
0
            lastContribution = std::max<Real>(lastContribution,
149
0
                std::fabs(baseResults->gamma /
150
0
               (std::fabs(results_.gamma)>QL_EPSILON ? results_.gamma : 1.0)));
151
152
0
            lastContribution = std::max<Real>(lastContribution,
153
0
                std::fabs(baseResults->theta /
154
0
               (std::fabs(results_.theta)>QL_EPSILON ? results_.theta : 1.0)));
155
156
0
            lastContribution = std::max<Real>(lastContribution,
157
0
                std::fabs(baseResults->vega /
158
0
               (std::fabs(results_.vega)>QL_EPSILON ? results_.vega : 1.0)));
159
160
0
            lastContribution = std::max<Real>(lastContribution,
161
0
                std::fabs(baseResults->rho /
162
0
               (std::fabs(results_.rho)>QL_EPSILON ? results_.rho : 1.0)));
163
164
0
            lastContribution = std::max<Real>(lastContribution,
165
0
                std::fabs(baseResults->dividendRho /
166
0
               (std::fabs(results_.dividendRho)>QL_EPSILON ?
167
0
                                          results_.dividendRho : 1.0)));
168
169
0
            lastContribution *= weight;
170
0
        }
171
0
        QL_ENSURE(i<maxIterations_,
172
0
                  i << " iterations have been not enough to reach "
173
0
                  << "the required " << relativeAccuracy_
174
0
                  << " accuracy. The " << io::ordinal(i)
175
0
                  << " addendum was " << lastContribution
176
0
                  << " while the running sum was " << results_.value);
177
0
    }
178
179
}
180