Coverage Report

Created: 2025-08-28 06:30

/src/quantlib/ql/experimental/risk/sensitivityanalysis.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 Ferdinando Ametrano
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
 <http://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/experimental/risk/sensitivityanalysis.hpp>
21
#include <ql/quotes/simplequote.hpp>
22
#include <ql/instrument.hpp>
23
24
using std::vector;
25
using std::pair;
26
27
namespace QuantLib {
28
29
    QL_DEPRECATED_DISABLE_WARNING
30
31
    std::ostream& operator<<(std::ostream& out,
32
0
                             SensitivityAnalysis s) {
33
0
        switch (s) {
34
0
          case OneSide:
35
0
            return out << "OneSide";
36
0
          case Centered:
37
0
            return out << "Centered";
38
0
          default:
39
0
            QL_FAIL("unknown SensitivityAnalysis (" << Integer(s) << ")");
40
0
        }
41
0
    }
42
43
    Real aggregateNPV(const vector<ext::shared_ptr<Instrument> >& instruments,
44
0
                      const vector<Real>& quant) {
45
0
        Size n = instruments.size();
46
0
        Real npv = 0.0;
47
0
        if (quant.empty() || (quant.size()==1 && quant[0]==1.0)) {
48
0
            for (Size k=0; k<n; ++k)
49
0
                npv += instruments[k]->NPV();
50
0
        } else {
51
0
            QL_REQUIRE(quant.size()==n,
52
0
                       "dimension mismatch between instruments (" << n <<
53
0
                       ") and quantities (" << quant.size() << ")");
54
0
            for (Size k=0; k<n; ++k)
55
0
                npv += quant[k] * instruments[k]->NPV();
56
0
        }
57
0
        return npv;
58
0
    }
59
60
    pair<Real, Real>
61
    parallelAnalysis(const vector<Handle<SimpleQuote> >& quotes,
62
                     const vector<ext::shared_ptr<Instrument> >& instruments,
63
                     const vector<Real>& quantities,
64
                     Real shift,
65
                     SensitivityAnalysis type,
66
                     Real referenceNpv)
67
0
    {
68
0
        QL_REQUIRE(!quotes.empty(), "empty SimpleQuote vector");
69
0
        Size n = quotes.size();
70
71
0
        QL_REQUIRE(shift!=0.0, "zero shift not allowed");
72
73
0
        pair<Real, Real> result(0.0, 0.0);
74
0
        if (instruments.empty()) return result;
75
76
0
        if (referenceNpv==Null<Real>())
77
0
            referenceNpv = aggregateNPV(instruments, quantities);
78
79
0
        vector<Real> quoteValues(n, Null<Real>());
80
0
        for (Size i=0; i<n; ++i)
81
0
            if (quotes[i]->isValid())
82
0
                quoteValues[i] = quotes[i]->value();
83
0
        try {
84
0
            for (Size i=0; i<n; ++i)
85
0
                if (quotes[i]->isValid())
86
0
                    quotes[i]->setValue(quoteValues[i]+shift);
87
0
            Real npv = aggregateNPV(instruments, quantities);
88
0
            switch (type) {
89
0
              case OneSide:
90
0
                result.first = (npv-referenceNpv)/shift;
91
0
                result.second = Null<Real>();
92
0
                break;
93
0
              case Centered:
94
0
                {
95
0
                for (Size i=0; i<n; ++i)
96
0
                    if (quotes[i]->isValid())
97
0
                        quotes[i]->setValue(quoteValues[i]-shift);
98
0
                Real npv2 = aggregateNPV(instruments, quantities);
99
0
                result.first = (npv-npv2)/(2.0*shift);
100
0
                result.second = (npv-2.0*referenceNpv+npv2)/(shift*shift);
101
0
                }
102
0
                break;
103
0
              default:
104
0
                  QL_FAIL("unknown SensitivityAnalysis (" <<
105
0
                          Integer(type) << ")");
106
0
            }
107
0
            for (Size i=0; i<n; ++i)
108
0
                if (quotes[i]->isValid())
109
0
                    quotes[i]->setValue(quoteValues[i]);
110
0
        } catch (...) {
111
0
            for (Size i=0; i<n; ++i)
112
0
                if (quoteValues[i]!=Null<Real>())
113
0
                    quotes[i]->setValue(quoteValues[i]);
114
0
            throw;
115
0
        }
116
117
0
        return result;
118
0
    }
119
120
    pair<Real, Real> bucketAnalysis(const Handle<SimpleQuote>& quote,
121
                                    const vector<ext::shared_ptr<Instrument> >& instruments,
122
                                    const vector<Real>& quantities,
123
                                    Real shift,
124
                                    SensitivityAnalysis type,
125
0
                                    Real referenceNpv) {
126
0
        QL_REQUIRE(shift!=0.0, "zero shift not allowed");
127
128
0
        pair<Real, Real> result(0.0, 0.0);
129
0
        if (instruments.empty()) return result;
130
131
0
        if (referenceNpv==Null<Real>())
132
0
            referenceNpv = aggregateNPV(instruments, quantities);
133
134
0
        if (!quote->isValid()) return result;
135
0
        Real quoteValue = quote->value();
136
137
0
        try {
138
0
            quote->setValue(quoteValue+shift);
139
0
            Real npv = aggregateNPV(instruments, quantities);
140
0
            switch (type) {
141
0
              case OneSide:
142
0
                result.first = (npv-referenceNpv)/shift;
143
0
                result.second = Null<Real>();
144
0
                break;
145
0
              case Centered:
146
0
                {
147
0
                quote->setValue(quoteValue-shift);
148
0
                Real npv2 = aggregateNPV(instruments, quantities);
149
0
                result.first = (npv-npv2)/(2.0*shift);
150
0
                result.second = (npv-2.0*referenceNpv+npv2)/(shift*shift);
151
0
                }
152
0
                break;
153
0
              default:
154
0
                  QL_FAIL("unknown SensitivityAnalysis (" <<
155
0
                          Integer(type) << ")");
156
0
            }
157
0
            quote->setValue(quoteValue);
158
0
        } catch (...) {
159
0
            quote->setValue(quoteValue);
160
0
            throw;
161
0
        }
162
163
0
        return result;
164
0
    }
165
166
167
    void bucketAnalysis(vector<Real>& deltaVector, // delta result
168
                        vector<Real>& gammaVector, // gamma result
169
                        vector<Real>& refVals,
170
                        const Handle<SimpleQuote>& quote,
171
                        const vector<Handle<Quote> >& params,
172
                        Real shift,
173
0
                        SensitivityAnalysis type) {
174
0
        QL_REQUIRE(shift!=0.0, "zero shift not allowed");
175
176
0
        QL_REQUIRE(!params.empty(), "empty parameters vector");
177
0
        Size m = params.size();
178
0
        deltaVector.resize(m);
179
0
        gammaVector.resize(m);
180
181
0
        if (!quote->isValid()) {
182
0
            for (Size j=0; j<m; ++j) {
183
0
                deltaVector[j]=Null<Real>();
184
0
                gammaVector[j]=Null<Real>();
185
0
            }
186
0
            return;
187
0
        }
188
0
        Real quoteValue = quote->value();
189
190
0
        if (!refVals.empty()) {
191
0
            QL_REQUIRE(refVals.size()==m,
192
0
                       "referenceValues has size " <<
193
0
                       refVals.size() << ", instead of " << m);
194
0
        } else {
195
            // calculate parameters' reference values
196
0
            refVals = vector<Real>(m, Null<Real>());
197
0
            for (Size j=0; j<m; ++j) {
198
0
                if (params[j]->isValid()) // fault tolerant
199
0
                    refVals[j] = params[j]->value();
200
0
            }
201
0
        }
202
203
0
        try {
204
0
            switch (type) {
205
0
              case OneSide:
206
0
                {
207
0
                    quote->setValue(quoteValue+shift);
208
0
                    for (Size j=0; j<m; ++j) {
209
0
                        gammaVector[j] = Null<Real>();
210
0
                        if (refVals[j] != Null<Real>())
211
0
                            deltaVector[j] = (params[j]->value()-refVals[j])/shift;
212
0
                        else
213
0
                            deltaVector[j] = Null<Real>();
214
0
                    }
215
0
                }
216
0
                break;
217
0
              case Centered:
218
0
                {
219
0
                    quote->setValue(quoteValue+shift);
220
0
                    vector<Real> plus(m);
221
0
                    for (Size j=0; j<m; ++j) {
222
0
                        if (refVals[j] != Null<Real>())
223
0
                            plus[j] = params[j]->value();
224
0
                    }
225
0
                    quote->setValue(quoteValue-shift);
226
0
                    for (Size j=0; j<m; ++j) {
227
0
                        if (refVals[j] != Null<Real>()) {
228
0
                            Real minus = params[j]->value();
229
0
                            deltaVector[j] = (plus[j]-minus)/(2.0*shift);
230
0
                            gammaVector[j] = (plus[j]-2.0*refVals[j]+minus)/(shift*shift);
231
0
                        } else {
232
0
                            deltaVector[j] = Null<Real>();
233
0
                            gammaVector[j] = Null<Real>();
234
0
                        }
235
0
                    }
236
0
                }
237
0
                break;
238
0
              default:
239
0
                  QL_FAIL("unknown SensitivityAnalysis (" <<
240
0
                          Integer(type) << ")");
241
0
            } // end switch
242
243
            // restore the quote to its original state
244
0
            quote->setValue(quoteValue);
245
246
0
            return;
247
0
        } catch (...) {
248
            // restore the quote to its original state
249
0
            quote->setValue(quoteValue);
250
0
            throw;
251
0
        }
252
253
0
    }
254
255
256
257
258
259
    pair<vector<Real>, vector<Real> >
260
    bucketAnalysis(const vector<Handle<SimpleQuote> >& quotes,
261
                   const vector<ext::shared_ptr<Instrument> >& instr,
262
                   const vector<Real>& quant,
263
                   Real shift,
264
                   SensitivityAnalysis type)
265
0
    {
266
0
        QL_REQUIRE(!quotes.empty(), "empty SimpleQuote vector");
267
0
        Size n = quotes.size();
268
0
        pair<vector<Real>, vector<Real> > result(vector<Real>(n, 0.0),
269
0
                                                 vector<Real>(n, 0.0));
270
271
0
        if (instr.empty()) return result;
272
273
0
        Real npv = aggregateNPV(instr, quant);
274
275
0
        pair<Real, Real> tmp;
276
0
        for (Size i=0; i<n; ++i) {
277
0
            tmp = bucketAnalysis(quotes[i], instr, quant, shift, type, npv);
278
0
            result.first[i] = tmp.first;
279
0
            result.second[i] = tmp.second;
280
0
        }
281
282
0
        return result;
283
0
    }
284
285
    void
286
    bucketAnalysis(std::vector<std::vector<Real> >& deltaMatrix, // result
287
                   std::vector<std::vector<Real> >& gammaMatrix, // result
288
                   const vector<Handle<SimpleQuote> >& quotes,
289
                   const vector<Handle<Quote> >& parameters,
290
                   Real shift,
291
                   SensitivityAnalysis type)
292
0
    {
293
0
        QL_REQUIRE(!quotes.empty(), "empty SimpleQuote vector");
294
0
        QL_REQUIRE(!parameters.empty(), "empty parameters vector");
295
296
0
        Size n = quotes.size();
297
0
        deltaMatrix.resize(n);
298
0
        gammaMatrix.resize(n);
299
300
0
        Size m = parameters.size();
301
0
        vector<Real> referenceValues(m, Null<Real>());
302
0
        for (Size i=0; i<m; ++i) {
303
0
            if (parameters[i]->isValid())
304
0
                referenceValues[i] = parameters[i]->value();
305
0
        }
306
307
0
        for (Size i=0; i<n; ++i) {
308
0
            bucketAnalysis(deltaMatrix[i], gammaMatrix[i], referenceValues,
309
0
                           quotes[i], parameters, shift, type);
310
0
        }
311
0
    }
312
313
    pair<vector<vector<Real> >, vector<vector<Real> > >
314
    bucketAnalysis(const vector<vector<Handle<SimpleQuote> > >& quotes,
315
                   const vector<ext::shared_ptr<Instrument> >& instr,
316
                   const vector<Real>& quant,
317
                   Real shift,
318
                   SensitivityAnalysis type)
319
0
    {
320
0
        QL_REQUIRE(!quotes.empty(), "empty SimpleQuote range");
321
0
        Size n = quotes.size();
322
0
        vector<vector<Real> > first(n);
323
0
        vector<vector<Real> > second(n);
324
0
        for (Size i=0; i<n; ++i) {
325
0
            Size tmp = quotes[i].size();
326
0
            first[i] = vector<Real>(tmp, 0.0);
327
0
            second[i] = vector<Real>(tmp, 0.0);
328
0
        }
329
330
0
        pair<vector<vector<Real> >, vector<vector<Real> > >
331
0
            result(first, second);
332
333
0
        if (instr.empty()) return result;
334
335
0
        Real npv = aggregateNPV(instr, quant);
336
337
0
        pair<Real, Real> tmp;
338
0
        for (Size i=0; i<n; ++i) {
339
0
          for (Size j=0; j<quotes[i].size(); ++j) {
340
0
            tmp = bucketAnalysis(quotes[i][j], instr, quant, shift, type, npv);
341
0
            result.first[i][j] = tmp.first;
342
0
            result.second[i][j] = tmp.second;
343
0
          }
344
0
        }
345
346
0
        return result;
347
0
    }
348
349
    QL_DEPRECATED_ENABLE_WARNING
350
}