Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
blackvolsurfaceabsolute.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 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
21
22#include <ql/experimental/fx/blackdeltacalculator.hpp>
23#include <ql/math/comparison.hpp>
24#include <ql/math/interpolations/cubicinterpolation.hpp>
25#include <ql/math/interpolations/linearinterpolation.hpp>
26#include <ql/math/optimization/levenbergmarquardt.hpp>
27#include <ql/pricingengines/blackformula.hpp>
28
29namespace QuantExt {
30
32 Date referenceDate, const std::vector<Date>& dates, const std::vector<std::vector<Real>>& strikes,
33 const std::vector<std::vector<Real>>& strikeQuotes, const DayCounter& dayCounter, const Calendar& calendar,
34 const Handle<Quote>& spot, const Size spotDays, const Calendar spotCalendar,
35 const Handle<YieldTermStructure>& domesticTS, const Handle<YieldTermStructure>& foreignTS,
36 const DeltaVolQuote::DeltaType dt, const DeltaVolQuote::AtmType at, const Period& switchTenor,
37 const DeltaVolQuote::DeltaType ltdt, const DeltaVolQuote::AtmType ltat, const SmileInterpolation smileInterpolation,
38 const bool flatExtrapolation)
39 : BlackVolatilityTermStructure(referenceDate, calendar, Following, dayCounter), dates_(dates), strikes_(strikes),
40 strikeQuotes_(strikeQuotes), spot_(spot), spotDays_(spotDays), spotCalendar_(spotCalendar),
41 domesticTS_(domesticTS), foreignTS_(foreignTS), dt_(dt), at_(at), switchTenor_(switchTenor), ltdt_(ltdt),
42 ltat_(ltat), smileInterpolation_(smileInterpolation), flatExtrapolation_(flatExtrapolation) {
43
44 // checks
45
46 QL_REQUIRE(!dates_.empty(), "BlackVolatilitySurfaceAbsolute: no expiry dates given");
47 QL_REQUIRE(!strikes_.empty(), "BlackVolatilitySurfaceAbsolute: no strikes given");
48
49 for (Size i = 0; i < strikes_.size(); ++i) {
50 if (strikes_[i].size() > 1) {
51 for (Size j = 0; j < strikes_[i].size() - 1; ++j) {
52 QL_REQUIRE(strikes_[i][j + 1] > strikes_[i][j] && !close_enough(strikes_[i][j + 1], strikes_[i][j]),
53 "BlackVolatilitySurfaceAbsolute: strikes are not strictly ascending at index "
54 << i << ", " << j
55 << ": " << strikes_[i][j] << ", " << strikes_[i][j + 1]);
56 }
57 }
58 }
59
60 QL_REQUIRE(strikeQuotes_.size() == dates_.size(), "BlackVolatilitySurfaceAbsolute: strikeQuotes ("
61 << strikeQuotes_.size() << ") mismatch with expiry dates ("
62 << dates_.size() << ")");
63 QL_REQUIRE(strikeQuotes_.size() == strikes_.size(), "BlackVolatilitySurfaceAbsolute: strikeQuotes ("
64 << strikeQuotes_.size() << ") mismatch with number of dates in strikes ("
65 << strikes_.size() << ")");
66 for (Size i = 0; i < strikes_.size(); ++i) {
67 QL_REQUIRE(strikeQuotes_[i].size() == strikes_[i].size(), "BlackVolatilitySurfaceAbsolute: strikeQuotes inner vector ("
68 << strikeQuotes_[i].size() << ") mismatch with strikes (" << strikes_[i].size() << ")");
69 }
70
71 // calculate times associated to expiry dates
72
73 expiryTimes_.clear();
74 settlementDates_.clear();
75 for (auto const& d : dates_) {
76 expiryTimes_.push_back(timeFromReference(d));
77 settlementDates_.push_back(spotCalendar_.advance(d, spotDays_ * Days));
78 }
79
80 // generate interpolator
81 interpolation_.resize(strikeQuotes_.size());
82 for (Size i = 0; i < strikeQuotes_.size(); ++i) {
83 if (strikes_[i].size() > 1) {
85 interpolation_[i] = QuantLib::ext::make_shared<LinearInterpolation>(strikes_[i].begin(), strikes_[i].end(),
86 strikeQuotes_[i].begin());
87 interpolation_[i]->enableExtrapolation();
89 interpolation_[i] = QuantLib::ext::make_shared<CubicInterpolation>(strikes_[i].begin(), strikes_[i].end(), strikeQuotes_[i].begin(), CubicInterpolation::Spline, false,
90 CubicInterpolation::SecondDerivative, 0.0, CubicInterpolation::SecondDerivative, 0.0);
91 interpolation_[i]->enableExtrapolation();
92 } else {
93 QL_FAIL("BlackVolatilitySurfaceAbsolute: Invalid interpolation type.");
94 }
95 }
97 interpolation_[i] = QuantLib::ext::make_shared<FlatExtrapolation>(interpolation_[i]);
98 interpolation_[i]->enableExtrapolation();
99 }
100 }
101
102 // register with observables
103
104 registerWith(spot_);
105 registerWith(domesticTS_);
106 registerWith(foreignTS_);
107}
108
110 BlackVolatilityTermStructure::update();
111}
112
113Volatility BlackVolatilitySurfaceAbsolute::blackVolImpl(Time t, Real strike) const {
114
115 /* minimum supported time is 1D, i.e. if t is smaller, we return the vol at 1D */
116
117 t = std::max(t, 1.0 / 365.0);
118
119 t = t <= expiryTimes_.back() ? t : expiryTimes_.back();
120
121 /* if we have cached the interpolated smile at t, we use that */
122
123 auto s = cachedInterpolatedVols_.find(std::make_pair(t, strike));
124 if (s != cachedInterpolatedVols_.end()) {
125 return s->second;
126 }
127
128 /* find the indices ip and im such that t_im <= t < t_ip, im will be null if t < first expiry,
129 ip will be null if t >= last expiry */
130
131 Size index_p = std::upper_bound(expiryTimes_.begin(), expiryTimes_.end(), t) - expiryTimes_.begin();
132 Size index_m = index_p == 0 ? Null<Size>() : index_p - 1;
133 if (index_p == expiryTimes_.size())
134 index_p = Null<Size>();
135
136 /* build the smiles on the indices, if we do not have them yet */
137 Volatility vol_p = 0, vol_m = 0;
138 if (index_p != Null<Size>()) {
139 if (strikeQuotes_[index_p].size() == 1) {
140 vol_p = strikeQuotes_[index_p][0];
141 } else {
142 vol_p = (*interpolation_[index_p])(strike);
143 }
144 }
145 if (index_m != Null<Size>()) {
146 if (strikeQuotes_[index_m].size() == 1) {
147 vol_m = strikeQuotes_[index_m][0];
148 } else {
149 vol_m = (*interpolation_[index_m])(strike);
150 }
151 }
152
153 /* find the interpolated vols */
154
155 Real vol;
156
157 if (index_p == Null<Size>()) {
158 vol = vol_m;
159 } else if (index_m == Null<Size>()) {
160 vol = vol_p;
161 } else {
162 // interpolate between two expiries
163 Real a = (t - expiryTimes_[index_m]) / (expiryTimes_[index_p] - expiryTimes_[index_m]);
164 vol = (1.0 - a) * vol_m + a * vol_p;
165 }
166
167 /* store the new smile in the cache */
168
169 cachedInterpolatedVols_[std::make_pair(t, strike)] = vol;
170
171 return vol;
172}
173
174} // namespace QuantExt
Black volatility surface based on absolute quotes.
std::vector< QuantLib::ext::shared_ptr< Interpolation > > interpolation_
std::vector< std::vector< Real > > strikes_
std::vector< std::vector< Real > > strikeQuotes_
BlackVolatilitySurfaceAbsolute(Date referenceDate, const std::vector< Date > &dates, const std::vector< std::vector< Real > > &strikes, const std::vector< std::vector< Real > > &strikeQuotes, const DayCounter &dayCounter, const Calendar &calendar, const Handle< Quote > &spot, const Size spotDays, const Calendar spotCalendar, const Handle< YieldTermStructure > &domesticTS, const Handle< YieldTermStructure > &foreignTS, const DeltaVolQuote::DeltaType dt=DeltaVolQuote::DeltaType::Spot, const DeltaVolQuote::AtmType at=DeltaVolQuote::AtmType::AtmDeltaNeutral, const Period &switchTenor=2 *Years, const DeltaVolQuote::DeltaType ltdt=DeltaVolQuote::DeltaType::Fwd, const DeltaVolQuote::AtmType ltat=DeltaVolQuote::AtmType::AtmDeltaNeutral, const SmileInterpolation smileInterpolation=SmileInterpolation::Cubic, const bool flatExtrapolation=true)
Volatility blackVolImpl(Time t, Real strike) const override
std::map< std::pair< Real, Real >, Real > cachedInterpolatedVols_
flat interpolation decorator
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
vector< Real > strikes