Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
strippedcpivolatilitystructure.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 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
19/*! \file strippedcpivolatilitystructure.hpp
20 \brief zero inflation volatility structure implied from a cpi cap/floor price surface
21 */
22
23#ifndef quantext_stripped_cpi_volatility_structure_hpp
24#define quantext_stripped_cpi_volatility_structure_hpp
25
26#include <iostream>
27#include <ql/experimental/inflation/cpicapfloortermpricesurface.hpp>
28#include <ql/math/solvers1d/brent.hpp>
33
34namespace QuantExt {
35using namespace QuantLib;
36
38
40 static constexpr QuantLib::Real upperVolBound = 1.0;
41 static constexpr QuantLib::Real lowerVolBound = 0.000001;
42 static constexpr QuantLib::Real solverTolerance = 1.0e-12;
43};
44
45//! Stripped zero inflation volatility structure
46/*!
47 The surface provides implied CPI Black volatilities for the union of strikes that occur in the underlying
48 cap and floor price surface.
49
50 The type argument determines which kind of price quotes are used with priority when there is an overlap,
51 i.e. strikes for which we have both cap and floor quotes:
52 If type is Cap: Use cap quotes where available, floor quotes otherwise
53 If type is Floor: Use floor quotes where available, cap quotes otherwise
54 If type is CapFloor: In case of overlap, use floor quotes up to the ATM strike, cap quotes for strikes beyond ATM
55 */
56
57
58
59
60template <class Interpolator2D> class StrippedCPIVolatilitySurface : public QuantExt::CPIVolatilitySurface {
61public:
62
63
65 const QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface>& priceSurface,
66 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& zeroIndex,
67 const QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine>& engine,
68 const QuantLib::Date& capFloorStartDate = Date(),
69 const QuantLib::Real& upperVolBound = StrippedCPIVolSurfaceDefaultValues::upperVolBound,
70 const QuantLib::Real& lowerVolBound = StrippedCPIVolSurfaceDefaultValues::lowerVolBound,
71 const QuantLib::Real& solverTolerance = StrippedCPIVolSurfaceDefaultValues::solverTolerance,
72 const Interpolator2D& interpolator2d = Interpolator2D(),
73 const QuantLib::VolatilityType& volType = QuantLib::ShiftedLognormal,
74 const double displacement = 0.0);
75
77 PriceQuotePreference type, const QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface>& priceSurface,
78 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& zeroIndex,
79 const bool quotedPricesUseInterpolatedCPIFixings,
80 const QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine>& engine, const QuantLib::Date& capFloorStartDate = Date(),
81 const QuantLib::Real& upperVolBound = StrippedCPIVolSurfaceDefaultValues::upperVolBound,
82 const QuantLib::Real& lowerVolBound = StrippedCPIVolSurfaceDefaultValues::lowerVolBound,
83 const QuantLib::Real& solverTolerance = StrippedCPIVolSurfaceDefaultValues::solverTolerance,
84 const Interpolator2D& interpolator2d = Interpolator2D(),
85 const QuantLib::VolatilityType& volType = QuantLib::ShiftedLognormal, const double displacement = 0.0);
86
87 //! \name LazyObject interface
88 //@{
89 void performCalculations() const;
90 //@}
91
92 //! \name Limits
93 //@{
94 //! the minimum strike for which the term structure can return vols
95 QuantLib::Real minStrike() const override;
96 //! the maximum strike for which the term structure can return vols
97 QuantLib::Real maxStrike() const override;
98 //! maximum date for which the term structure can return vols
99 QuantLib::Date maxDate() const override;
100 //@}
101
102 //! \name Inspectors
103 //@{
104 const std::vector<QuantLib::Real>& strikes() { return strikes_; }
105 const std::vector<QuantLib::Period>& maturities() { return maturities_; }
106 const QuantLib::Matrix& volData() { return volData_; }
107 //@}
108 QuantLib::Real atmStrike(const QuantLib::Date& maturity,
109 const QuantLib::Period& obsLag = QuantLib::Period(-1, QuantLib::Days)) const override;
110
111private:
112 QuantLib::Volatility volatilityImpl(QuantLib::Time length, QuantLib::Rate strike) const override;
113
114 bool chooseFloor(QuantLib::Real strike, QuantLib::Real atmRate) const;
115
117 public:
118 // Pass price to match, CPICapFloor engine, and all data for creating a quoted CPICapFloor
119 ObjectiveFunction(QuantLib::Real priceToMatch, bool useFloor, QuantLib::Real strike, QuantLib::Date startDate,
120 QuantLib::Date maturityDate, QuantLib::Real baseCPI,
121 QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface> priceSurface,
122 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& zeroIndex,
123 const QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine>& engine,
124 const CPI::InterpolationType interpolationType);
125 // Update the engine's volatility, return difference between capfloor NPV and priceToMatch
126 QuantLib::Real operator()(QuantLib::Volatility guess) const;
127
128 private:
129 QuantLib::Real priceToMatch_;
131 QuantLib::Real strike_;
132 QuantLib::Date startDate_, maturityDate_;
133 QuantLib::Real baseCPI_;
134 QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface> priceSurface_;
135 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex> index_;
136 QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine> engine_;
137 CPI::InterpolationType interpolationType_;
139 QuantLib::CPICapFloor cpiCapFloor_;
140
141 };
142
144 QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface> priceSurface_;
145 QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex> index_;
146 QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine> engine_;
147 QuantLib::Real upperVolBound_;
148 QuantLib::Real lowerVolBound_;
149 QuantLib::Real solverTolerance_;
150
151 mutable std::vector<QuantLib::Rate> strikes_;
152 mutable std::vector<QuantLib::Period> maturities_;
153 mutable std::vector<QuantLib::Time> maturityTimes_;
154 mutable QuantLib::Matrix volData_;
155 Interpolator2D interpolator2d_;
156 mutable QuantLib::Interpolation2D vols_;
158};
159
160template <class Interpolator2D>
162 PriceQuotePreference type, const QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface>& priceSurface,
163 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& index, const bool quotedPriceUseInterpolatedCPIFixing,
164 const QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine>& engine, // const QuantLib::Real& baseCPI,
165 const QuantLib::Date& capFloorStartDate, const QuantLib::Real& upperVolBound, const QuantLib::Real& lowerVolBound,
166 const QuantLib::Real& solverTolerance,
167 const Interpolator2D& interpolator2d, const QuantLib::VolatilityType& volType,
168 const double displacement)
169 : QuantExt::CPIVolatilitySurface(priceSurface->settlementDays(), priceSurface->calendar(),
170 priceSurface->businessDayConvention(), priceSurface->dayCounter(),
171 priceSurface->observationLag(), index->frequency(),
172 quotedPriceUseInterpolatedCPIFixing,
173 capFloorStartDate, volType, displacement),
174 preference_(type), priceSurface_(priceSurface), index_(index), engine_(engine), upperVolBound_(upperVolBound),
175 lowerVolBound_(lowerVolBound), solverTolerance_(solverTolerance), interpolator2d_(interpolator2d) {
176
178}
179
180QL_DEPRECATED_DISABLE_WARNING
181template <class Interpolator2D>
183 PriceQuotePreference type, const QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface>& priceSurface,
184 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& index,
185 const QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine>& engine, // const QuantLib::Real& baseCPI,
186 const QuantLib::Date& capFloorStartDate, const QuantLib::Real& upperVolBound, const QuantLib::Real& lowerVolBound,
187 const QuantLib::Real& solverTolerance, const Interpolator2D& interpolator2d,
188 const QuantLib::VolatilityType& volType, const double displacement)
190 type, priceSurface, index, index->interpolated(), engine, capFloorStartDate, upperVolBound, lowerVolBound,
191 solverTolerance, interpolator2d, volType, displacement) {}
192QL_DEPRECATED_ENABLE_WARNING
193
194template <class Interpolator2D> void StrippedCPIVolatilitySurface<Interpolator2D>::performCalculations() const {
195 strikes_ = priceSurface_->strikes();
196 maturities_ = priceSurface_->maturities();
197 volData_ = QuantLib::Matrix(strikes_.size(), maturities_.size(), QuantLib::Null<QuantLib::Real>());
198 QuantLib::Brent solver;
199 QuantLib::Real guess = (upperVolBound_ + lowerVolBound_) / 2.0;
200 QuantLib::Date startDate = capFloorStartDate();
201 QuantLib::Date underlyingBaseDate =
202 ZeroInflation::fixingDate(startDate, observationLag(), frequency(), indexIsInterpolated());
203 double baseCPI = ZeroInflation::cpiFixing(index_, startDate, observationLag(), indexIsInterpolated());
204 for (QuantLib::Size i = 0; i < strikes_.size(); i++) {
205 for (QuantLib::Size j = 0; j < maturities_.size(); j++) {
206
207 QuantLib::Date maturityDate = optionDateFromTenor(maturities_[j]);
208 QuantLib::Date fixDate =
209 ZeroInflation::fixingDate(maturityDate, observationLag(), frequency(), indexIsInterpolated());
210 Rate I1 = ZeroInflation::cpiFixing(index_, maturityDate, observationLag(), indexIsInterpolated());
211
212 Time timeToMaturity = dayCounter().yearFraction(underlyingBaseDate, fixDate);
213 QuantLib::Real atmRate = std::pow(I1 / baseCPI, 1 / timeToMaturity) - 1.0;
214
215 bool useFloor = chooseFloor(strikes_[i], atmRate);
216
217 // FIXME: Do we need an effective maturity here ?
218 QuantLib::Real priceToMatch = useFloor ? priceSurface_->floorPrice(maturities_[j], strikes_[i])
219 : priceSurface_->capPrice(maturities_[j], strikes_[i]);
220 try {
221 auto interpolationType =
222 indexIsInterpolated() ? CPI::InterpolationType::Linear : CPI::InterpolationType::Flat;
223 ObjectiveFunction func(priceToMatch, useFloor, strikes_[i], startDate, maturityDate, baseCPI,
224 priceSurface_, index_, engine_, interpolationType);
225 QuantLib::Real found = solver.solve(func, solverTolerance_, guess, lowerVolBound_, upperVolBound_);
226 volData_[i][j] = found;
227 } catch (std::exception& e) {
228 QL_FAIL("failed to find implied vol for "
229 << (useFloor ? "Floor" : "Cap") << " with strike " << strikes_[i] << " and maturity "
230 << maturities_[j] << ", because: " << e.what() << " "
231 << QuantLib::io::iso_date(startDate + maturities_[j]) << " " << maturityDate);
232 }
233 }
234 }
235
236 maturityTimes_.clear();
237 for (QuantLib::Size i = 0; i < maturities_.size(); i++) {
238 QuantLib::Date d = optionDateFromTenor(maturities_[i]);
239 maturityTimes_.push_back(fixingTime(d));
240 }
241
242 vols_ = interpolator2d_.interpolate(maturityTimes_.begin(), maturityTimes_.end(), strikes_.begin(), strikes_.end(),
243 volData_);
244 vols_.enableExtrapolation();
245}
246
247template <class Interpolator2D>
248bool StrippedCPIVolatilitySurface<Interpolator2D>::chooseFloor(QuantLib::Real strike, QuantLib::Real atmRate) const {
249 if (preference_ == Floor) {
250 if (strike <= priceSurface_->floorStrikes().back())
251 return true;
252 else
253 return false;
254 }
255
256 if (preference_ == Cap) {
257 if (strike < priceSurface_->capStrikes().front())
258 return true;
259 else
260 return false;
261 }
262
263 // else: Use floors where we have floor quotes only, caps where we have cap quotes only,
264 // and decide based on ATM where we have both cap and floor quotes
265
266 // 1) strike < maxFloorStrike < minCapStrike: Floor!
267 if (strike <= priceSurface_->floorStrikes().back() && strike < priceSurface_->capStrikes().front())
268 return true;
269 // 2) strike > maxFloorStrike and strike >= minCapStrike: Cap!
270 else if (strike > priceSurface_->floorStrikes().back() && strike >= priceSurface_->capStrikes().front())
271 return false;
272 // 3) Overlap, maxFloorStrike > minCapStrike and strike in between: Depends on atmRate which surface we pick
273 else if (strike <= priceSurface_->floorStrikes().back() && strike >= priceSurface_->capStrikes().front()) {
274 // we have overlapping strikes, decide depending on atm level
275 if (strike < atmRate)
276 return true;
277 else
278 return false;
279 // 4) Gap, maxFloorStrike < minCapStrike and strike in the gap: Depends on atmRate which surface we
280 // extrapolate
281 } else if (strike > priceSurface_->floorStrikes().back() && strike < priceSurface_->capStrikes().front()) {
282 // there is a gap between floor end and caps begin, decide again depending on strike level
283 if (strike < atmRate)
284 return true;
285 else
286 return false;
287 } else {
288 QL_FAIL("case not covered in StrippedCPIVolatilitySurface: strike="
289 << strike << " maxFloorStrike=" << priceSurface_->floorStrikes().back()
290 << " minCapStrike=" << priceSurface_->capStrikes().front() << " atm=" << atmRate);
291 return false;
292 }
293}
294
295template <class Interpolator2D> QuantLib::Real StrippedCPIVolatilitySurface<Interpolator2D>::minStrike() const {
296 return strikes_.front() - QL_EPSILON;
297}
298
299template <class Interpolator2D> QuantLib::Real StrippedCPIVolatilitySurface<Interpolator2D>::maxStrike() const {
300 return strikes_.back() + QL_EPSILON;
301}
302
303template <class Interpolator2D> QuantLib::Date StrippedCPIVolatilitySurface<Interpolator2D>::maxDate() const {
304 QuantLib::Date today = QuantLib::Settings::instance().evaluationDate();
305 return today + maturities_.back();
306}
307
308template <class Interpolator2D>
309QuantLib::Volatility StrippedCPIVolatilitySurface<Interpolator2D>::volatilityImpl(QuantLib::Time length,
310 QuantLib::Rate strike) const {
311 return vols_(length, strike);
312}
313
314template <class Interpolator2D>
316 QuantLib::Real priceToMatch, bool useFloor, QuantLib::Real strike, QuantLib::Date startDate,
317 QuantLib::Date maturityDate, QuantLib::Real baseCPI,
318 QuantLib::Handle<QuantLib::CPICapFloorTermPriceSurface> priceSurface,
319 const QuantLib::ext::shared_ptr<QuantLib::ZeroInflationIndex>& zeroIndex,
320 const QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine>& engine, const CPI::InterpolationType interpolationType)
321 : priceToMatch_(priceToMatch), useFloor_(useFloor), strike_(strike), startDate_(startDate),
322 maturityDate_(maturityDate), baseCPI_(baseCPI), priceSurface_(priceSurface), index_(zeroIndex), engine_(engine),
323 interpolationType_(interpolationType),
324 cpiCapFloor_(QuantLib::CPICapFloor(useFloor_ ? QuantLib::Option::Put : QuantLib::Option::Call,
325 1.0, // unit nominal, because the price surface returns unit nominal prices
326 startDate_, baseCPI_, maturityDate_, priceSurface_->calendar(),
327 priceSurface_->businessDayConvention(), priceSurface_->calendar(),
328 priceSurface_->businessDayConvention(), strike_, index_,
329 priceSurface_->observationLag(), interpolationType)) {
330
331 // FIXME: observation interpolation (last argument) uses default setting here CPI::AsIndex
332
333 cpiCapFloor_.setPricingEngine(engine_);
334}
335
336template <class Interpolator2D>
337QuantLib::Real
339
340 QL_DEPRECATED_DISABLE_WARNING
341 bool isInterpolated = interpolationType_ == CPI::InterpolationType::Linear ||
342 (interpolationType_ == CPI::InterpolationType::AsIndex && index_->interpolated());
343 QL_DEPRECATED_ENABLE_WARNING
344
345 QuantLib::ext::shared_ptr<QuantExt::ConstantCPIVolatility> vol = QuantLib::ext::make_shared<QuantExt::ConstantCPIVolatility>(
346 guess, priceSurface_->settlementDays(), priceSurface_->calendar(), priceSurface_->businessDayConvention(),
347 priceSurface_->dayCounter(), priceSurface_->observationLag(), priceSurface_->frequency(), isInterpolated,
348 startDate_);
349
350 engine_->setVolatility(QuantLib::Handle<QuantLib::CPIVolatilitySurface>(vol));
351
352 QuantLib::Real npv = cpiCapFloor_.NPV();
353 return priceToMatch_ - npv;
354}
355
356template <class Interpolator2D>
357QuantLib::Real StrippedCPIVolatilitySurface<Interpolator2D>::atmStrike(const QuantLib::Date& maturity,
358 const QuantLib::Period& obsLag) const {
359 QuantLib::Period lag = obsLag == -1 * QuantLib::Days ? observationLag() : obsLag;
360 QuantLib::Date fixingDate = ZeroInflation::fixingDate(maturity, lag, frequency(), indexIsInterpolated());
361 double forwardCPI = ZeroInflation::cpiFixing(index_, maturity, lag, indexIsInterpolated());
362 double baseCPI = ZeroInflation::cpiFixing(index_, capFloorStartDate(), observationLag(), indexIsInterpolated());
363 double atm = forwardCPI / baseCPI;
364 double ttm =
365 QuantLib::inflationYearFraction(frequency(), indexIsInterpolated(), dayCounter(), baseDate(), fixingDate);
366 return std::pow(atm, 1.0 / ttm) - 1.0;
367}
368
369} // namespace QuantExt
370
371#endif
QuantLib::ext::shared_ptr< PricingEngine > engine_
Definition: cdsoption.cpp:78
double displacement() const
Returns the displacement for lognormal volatilities.
QuantLib::Date baseDate() const override
base date will be in the past
QuantLib::Date capFloorStartDate() const
ObjectiveFunction(QuantLib::Real priceToMatch, bool useFloor, QuantLib::Real strike, QuantLib::Date startDate, QuantLib::Date maturityDate, QuantLib::Real baseCPI, QuantLib::Handle< QuantLib::CPICapFloorTermPriceSurface > priceSurface, const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &zeroIndex, const QuantLib::ext::shared_ptr< QuantExt::CPICapFloorEngine > &engine, const CPI::InterpolationType interpolationType)
QuantLib::Handle< QuantLib::CPICapFloorTermPriceSurface > priceSurface_
QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > index_
QuantLib::ext::shared_ptr< QuantExt::CPICapFloorEngine > engine_
Stripped zero inflation volatility structure.
QL_DEPRECATED StrippedCPIVolatilitySurface(PriceQuotePreference type, const QuantLib::Handle< QuantLib::CPICapFloorTermPriceSurface > &priceSurface, const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &zeroIndex, const QuantLib::ext::shared_ptr< QuantExt::CPICapFloorEngine > &engine, const QuantLib::Date &capFloorStartDate=Date(), const QuantLib::Real &upperVolBound=StrippedCPIVolSurfaceDefaultValues::upperVolBound, const QuantLib::Real &lowerVolBound=StrippedCPIVolSurfaceDefaultValues::lowerVolBound, const QuantLib::Real &solverTolerance=StrippedCPIVolSurfaceDefaultValues::solverTolerance, const Interpolator2D &interpolator2d=Interpolator2D(), const QuantLib::VolatilityType &volType=QuantLib::ShiftedLognormal, const double displacement=0.0)
const std::vector< QuantLib::Period > & maturities()
const std::vector< QuantLib::Real > & strikes()
QuantLib::Real minStrike() const override
the minimum strike for which the term structure can return vols
QuantLib::Handle< QuantLib::CPICapFloorTermPriceSurface > priceSurface_
bool chooseFloor(QuantLib::Real strike, QuantLib::Real atmRate) const
QuantLib::Real maxStrike() const override
the maximum strike for which the term structure can return vols
QuantLib::Date maxDate() const override
maximum date for which the term structure can return vols
QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > index_
QuantLib::ext::shared_ptr< QuantExt::CPICapFloorEngine > engine_
QuantLib::Real atmStrike(const QuantLib::Date &maturity, const QuantLib::Period &obsLag=QuantLib::Period(-1, QuantLib::Days)) const override
QuantLib::Volatility volatilityImpl(QuantLib::Time length, QuantLib::Rate strike) const override
Constant CPI Volatility Surface.
CPI cap/floor engine using the Black pricing formula and interpreting the volatility data as lognorma...
interpolated correlation term structure
some inflation related utilities.
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
Definition: inflation.cpp:183
QuantLib::Rate cpiFixing(const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &index, const QuantLib::Date &maturity, const QuantLib::Period &obsLag, bool interpolated)
Computes a CPI fixing giving an zeroIndex, with interpolation if needed.
Definition: inflation.cpp:166