Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/methods/finitedifferences/operators/fdm2dblackscholesop.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2010 Klaus Spanderen
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
/*! \file fdm2dblackscholesop.cpp
21
*/
22
23
24
#include <ql/processes/blackscholesprocess.hpp>
25
#include <ql/methods/finitedifferences/meshers/fdmmesher.hpp>
26
#include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp>
27
#include <ql/methods/finitedifferences/operators/fdm2dblackscholesop.hpp>
28
#include <ql/methods/finitedifferences/operators/secondordermixedderivativeop.hpp>
29
#include <boost/numeric/ublas/matrix.hpp>
30
31
namespace QuantLib {
32
33
    Fdm2dBlackScholesOp::Fdm2dBlackScholesOp(
34
            const ext::shared_ptr<FdmMesher>& mesher,
35
            const ext::shared_ptr<GeneralizedBlackScholesProcess>& p1,
36
            const ext::shared_ptr<GeneralizedBlackScholesProcess>& p2,
37
            Real correlation,
38
            Time /*maturity*/,
39
            bool localVol,
40
            Real illegalLocalVolOverwrite)
41
0
    : mesher_(mesher),
42
0
      p1_(p1),
43
0
      p2_(p2),
44
      
45
0
      localVol1_((localVol) ? p1->localVolatility().currentLink()
46
0
                            : ext::shared_ptr<LocalVolTermStructure>()),
47
0
      localVol2_((localVol) ? p2->localVolatility().currentLink()
48
0
                            : ext::shared_ptr<LocalVolTermStructure>()),
49
                            
50
0
      x_((localVol) ? Array(Exp(mesher->locations(0))) : Array()),
51
0
      y_((localVol) ? Array(Exp(mesher->locations(1))) : Array()),
52
53
0
      opX_(mesher, p1, p1->x0(), localVol, illegalLocalVolOverwrite, 0),
54
0
      opY_(mesher, p2, p2->x0(), localVol, illegalLocalVolOverwrite, 1),
55
      
56
0
      corrMapT_(0, 1, mesher),
57
0
      corrMapTemplate_(SecondOrderMixedDerivativeOp(0, 1, mesher)
58
0
                      .mult(Array(mesher->layout()->size(), correlation))),
59
                      
60
0
      illegalLocalVolOverwrite_(illegalLocalVolOverwrite) { 
61
0
    }
62
           
63
0
    Size Fdm2dBlackScholesOp::size() const {
64
0
        return 2;
65
0
    }
66
    
67
0
    void Fdm2dBlackScholesOp::setTime(Time t1, Time t2) {
68
0
        opX_.setTime(t1, t2);
69
0
        opY_.setTime(t1, t2);
70
71
0
        if (localVol1_ != nullptr) {
72
0
            Array vol1(mesher_->layout()->size()), vol2(mesher_->layout()->size());
73
0
            for (const auto& iter : *mesher_->layout()) {
74
0
                const Size i = iter.index();
75
76
0
                if (illegalLocalVolOverwrite_ < 0.0) {
77
0
                    vol1[i] = localVol1_->localVol(0.5*(t1+t2), x_[i], true);
78
0
                    vol2[i] = localVol2_->localVol(0.5*(t1+t2), y_[i], true);
79
0
                }
80
0
                else {
81
0
                    try {
82
0
                        vol1[i] = localVol1_->localVol(0.5*(t1+t2), x_[i],true);
83
0
                    } catch (Error&) {
84
0
                        vol1[i] = illegalLocalVolOverwrite_;
85
0
                    }
86
0
                    try {
87
0
                        vol2[i] = localVol2_->localVol(0.5*(t1+t2), y_[i],true);
88
0
                    } catch (Error&) {
89
0
                        vol2[i] = illegalLocalVolOverwrite_;
90
0
                    }
91
92
0
                }
93
0
            }
94
0
            corrMapT_ = corrMapTemplate_.mult(vol1*vol2);
95
0
        } else {
96
0
            const Real vol1 = p1_
97
0
                    ->blackVolatility()->blackForwardVol(t1, t2, p1_->x0());
98
    
99
0
            const Real vol2 = p2_
100
0
                    ->blackVolatility()->blackForwardVol(t1, t2, p2_->x0());
101
            
102
0
            corrMapT_ = corrMapTemplate_
103
0
                      .mult(Array(mesher_->layout()->size(), vol1*vol2));
104
0
        }
105
106
0
        currentForwardRate_ = p1_->riskFreeRate()
107
0
                                 ->forwardRate(t1, t2, Continuous).rate();
108
0
    }
109
110
0
    Array Fdm2dBlackScholesOp::apply(const Array& x) const {
111
0
        return opX_.apply(x) + opY_.apply(x) + apply_mixed(x);
112
0
    }
113
    
114
0
    Array Fdm2dBlackScholesOp::apply_mixed(const Array& x) const {
115
0
        return corrMapT_.apply(x) + currentForwardRate_*x;
116
0
    }
117
    
118
    Array Fdm2dBlackScholesOp::apply_direction(
119
0
                                       Size direction, const Array& x) const {
120
0
        if (direction == 0) {
121
0
            return opX_.apply(x);
122
0
        }
123
0
        else if (direction == 1) {
124
0
            return opY_.apply(x);
125
0
        }
126
0
        else {
127
0
            QL_FAIL("direction is too large");
128
0
        }
129
0
    }
130
    
131
    Array Fdm2dBlackScholesOp::solve_splitting(Size direction,
132
0
                                               const Array& x, Real s) const {
133
0
        if (direction == 0) {
134
0
            return opX_.solve_splitting(direction, x, s);
135
0
        }
136
0
        else if (direction == 1) {
137
0
            return opY_.solve_splitting(direction, x, s);
138
0
        }
139
0
        else
140
0
            QL_FAIL("direction is too large");
141
0
    }
142
    
143
    Array Fdm2dBlackScholesOp::preconditioner(const Array& r, 
144
0
                                              Real dt) const {
145
0
        return solve_splitting(0, r, dt);
146
0
    }
147
148
0
    std::vector<SparseMatrix> Fdm2dBlackScholesOp::toMatrixDecomp() const {
149
0
        return {
150
0
            opX_.toMatrix(),
151
0
            opY_.toMatrix(),
152
0
            corrMapT_.toMatrix() +
153
0
            currentForwardRate_*boost::numeric::ublas::identity_matrix<Real>(
154
0
                    mesher_->layout()->size())
155
0
        };
156
0
    }
157
158
}