QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
lognormalcmsspreadpricer.cpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 Copyright (C) 2014, 2015, 2018 Peter Caspers
4
5 This file is part of QuantLib, a free-software/open-source library
6 for financial quantitative analysts and developers - http://quantlib.org/
7
8 QuantLib is free software: you can redistribute it and/or modify it
9 under the terms of the QuantLib license. You should have received a
10 copy of the license along with this program; if not, please email
11 <quantlib-dev@lists.sf.net>. The license is also available online at
12 <http://quantlib.org/license.shtml>.
13
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20/*! \file lognormalcmsspreadpricer.cpp
21*/
22
28#include <ql/optional.hpp>
29#include <utility>
30
31using std::sqrt;
32
33namespace QuantLib {
34
35 class LognormalCmsSpreadPricer::integrand_f {
36 const LognormalCmsSpreadPricer* pricer;
37 public:
38 explicit integrand_f(const LognormalCmsSpreadPricer* pricer)
39 : pricer(pricer) {}
40 Real operator()(Real x) const {
41 return pricer->integrand(x);
42 }
43 };
44
46 const ext::shared_ptr<CmsCouponPricer>& cmsPricer,
47 const Handle<Quote>& correlation,
48 Handle<YieldTermStructure> couponDiscountCurve,
49 const Size integrationPoints,
50 const ext::optional<VolatilityType>& volatilityType,
51 const Real shift1,
52 const Real shift2)
53 : CmsSpreadCouponPricer(correlation), cmsPricer_(cmsPricer),
54 couponDiscountCurve_(std::move(couponDiscountCurve)) {
55
57 if (!couponDiscountCurve_.empty())
60
61 QL_REQUIRE(integrationPoints >= 4,
62 "at least 4 integration points should be used ("
63 << integrationPoints << ")");
65 ext::make_shared<GaussHermiteIntegration>(integrationPoints);
66
67 cnd_ = ext::make_shared<CumulativeNormalDistribution>(0.0, 1.0);
68
69 if (!volatilityType) {
70 QL_REQUIRE(shift1 == Null<Real>() && shift2 == Null<Real>(),
71 "if volatility type is inherited, no shifts should be "
72 "specified");
74 volType_ = cmsPricer->swaptionVolatility()->volatilityType();
75 } else {
76 shift1_ = shift1 == Null<Real>() ? 0.0 : shift1;
77 shift2_ = shift2 == Null<Real>() ? 0.0 : shift2;
79 volType_ = *volatilityType;
80 }
81 }
82
84
85 // this is Brigo, 13.16.2 with x = v / sqrt(2)
86
87 Real v = M_SQRT2 * x;
88 Real h =
89 k_ - b_ * s2_ * std::exp((m2_ - 0.5 * v2_ * v2_) * fixingTime_ +
90 v2_ * std::sqrt(fixingTime_) * v);
91 Real phi1, phi2;
92 phi1 = (*cnd_)(
93 phi_ * (std::log(a_ * s1_ / h) +
94 (m1_ + (0.5 - rho_ * rho_) * v1_ * v1_) * fixingTime_ +
95 rho_ * v1_ * std::sqrt(fixingTime_) * v) /
96 (v1_ * std::sqrt(fixingTime_ * (1.0 - rho_ * rho_))));
97 phi2 = (*cnd_)(
98 phi_ * (std::log(a_ * s1_ / h) +
99 (m1_ - 0.5 * v1_ * v1_) * fixingTime_ +
100 rho_ * v1_ * std::sqrt(fixingTime_) * v) /
101 (v1_ * std::sqrt(fixingTime_ * (1.0 - rho_ * rho_))));
102 Real f = a_ * phi_ * s1_ *
103 std::exp(m1_ * fixingTime_ -
104 0.5 * rho_ * rho_ * v1_ * v1_ * fixingTime_ +
105 rho_ * v1_ * std::sqrt(fixingTime_) * v) *
106 phi1 -
107 phi_ * h * phi2;
108 return std::exp(-x * x) * f;
109 }
110
112
113 // this is http://ssrn.com/abstract=2686998, 3.20 with x = s / sqrt(2)
114
115 Real s = M_SQRT2 * x;
116
117 Real beta =
118 phi_ *
120 std::sqrt(fixingTime_) *
121 (rho_ * gearing1_ * vol1_ + gearing2_ * vol2_) * s);
122 Real f =
123 close_enough(alpha_, 0.0)
124 ? Real(std::max(beta, 0.0))
125 : psi_ * alpha_ / (M_SQRTPI * M_SQRT2) *
126 std::exp(-beta * beta / (2.0 * alpha_ * alpha_)) +
127 beta * (1.0 - (*cnd_)(-psi_ * beta / alpha_));
128 return std::exp(-x * x) * f;
129 }
130
131 void
133
134 coupon_ = dynamic_cast<const CmsSpreadCoupon *>(&coupon);
135 QL_REQUIRE(coupon_, "CMS spread coupon needed");
139
142
143 // if no coupon discount curve is given just use the discounting curve
144 // from the _first_ swap index.
145 // for rate calculation this curve cancels out in the computation, so
146 // e.g. the discounting
147 // swap engine will produce correct results, even if the
148 // couponDiscountCurve is not set here.
149 // only the price member function in this class will be dependent on the
150 // coupon discount curve.
151
153
154 if (couponDiscountCurve_.empty())
156 index_->swapIndex1()->exogenousDiscount()
157 ? index_->swapIndex1()->discountingTermStructure()
158 : index_->swapIndex1()->forwardingTermStructure();
159
160 discount_ = paymentDate_ > couponDiscountCurve_->referenceDate()
162 : 1.0;
163
165
166 gearing1_ = index_->gearing1();
167 gearing2_ = index_->gearing2();
168
169 QL_REQUIRE(gearing1_ > 0.0 && gearing2_ < 0.0,
170 "gearing1 (" << gearing1_
171 << ") should be positive while gearing2 ("
172 << gearing2_ << ") should be negative");
173
174 c1_ = ext::make_shared<CmsCoupon>(
177 index_->swapIndex1(), 1.0, 0.0, coupon_->referencePeriodStart(),
180
181 c2_ = ext::make_shared<CmsCoupon>(
184 index_->swapIndex2(), 1.0, 0.0, coupon_->referencePeriodStart(),
187
188 c1_->setPricer(cmsPricer_);
189 c2_->setPricer(cmsPricer_);
190
191 if (fixingDate_ > today_) {
192
193 fixingTime_ = cmsPricer_->swaptionVolatility()->timeFromReference(
195
196 swapRate1_ = c1_->indexFixing();
197 swapRate2_ = c2_->indexFixing();
198
199 adjustedRate1_ = c1_->adjustedFixing();
200 adjustedRate2_ = c2_->adjustedFixing();
201
202 ext::shared_ptr<SwaptionVolatilityStructure> swvol =
203 *cmsPricer_->swaptionVolatility();
204 ext::shared_ptr<SwaptionVolatilityCube> swcub =
205 ext::dynamic_pointer_cast<SwaptionVolatilityCube>(swvol);
206
208 shift1_ =
209 swvol->shift(fixingDate_, index_->swapIndex1()->tenor());
210 shift2_ =
211 swvol->shift(fixingDate_, index_->swapIndex2()->tenor());
212 }
213
214 if (swcub == nullptr) {
215 // not a cube, just an atm surface given, so we can
216 // not easily convert volatilities and just forbid it
218 "if only an atm surface is given, the volatility "
219 "type must be inherited");
220 vol1_ = swvol->volatility(
221 fixingDate_, index_->swapIndex1()->tenor(), swapRate1_);
222 vol2_ = swvol->volatility(
223 fixingDate_, index_->swapIndex2()->tenor(), swapRate2_);
224 } else {
225 vol1_ = swcub->smileSection(fixingDate_,
226 index_->swapIndex1()->tenor())
227 ->volatility(swapRate1_, volType_, shift1_);
228 vol2_ = swcub->smileSection(fixingDate_,
229 index_->swapIndex2()->tenor())
230 ->volatility(swapRate2_, volType_, shift2_);
231 }
232
234 mu1_ = 1.0 / fixingTime_ * std::log((adjustedRate1_ + shift1_) /
235 (swapRate1_ + shift1_));
236 mu2_ = 1.0 / fixingTime_ * std::log((adjustedRate2_ + shift2_) /
237 (swapRate2_ + shift2_));
238 }
239 // for the normal volatility case we do not need the drifts
240 // but rather use adjusted rates directly in the integrand
241
242 rho_ = std::max(std::min(correlation()->value(), 0.9999),
243 -0.9999); // avoid division by zero in integrand
244 } else {
245 // fixing is in the past or today
246 adjustedRate1_ = c1_->indexFixing();
247 adjustedRate2_ = c2_->indexFixing();
248 }
249 }
250
252 Real strike) const {
253 // this method is only called for future fixings
254 optionType_ = optionType;
255 phi_ = optionType == Option::Call ? 1.0 : -1.0;
256 Real res = 0.0;
257 if (volType_ == ShiftedLognormal) {
258 // (shifted) lognormal volatility
259 if (strike >= 0.0) {
260 a_ = gearing1_;
261 b_ = gearing2_;
264 m1_ = mu1_;
265 m2_ = mu2_;
266 v1_ = vol1_;
267 v2_ = vol2_;
268 k_ = strike + gearing1_ * shift1_ + gearing2_ * shift2_;
269 } else {
270 a_ = -gearing2_;
271 b_ = -gearing1_;
274 m1_ = mu2_;
275 m2_ = mu1_;
276 v1_ = vol2_;
277 v2_ = vol1_;
278 k_ = -strike - gearing1_ * shift1_ - gearing2_ * shift2_;
279 res += phi_ * (gearing1_ * adjustedRate1_ +
280 gearing2_ * adjustedRate2_ - strike);
281 }
282 res +=
283 1.0 / M_SQRTPI * (*integrator_)(integrand_f(this));
284 } else {
285 // normal volatility
286 Real forward = gearing1_ * adjustedRate1_ +
288 Real stddev =
289 std::sqrt(fixingTime_ *
292 2.0 * gearing1_ * gearing2_ * rho_ * vol1_ * vol2_));
293 res =
294 bachelierBlackFormula(optionType_, strike, forward, stddev, 1.0);
295 }
296 return res * discount_ * coupon_->accrualPeriod();
297 }
298
301 }
302
304 // caplet is equivalent to call option on fixing
305 if (fixingDate_ <= today_) {
306 // the fixing is determined
307 const Rate Rs = std::max(
308 coupon_->index()->fixing(fixingDate_) - effectiveCap, 0.);
309 Rate price = gearing_ * Rs * coupon_->accrualPeriod() * discount_;
310 return price;
311 } else {
313 return gearing_ * capletPrice;
314 }
315 }
316
318 return capletPrice(effectiveCap) /
320 }
321
323 // floorlet is equivalent to put option on fixing
324 if (fixingDate_ <= today_) {
325 // the fixing is determined
326 const Rate Rs = std::max(
327 effectiveFloor - coupon_->index()->fixing(fixingDate_), 0.);
328 Rate price = gearing_ * Rs * coupon_->accrualPeriod() * discount_;
329 return price;
330 } else {
331 Real floorletPrice = optionletPrice(Option::Put, effectiveFloor);
332 return gearing_ * floorletPrice;
333 }
334 }
335
337 return floorletPrice(effectiveFloor) /
339 }
340
345 }
346}
Black formula.
CMS spread coupon class.
const ext::shared_ptr< SwapSpreadIndex > & swapSpreadIndex() const
base pricer for vanilla CMS spread coupons
Handle< Quote > correlation() const
const Date & referencePeriodEnd() const
end date of the reference period
Definition: coupon.hpp:116
const Date & accrualStartDate() const
start of the accrual period
Definition: coupon.hpp:104
virtual Real nominal() const
Definition: coupon.hpp:100
const Date & accrualEndDate() const
end of the accrual period
Definition: coupon.hpp:108
Date date() const override
Definition: coupon.hpp:53
Time accrualPeriod() const
accrual period as fraction of year
Definition: coupon.cpp:44
const Date & referencePeriodStart() const
start date of the reference period
Definition: coupon.hpp:112
base floating-rate coupon class
virtual Date fixingDate() const
fixing date
Natural fixingDays() const
fixing days
Real gearing() const
index gearing, i.e. multiplicative coefficient for the index
DayCounter dayCounter() const override
day counter for accrual calculation
const ext::shared_ptr< InterestRateIndex > & index() const
floating index
Spread spread() const
spread paid over the fixing of the underlying index
bool isInArrears() const
whether or not the coupon fixes in arrears
Shared handle to an observable.
Definition: handle.hpp:41
Real capletPrice(Rate effectiveCap) const override
Rate floorletRate(Rate effectiveFloor) const override
void initialize(const FloatingRateCoupon &coupon) override
ext::shared_ptr< GaussianQuadrature > integrator_
ext::shared_ptr< CmsCouponPricer > cmsPricer_
LognormalCmsSpreadPricer(const ext::shared_ptr< CmsCouponPricer > &cmsPricer, const Handle< Quote > &correlation, Handle< YieldTermStructure > couponDiscountCurve=Handle< YieldTermStructure >(), Size IntegrationPoints=16, const ext::optional< VolatilityType > &volatilityType=ext::nullopt, Real shift1=Null< Real >(), Real shift2=Null< Real >())
ext::shared_ptr< CumulativeNormalDistribution > cnd_
Handle< YieldTermStructure > couponDiscountCurve_
Real floorletPrice(Rate effectiveFloor) const override
ext::shared_ptr< SwapSpreadIndex > index_
Real optionletPrice(Option::Type optionType, Real strike) const
Rate capletRate(Rate effectiveCap) const override
template class providing a null value for a given type.
Definition: null.hpp:76
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
DateProxy & evaluationDate()
the date at which pricing is to be performed.
Definition: settings.hpp:147
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
CMS spread coupon.
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
QL_REAL Real
real number
Definition: types.hpp:50
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
Integral of a 1-dimensional function using the Gauss-Kronrod method.
cms spread coupon pricer as in Brigo, Mercurio, 13.6.2, with extensions for shifted lognormal and nor...
#define M_SQRTPI
#define M_SQRT2
Definition: any.hpp:35
Real bachelierBlackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount)
bool close_enough(const Quantity &m1, const Quantity &m2, Size n)
Definition: quantity.cpp:182
STL namespace.
Maps optional to either the boost or std implementation.
ext::shared_ptr< BlackVolTermStructure > v
Real beta
Definition: sabr.cpp:200
Swaption volatility cube.