Coverage Report

Created: 2025-11-04 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/models/marketmodels/proxygreekengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2006 Mark Joshi
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/models/marketmodels/constrainedevolver.hpp>
21
#include <ql/models/marketmodels/curvestate.hpp>
22
#include <ql/models/marketmodels/discounter.hpp>
23
#include <ql/models/marketmodels/evolutiondescription.hpp>
24
#include <ql/models/marketmodels/proxygreekengine.hpp>
25
#include <algorithm>
26
#include <utility>
27
28
namespace QuantLib {
29
30
    ProxyGreekEngine::ProxyGreekEngine(
31
        ext::shared_ptr<MarketModelEvolver> evolver,
32
        std::vector<std::vector<ext::shared_ptr<ConstrainedEvolver> > > constrainedEvolvers,
33
        std::vector<std::vector<std::vector<Real> > > diffWeights,
34
        std::vector<Size> startIndexOfConstraint,
35
        std::vector<Size> endIndexOfConstraint,
36
        const Clone<MarketModelMultiProduct>& product,
37
        Real initialNumeraireValue)
38
0
    : originalEvolver_(std::move(evolver)), constrainedEvolvers_(std::move(constrainedEvolvers)),
39
0
      diffWeights_(std::move(diffWeights)),
40
0
      startIndexOfConstraint_(std::move(startIndexOfConstraint)),
41
0
      endIndexOfConstraint_(std::move(endIndexOfConstraint)), product_(product),
42
0
      initialNumeraireValue_(initialNumeraireValue), numberProducts_(product->numberOfProducts()),
43
0
      numerairesHeld_(product->numberOfProducts()),
44
0
      numberCashFlowsThisStep_(product->numberOfProducts()),
45
0
      cashFlowsGenerated_(product->numberOfProducts()) {
46
0
        for (Size i=0; i<numberProducts_; ++i)
47
0
            cashFlowsGenerated_[i].resize(
48
0
                       product_->maxNumberOfCashFlowsPerProductPerStep());
49
50
0
        const std::vector<Time>& cashFlowTimes =
51
0
            product_->possibleCashFlowTimes();
52
0
        const std::vector<Rate>& rateTimes = product_->evolution().rateTimes();
53
0
        Size n = cashFlowTimes.size();
54
0
        discounters_.reserve(n);
55
0
        for (Size j=0; j<n; ++j)
56
0
            discounters_.emplace_back(cashFlowTimes[j], rateTimes);
57
0
        const std::vector<Rate>& evolutionTimes =
58
0
            product_->evolution().evolutionTimes();
59
0
        constraints_.resize(evolutionTimes.size());
60
0
        constraintsActive_.resize(evolutionTimes.size());
61
0
    }
62
63
    void ProxyGreekEngine::singlePathValues(
64
              std::vector<Real>& values,
65
0
              std::vector<std::vector<std::vector<Real> > >& modifiedValues) {
66
0
        singleEvolverValues(*originalEvolver_, values, true);
67
0
        for (Size i=0; i<constrainedEvolvers_.size(); ++i) {
68
0
            for (Size j=0; j<constrainedEvolvers_[i].size(); ++j) {
69
0
                constrainedEvolvers_[i][j]->setThisConstraint(
70
0
                                            constraints_, constraintsActive_);
71
0
                singleEvolverValues(*(constrainedEvolvers_[i][j]),
72
0
                                    modifiedValues[i][j]);
73
0
            }
74
0
        }
75
0
    }
76
77
    void ProxyGreekEngine::multiplePathValues(
78
                  SequenceStatisticsInc& stats,
79
                  std::vector<std::vector<SequenceStatisticsInc> >& modifiedStats,
80
0
                  Size numberOfPaths) {
81
0
        Size N = product_->numberOfProducts();
82
83
0
        std::vector<Real> values(N);
84
0
        std::vector<std::vector<std::vector<Real> > > modifiedValues;
85
0
        modifiedValues.resize(constrainedEvolvers_.size());
86
0
        for (Size i=0; i<modifiedValues.size(); ++i) {
87
0
            modifiedValues[i].resize(constrainedEvolvers_[i].size());
88
0
            for (auto& j : modifiedValues[i])
89
0
                j.resize(N);
90
0
        }
91
92
0
        std::vector<Real> results(N);
93
94
0
        for (Size i=0; i<numberOfPaths; ++i) {
95
0
            singlePathValues(values, modifiedValues);
96
0
            stats.add(values);
97
98
0
            for (Size j=0; j<diffWeights_.size(); ++j) {
99
0
                for (Size k=0; k<diffWeights_[j].size(); ++k) {
100
0
                    const std::vector<Real>& weights = diffWeights_[j][k];
101
0
                    for (Size l=0; l<N; ++l) {
102
0
                        results[l] = weights[0]*values[l];
103
0
                        for (Size n=1; n<weights.size(); ++n)
104
0
                            results[l] += weights[n]*modifiedValues[j][n-1][l];
105
0
                    }
106
0
                    modifiedStats[j][k].add(results);
107
0
                }
108
0
            }
109
0
        }
110
0
    }
111
112
    void ProxyGreekEngine::singleEvolverValues(MarketModelEvolver& evolver,
113
                                               std::vector<Real>& values,
114
0
                                               bool storeRates) {
115
116
0
        std::fill(numerairesHeld_.begin(), numerairesHeld_.end(), 0.0);
117
0
        Real weight = evolver.startNewPath();
118
0
        product_->reset();
119
0
        Real principalInNumerairePortfolio = 1.0;
120
121
0
        if (storeRates)
122
0
            constraintsActive_ =false;
123
        //            std::fill(constraintsActive_.begin(),
124
        //                    constraintsActive_.end(),
125
        //                  false);
126
        //  }
127
128
0
        bool done = false;
129
0
        do {
130
0
            Size thisStep = evolver.currentStep();
131
0
            weight *= evolver.advanceStep();
132
0
            done = product_->nextTimeStep(evolver.currentState(),
133
0
                                          numberCashFlowsThisStep_,
134
0
                                          cashFlowsGenerated_);
135
0
            if (storeRates) {
136
0
                constraints_[thisStep] = evolver.currentState().swapRate(
137
0
                                        startIndexOfConstraint_[thisStep],
138
0
                                        endIndexOfConstraint_[thisStep]);
139
0
                constraintsActive_[thisStep] = true;
140
0
            }
141
142
0
            Size numeraire =
143
0
                evolver.numeraires()[thisStep];
144
145
            // for each product...
146
0
            for (Size i=0; i<numberProducts_; ++i) {
147
                // ...and each cash flow...
148
0
                const std::vector<MarketModelMultiProduct::CashFlow>& cashflows =
149
0
                    cashFlowsGenerated_[i];
150
0
                for (Size j=0; j<numberCashFlowsThisStep_[i]; ++j) {
151
                    // ...convert the cash flow to numeraires.
152
                    // This is done by calculating the number of
153
                    // numeraire bonds corresponding to such cash flow...
154
0
                    const MarketModelDiscounter& discounter =
155
0
                        discounters_[cashflows[j].timeIndex];
156
157
0
                    Real bonds = cashflows[j].amount *
158
0
                        discounter.numeraireBonds(evolver.currentState(),
159
0
                                                  numeraire);
160
161
                    // ...and adding the newly bought bonds to the number
162
                    // of numeraires held.
163
0
                    numerairesHeld_[i] +=
164
0
                        weight*bonds/principalInNumerairePortfolio;
165
0
                }
166
0
            }
167
168
0
            if (!done) {
169
170
                // The numeraire might change between steps. This implies
171
                // that we might have to convert the numeraire bonds for
172
                // this step into a corresponding amount of numeraire
173
                // bonds for the next step. This can be done by changing
174
                // the principal of the numeraire and updating the number
175
                // of bonds in the numeraire portfolio accordingly.
176
177
0
                Size nextNumeraire = evolver.numeraires()[thisStep+1];
178
179
0
                principalInNumerairePortfolio *=
180
0
                    evolver.currentState().discountRatio(numeraire,
181
0
                                                         nextNumeraire);
182
0
            }
183
184
0
        } while (!done);
185
186
0
        for (Size i=0; i<numerairesHeld_.size(); ++i)
187
0
            values[i] = numerairesHeld_[i] * initialNumeraireValue_;
188
189
0
    }
190
191
}