Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
dynamicblackvoltermstructure.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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 termstructures/dynamicblackvoltermstructure.hpp
20 \brief dynamic black volatility term structure
21 \ingroup termstructures
22*/
23
24#ifndef quantext_dynamic_black_volatility_termstructure_hpp
25#define quantext_dynamic_black_volatility_termstructure_hpp
26
29
30#include <ql/termstructures/volatility/equityfx/blackvoltermstructure.hpp>
31#include <ql/termstructures/yieldtermstructure.hpp>
32
33#include <ql/math/comparison.hpp>
34
35namespace QuantExt {
36using namespace QuantLib;
37
38namespace tag {
39struct curve {};
40struct surface {};
41} // namespace tag
42
43//! Takes a BlackVolTermStructure with fixed reference date and turns it into a floating reference date term structure.
44/*! This class takes a BlackVolTermStructure with fixed reference date
45 and turns it into a floating reference date term structure.
46 There are different ways of reacting to time decay that can be
47 specified. As an additional feature, the class will return the
48 ATM volatility if a null strike is given (currently, for this
49 extrapolation must be allowed, since there is a check in
50 VolatilityTermStructure we can no extend or bypass). ATM is
51 defined as the forward level here (which is of particular
52 interest for FX term structures).
53
54 if curve is specified, a more efficient implementation for
55 variance and volatility is used just passing through the
56 given strike to the source term structure; note that in this
57 case a null strike will not be converted to atm though.
58
59 \ingroup termstructures
60*/
61template <typename mode = tag::surface> class DynamicBlackVolTermStructure : public BlackVolTermStructure {
62public:
63 /* For a stickiness that involves ATM calculations, the yield term
64 structures and the spot (as of today, i.e. without settlement lag)
65 must be given. They are also required if an ATM volatility with null
66 strike is requested. The termstructures are expected to have a
67 floating reference date consistent with the spot.
68 Since we have to store the initial forward curve at construction,
69 we sample it on a grid that can be customized here, too. The curve
70 is then linearly interpolated and extrapolated flat after the
71 last grid point. */
72
73 DynamicBlackVolTermStructure(const Handle<BlackVolTermStructure>& source, Natural settlementDays,
74 const Calendar& calendar, ReactionToTimeDecay decayMode = ConstantVariance,
75 Stickyness stickyness = StickyLogMoneyness,
76 const Handle<YieldTermStructure>& riskfree = Handle<YieldTermStructure>(),
77 const Handle<YieldTermStructure>& dividend = Handle<YieldTermStructure>(),
78 const Handle<Quote>& spot = Handle<Quote>(),
79 const std::vector<Real> initialForwardGrid = std::vector<Real>());
80
81 Real atm() const;
82
83 /* VolatilityTermStructure interface */
84 Real minStrike() const override;
85 Real maxStrike() const override;
86 /* TermStructure interface */
87 Date maxDate() const override;
88 /* Observer interface */
89 void update() override;
90
91protected:
92 /* BlackVolTermStructure interface */
93 Real blackVarianceImpl(Time t, Real strike) const override;
94 Volatility blackVolImpl(Time t, Real strike) const override;
95 /* implementations for curve and surface tags */
96 Real blackVarianceImplTag(Time t, Real strike, tag::curve) const;
97 Real blackVarianceImplTag(Time t, Real strike, tag::surface) const;
98
99private:
100 const Handle<BlackVolTermStructure> source_;
103 const Handle<YieldTermStructure> riskfree_, dividend_;
104 const Handle<Quote> spot_;
106 const bool atmKnown_;
108 QuantLib::ext::shared_ptr<Interpolation> initialForwardCurve_;
109};
110
111template <typename mode>
113 Natural settlementDays, const Calendar& calendar,
114 ReactionToTimeDecay decayMode, Stickyness stickyness,
115 const Handle<YieldTermStructure>& riskfree,
116 const Handle<YieldTermStructure>& dividend,
117 const Handle<Quote>& spot,
118 const std::vector<Real> forwardCurveSampleGrid)
119 : BlackVolTermStructure(settlementDays, calendar, source->businessDayConvention(), source->dayCounter()),
120 source_(source), decayMode_(decayMode), stickyness_(stickyness), riskfree_(riskfree), dividend_(dividend),
121 spot_(spot), originalReferenceDate_(source->referenceDate()),
122 atmKnown_(!riskfree.empty() && !dividend.empty() && !spot.empty()),
123 forwardCurveSampleGrid_(forwardCurveSampleGrid) {
124
125 QL_REQUIRE(stickyness == StickyStrike || stickyness == StickyLogMoneyness,
126 "stickiness (" << stickyness << ") not supported");
127 QL_REQUIRE(decayMode == ConstantVariance || decayMode == ForwardForwardVariance,
128 "reaction to time decay (" << decayMode << ") not supported");
129
130 registerWith(source);
131
132 if (stickyness != StickyStrike) {
133 QL_REQUIRE(atmKnown_, "for stickiness other than strike, the term "
134 "structures and spot must be given");
135 QL_REQUIRE(riskfree_->referenceDate() == source_->referenceDate(),
136 "at construction time the reference dates of the volatility "
137 "term structure ("
138 << source->referenceDate() << ") and the risk free yield term structure ("
139 << riskfree_->referenceDate() << ") must be the same");
140 QL_REQUIRE(dividend_->referenceDate() == source_->referenceDate(),
141 "at construction time the reference dates of the volatility "
142 "term structure ("
143 << source->referenceDate() << ") and the dividend term structure (" << riskfree_->referenceDate()
144 << ") must be the same");
145 registerWith(riskfree_);
146 registerWith(dividend_);
147 registerWith(spot_);
148 }
149
150 if (atmKnown_) {
151 if (forwardCurveSampleGrid_.size() == 0) {
152 // use default grid
153 Real tmp[] = { 0.0, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0,
154 8.0, 9.0, 10.0, 12.0, 15.0, 20.0, 25.0, 30.0, 40.0, 50.0, 60.0 };
155 forwardCurveSampleGrid_ = std::vector<Real>(tmp, tmp + sizeof(tmp) / sizeof(tmp[0]));
156 }
157 QL_REQUIRE(QuantLib::close_enough(forwardCurveSampleGrid_[0], 0.0),
158 "forward curve sample grid must start at 0 (" << forwardCurveSampleGrid_[0]);
160 for (Size i = 1; i < forwardCurveSampleGrid_.size(); ++i) {
162 "forward curve sample grid must have increasing times (at "
163 << (i - 1) << ", " << i << ": " << forwardCurveSampleGrid_[i - 1] << ", "
165 }
166 for (Size i = 0; i < forwardCurveSampleGrid_.size(); ++i) {
167 Real t = forwardCurveSampleGrid_[i];
168 initialForwards_[i] = spot_->value() / riskfree_->discount(t) * dividend_->discount(t);
169 }
170 initialForwardCurve_ = QuantLib::ext::make_shared<FlatExtrapolation>(QuantLib::ext::make_shared<LinearInterpolation>(
172 initialForwardCurve_->enableExtrapolation();
173 }
174}
175
176template <typename mode> void DynamicBlackVolTermStructure<mode>::update() { BlackVolTermStructure::update(); }
177
178template <typename mode> Date DynamicBlackVolTermStructure<mode>::maxDate() const {
179 if (decayMode_ == ForwardForwardVariance) {
180 return source_->maxDate();
181 }
182 if (decayMode_ == ConstantVariance) {
183 return Date(std::min(Date::maxDate().serialNumber(), referenceDate().serialNumber() -
184 originalReferenceDate_.serialNumber() +
185 source_->maxDate().serialNumber()));
186 }
187 QL_FAIL("unexpected decay mode (" << decayMode_ << ")");
188}
189
190template <typename mode> Real DynamicBlackVolTermStructure<mode>::minStrike() const {
191 if (stickyness_ == StickyStrike) {
192 return source_->minStrike();
193 }
194 if (stickyness_ == StickyLogMoneyness) {
195 // we do not specify this, since it is maturity dependent
196 // instead we allow for extrapolation when asking the
197 // source for a volatility and are not in sticky strike mode
198 return 0.0;
199 }
200 QL_FAIL("unexpected stickiness (" << stickyness_ << ")");
201}
202
203template <typename mode> Real DynamicBlackVolTermStructure<mode>::maxStrike() const {
204 if (stickyness_ == StickyStrike) {
205 return source_->maxStrike();
206 }
207 if (stickyness_ == StickyLogMoneyness) {
208 // see above
209 return QL_MAX_REAL;
210 }
211 QL_FAIL("unexpected stickiness (" << stickyness_ << ")");
212}
213
214template <typename mode> Volatility DynamicBlackVolTermStructure<mode>::blackVolImpl(Time t, Real strike) const {
215 Real tmp = std::max(1.0E-6, t);
216 return std::sqrt(blackVarianceImpl(tmp, strike) / tmp);
217}
218
219template <typename mode> Real DynamicBlackVolTermStructure<mode>::blackVarianceImpl(Time t, Real strike) const {
220 return blackVarianceImplTag(t, strike, mode());
221}
222
223template <typename mode>
225 if (strike == Null<Real>()) {
226 QL_REQUIRE(atmKnown_, "can not calculate atm level (null strike is "
227 "given) because a curve or the spot is missing");
228 strike = spot_->value() / riskfree_->discount(t) * dividend_->discount(t);
229 }
230 Real scenarioT0 = 0.0, scenarioT1 = t;
231 Real scenarioStrike0 = strike, scenarioStrike1 = strike;
232 if (decayMode_ == ForwardForwardVariance) {
233 scenarioT0 = source_->timeFromReference(referenceDate());
234 scenarioT1 = scenarioT0 + t;
235 }
236 if (stickyness_ == StickyLogMoneyness) {
237 Real forward = spot_->value() / riskfree_->discount(t) * dividend_->discount(t);
238 scenarioStrike1 = initialForwardCurve_->operator()(scenarioT1) / forward * strike;
239 scenarioStrike0 = initialForwardCurve_->operator()(scenarioT0) / spot_->value() * strike;
240 }
241 return std::max(0.0, source_->blackVariance(scenarioT1, scenarioStrike1, true) -
242 source_->blackVariance(scenarioT0, scenarioStrike0, true));
243}
244
245template <typename mode>
247 if (decayMode_ == ForwardForwardVariance) {
248 Real scenarioT0 = source_->timeFromReference(referenceDate());
249 Real scenarioT1 = scenarioT0 + t;
250 return std::max(0.0, source_->blackVariance(scenarioT1, strike, true) - source_->blackVariance(scenarioT0, strike, true));
251 } else {
252 return source_->blackVariance(t, strike, true);
253 }
254}
255
256} // namespace QuantExt
257
258#endif
Takes a BlackVolTermStructure with fixed reference date and turns it into a floating reference date t...
Real blackVarianceImplTag(Time t, Real strike, tag::curve) const
DynamicBlackVolTermStructure(const Handle< BlackVolTermStructure > &source, Natural settlementDays, const Calendar &calendar, ReactionToTimeDecay decayMode=ConstantVariance, Stickyness stickyness=StickyLogMoneyness, const Handle< YieldTermStructure > &riskfree=Handle< YieldTermStructure >(), const Handle< YieldTermStructure > &dividend=Handle< YieldTermStructure >(), const Handle< Quote > &spot=Handle< Quote >(), const std::vector< Real > initialForwardGrid=std::vector< Real >())
QuantLib::ext::shared_ptr< Interpolation > initialForwardCurve_
Real blackVarianceImpl(Time t, Real strike) const override
Volatility blackVolImpl(Time t, Real strike) const override
const Handle< BlackVolTermStructure > source_
dynamics type definitions
flat interpolation decorator
Stickyness
Stickiness.
ReactionToTimeDecay
Reaction to Time Decay.
@ StickyLogMoneyness
@ ConstantVariance
@ ForwardForwardVariance