Coverage Report

Created: 2026-03-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/methods/finitedifferences/operators/fdmcirop.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2020 Lew Wei Hao
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/methods/finitedifferences/meshers/fdmmesher.hpp>
21
#include <ql/methods/finitedifferences/operators/fdmcirop.hpp>
22
#include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp>
23
#include <ql/methods/finitedifferences/operators/secondderivativeop.hpp>
24
#include <ql/methods/finitedifferences/operators/secondordermixedderivativeop.hpp>
25
#include <ql/processes/blackscholesprocess.hpp>
26
27
namespace QuantLib {
28
29
    FdmCIREquityPart::FdmCIREquityPart(
30
        const ext::shared_ptr<FdmMesher>& mesher,
31
        const ext::shared_ptr<GeneralizedBlackScholesProcess>& bsProcess,
32
        Real strike)
33
0
    : dxMap_ (FirstDerivativeOp(0, mesher)),
34
0
      dxxMap_(SecondDerivativeOp(0, mesher)),
35
0
      mapT_ (0, mesher),
36
0
      mesher_(mesher),
37
0
      qTS_(bsProcess->dividendYield().currentLink()),
38
0
      strike_(strike),
39
0
      sigma1_(bsProcess->blackVolatility().currentLink()){
40
0
    }
41
42
0
    void FdmCIREquityPart::setTime(Time t1, Time t2) {
43
0
        const Rate q = qTS_->forwardRate(t1, t2, Continuous).rate();
44
45
0
        const Real v = sigma1_->blackForwardVariance(t1, t2, strike_)/(t2-t1);
46
47
0
        mapT_.axpyb(mesher_->locations(1) - q - 0.5*v, dxMap_,
48
0
                        dxxMap_.mult(Array(mesher_->layout()->size(), v/2)), -0.5*mesher_->locations(1));
49
0
    }
50
51
0
    const TripleBandLinearOp& FdmCIREquityPart::getMap() const {
52
0
        return mapT_;
53
0
    }
54
55
    FdmCIRRatesPart::FdmCIRRatesPart(
56
        const ext::shared_ptr<FdmMesher>& mesher,
57
        Real sigma, Real kappa, Real theta)
58
0
    : dyMap_(SecondDerivativeOp(1, mesher)
59
0
                   .mult(sigma*sigma*mesher->locations(1))
60
0
                   .add(FirstDerivativeOp(1, mesher)
61
0
                   .mult(kappa*(theta - mesher->locations(1))))),
62
0
      mapT_(1, mesher),
63
0
      mesher_(mesher){
64
0
    }
65
66
0
    void FdmCIRRatesPart::setTime(Time t1, Time t2) {
67
0
        mapT_.axpyb(Array(), dyMap_, dyMap_, -0.5*mesher_->locations(1));
68
0
    }
69
70
0
    const TripleBandLinearOp& FdmCIRRatesPart::getMap() const {
71
0
        return mapT_;
72
0
    }
73
74
    FdmCIRMixedPart::FdmCIRMixedPart(
75
        const ext::shared_ptr<FdmMesher>& mesher,
76
        const ext::shared_ptr<CoxIngersollRossProcess> & cirProcess,
77
        const ext::shared_ptr<GeneralizedBlackScholesProcess> & bsProcess,
78
        const Real rho,
79
        const Real strike)
80
0
        : dyMap_(SecondOrderMixedDerivativeOp(0, 1, mesher)
81
0
                     .mult(Array(mesher->layout()->size(), 2*rho*cirProcess->volatility()))),
82
0
          mapT_(0, 1, mesher),
83
0
          mesher_(mesher),
84
0
          sigma1_(bsProcess->blackVolatility().currentLink()),
85
0
          strike_(strike){
86
0
    }
87
88
0
    void FdmCIRMixedPart::setTime(Time t1, Time t2) {
89
0
        const Real v = std::sqrt(sigma1_->blackForwardVariance(t1, t2, strike_)/(t2-t1));
90
0
        NinePointLinearOp op(dyMap_.mult(Array(mesher_->layout()->size(), v)));
91
0
        mapT_.swap(op);
92
0
    }
93
94
0
    const NinePointLinearOp& FdmCIRMixedPart::getMap() const {
95
0
        return mapT_;
96
0
    }
97
98
    FdmCIROp::FdmCIROp(
99
        const ext::shared_ptr<FdmMesher>& mesher,
100
        const ext::shared_ptr<CoxIngersollRossProcess> & cirProcess,
101
        const ext::shared_ptr<GeneralizedBlackScholesProcess> & bsProcess,
102
        const Real rho,
103
        const Real strike)
104
0
    : dxMap_(mesher,
105
0
             bsProcess,
106
0
             strike),
107
0
      dyMap_(mesher,
108
0
             cirProcess->volatility(),
109
0
             cirProcess->speed(),
110
0
             cirProcess->level()),
111
0
      dzMap_(mesher,
112
0
             cirProcess,
113
0
             bsProcess,
114
0
             rho,
115
0
             strike){
116
0
    }
117
118
119
0
    void FdmCIROp::setTime(Time t1, Time t2) {
120
0
        dxMap_.setTime(t1, t2);
121
0
        dyMap_.setTime(t1, t2);
122
0
        dzMap_.setTime(t1, t2);
123
0
    }
124
125
0
    Size FdmCIROp::size() const {
126
0
        return 2;
127
0
    }
128
129
0
    Array FdmCIROp::apply(const Array& u) const {
130
0
        Array dx = dxMap_.getMap().apply(u);
131
0
        Array dy = dyMap_.getMap().apply(u);
132
0
        Array dz = dzMap_.getMap().apply(u);
133
134
0
        return (dy + dx + dz);
135
0
    }
136
137
    Array FdmCIROp::apply_direction(Size direction,
138
0
                                    const Array& r) const {
139
0
        if (direction == 0)
140
0
            return dxMap_.getMap().apply(r);
141
0
        else if (direction == 1)
142
0
            return dyMap_.getMap().apply(r);
143
0
        else
144
0
            QL_FAIL("direction too large");
145
0
    }
146
147
0
    Array FdmCIROp::apply_mixed(const Array& r) const {
148
0
        return dzMap_.getMap().apply(r);
149
0
    }
150
151
    Array FdmCIROp::solve_splitting(Size direction,
152
0
                                    const Array& r, Real a) const {
153
0
        if (direction == 0) {
154
0
            return dxMap_.getMap().solve_splitting(r, a, 1.0);
155
0
        }
156
0
        else if (direction == 1) {
157
0
            return dyMap_.getMap().solve_splitting(r, a, 1.0);
158
0
        }
159
0
        else
160
0
            QL_FAIL("direction too large");
161
0
    }
162
163
0
    Array FdmCIROp::preconditioner(const Array& r, Real dt) const {
164
0
        return solve_splitting(1, solve_splitting(0, r, dt), dt) ;
165
0
    }
166
167
0
    std::vector<SparseMatrix> FdmCIROp::toMatrixDecomp() const {
168
0
        return {
169
0
            dxMap_.getMap().toMatrix(),
170
0
            dyMap_.getMap().toMatrix(),
171
0
            dzMap_.getMap().toMatrix()
172
0
        };
173
0
    }
174
175
}