QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
lineartsrpricer.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2014, 2016 Peter Caspers
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
24#include <ql/cashflows/cmscoupon.hpp>
25#include <ql/cashflows/fixedratecoupon.hpp>
26#include <ql/cashflows/iborcoupon.hpp>
27#include <ql/cashflows/lineartsrpricer.hpp>
28#include <ql/indexes/iborindex.hpp>
29#include <ql/instruments/vanillaswap.hpp>
30#include <ql/math/integrals/kronrodintegral.hpp>
31#include <ql/math/solvers1d/brent.hpp>
32#include <ql/pricingengines/blackformula.hpp>
33#include <ql/quotes/simplequote.hpp>
34#include <ql/termstructures/volatility/atmsmilesection.hpp>
35#include <ql/termstructures/yieldtermstructure.hpp>
36#include <ql/time/schedule.hpp>
37#include <utility>
38
39namespace QuantLib {
40
41 class LinearTsrPricer::integrand_f {
42 const LinearTsrPricer* pricer;
43 public:
44 explicit integrand_f(const LinearTsrPricer* pricer) : pricer(pricer) {}
45 Real operator()(Real x) const {
46 return pricer->integrand(x);
47 }
48 };
49
52
54 Handle<Quote> meanReversion,
55 Handle<YieldTermStructure> couponDiscountCurve,
56 const Settings& settings,
57 ext::shared_ptr<Integrator> integrator)
58 : CmsCouponPricer(swaptionVol), meanReversion_(std::move(meanReversion)),
59 couponDiscountCurve_(std::move(couponDiscountCurve)), settings_(settings),
60 volDayCounter_(swaptionVol->dayCounter()), integrator_(std::move(integrator)) {
61
62 if (!couponDiscountCurve_.empty())
64
65 if (integrator_ == nullptr)
67 ext::make_shared<GaussKronrodNonAdaptive>(1E-10, 5000, 1E-10);
68 }
69
71
73 if (std::fabs(meanReversion_->value()) < 1.0E-4)
74 return yf;
75 else
76 return (1.0 - std::exp(-meanReversion_->value() * yf)) /
77 meanReversion_->value();
78 }
79
81 const Real strike) const {
82
83 Real omega = (type == Option::Call ? 1.0 : -1.0);
84 Real s1 = std::max(omega * (swapRateValue_ - strike), 0.0) *
85 (a_ * swapRateValue_ + b_);
86 Real s2 = (a_ * strike + b_) *
87 smileSection_->optionPrice(strike, strike < swapRateValue_
89 : Option::Call);
90 return s1 + s2;
91 }
92
93 Real LinearTsrPricer::integrand(const Real strike) const {
94 return 2.0 * a_ * smileSection_->optionPrice(
95 strike, strike < swapRateValue_ ? Option::Put
96 : Option::Call);
97 }
98
100
101 coupon_ = dynamic_cast<const CmsCoupon *>(&coupon);
102 QL_REQUIRE(coupon_, "CMS coupon needed");
105
109
110 forwardCurve_ = swapIndex_->forwardingTermStructure();
111 if (swapIndex_->exogenousDiscount())
112 discountCurve_ = swapIndex_->discountingTermStructure();
113 else
115
116 // if no coupon discount curve is given just use the discounting curve
117 // from the swap index. for rate calculation this curve cancels out in
118 // the computation, so e.g. the discounting swap engine will produce
119 // correct results, even if the couponDiscountCurve is not set here.
120 // only the price member function in this class will be dependent on the
121 // coupon discount curve.
122
123 today_ = QuantLib::Settings::instance().evaluationDate();
124
125 if (paymentDate_ > today_ && !couponDiscountCurve_.empty())
128 discountCurve_->discount(paymentDate_);
129 else
131
133 discountCurve_->discount(paymentDate_) *
135
136 if (fixingDate_ > today_) {
137
138 swapTenor_ = swapIndex_->tenor();
139 swap_ = swapIndex_->underlyingSwap(fixingDate_);
140
141 swapRateValue_ = swap_->fairRate();
142 annuity_ = 1.0E4 * std::fabs(swap_->fixedLegBPS());
143
144 ext::shared_ptr<SmileSection> sectionTmp =
145 swaptionVolatility()->smileSection(fixingDate_, swapTenor_);
146
149
150 if(sectionTmp->volatilityType() == Normal) {
151 // adjust lower bound if it was not set explicitly
154 } else {
155 // adjust bounds by section's shift
156 adjustedLowerBound_ -= sectionTmp->shift();
157 adjustedUpperBound_ -= sectionTmp->shift();
158 }
159
160 // if the section does not provide an atm level, we enhance it to
161 // have one, no need to exit with an exception ...
162
163 if (sectionTmp->atmLevel() == Null<Real>())
164 smileSection_ = ext::make_shared<AtmSmileSection>(
165 sectionTmp, swapRateValue_);
166 else
167 smileSection_ = sectionTmp;
168
169 // compute linear model's parameters
170
171 Real gx = 0.0, gy = 0.0;
172 for (const auto& i : swap_->fixedLeg()) {
173 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(i);
174 Real yf = c->accrualPeriod();
175 Date d = c->date();
176 Real pv = yf * discountCurve_->discount(d);
177 gx += pv * GsrG(d);
178 gy += pv;
179 }
180
181 Real gamma = gx / gy;
182 Date lastd = swap_->fixedLeg().back()->date();
183
184 a_ = discountCurve_->discount(paymentDate_) *
185 (gamma - GsrG(paymentDate_)) /
186 (discountCurve_->discount(lastd) * GsrG(lastd) +
187 swapRateValue_ * gy * gamma);
188
189 b_ = discountCurve_->discount(paymentDate_) / gy -
191 }
192 }
193
195 Option::Type optionType,
196 Real referenceStrike) const {
197
198 Real a, b, min, max, k;
199 if (optionType == Option::Call) {
200 a = swapRateValue_;
201 min = referenceStrike;
202 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
203 b = max = k =
204 std::min(smileSection_->maxStrike(), adjustedUpperBound_);
205 } else {
206 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
207 a = min = k =
208 std::max(smileSection_->minStrike(), adjustedLowerBound_);
209 b = swapRateValue_;
210 max = referenceStrike;
211 }
212
214 smileSection_->vega(swapRateValue_) * ratio);
215 Brent solver;
216
217 try {
218 k = solver.solve(h, 1.0E-5, (a + b) / 2.0, a, b);
219 }
220 catch (...) {
221 // use default value set above
222 }
223
224 return std::min(std::max(k, min), max);
225 }
226
228 Real referenceStrike) const {
229
230 Real a, b, min, max, k;
231 if (optionType == Option::Call) {
232 a = swapRateValue_;
233 min = referenceStrike;
234 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
235 b = max = k =
236 std::min(smileSection_->maxStrike(), adjustedUpperBound_);
237 } else {
238 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
239 a = min = k =
240 std::max(smileSection_->minStrike(), adjustedLowerBound_);
241 b = swapRateValue_;
242 max = referenceStrike;
243 }
244
245 PriceHelper h(&*smileSection_, optionType, price);
246 Brent solver;
247
248 try {
249 k = solver.solve(h, 1.0E-5, swapRateValue_, a, b);
250 }
251 catch (...) {
252 // use default value set above
253 }
254
255 return std::min(std::max(k, min), max);
256 }
257
259 Real strike) const {
260
261 if (optionType == Option::Call && strike >= adjustedUpperBound_)
262 return 0.0;
263 if (optionType == Option::Put && strike <= adjustedLowerBound_)
264 return 0.0;
265
266 // determine lower or upper integration bound (depending on option type)
267
268 Real lower = strike, upper = strike;
269
270 switch (settings_.strategy_) {
271
272 case Settings::RateBound: {
273 if (optionType == Option::Call)
274 upper = adjustedUpperBound_;
275 else
276 lower = adjustedLowerBound_;
277 break;
278 }
279
280 case Settings::VegaRatio: {
281 // strikeFromVegaRatio ensures that returned strike is on the
282 // expected side of strike
283 Real bound =
284 strikeFromVegaRatio(settings_.vegaRatio_, optionType, strike);
285 if (optionType == Option::Call)
286 upper = std::min(bound, adjustedUpperBound_);
287 else
288 lower = std::max(bound, adjustedLowerBound_);
289 break;
290 }
291
293 // strikeFromPrice ensures that returned strike is on the expected
294 // side of strike
295 Real bound =
296 strikeFromPrice(settings_.vegaRatio_, optionType, strike);
297 if (optionType == Option::Call)
298 upper = std::min(bound, adjustedUpperBound_);
299 else
300 lower = std::max(bound, adjustedLowerBound_);
301 break;
302 }
303
304 case Settings::BSStdDevs : {
305 Real atm = smileSection_->atmLevel();
306 Real atmVol = smileSection_->volatility(atm);
307 Real shift = smileSection_->shift();
308 Real lowerTmp, upperTmp;
309 if (smileSection_->volatilityType() == ShiftedLognormal) {
310 upperTmp = (atm + shift) *
311 std::exp(settings_.stdDevs_ * atmVol -
312 0.5 * atmVol * atmVol *
313 smileSection_->exerciseTime()) -
314 shift;
315 lowerTmp = (atm + shift) *
316 std::exp(-settings_.stdDevs_ * atmVol -
317 0.5 * atmVol * atmVol *
318 smileSection_->exerciseTime()) -
319 shift;
320 } else {
321 Real tmp = settings_.stdDevs_ * atmVol *
322 std::sqrt(smileSection_->exerciseTime());
323 upperTmp = atm + tmp;
324 lowerTmp = atm - tmp;
325 }
326 upper = std::min(upperTmp - shift, adjustedUpperBound_);
327 lower = std::max(lowerTmp - shift, adjustedLowerBound_);
328 break;
329 }
330
331 default:
332 QL_FAIL("Unknown strategy (" << settings_.strategy_ << ")");
333 }
334
335 // compute the relevant integral
336
337 Real result = 0.0;
338 Real tmpBound;
339 if (upper > lower) {
340 tmpBound = std::min(upper, swapRateValue_);
341 if (tmpBound > lower) {
342 result += (*integrator_)(integrand_f(this),
343 lower, tmpBound);
344 }
345 tmpBound = std::max(lower, swapRateValue_);
346 if (upper > tmpBound) {
347 result += (*integrator_)(integrand_f(this),
348 tmpBound, upper);
349 }
350 result *= (optionType == Option::Call ? 1.0 : -1.0);
351 }
352
353 result += singularTerms(optionType, strike);
354
355 return annuity_ * result * couponDiscountRatio_ *
357 }
358
360
362 return swapletPrice() /
365 }
366
368 // caplet is equivalent to call option on fixing
369 if (fixingDate_ <= today_) {
370 // the fixing is determined
371 const Rate Rs = std::max(
372 coupon_->swapIndex()->fixing(fixingDate_) - effectiveCap, 0.);
373 Rate price =
374 (gearing_ * Rs) *
377 return price;
378 } else {
380 return gearing_ * capletPrice;
381 }
382 }
383
385 return capletPrice(effectiveCap) /
388 }
389
391 // floorlet is equivalent to put option on fixing
392 if (fixingDate_ <= today_) {
393 // the fixing is determined
394 const Rate Rs = std::max(
395 effectiveFloor - coupon_->swapIndex()->fixing(fixingDate_), 0.);
396 Rate price =
397 (gearing_ * Rs) *
400 return price;
401 } else {
402 Real floorletPrice = optionletPrice(Option::Put, effectiveFloor);
403 return gearing_ * floorletPrice;
404 }
405 }
406
408 return floorletPrice(effectiveFloor) /
411 }
412
414 if (fixingDate_ <= today_) {
415 // the fixing is determined
416 const Rate Rs = coupon_->swapIndex()->fixing(fixingDate_);
417 Rate price =
418 (gearing_ * Rs + spread_) *
421 return price;
422 } else {
424 Real atmFloorletPrice = optionletPrice(Option::Put, swapRateValue_);
425 return gearing_ * (coupon_->accrualPeriod() *
426 discountCurve_->discount(paymentDate_) *
428 atmCapletPrice - atmFloorletPrice) +
430 }
431 }
432}
Brent 1-D solver
Definition: brent.hpp:37
CMS coupon class.
Definition: cmscoupon.hpp:39
const ext::shared_ptr< SwapIndex > & swapIndex() const
Definition: cmscoupon.hpp:56
base pricer for vanilla CMS coupons
Handle< SwaptionVolatilityStructure > swaptionVolatility() const
Date date() const override
Definition: coupon.hpp:53
Time accrualPeriod() const
accrual period as fraction of year
Definition: coupon.cpp:44
Concrete date class.
Definition: date.hpp:125
Time yearFraction(const Date &, const Date &, const Date &refPeriodStart=Date(), const Date &refPeriodEnd=Date()) const
Returns the period between two dates as a fraction of year.
Definition: daycounter.hpp:128
base floating-rate coupon class
virtual Date fixingDate() const
fixing date
Real gearing() const
index gearing, i.e. multiplicative coefficient for the index
Spread spread() const
spread paid over the fixing of the underlying index
Shared handle to an observable.
Definition: handle.hpp:41
Real capletPrice(Rate effectiveCap) const override
Rate floorletRate(Rate effectiveFloor) const override
Handle< YieldTermStructure > discountCurve_
Real singularTerms(Option::Type type, Real strike) const
Real strikeFromPrice(Real price, Option::Type optionType, Real referenceStrike) const
LinearTsrPricer(const Handle< SwaptionVolatilityStructure > &swaptionVol, Handle< Quote > meanReversion, Handle< YieldTermStructure > couponDiscountCurve=Handle< YieldTermStructure >(), const Settings &settings=Settings(), ext::shared_ptr< Integrator > integrator=ext::shared_ptr< Integrator >())
ext::shared_ptr< SmileSection > smileSection_
void initialize(const FloatingRateCoupon &coupon) override
ext::shared_ptr< VanillaSwap > swap_
ext::shared_ptr< Integrator > integrator_
ext::shared_ptr< SwapIndex > swapIndex_
static const Real defaultLowerBound
Real integrand(Real strike) const
Real strikeFromVegaRatio(Real ratio, Option::Type optionType, Real referenceStrike) const
static const Real defaultUpperBound
Handle< YieldTermStructure > couponDiscountCurve_
Real GsrG(const Date &d) const
Rate swapletRate() const override
Real floorletPrice(Rate effectiveFloor) const override
Real meanReversion() const override
Handle< YieldTermStructure > forwardCurve_
Real swapletPrice() const override
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
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
Real solve(const F &f, Real accuracy, Real guess, Real step) const
Definition: solver1d.hpp:84
QL_REAL Real
real number
Definition: types.hpp:50
Real Rate
interest rates
Definition: types.hpp:70
Definition: any.hpp:35
STL namespace.