21#include <ql/math/interpolations/bilinearinterpolation.hpp>
22#include <ql/math/interpolations/flatextrapolation2d.hpp>
23#include <ql/math/interpolations/linearinterpolation.hpp>
24#include <ql/quotes/simplequote.hpp>
25#include <ql/termstructures/yield/forwardcurve.hpp>
26#include <ql/utilities/dataformatters.hpp>
28#include <boost/make_shared.hpp>
35 const Handle<BlackVolTermStructure>& referenceVol,
const Handle<Quote>& movingSpot,
const std::vector<Time>& times,
36 const std::vector<Real>& moneyness,
const std::vector<std::vector<Handle<Quote>>>& volSpreads,
37 const Handle<Quote>& stickySpot,
const Handle<YieldTermStructure>& stickyDividendTs,
38 const Handle<YieldTermStructure>& stickyRiskFreeTs,
const Handle<YieldTermStructure>& movingDividendTs,
39 const Handle<YieldTermStructure>& movingRiskFreeTs,
bool stickyStrike)
40 : BlackVolatilityTermStructure(referenceVol->businessDayConvention(), referenceVol->dayCounter()),
41 referenceVol_(referenceVol), movingSpot_(movingSpot), times_(times), moneyness_(moneyness),
42 volSpreads_(volSpreads), stickySpot_(stickySpot), stickyDividendTs_(stickyDividendTs),
43 stickyRiskFreeTs_(stickyRiskFreeTs), movingDividendTs_(movingDividendTs), movingRiskFreeTs_(movingRiskFreeTs),
44 stickyStrike_(stickyStrike) {
48 registerWith(referenceVol_);
49 registerWith(movingSpot_);
50 registerWith(stickySpot_);
52 for (
auto const& v : volSpreads_)
53 for (
auto const& s : v)
56 registerWith(stickyDividendTs_);
57 registerWith(stickyRiskFreeTs_);
58 registerWith(movingDividendTs_);
59 registerWith(movingRiskFreeTs_);
63 QL_REQUIRE(!times_.empty(),
"no times given");
64 QL_REQUIRE(!moneyness_.empty(),
"no moneyness values given");
65 QL_REQUIRE(moneyness_.size() == volSpreads_.size(),
"mismatch between moneyness vector and vol matrix rows");
67 for (
auto const& v : volSpreads_) {
68 QL_REQUIRE(times_.size() == v.size(),
"mismatch between times vector and vol matrix columns");
71 for (Size j = 1; j < times_.size(); ++j) {
72 QL_REQUIRE(times_[j] > times_[j - 1],
"Times must be sorted and unique but found that the "
73 << io::ordinal(j) <<
" time, " << times_[j]
74 <<
", is not greater than the " << io::ordinal(j - 1) <<
" time, "
75 << times_[j - 1] <<
".");
80 if (times_.size() == 1) {
81 times_.push_back(times_.back() + 1.0);
82 for (
auto& v : volSpreads_)
83 v.push_back(v.back());
88 if (moneyness_.size() == 1) {
89 moneyness_.push_back(moneyness_.back() + 1.0);
90 volSpreads_.push_back(volSpreads_.back());
95 data_ = Matrix(moneyness_.size(), times_.size(), 0.0);
96 volSpreadSurface_ = FlatExtrapolator2D(QuantLib::ext::make_shared<BilinearInterpolation>(
97 times_.begin(), times_.end(), moneyness_.begin(), moneyness_.end(), data_));
98 volSpreadSurface_.enableExtrapolation();
109 LazyObject::update();
110 BlackVolatilityTermStructure::update();
116 for (Size j = 0; j <
data_.columns(); ++j) {
117 for (Size i = 0; i <
data_.rows(); ++i) {
126 QL_REQUIRE(!
referenceVol_.empty(),
"SpreadedBlackVolatilitySurfaceMoneyness: reference vol is empty");
128 QL_REQUIRE(std::isfinite(m),
129 "SpreadedBlackVolatilitySurfaceMoneyness: got invalid moneyness (dynamic reference) at t = "
130 << t <<
", strike = " << strike <<
": " << m);
136 QL_REQUIRE(std::isfinite(effStrike),
137 "SpreadedBlackVolatilitySurfaceMoneyness: got invalid strike from moneyness at t = "
138 << t <<
", input strike = " << strike <<
", moneyness = " << m);
141 QL_REQUIRE(std::isfinite(m2),
142 "SpreadedBlackVolatilitySurfaceMoneyness: got invalid moneyness (sticky reference) at t = "
143 << t <<
", strike = " << strike <<
": " << m2);
148 const bool stickyReference)
const {
149 if (strike == Null<Real>() ||
close_enough(strike, 0.0)) {
152 QL_REQUIRE(!stickyReference || !
stickySpot_.empty(),
153 "SpreadedBlackVolatilitySurfaceMoneynessSpot: stickySpot is empty");
154 QL_REQUIRE(stickyReference || !
movingSpot_.empty(),
155 "SpreadedBlackVolatilitySurfaceMoneynessSpot: movingSpot is empty");
161 const bool stickyReference)
const {
162 QL_REQUIRE(!stickyReference || !
stickySpot_.empty(),
163 "SpreadedBlackVolatilitySurfaceMoneynessSpot: stickySpot is empty");
164 QL_REQUIRE(stickyReference || !
movingSpot_.empty(),
165 "SpreadedBlackVolatilitySurfaceMoneynessSpot: movingSpot is empty");
170 const bool stickyReference)
const {
171 if (strike == Null<Real>() ||
close_enough(strike, 0.0)) {
174 QL_REQUIRE(!stickyReference || !
stickySpot_.empty(),
175 "SpreadedBlackVolatilitySurfaceLogMoneynessSpot: stickySpot is empty");
176 QL_REQUIRE(stickyReference || !
movingSpot_.empty(),
177 "SpreadedBlackVolatilitySurfaceLogMoneynessSpot: movingSpot is empty");
183 const bool stickyReference)
const {
184 QL_REQUIRE(!stickyReference || !
stickySpot_.empty(),
185 "SpreadedBlackVolatilitySurfaceLogMoneynessSpot: stickySpot is empty");
186 QL_REQUIRE(stickyReference || !
movingSpot_.empty(),
187 "SpreadedBlackVolatilitySurfaceLogMoneynessSpot: movingSpot is empty");
192 const bool stickyReference)
const {
193 if (strike == Null<Real>() ||
close_enough(strike, 0.0))
197 if (stickyReference) {
198 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: stickySpot is empty");
200 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyDividendTs is empty");
202 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyRiskFreeTs is empty");
205 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: movingSpot is empty");
207 "SpreadedBlackVolatilitySurfaceMoneynessForward: movingDividendTs is empty");
209 "SpreadedBlackVolatilitySurfaceMoneynessForward: mocingRiskFreeTs is empty");
212 return strike / forward;
217 const bool stickyReference)
const {
219 if (stickyReference) {
220 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: stickySpot is empty");
222 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyDividendTs is empty");
224 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyRiskFreeTs is empty");
227 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: movingSpot is empty");
229 "SpreadedBlackVolatilitySurfaceMoneynessForward: movingDividendTs is empty");
231 "SpreadedBlackVolatilitySurfaceMoneynessForward: mocingRiskFreeTs is empty");
238 const bool stickyReference)
const {
239 if (strike == Null<Real>() ||
close_enough(strike, 0.0))
243 if (stickyReference) {
244 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceLogMoneynessForward: stickySpot is empty");
246 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: stickyDividendTs is empty");
248 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: stickyRiskFreeTs is empty");
251 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceLogMoneynessForward: movingSpot is empty");
253 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: movingDividendTs is empty");
255 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: mocingRiskFreeTs is empty");
258 return std::log(strike / forward);
263 const bool stickyReference)
const {
265 if (stickyReference) {
266 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceLogMoneynessForward: stickySpot is empty");
268 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: stickyDividendTs is empty");
270 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: stickyRiskFreeTs is empty");
273 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceLogMoneynessForward: movingSpot is empty");
275 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: movingDividendTs is empty");
277 "SpreadedBlackVolatilitySurfaceLogMoneynessForward: mocingRiskFreeTs is empty");
287 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: stickySpot is empty");
288 QL_REQUIRE(!
stickyDividendTs_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: stickyDividendTs is empty");
289 QL_REQUIRE(!
stickyRiskFreeTs_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: stickyRiskFreeTs is empty");
292 if (stickyReference) {
293 forward = stickyForward;
295 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: movingSpot is empty");
296 QL_REQUIRE(!
movingDividendTs_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: movingDividendTs is empty");
297 QL_REQUIRE(!
movingRiskFreeTs_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: mocingRiskFreeTs is empty");
304 return std::log(strike / forward) / (vol * std::sqrt(t));
309 const bool stickyReference)
const {
312 if (stickyReference) {
313 forward = stickyForward;
315 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: movingSpot is empty");
316 QL_REQUIRE(!
movingDividendTs_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: movingDividendTs is empty");
317 QL_REQUIRE(!
movingRiskFreeTs_.empty(),
"SpreadedBlackVolatilitySurfaceStdDevs: mocingRiskFreeTs is empty");
324 return std::exp(
moneyness * vol * std::sqrt(t)) * forward;
328 const bool stickyReference)
const {
329 if (strike == Null<Real>() ||
close_enough(strike, 0.0)) {
332 QL_REQUIRE(!stickyReference || !
stickySpot_.empty(),
333 "SpreadedBlackVolatilitySurfaceMoneynessSpot: stickySpot is empty");
334 QL_REQUIRE(stickyReference || !
movingSpot_.empty(),
335 "SpreadedBlackVolatilitySurfaceMoneynessSpot: movingSpot is empty");
341 const bool stickyReference)
const {
342 QL_REQUIRE(!stickyReference || !
stickySpot_.empty(),
343 "SpreadedBlackVolatilitySurfaceMoneynessSpot: stickySpot is empty");
344 QL_REQUIRE(stickyReference || !
movingSpot_.empty(),
345 "SpreadedBlackVolatilitySurfaceMoneynessSpot: movingSpot is empty");
350 const bool stickyReference)
const {
351 if (strike == Null<Real>() ||
close_enough(strike, 0.0))
355 if (stickyReference) {
356 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: stickySpot is empty");
358 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyDividendTs is empty");
360 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyRiskFreeTs is empty");
363 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: movingSpot is empty");
365 "SpreadedBlackVolatilitySurfaceMoneynessForward: movingDividendTs is empty");
367 "SpreadedBlackVolatilitySurfaceMoneynessForward: mocingRiskFreeTs is empty");
370 return strike - forward;
375 const bool stickyReference)
const {
377 if (stickyReference) {
378 QL_REQUIRE(!
stickySpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: stickySpot is empty");
380 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyDividendTs is empty");
382 "SpreadedBlackVolatilitySurfaceMoneynessForward: stickyRiskFreeTs is empty");
385 QL_REQUIRE(!
movingSpot_.empty(),
"SpreadedBlackVolatilitySurfaceMoneynessForward: movingSpot is empty");
387 "SpreadedBlackVolatilitySurfaceMoneynessForward: movingDividendTs is empty");
389 "SpreadedBlackVolatilitySurfaceMoneynessForward: mocingRiskFreeTs is empty");
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
void performCalculations() const override
Handle< Quote > movingSpot_
Handle< BlackVolTermStructure > referenceVol_
Calendar calendar() const override
virtual Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const =0
virtual Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const =0
Handle< Quote > stickySpot_
const std::vector< QuantLib::Real > & moneyness() const
const Date & referenceDate() const override
Real minStrike() const override
Handle< YieldTermStructure > movingRiskFreeTs_
SpreadedBlackVolatilitySurfaceMoneyness(const Handle< BlackVolTermStructure > &referenceVol, const Handle< Quote > &movingSpot, const std::vector< Time > ×, const std::vector< Real > &moneyness, const std::vector< std::vector< Handle< Quote > > > &volSpreads, const Handle< Quote > &stickySpot, const Handle< YieldTermStructure > &stickyDividendTs, const Handle< YieldTermStructure > &stickyRiskFreeTs, const Handle< YieldTermStructure > &movingDividendTs, const Handle< YieldTermStructure > &movingRiskFreeTs, bool stickyStrike)
Natural settlementDays() const override
Handle< YieldTermStructure > movingDividendTs_
Date maxDate() const override
Handle< YieldTermStructure > stickyRiskFreeTs_
std::vector< std::vector< Handle< Quote > > > volSpreads_
Real blackVolImpl(Time t, Real strike) const override
Interpolation2D volSpreadSurface_
Handle< YieldTermStructure > stickyDividendTs_
Real maxStrike() const override
std::vector< Real > moneyness_
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
Real strikeFromMoneyness(Time t, Real moneyness, const bool stickyReference) const override
Real moneynessFromStrike(Time t, Real strike, const bool stickyReference) const override
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Spreaded Black volatility surface based on moneyness.