Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/experimental/finitedifferences/fdsimpleextoustorageengine.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) 2011 Klaus Spanderen
5
 Copyright (C) 2014 Ralph Schreyer
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
/*! \file fdsimpleextoustorageengine.cpp
22
    \brief Finite Differences extended OU engine for simple storage options
23
*/
24
25
#include <ql/experimental/finitedifferences/fdmexpextouinnervaluecalculator.hpp>
26
#include <ql/experimental/finitedifferences/fdmsimple2dextousolver.hpp>
27
#include <ql/experimental/finitedifferences/fdsimpleextoustorageengine.hpp>
28
#include <ql/experimental/processes/extendedornsteinuhlenbeckprocess.hpp>
29
#include <ql/math/comparison.hpp>
30
#include <ql/methods/finitedifferences/meshers/fdmmeshercomposite.hpp>
31
#include <ql/methods/finitedifferences/meshers/fdmsimpleprocess1dmesher.hpp>
32
#include <ql/methods/finitedifferences/meshers/predefined1dmesher.hpp>
33
#include <ql/methods/finitedifferences/meshers/uniform1dmesher.hpp>
34
#include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp>
35
#include <ql/methods/finitedifferences/solvers/fdmbackwardsolver.hpp>
36
#include <ql/methods/finitedifferences/solvers/fdmsolverdesc.hpp>
37
#include <ql/methods/finitedifferences/stepconditions/fdmsimplestoragecondition.hpp>
38
#include <ql/methods/finitedifferences/stepconditions/fdmstepconditioncomposite.hpp>
39
#include <ql/methods/finitedifferences/utilities/fdminnervaluecalculator.hpp>
40
#include <ql/pricingengines/vanilla/fdsimplebsswingengine.hpp>
41
#include <ql/termstructures/yieldtermstructure.hpp>
42
#include <utility>
43
44
namespace QuantLib {
45
46
    namespace {
47
        class FdmStorageValue : public FdmInnerValueCalculator {
48
          public:
49
            explicit FdmStorageValue(ext::shared_ptr<FdmMesher> mesher)
50
0
            : mesher_(std::move(mesher)) {}
51
52
0
            Real innerValue(const FdmLinearOpIterator& iter, Time) override {
53
0
                const Real s = std::exp(mesher_->location(iter, 0));
54
0
                const Real v = mesher_->location(iter, 1);
55
0
                return s*v;
56
0
            }
57
0
            Real avgInnerValue(const FdmLinearOpIterator& iter, Time t) override {
58
0
                return innerValue(iter, t);
59
0
            }
60
61
          private:
62
            const ext::shared_ptr<FdmMesher> mesher_;
63
64
        };
65
66
        class LessButNotCloseEnough {
67
          public:
68
0
            bool operator()(Real a, Real b) const {
69
0
                return !(close_enough(a, b, 100) || b < a);
70
0
            }
71
        };
72
    }
73
74
    FdSimpleExtOUStorageEngine::FdSimpleExtOUStorageEngine(
75
        ext::shared_ptr<ExtendedOrnsteinUhlenbeckProcess> process,
76
        ext::shared_ptr<YieldTermStructure> rTS,
77
        Size tGrid,
78
        Size xGrid,
79
        Size yGrid,
80
        ext::shared_ptr<Shape> shape,
81
        const FdmSchemeDesc& schemeDesc)
82
0
    : process_(std::move(process)), rTS_(std::move(rTS)), tGrid_(tGrid), xGrid_(xGrid),
83
0
      yGrid_(yGrid), shape_(std::move(shape)), schemeDesc_(schemeDesc) {}
84
85
0
    void FdSimpleExtOUStorageEngine::calculate() const {
86
87
        // 1. Exercise
88
0
        QL_REQUIRE(arguments_.exercise->type() == Exercise::Bermudan,
89
0
                   "Bermudan exercise supported only");
90
91
        // 2. Mesher
92
0
        const Time maturity
93
0
            = rTS_->dayCounter().yearFraction(rTS_->referenceDate(),
94
0
                                              arguments_.exercise->lastDate());
95
96
0
        const ext::shared_ptr<Fdm1dMesher> xMesher(
97
0
                     new FdmSimpleProcess1dMesher(xGrid_, process_, maturity));
98
99
0
        ext::shared_ptr<Fdm1dMesher> storageMesher;
100
101
0
        if(yGrid_ == Null<Size>()){
102
            //elevator mesher
103
0
            std::vector<Real> storageValues(1, arguments_.capacity);
104
0
            storageValues.reserve(
105
0
                Size(arguments_.capacity/arguments_.changeRate)+1);
106
107
0
            for (Real level=0; level <= arguments_.capacity;
108
0
                    level+=arguments_.changeRate) {
109
0
                    storageValues.push_back(level);
110
0
                    storageValues.push_back(arguments_.capacity - level);
111
0
            }
112
113
0
            const std::set<Real, LessButNotCloseEnough>    orderedValues(
114
0
                storageValues.begin(), storageValues.end());
115
0
            storageValues.assign(orderedValues.begin(), orderedValues.end());
116
117
0
            storageMesher =    ext::shared_ptr<Fdm1dMesher>(
118
0
                new Predefined1dMesher(storageValues));
119
0
        }
120
0
        else {
121
            // uniform mesher
122
0
            storageMesher = ext::shared_ptr<Fdm1dMesher>(
123
0
                new Uniform1dMesher(0, arguments_.capacity, yGrid_));
124
0
        }
125
126
0
        const ext::shared_ptr<FdmMesher> mesher (
127
0
            new FdmMesherComposite(xMesher, storageMesher));
128
129
        // 3. Calculator
130
0
        ext::shared_ptr<FdmInnerValueCalculator> storageCalculator(
131
0
                                                  new FdmStorageValue(mesher));
132
133
        // 4. Step conditions
134
0
        std::list<ext::shared_ptr<StepCondition<Array> > > stepConditions;
135
0
        std::list<std::vector<Time> > stoppingTimes;
136
137
        // 4.1 Bermudan step conditions
138
0
        std::vector<Time> exerciseTimes;
139
0
        for (auto i : arguments_.exercise->dates()) {
140
0
            const Time t = rTS_->dayCounter().yearFraction(rTS_->referenceDate(), i);
141
142
0
            QL_REQUIRE(t >= 0, "exercise dates must not contain past date");
143
0
            exerciseTimes.push_back(t);
144
0
        }
145
0
        stoppingTimes.push_back(exerciseTimes);
146
147
0
        ext::shared_ptr<Payoff> payoff(
148
0
                                    new PlainVanillaPayoff(Option::Call, 0.0));
149
150
0
        ext::shared_ptr<FdmInnerValueCalculator> underlyingCalculator(
151
0
            new FdmExpExtOUInnerValueCalculator(payoff, mesher, shape_));
152
153
0
        stepConditions.push_back(ext::shared_ptr<StepCondition<Array> >(
154
0
            new FdmSimpleStorageCondition(exerciseTimes,
155
0
                                          mesher, underlyingCalculator,
156
0
                                          arguments_.changeRate)));
157
158
0
        ext::shared_ptr<FdmStepConditionComposite> conditions(
159
0
                new FdmStepConditionComposite(stoppingTimes, stepConditions));
160
161
        // 5. Boundary conditions
162
0
        const FdmBoundaryConditionSet boundaries;
163
164
        // 6. Solver
165
0
        FdmSolverDesc solverDesc = { mesher, boundaries, conditions,
166
0
                                     storageCalculator, maturity, tGrid_, 0 };
167
168
0
        ext::shared_ptr<FdmSimple2dExtOUSolver> solver(
169
0
                new FdmSimple2dExtOUSolver(
170
0
                           Handle<ExtendedOrnsteinUhlenbeckProcess>(process_),
171
0
                           rTS_, solverDesc, schemeDesc_));
172
173
0
        const Real x = process_->x0();
174
0
        const Real y = arguments_.load;
175
176
0
        results_.value = solver->valueAt(x, y);
177
0
    }
178
}