Coverage Report

Created: 2025-08-28 06:30

/src/quantlib/ql/methods/finitedifferences/operators/fdmblackscholesop.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) 2008 Andreas Gaida
5
 Copyright (C) 2008, 2009 Ralph Schreyer
6
 Copyright (C) 2008, 2009 Klaus Spanderen
7
8
 This file is part of QuantLib, a free-software/open-source library
9
 for financial quantitative analysts and developers - http://quantlib.org/
10
11
 QuantLib is free software: you can redistribute it and/or modify it
12
 under the terms of the QuantLib license.  You should have received a
13
 copy of the license along with this program; if not, please email
14
 <quantlib-dev@lists.sf.net>. The license is also available online at
15
 <http://quantlib.org/license.shtml>.
16
17
 This program is distributed in the hope that it will be useful, but WITHOUT
18
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 FOR A PARTICULAR PURPOSE.  See the license for more details.
20
*/
21
22
#include <ql/instruments/payoffs.hpp>
23
#include <ql/math/functional.hpp>
24
#include <ql/methods/finitedifferences/meshers/fdmmesher.hpp>
25
#include <ql/methods/finitedifferences/operators/fdmblackscholesop.hpp>
26
#include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp>
27
#include <ql/methods/finitedifferences/operators/secondderivativeop.hpp>
28
#include <utility>
29
30
namespace QuantLib {
31
32
    FdmBlackScholesOp::FdmBlackScholesOp(
33
        const ext::shared_ptr<FdmMesher>& mesher,
34
        const ext::shared_ptr<GeneralizedBlackScholesProcess>& bsProcess,
35
        Real strike,
36
        bool localVol,
37
        Real illegalLocalVolOverwrite,
38
        Size direction,
39
        ext::shared_ptr<FdmQuantoHelper> quantoHelper)
40
0
    : mesher_(mesher), rTS_(bsProcess->riskFreeRate().currentLink()),
41
0
      qTS_(bsProcess->dividendYield().currentLink()),
42
0
      volTS_(bsProcess->blackVolatility().currentLink()),
43
0
      localVol_((localVol) ? bsProcess->localVolatility().currentLink() :
44
0
                             ext::shared_ptr<LocalVolTermStructure>()),
45
0
      x_((localVol) ? Array(Exp(mesher->locations(direction))) : Array()),
46
0
      dxMap_(FirstDerivativeOp(direction, mesher)), dxxMap_(SecondDerivativeOp(direction, mesher)),
47
0
      mapT_(direction, mesher), strike_(strike),
48
0
      illegalLocalVolOverwrite_(illegalLocalVolOverwrite), direction_(direction),
49
0
      quantoHelper_(std::move(quantoHelper)) {}
50
51
0
    void FdmBlackScholesOp::setTime(Time t1, Time t2) {
52
0
        const Rate r = rTS_->forwardRate(t1, t2, Continuous).rate();
53
0
        const Rate q = qTS_->forwardRate(t1, t2, Continuous).rate();
54
55
0
        if (localVol_ != nullptr) {
56
0
            Array v(mesher_->layout()->size());
57
0
            for (const auto& iter : *mesher_->layout()) {
58
0
                const Size i = iter.index();
59
60
0
                if (illegalLocalVolOverwrite_ < 0.0) {
61
0
                    v[i] = squared(localVol_->localVol(0.5*(t1+t2), x_[i], true));
62
0
                }
63
0
                else {
64
0
                    try {
65
0
                        v[i] = squared(localVol_->localVol(0.5*(t1+t2), x_[i], true));
66
0
                    } catch (Error&) {
67
0
                        v[i] = squared(illegalLocalVolOverwrite_);
68
0
                    }
69
0
                }
70
0
            }
71
72
0
            if (quantoHelper_ != nullptr) {
73
0
                mapT_.axpyb(r - q - 0.5*v
74
0
                    - quantoHelper_->quantoAdjustment(Sqrt(v), t1, t2),
75
0
                    dxMap_, dxxMap_.mult(0.5*v), Array(1, -r));
76
0
            } else {
77
0
                mapT_.axpyb(r - q - 0.5*v, dxMap_,
78
0
                            dxxMap_.mult(0.5*v), Array(1, -r));
79
0
            }
80
0
        } else {
81
0
            const Real v
82
0
                = volTS_->blackForwardVariance(t1, t2, strike_)/(t2-t1);
83
84
0
            if (quantoHelper_ != nullptr) {
85
0
                mapT_.axpyb(
86
0
                    Array(1, r - q - 0.5*v)
87
0
                        - quantoHelper_->quantoAdjustment(
88
0
                            Array(1, std::sqrt(v)), t1, t2),
89
0
                    dxMap_,
90
0
                    dxxMap_.mult(0.5*Array(mesher_->layout()->size(), v)),
91
0
                    Array(1, -r));
92
0
            } else {
93
0
                mapT_.axpyb(Array(1, r - q - 0.5*v), dxMap_,
94
0
                    dxxMap_.mult(0.5*Array(mesher_->layout()->size(), v)),
95
0
                    Array(1, -r));
96
0
            }
97
0
        }
98
0
    }
99
100
0
    Size FdmBlackScholesOp::size() const { return 1U; }
101
102
0
    Array FdmBlackScholesOp::apply(const Array& u) const {
103
0
        return mapT_.apply(u);
104
0
    }
105
106
    Array FdmBlackScholesOp::apply_direction(Size direction,
107
0
                                             const Array& r) const {
108
0
        if (direction == direction_)
109
0
            return mapT_.apply(r);
110
0
        else {
111
0
            return Array(r.size(), 0.0);
112
0
        }
113
0
    }
114
115
0
    Array FdmBlackScholesOp::apply_mixed(const Array& r) const {
116
0
        return Array(r.size(), 0.0);
117
0
    }
118
119
    Array FdmBlackScholesOp::solve_splitting(Size direction,
120
0
                                             const Array& r, Real dt) const {
121
0
        if (direction == direction_)
122
0
            return mapT_.solve_splitting(r, dt, 1.0);
123
0
        else {
124
0
            return r;
125
0
        }
126
0
    }
127
128
    Array FdmBlackScholesOp::preconditioner(const Array& r,
129
0
                                            Real dt) const {
130
0
        return solve_splitting(direction_, r, dt);
131
0
    }
132
133
0
    std::vector<SparseMatrix> FdmBlackScholesOp::toMatrixDecomp() const {
134
0
        return std::vector<SparseMatrix>(1, mapT_.toMatrix());
135
0
    }
136
137
}