Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/termstructures/volatility/optionlet/optionletstripper1.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2007, 2008 Ferdinando Ametrano
5
 Copyright (C) 2007 François du Vignaud
6
 Copyright (C) 2007 Katiuscia Manzoni
7
 Copyright (C) 2007 Giorgio Facchinetti
8
 Copyright (C) 2015 Michael von den Driesch
9
 Copyright (C) 2015 Peter Caspers
10
11
 This file is part of QuantLib, a free-software/open-source library
12
 for financial quantitative analysts and developers - http://quantlib.org/
13
14
 QuantLib is free software: you can redistribute it and/or modify it
15
 under the terms of the QuantLib license.  You should have received a
16
 copy of the license along with this program; if not, please email
17
 <quantlib-dev@lists.sf.net>. The license is also available online at
18
 <https://www.quantlib.org/license.shtml>.
19
20
 This program is distributed in the hope that it will be useful, but WITHOUT
21
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22
 FOR A PARTICULAR PURPOSE.  See the license for more details.
23
*/
24
25
#include <ql/termstructures/volatility/optionlet/optionletstripper1.hpp>
26
#include <ql/instruments/makecapfloor.hpp>
27
#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
28
#include <ql/pricingengine.hpp>
29
#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
30
#include <ql/pricingengines/blackformula.hpp>
31
#include <ql/indexes/iborindex.hpp>
32
#include <ql/quotes/simplequote.hpp>
33
#include <ql/utilities/dataformatters.hpp>
34
35
namespace QuantLib {
36
37
    OptionletStripper1::OptionletStripper1(
38
        const ext::shared_ptr<CapFloorTermVolSurface>& termVolSurface,
39
        const ext::shared_ptr<IborIndex>& index,
40
        Rate switchStrike,
41
        Real accuracy,
42
        Natural maxIter,
43
        const Handle<YieldTermStructure>& discount,
44
        const VolatilityType type,
45
        const Real displacement,
46
        bool dontThrow,
47
        ext::optional<Period> optionletFrequency)
48
0
    : OptionletStripper(termVolSurface, index, discount, type, displacement, optionletFrequency),
49
0
      floatingSwitchStrike_(switchStrike == Null<Rate>()), switchStrike_(switchStrike),
50
0
      accuracy_(accuracy), maxIter_(maxIter), dontThrow_(dontThrow) {
51
52
0
        capFloorPrices_ = Matrix(nOptionletTenors_, nStrikes_);
53
0
        optionletPrices_ = Matrix(nOptionletTenors_, nStrikes_);
54
0
        capletVols_ = Matrix(nOptionletTenors_, nStrikes_);
55
0
        capFloorVols_ = Matrix(nOptionletTenors_, nStrikes_);
56
57
0
        Real firstGuess = 0.14; // guess is only used for shifted lognormal vols
58
0
        optionletStDevs_ = Matrix(nOptionletTenors_, nStrikes_, firstGuess);
59
0
    }
Unexecuted instantiation: QuantLib::OptionletStripper1::OptionletStripper1(boost::shared_ptr<QuantLib::CapFloorTermVolSurface> const&, boost::shared_ptr<QuantLib::IborIndex> const&, double, double, unsigned int, QuantLib::Handle<QuantLib::YieldTermStructure> const&, QuantLib::VolatilityType, double, bool, std::__1::optional<QuantLib::Period>)
Unexecuted instantiation: QuantLib::OptionletStripper1::OptionletStripper1(boost::shared_ptr<QuantLib::CapFloorTermVolSurface> const&, boost::shared_ptr<QuantLib::IborIndex> const&, double, double, unsigned int, QuantLib::Handle<QuantLib::YieldTermStructure> const&, QuantLib::VolatilityType, double, bool, std::__1::optional<QuantLib::Period>)
60
61
0
    void OptionletStripper1::performCalculations() const {
62
63
        // update dates
64
0
        const Date& referenceDate = termVolSurface_->referenceDate();
65
0
        const DayCounter& dc = termVolSurface_->dayCounter();
66
0
        ext::shared_ptr<BlackCapFloorEngine> dummy(new
67
0
                    BlackCapFloorEngine(// discounting does not matter here
68
0
                                        iborIndex_->forwardingTermStructure(),
69
0
                                        0.20, dc));
70
0
        for (Size i=0; i<nOptionletTenors_; ++i) {
71
0
            CapFloor temp = MakeCapFloor(CapFloor::Cap,
72
0
                                         capFloorLengths_[i],
73
0
                                         iborIndex_,
74
0
                                         0.04, // dummy strike
75
0
                                         0*Days)
76
0
                .withPricingEngine(dummy);
77
0
            ext::shared_ptr<FloatingRateCoupon> lFRC =
78
0
                                                temp.lastFloatingRateCoupon();
79
0
            optionletDates_[i] = lFRC->fixingDate();
80
0
            optionletPaymentDates_[i] = lFRC->date();
81
0
            optionletAccrualPeriods_[i] = lFRC->accrualPeriod();
82
0
            optionletTimes_[i] = dc.yearFraction(referenceDate,
83
0
                                                 optionletDates_[i]);
84
0
            atmOptionletRate_[i] = lFRC->indexFixing();
85
0
        }
86
87
0
        if (floatingSwitchStrike_) {
88
0
            Rate averageAtmOptionletRate = 0.0;
89
0
            for (Size i=0; i<nOptionletTenors_; ++i) {
90
0
                averageAtmOptionletRate += atmOptionletRate_[i];
91
0
            }
92
0
            switchStrike_ = averageAtmOptionletRate / nOptionletTenors_;
93
0
        }
94
95
0
        const Handle<YieldTermStructure>& discountCurve =
96
0
            discount_.empty() ?
97
0
                iborIndex_->forwardingTermStructure() :
98
0
                discount_;
99
100
0
        const std::vector<Rate>& strikes = termVolSurface_->strikes();
101
102
0
        ext::shared_ptr<PricingEngine> capFloorEngine;
103
0
        ext::shared_ptr<SimpleQuote> volQuote(new SimpleQuote);
104
105
0
        if (volatilityType_ == ShiftedLognormal) {
106
0
            capFloorEngine = ext::make_shared<BlackCapFloorEngine>(
107
                        
108
0
                            discountCurve, Handle<Quote>(volQuote),
109
0
                            dc, displacement_);
110
0
        } else if (volatilityType_ == Normal) {
111
0
            capFloorEngine = ext::make_shared<BachelierCapFloorEngine>(
112
                        
113
0
                            discountCurve, Handle<Quote>(volQuote),
114
0
                            dc);
115
0
        } else {
116
0
            QL_FAIL("unknown volatility type: " << volatilityType_);
117
0
        }
118
119
0
        for (Size j=0; j<nStrikes_; ++j) {
120
            // using out-of-the-money options
121
0
            CapFloor::Type capFloorType =
122
0
                strikes[j] < switchStrike_ ? CapFloor::Floor : CapFloor::Cap;
123
0
            Option::Type optionletType =
124
0
                strikes[j] < switchStrike_ ? Option::Put : Option::Call;
125
126
0
            Real previousCapFloorPrice = 0.0;
127
0
            for (Size i=0; i<nOptionletTenors_; ++i) {
128
129
0
                capFloorVols_[i][j] = termVolSurface_->volatility(
130
0
                    capFloorLengths_[i], strikes[j], true);
131
0
                volQuote->setValue(capFloorVols_[i][j]);
132
0
                ext::shared_ptr<CapFloor> capFloor =
133
0
                    MakeCapFloor(capFloorType, capFloorLengths_[i],
134
0
                                 iborIndex_, strikes[j], -0 * Days)
135
0
                        .withPricingEngine(capFloorEngine);
136
0
                capFloorPrices_[i][j] = capFloor->NPV();
137
0
                optionletPrices_[i][j] = capFloorPrices_[i][j] -
138
0
                                                        previousCapFloorPrice;
139
0
                previousCapFloorPrice = capFloorPrices_[i][j];
140
0
                DiscountFactor d =
141
0
                    discountCurve->discount(optionletPaymentDates_[i]);
142
0
                DiscountFactor optionletAnnuity=optionletAccrualPeriods_[i]*d;
143
0
                try {
144
0
                  if (volatilityType_ == ShiftedLognormal) {
145
0
                    optionletStDevs_[i][j] = blackFormulaImpliedStdDev(
146
0
                        optionletType, strikes[j], atmOptionletRate_[i],
147
0
                        optionletPrices_[i][j], optionletAnnuity, displacement_,
148
0
                        optionletStDevs_[i][j], accuracy_, maxIter_);
149
0
                  } else if (volatilityType_ == Normal) {
150
0
                    optionletStDevs_[i][j] =
151
0
                        std::sqrt(optionletTimes_[i]) *
152
0
                        bachelierBlackFormulaImpliedVol(
153
0
                            optionletType, strikes[j], atmOptionletRate_[i],
154
0
                            optionletTimes_[i], optionletPrices_[i][j],
155
0
                            optionletAnnuity);
156
0
                  } else {
157
0
                    QL_FAIL("Unknown volatility type: " << volatilityType_);
158
0
                  }
159
0
                }
160
0
                catch (std::exception &e) {
161
0
                    if(dontThrow_)
162
0
                        optionletStDevs_[i][j]=0.0;
163
0
                    else
164
0
                        QL_FAIL("could not bootstrap optionlet:"
165
0
                            "\n type:    " << optionletType <<
166
0
                            "\n strike:  " << io::rate(strikes[j]) <<
167
0
                            "\n atm:     " << io::rate(atmOptionletRate_[i]) <<
168
0
                            "\n price:   " << optionletPrices_[i][j] <<
169
0
                            "\n annuity: " << optionletAnnuity <<
170
0
                            "\n expiry:  " << optionletDates_[i] <<
171
0
                            "\n error:   " << e.what());
172
0
                }
173
0
                optionletVolatilities_[i][j] = optionletStDevs_[i][j] /
174
0
                                                std::sqrt(optionletTimes_[i]);
175
0
            }
176
0
        }
177
178
0
    }
179
180
0
    const Matrix &OptionletStripper1::capletVols() const {
181
0
        calculate();
182
0
        return capletVols_;
183
0
    }
184
185
0
    const Matrix& OptionletStripper1::capFloorPrices() const {
186
0
        calculate();
187
0
        return capFloorPrices_;
188
0
    }
189
190
0
    const Matrix& OptionletStripper1::capFloorVolatilities() const {
191
0
        calculate();
192
0
        return capFloorVols_;
193
0
    }
194
195
0
    const Matrix& OptionletStripper1::optionletPrices() const {
196
0
        calculate();
197
0
        return optionletPrices_;
198
0
    }
199
200
0
    Rate OptionletStripper1::switchStrike() const {
201
0
        if (floatingSwitchStrike_)
202
0
            calculate();
203
0
        return switchStrike_;
204
0
    }
205
206
}