Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
spreadedswaptionvolatility.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
22
23#include <ql/math/interpolations/bilinearinterpolation.hpp>
24#include <ql/math/interpolations/flatextrapolation2d.hpp>
25
26#include <iostream>
27
28namespace QuantExt {
29
31 const Handle<SwaptionVolatilityStructure>& base, const std::vector<Period>& optionTenors,
32 const std::vector<Period>& swapTenors, const std::vector<Real>& strikeSpreads,
33 const std::vector<std::vector<Handle<Quote>>>& volSpreads, const QuantLib::ext::shared_ptr<SwapIndex>& baseSwapIndexBase,
34 const QuantLib::ext::shared_ptr<SwapIndex>& baseShortSwapIndexBase,
35 const QuantLib::ext::shared_ptr<SwapIndex>& simulatedSwapIndexBase,
36 const QuantLib::ext::shared_ptr<SwapIndex>& simulatedShortSwapIndexBase, const bool stickyAbsMoney)
37 : SwaptionVolatilityDiscrete(optionTenors, swapTenors, 0, base->calendar(), base->businessDayConvention(),
38 base->dayCounter()),
39 base_(base), strikeSpreads_(strikeSpreads), volSpreads_(volSpreads), baseSwapIndexBase_(baseSwapIndexBase),
40 baseShortSwapIndexBase_(baseShortSwapIndexBase), simulatedSwapIndexBase_(simulatedSwapIndexBase),
41 simulatedShortSwapIndexBase_(simulatedShortSwapIndexBase), stickyAbsMoney_(stickyAbsMoney) {
42 enableExtrapolation(base_->allowsExtrapolation());
43 registerWith(base_);
44 QL_REQUIRE(
45 (baseSwapIndexBase_ == nullptr && baseShortSwapIndexBase_ == nullptr) ||
46 (baseSwapIndexBase_ != nullptr && baseShortSwapIndexBase_ != nullptr),
47 "SpreadedSwaptionVolatility: baseSwapIndexBase and baseShortSwapIndexBase must be both null or non-null");
48 QL_REQUIRE((simulatedSwapIndexBase_ == nullptr && simulatedShortSwapIndexBase_ == nullptr) ||
50 "SpreadedSwaptionVolatility: simulatedSwapIndexBase and simulatedShortSwapIndexBase must be both null "
51 "or non-null");
53 registerWith(baseSwapIndexBase_);
55 registerWith(baseShortSwapIndexBase_);
57 registerWith(simulatedSwapIndexBase_);
59 registerWith(simulatedShortSwapIndexBase_);
60 QL_REQUIRE(!strikeSpreads_.empty(), "SpreadedSwaptionVolatility: empty strike spreads");
61 QL_REQUIRE(!optionTenors_.empty(), "SpreadedSwaptionVolatility: empty option tenors");
62 QL_REQUIRE(!swapTenors_.empty(), "SpreadedSwaptionVolatility: empty swap tenors");
63 QL_REQUIRE(optionTenors.size() * swapTenors.size() == volSpreads.size(),
64 "SpreadedSwaptionVolatility: optionTenors (" << optionTenors.size() << ") * swapTenors ("
65 << swapTenors.size() << ") inconsistent with vol spreads ("
66 << volSpreads.size() << ")");
67 for (auto const& s : volSpreads_) {
68 QL_REQUIRE(s.size() == strikeSpreads_.size(), "SpreadedSwaptionVolatility: got " << strikeSpreads_.size()
69 << " strike spreads, but "
70 << s.size() << " vol spreads");
71 for (auto const& q : s)
72 registerWith(q);
73 }
74 volSpreadValues_ = std::vector<Matrix>(strikeSpreads_.size(), Matrix(optionTenors.size(), swapTenors.size(), 0.0));
75 volSpreadInterpolation_ = std::vector<Interpolation2D>(strikeSpreads_.size());
76}
77
78DayCounter SpreadedSwaptionVolatility::dayCounter() const { return base_->dayCounter(); }
79Date SpreadedSwaptionVolatility::maxDate() const { return base_->maxDate(); }
80Time SpreadedSwaptionVolatility::maxTime() const { return base_->maxTime(); }
81const Date& SpreadedSwaptionVolatility::referenceDate() const { return base_->referenceDate(); }
82Calendar SpreadedSwaptionVolatility::calendar() const { return base_->calendar(); }
83Natural SpreadedSwaptionVolatility::settlementDays() const { return base_->settlementDays(); }
84Rate SpreadedSwaptionVolatility::minStrike() const { return base_->minStrike(); }
85Rate SpreadedSwaptionVolatility::maxStrike() const { return base_->maxStrike(); }
86const Period& SpreadedSwaptionVolatility::maxSwapTenor() const { return base_->maxSwapTenor(); }
87VolatilityType SpreadedSwaptionVolatility::volatilityType() const { return base_->volatilityType(); }
89 base_->update();
90 update();
91}
92const Handle<SwaptionVolatilityStructure>& SpreadedSwaptionVolatility::baseVol() { return base_; }
93
94Real SpreadedSwaptionVolatility::getAtmLevel(const Real optionTime, const Real swapLength,
95 const QuantLib::ext::shared_ptr<SwapIndex> swapIndexBase,
96 const QuantLib::ext::shared_ptr<SwapIndex> shortSwapIndexBase) const {
97 Date optionDate = optionDateFromTime(optionTime);
98 Rounding rounder(0);
99 Period swapTenor(static_cast<Integer>(rounder(swapLength * 12.0)), Months);
100 optionDate = swapTenor > shortSwapIndexBase->tenor()
101 ? swapIndexBase->fixingCalendar().adjust(optionDate, Following)
102 : shortSwapIndexBase->fixingCalendar().adjust(optionDate, Following);
103 if (swapTenor > shortSwapIndexBase->tenor()) {
104 return swapIndexBase->clone(swapTenor)->fixing(optionDate);
105 } else {
106 return shortSwapIndexBase->clone(swapTenor)->fixing(optionDate);
107 }
108}
109
110QuantLib::ext::shared_ptr<SmileSection> SpreadedSwaptionVolatility::smileSectionImpl(Time optionTime, Time swapLength) const {
111 calculate();
112 auto baseSection = base_->smileSection(optionTime, swapLength);
113 Real baseAtmLevel = Null<Real>();
114 Real simulatedAtmLevel = Null<Real>();
115 if ((stickyAbsMoney_ || strikeSpreads_.size() > 1) && baseSection->atmLevel() == Null<Real>()) {
116 QL_REQUIRE(baseSwapIndexBase_ != nullptr,
117 "SpreadedSwaptionVolatility::smileSecitonImpl: require baseSwapIndexBase, since stickyAbsMoney is "
118 "true and the base vol smile section does not provide an ATM level.");
119 baseAtmLevel = getAtmLevel(optionTime, swapLength, baseSwapIndexBase_, baseShortSwapIndexBase_);
120 }
121 if (stickyAbsMoney_) {
122 QL_REQUIRE(simulatedSwapIndexBase_ != nullptr, "SpreadedSwaptionVolatility::smileSectionImpl: required "
123 "simualtedSwapIndexBase, since stickyAbsMoney is true");
124 simulatedAtmLevel = getAtmLevel(optionTime, swapLength, simulatedSwapIndexBase_, simulatedShortSwapIndexBase_);
125 }
126 // interpolate vol spreads
127 std::vector<Real> volSpreads(strikeSpreads_.size());
128 for (Size k = 0; k < volSpreads.size(); ++k) {
129 volSpreads[k] = volSpreadInterpolation_[k](swapLength, optionTime);
130 }
131 // create smile section
132 return QuantLib::ext::make_shared<SpreadedSmileSection2>(base_->smileSection(optionTime, swapLength), volSpreads,
133 strikeSpreads_, true, baseAtmLevel, simulatedAtmLevel,
135}
136
137Volatility SpreadedSwaptionVolatility::volatilityImpl(Time optionTime, Time swapLength, Rate strike) const {
138 if (strike == Null<Real>()) {
139 calculate();
140 // support input strike null, interpret this as atm
141 std::vector<Real> volSpreads(strikeSpreads_.size());
142 for (Size k = 0; k < volSpreads.size(); ++k) {
143 volSpreads[k] = volSpreadInterpolation_[k](swapLength, optionTime);
144 }
145 Real volSpread;
146 if (volSpreads.size() > 1) {
147 auto spreadInterpolation =
148 LinearFlat().interpolate(strikeSpreads_.begin(), strikeSpreads_.end(), volSpreads.begin());
149 volSpread = spreadInterpolation(0.0);
150 } else {
151 volSpread = volSpreads.front();
152 }
153 return base_->volatility(optionTime, swapLength, strike) + volSpread;
154 } else {
155 // if input strike != null, use smile section implementation
156 return smileSectionImpl(optionTime, swapLength)->volatility(strike);
157 }
158}
159
160Real SpreadedSwaptionVolatility::shiftImpl(const Date& optionDate, const Period& swapTenor) const {
161 return base_->shift(optionDate, swapTenor);
162}
163
164Real SpreadedSwaptionVolatility::shiftImpl(Time optionTime, Time swapLength) const {
165 return base_->shift(optionTime, swapLength);
166}
167
169 SwaptionVolatilityDiscrete::performCalculations();
170 for (Size k = 0; k < strikeSpreads_.size(); ++k) {
171 for (Size i = 0; i < optionTenors_.size(); ++i) {
172 for (Size j = 0; j < swapTenors_.size(); ++j) {
173 Size index = i * swapTenors_.size() + j;
174 QL_REQUIRE(!volSpreads_[index][k].empty(), "SpreadedSwaptionVolatility: vol spread quote at index ("
175 << i << "," << j << "," << k << ") is empty");
176 volSpreadValues_[k](i, j) = volSpreads_[index][k]->value();
177 }
178 }
179 volSpreadInterpolation_[k] = FlatExtrapolator2D(QuantLib::ext::make_shared<BilinearInterpolation>(
180 swapLengths_.begin(), swapLengths_.end(), optionTimes_.begin(), optionTimes_.end(), volSpreadValues_[k]));
181 volSpreadInterpolation_[k].enableExtrapolation();
182 }
183}
184
185} // namespace QuantExt
Linear-interpolation and flat extrapolation factory and traits
Interpolation interpolate(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin) const
QuantLib::ext::shared_ptr< SwapIndex > simulatedShortSwapIndexBase_
Real getAtmLevel(const Real optionTime, const Real swapLength, const QuantLib::ext::shared_ptr< SwapIndex > swapIndexBase, const QuantLib::ext::shared_ptr< SwapIndex > shortSwapIndexBase) const
Volatility volatilityImpl(Time optionTime, Time swapLength, Rate strike) const override
QuantLib::ext::shared_ptr< SmileSection > smileSectionImpl(Time optionTime, Time swapLength) const override
const Handle< SwaptionVolatilityStructure > & baseVol()
QuantLib::ext::shared_ptr< SwapIndex > baseSwapIndexBase_
SpreadedSwaptionVolatility(const Handle< SwaptionVolatilityStructure > &base, const std::vector< Period > &optionTenors, const std::vector< Period > &swapTenors, const std::vector< Real > &strikeSpreads, const std::vector< std::vector< Handle< Quote > > > &volSpreads, const QuantLib::ext::shared_ptr< SwapIndex > &baseSwapIndexBase=nullptr, const QuantLib::ext::shared_ptr< SwapIndex > &baseShortSwapIndexBase=nullptr, const QuantLib::ext::shared_ptr< SwapIndex > &simulatedSwapIndexBase=nullptr, const QuantLib::ext::shared_ptr< SwapIndex > &simulatedShortSwapIndexBase=nullptr, const bool stickyAbsMoney=false)
Real shiftImpl(const Date &optionDate, const Period &swapTenor) const override
Handle< SwaptionVolatilityStructure > base_
VolatilityType volatilityType() const override
QuantLib::ext::shared_ptr< SwapIndex > simulatedSwapIndexBase_
std::vector< Interpolation2D > volSpreadInterpolation_
std::vector< std::vector< Handle< Quote > > > volSpreads_
const Period & maxSwapTenor() const override
QuantLib::ext::shared_ptr< SwapIndex > baseShortSwapIndexBase_
flat interpolation decorator
smile section with linear interpolated vol spreads
swaption cube defined via atm vol spreads over another cube