Coverage Report

Created: 2026-06-23 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/processes/gjrgarchprocess.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2008 Yee Man Chan
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/math/distributions/chisquaredistribution.hpp>
21
#include <ql/math/distributions/normaldistribution.hpp>
22
#include <ql/processes/eulerdiscretization.hpp>
23
#include <ql/processes/gjrgarchprocess.hpp>
24
#include <ql/quotes/simplequote.hpp>
25
#include <utility>
26
27
namespace QuantLib {
28
29
    GJRGARCHProcess::GJRGARCHProcess(Handle<YieldTermStructure> riskFreeRate,
30
                                     Handle<YieldTermStructure> dividendYield,
31
                                     Handle<Quote> s0,
32
                                     Real v0,
33
                                     Real omega,
34
                                     Real alpha,
35
                                     Real beta,
36
                                     Real gamma,
37
                                     Real lambda,
38
                                     Real daysPerYear,
39
                                     Discretization d)
40
0
    : StochasticProcess(ext::shared_ptr<discretization>(new EulerDiscretization)),
41
0
      riskFreeRate_(std::move(riskFreeRate)), dividendYield_(std::move(dividendYield)),
42
0
      s0_(std::move(s0)), v0_(v0), omega_(omega), alpha_(alpha), beta_(beta), gamma_(gamma),
43
0
      lambda_(lambda), daysPerYear_(daysPerYear), discretization_(d) {
44
0
        registerWith(riskFreeRate_);
45
0
        registerWith(dividendYield_);
46
0
        registerWith(s0_);
47
0
    }
48
49
0
    Size GJRGARCHProcess::size() const {
50
0
        return 2;
51
0
    }
52
53
0
    Array GJRGARCHProcess::initialValues() const {
54
0
        return { s0_->value(), daysPerYear_*v0_ };
55
0
    }
56
57
0
    Array GJRGARCHProcess::drift(Time t, const Array& x) const {
58
0
        const Real N = CumulativeNormalDistribution()(lambda_);
59
0
        const Real n = std::exp(-lambda_*lambda_/2.0)/std::sqrt(2*M_PI);
60
0
        const Real q2 = 1.0 + lambda_*lambda_;
61
0
        const Real q3 = lambda_*n + N + lambda_*lambda_*N;
62
0
        const Real vol = (x[1] > 0.0) ? std::sqrt(x[1])
63
0
                         : (discretization_ == Reflection) ? Real(-std::sqrt(-x[1]))
64
0
                         : 0.0;
65
66
0
        return {
67
0
            riskFreeRate_->forwardRate(t, t, Continuous).rate()
68
0
               - dividendYield_->forwardRate(t, t, Continuous).rate()
69
0
               - 0.5 * vol * vol,
70
0
            daysPerYear_*daysPerYear_*omega_ + daysPerYear_*(beta_ 
71
0
                                             + alpha_*q2 + gamma_*q3 - 1.0) *
72
0
               ((discretization_==PartialTruncation) ? x[1] : vol*vol)
73
0
        };
74
0
    }
75
76
0
    Matrix GJRGARCHProcess::diffusion(Time, const Array& x) const {
77
        /* the correlation matrix is
78
           |  1   rho |
79
           | rho   1  |
80
           whose square root (which is used here) is
81
           |  1          0       |
82
           | rho   std::sqrt(1-rho^2) |
83
        */
84
0
        Matrix tmp(2,2);
85
0
        const Real N = CumulativeNormalDistribution()(lambda_);
86
0
        const Real n = std::exp(-lambda_*lambda_/2.0)/std::sqrt(2*M_PI);
87
0
        const Real sigma2 = 2.0 + 4.0*lambda_*lambda_;
88
0
        const Real q3 = lambda_*n + N + lambda_*lambda_*N;
89
0
        const Real Eml_e4 = lambda_*lambda_*lambda_*n + 5.0*lambda_*n 
90
0
            + 3.0*N + lambda_*lambda_*lambda_*lambda_*N 
91
0
            + 6.0*lambda_*lambda_*N;
92
0
        const Real sigma3 = Eml_e4 - q3*q3;
93
0
        const Real sigma12 = -2.0*lambda_;
94
0
        const Real sigma13 = -2.0*n - 2*lambda_*N;
95
0
        const Real sigma23 = 2.0*N + sigma12*sigma13;
96
0
        const Real vol = (x[1] > 0.0) ? std::sqrt(x[1])
97
0
                         : (discretization_ == Reflection) ? Real(- std::sqrt(-x[1]))
98
0
                         : 1e-8; // set vol to (almost) zero but still
99
                                 // expose some correlation information
100
0
        const Real rho1 = std::sqrt(daysPerYear_)*(alpha_*sigma12 
101
0
                                            + gamma_*sigma13) * vol * vol;
102
0
        const Real rho2 = vol*vol*std::sqrt(daysPerYear_)
103
0
            *std::sqrt(alpha_*alpha_*(sigma2 - sigma12*sigma12) 
104
0
                       + gamma_*gamma_*(sigma3 - sigma13*sigma13) 
105
0
                       + 2.0*alpha_*gamma_*(sigma23 - sigma12*sigma13)); 
106
107
            // tmp[0][0], tmp[0][1] are the coefficients of dW_1 and dW_2 
108
            // in asset return stochastic process
109
0
        tmp[0][0] = vol;  tmp[0][1] = 0.0;
110
0
        tmp[1][0] = rho1; tmp[1][1] = rho2;
111
0
        return tmp;
112
0
    }
113
114
    Array GJRGARCHProcess::apply(const Array& x0,
115
0
                                 const Array& dx) const {
116
0
        return { x0[0] * std::exp(dx[0]), x0[1] + dx[1] };
117
0
    }
118
119
    Array GJRGARCHProcess::evolve(Time t0, const Array& x0,
120
0
                                  Time dt, const Array& dw) const {
121
0
        Array retVal(2);
122
0
        Real vol, mu, nu;
123
124
0
        const Real sdt = std::sqrt(dt);
125
0
        const Real N = CumulativeNormalDistribution()(lambda_);
126
0
        const Real n = std::exp(-lambda_*lambda_/2.0)/std::sqrt(2*M_PI);
127
0
        const Real sigma2 = 2.0 + 4.0*lambda_*lambda_;
128
0
        const Real q2 = 1.0 + lambda_*lambda_;
129
0
        const Real q3 = lambda_*n + N + lambda_*lambda_*N;
130
0
        const Real Eml_e4 = lambda_*lambda_*lambda_*n + 5.0*lambda_*n 
131
0
            + 3.0*N + lambda_*lambda_*lambda_*lambda_*N 
132
0
            + 6.0*lambda_*lambda_*N;
133
0
        const Real sigma3 = Eml_e4 - q3*q3;
134
0
        const Real sigma12 = -2.0*lambda_;
135
0
        const Real sigma13 = -2.0*n - 2*lambda_*N;
136
0
        const Real sigma23 = 2.0*N + sigma12*sigma13;
137
0
        const Real rho1 = std::sqrt(daysPerYear_)*(alpha_*sigma12 + gamma_*sigma13);
138
0
        const Real rho2 = std::sqrt(daysPerYear_)
139
0
            *std::sqrt(alpha_*alpha_*(sigma2 - sigma12*sigma12) 
140
0
                       + gamma_*gamma_*(sigma3 - sigma13*sigma13) 
141
0
                       + 2.0*alpha_*gamma_*(sigma23 - sigma12*sigma13));
142
143
0
        switch (discretization_) {
144
          // For the definition of PartialTruncation, FullTruncation
145
          // and Reflection  see Lord, R., R. Koekkoek and D. van Dijk (2006),
146
          // "A Comparison of biased simulation schemes for
147
          //  stochastic volatility models",
148
          // Working Paper, Tinbergen Institute
149
0
          case PartialTruncation:
150
0
            vol = (x0[1] > 0.0) ? Real(std::sqrt(x0[1])) : 0.0;
151
0
            mu =    riskFreeRate_->forwardRate(t0, t0+dt, Continuous).rate()
152
0
                  - dividendYield_->forwardRate(t0, t0+dt, Continuous).rate()
153
0
                    - 0.5 * vol * vol;
154
0
            nu = daysPerYear_*daysPerYear_*omega_ 
155
0
                + daysPerYear_*(beta_ + alpha_*q2 + gamma_*q3 - 1.0) * x0[1];
156
157
0
            retVal[0] = x0[0] * std::exp(mu*dt+vol*dw[0]*sdt);
158
0
            retVal[1] = x0[1] + nu*dt + sdt*vol*vol*(rho1*dw[0] + rho2*dw[1]);
159
0
            break;
160
0
          case FullTruncation:
161
0
            vol = (x0[1] > 0.0) ? Real(std::sqrt(x0[1])) : 0.0;
162
0
            mu =    riskFreeRate_->forwardRate(t0, t0+dt, Continuous).rate()
163
0
                  - dividendYield_->forwardRate(t0, t0+dt, Continuous).rate()
164
0
                    - 0.5 * vol * vol;
165
0
            nu = daysPerYear_*daysPerYear_*omega_ 
166
0
                + daysPerYear_*(beta_ + alpha_*q2 + gamma_*q3 - 1.0) * vol *vol;
167
168
0
            retVal[0] = x0[0] * std::exp(mu*dt+vol*dw[0]*sdt);
169
0
            retVal[1] = x0[1] + nu*dt + sdt*vol*vol*(rho1*dw[0] + rho2*dw[1]);
170
0
            break;
171
0
          case Reflection:
172
0
            vol = std::sqrt(std::fabs(x0[1]));
173
0
            mu =    riskFreeRate_->forwardRate(t0, t0+dt, Continuous).rate()
174
0
                  - dividendYield_->forwardRate(t0, t0+dt, Continuous).rate()
175
0
                    - 0.5 * vol*vol;
176
0
            nu = daysPerYear_*daysPerYear_*omega_ 
177
0
                + daysPerYear_*(beta_ + alpha_*q2 + gamma_*q3 - 1.0) * vol * vol;
178
179
0
            retVal[0] = x0[0]*std::exp(mu*dt+vol*dw[0]*sdt);
180
0
            retVal[1] = vol*vol
181
0
                        +nu*dt + sdt*vol*vol*(rho1*dw[0] + rho2*dw[1]);
182
0
            break;
183
0
          default:
184
0
            QL_FAIL("unknown discretization schema");
185
0
        }
186
187
0
        return retVal;
188
0
    }
189
190
0
    const Handle<Quote>& GJRGARCHProcess::s0() const {
191
0
        return s0_;
192
0
    }
193
194
0
    const Handle<YieldTermStructure>& GJRGARCHProcess::dividendYield() const {
195
0
        return dividendYield_;
196
0
    }
197
198
0
    const Handle<YieldTermStructure>& GJRGARCHProcess::riskFreeRate() const {
199
0
        return riskFreeRate_;
200
0
    }
201
202
0
    Time GJRGARCHProcess::time(const Date& d) const {
203
0
        return riskFreeRate_->dayCounter().yearFraction(
204
0
                                           riskFreeRate_->referenceDate(), d);
205
0
    }
206
207
}