QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
lineartsrpricer.cpp
Go to the documentation of this file.
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
21/*! \file lineartsrpricer.cpp
22*/
23
37#include <ql/time/schedule.hpp>
38#include <utility>
39
40namespace QuantLib {
41
42 class LinearTsrPricer::integrand_f {
43 const LinearTsrPricer* pricer;
44 public:
45 explicit integrand_f(const LinearTsrPricer* pricer) : pricer(pricer) {}
46 Real operator()(Real x) const {
47 return pricer->integrand(x);
48 }
49 };
50
53
55 Handle<Quote> meanReversion,
56 Handle<YieldTermStructure> couponDiscountCurve,
57 const Settings& settings,
58 ext::shared_ptr<Integrator> integrator)
59 : CmsCouponPricer(swaptionVol), meanReversion_(std::move(meanReversion)),
60 couponDiscountCurve_(std::move(couponDiscountCurve)), settings_(settings),
61 volDayCounter_(swaptionVol->dayCounter()), integrator_(std::move(integrator)) {
62
63 if (!couponDiscountCurve_.empty())
65
66 if (integrator_ == nullptr)
68 ext::make_shared<GaussKronrodNonAdaptive>(1E-10, 5000, 1E-10);
69 }
70
72
74 if (std::fabs(meanReversion_->value()) < 1.0E-4)
75 return yf;
76 else
77 return (1.0 - std::exp(-meanReversion_->value() * yf)) /
78 meanReversion_->value();
79 }
80
82 const Real strike) const {
83
84 Real omega = (type == Option::Call ? 1.0 : -1.0);
85 Real s1 = std::max(omega * (swapRateValue_ - strike), 0.0) *
86 (a_ * swapRateValue_ + b_);
87 Real s2 = (a_ * strike + b_) *
88 smileSection_->optionPrice(strike, strike < swapRateValue_
90 : Option::Call);
91 return s1 + s2;
92 }
93
94 Real LinearTsrPricer::integrand(const Real strike) const {
95 return 2.0 * a_ * smileSection_->optionPrice(
96 strike, strike < swapRateValue_ ? Option::Put
97 : Option::Call);
98 }
99
101
102 coupon_ = dynamic_cast<const CmsCoupon *>(&coupon);
103 QL_REQUIRE(coupon_, "CMS coupon needed");
106
110
111 forwardCurve_ = swapIndex_->forwardingTermStructure();
112 if (swapIndex_->exogenousDiscount())
113 discountCurve_ = swapIndex_->discountingTermStructure();
114 else
116
117 // if no coupon discount curve is given just use the discounting curve
118 // from the swap index. for rate calculation this curve cancels out in
119 // the computation, so e.g. the discounting swap engine will produce
120 // correct results, even if the couponDiscountCurve is not set here.
121 // only the price member function in this class will be dependent on the
122 // coupon discount curve.
123
124 today_ = QuantLib::Settings::instance().evaluationDate();
125
126 Real couponCurvePaymentDiscount;
127 if (!couponDiscountCurve_.empty() && paymentDate_ > couponDiscountCurve_->referenceDate()) {
128 couponCurvePaymentDiscount = couponDiscountCurve_->discount(paymentDate_);
129 } else {
130 couponCurvePaymentDiscount = 1.0;
131 }
132
133 if (paymentDate_ > discountCurve_->referenceDate()) {
135 } else {
137 }
138
139
140 couponDiscountRatio_ = couponCurvePaymentDiscount / discountCurvePaymentDiscount_;
141
144
145 if (fixingDate_ > today_) {
146
147 swapTenor_ = swapIndex_->tenor();
148
149 if (auto on = ext::dynamic_pointer_cast<OvernightIndexedSwapIndex>(swapIndex_)) {
150 swap_ = on->underlyingSwap(fixingDate_);
151 } else {
152 swap_ = swapIndex_->underlyingSwap(fixingDate_);
153 }
154 swapRateValue_ = swap_->fairRate();
155 annuity_ = 1.0E4 * std::fabs(swap_->fixedLegBPS());
156 Leg swapFixedLeg = swap_->fixedLeg();
157
158 ext::shared_ptr<SmileSection> sectionTmp =
159 swaptionVolatility()->smileSection(fixingDate_, swapTenor_);
160
163
164 if(sectionTmp->volatilityType() == Normal) {
165 // adjust lower bound if it was not set explicitly
168 } else {
169 // adjust bounds by section's shift
170 adjustedLowerBound_ -= sectionTmp->shift();
171 adjustedUpperBound_ -= sectionTmp->shift();
172 }
173
174 // if the section does not provide an atm level, we enhance it to
175 // have one, no need to exit with an exception ...
176
177 if (sectionTmp->atmLevel() == Null<Real>())
178 smileSection_ = ext::make_shared<AtmSmileSection>(
179 sectionTmp, swapRateValue_);
180 else
181 smileSection_ = sectionTmp;
182
183 // compute linear model's parameters
184
185 Real gx = 0.0, gy = 0.0;
186 for (const auto& i : swapFixedLeg) {
187 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(i);
188 Real yf = c->accrualPeriod();
189 Date d = c->date();
190 Real pv = yf * discountCurve_->discount(d);
191 gx += pv * GsrG(d);
192 gy += pv;
193 }
194
195 Real gamma = gx / gy;
196 Date lastd = swapFixedLeg.back()->date();
197
198 a_ = discountCurve_->discount(paymentDate_) *
199 (gamma - GsrG(paymentDate_)) /
200 (discountCurve_->discount(lastd) * GsrG(lastd) +
201 swapRateValue_ * gy * gamma);
202
203 b_ = discountCurve_->discount(paymentDate_) / gy -
205 }
206 }
207
209 Option::Type optionType,
210 Real referenceStrike) const {
211
212 Real a, b, min, max, k;
213 if (optionType == Option::Call) {
214 a = swapRateValue_;
215 min = referenceStrike;
216 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
217 b = max = k =
218 std::min(smileSection_->maxStrike(), adjustedUpperBound_);
219 } else {
220 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
221 a = min = k =
222 std::max(smileSection_->minStrike(), adjustedLowerBound_);
224 max = referenceStrike;
225 }
226
228 smileSection_->vega(swapRateValue_) * ratio);
229 Brent solver;
230
231 try {
232 k = solver.solve(h, 1.0E-5, (a + b) / 2.0, a, b);
233 }
234 catch (...) {
235 // use default value set above
236 }
237
238 return std::min(std::max(k, min), max);
239 }
240
242 Real referenceStrike) const {
243
244 Real a, b, min, max, k;
245 if (optionType == Option::Call) {
246 a = swapRateValue_;
247 min = referenceStrike;
248 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
249 b = max = k =
250 std::min(smileSection_->maxStrike(), adjustedUpperBound_);
251 } else {
252 // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
253 a = min = k =
254 std::max(smileSection_->minStrike(), adjustedLowerBound_);
256 max = referenceStrike;
257 }
258
259 PriceHelper h(&*smileSection_, optionType, price);
260 Brent solver;
261
262 try {
263 k = solver.solve(h, 1.0E-5, swapRateValue_, a, b);
264 }
265 catch (...) {
266 // use default value set above
267 }
268
269 return std::min(std::max(k, min), max);
270 }
271
273 Real strike) const {
274
275 if (optionType == Option::Call && strike >= adjustedUpperBound_)
276 return 0.0;
277 if (optionType == Option::Put && strike <= adjustedLowerBound_)
278 return 0.0;
279
280 // determine lower or upper integration bound (depending on option type)
281
282 Real lower = strike, upper = strike;
283
284 switch (settings_.strategy_) {
285
286 case Settings::RateBound: {
287 if (optionType == Option::Call)
288 upper = adjustedUpperBound_;
289 else
290 lower = adjustedLowerBound_;
291 break;
292 }
293
294 case Settings::VegaRatio: {
295 // strikeFromVegaRatio ensures that returned strike is on the
296 // expected side of strike
297 Real bound =
298 strikeFromVegaRatio(settings_.vegaRatio_, optionType, strike);
299 if (optionType == Option::Call)
300 upper = std::min(bound, adjustedUpperBound_);
301 else
302 lower = std::max(bound, adjustedLowerBound_);
303 break;
304 }
305
307 // strikeFromPrice ensures that returned strike is on the expected
308 // side of strike
309 Real bound =
310 strikeFromPrice(settings_.vegaRatio_, optionType, strike);
311 if (optionType == Option::Call)
312 upper = std::min(bound, adjustedUpperBound_);
313 else
314 lower = std::max(bound, adjustedLowerBound_);
315 break;
316 }
317
318 case Settings::BSStdDevs : {
319 Real atm = smileSection_->atmLevel();
320 Real atmVol = smileSection_->volatility(atm);
321 Real shift = smileSection_->shift();
322 Real lowerTmp, upperTmp;
323 if (smileSection_->volatilityType() == ShiftedLognormal) {
324 upperTmp = (atm + shift) *
325 std::exp(settings_.stdDevs_ * atmVol -
326 0.5 * atmVol * atmVol *
327 smileSection_->exerciseTime()) -
328 shift;
329 lowerTmp = (atm + shift) *
330 std::exp(-settings_.stdDevs_ * atmVol -
331 0.5 * atmVol * atmVol *
332 smileSection_->exerciseTime()) -
333 shift;
334 } else {
335 Real tmp = settings_.stdDevs_ * atmVol *
336 std::sqrt(smileSection_->exerciseTime());
337 upperTmp = atm + tmp;
338 lowerTmp = atm - tmp;
339 }
340 upper = std::min(upperTmp - shift, adjustedUpperBound_);
341 lower = std::max(lowerTmp - shift, adjustedLowerBound_);
342 break;
343 }
344
345 default:
346 QL_FAIL("Unknown strategy (" << settings_.strategy_ << ")");
347 }
348
349 // compute the relevant integral
350
351 Real result = 0.0;
352 Real tmpBound;
353 if (upper > lower) {
354 tmpBound = std::min(upper, swapRateValue_);
355 if (tmpBound > lower) {
356 result += (*integrator_)(integrand_f(this),
357 lower, tmpBound);
358 }
359 tmpBound = std::max(lower, swapRateValue_);
360 if (upper > tmpBound) {
361 result += (*integrator_)(integrand_f(this),
362 tmpBound, upper);
363 }
364 result *= (optionType == Option::Call ? 1.0 : -1.0);
365 }
366
367 result += singularTerms(optionType, strike);
368
369 return annuity_ * result * couponDiscountRatio_ *
371 }
372
374
376 return swapletPrice() /
378 }
379
381 // caplet is equivalent to call option on fixing
382 if (fixingDate_ <= today_) {
383 // the fixing is determined
384 const Rate Rs = std::max(
385 coupon_->swapIndex()->fixing(fixingDate_) - effectiveCap, 0.);
386 Rate price = (gearing_ * Rs) * (coupon_->accrualPeriod() *
388 return price;
389 } else {
391 return gearing_ * capletPrice;
392 }
393 }
394
396 return capletPrice(effectiveCap) /
398 }
399
401 // floorlet is equivalent to put option on fixing
402 if (fixingDate_ <= today_) {
403 // the fixing is determined
404 const Rate Rs = std::max(
405 effectiveFloor - coupon_->swapIndex()->fixing(fixingDate_), 0.);
406 Rate price = (gearing_ * Rs) * (coupon_->accrualPeriod() *
408 return price;
409 } else {
410 Real floorletPrice = optionletPrice(Option::Put, effectiveFloor);
411 return gearing_ * floorletPrice;
412 }
413 }
414
416 return floorletPrice(effectiveFloor) /
418 }
419
421 if (fixingDate_ <= today_) {
422 // the fixing is determined
423 const Rate Rs = coupon_->swapIndex()->fixing(fixingDate_);
424 Rate price =
425 (gearing_ * Rs + spread_) *
427 return price;
428 } else {
430 Real atmFloorletPrice = optionletPrice(Option::Put, swapRateValue_);
433 atmCapletPrice - atmFloorletPrice) +
435 }
436 }
437}
smile section that allows for explicit / alternate specification of atm level
Black formula.
Brent 1-D solver.
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< 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
ext::shared_ptr< FixedVsFloatingSwap > swap_
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
CMS coupon.
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
Date d
ext::function< Real(Real)> b
Coupon paying a fixed annual rate.
QL_REAL Real
real number
Definition: types.hpp:50
Real Rate
interest rates
Definition: types.hpp:70
Coupon paying a Libor-type index.
base class for Inter-Bank-Offered-Rate indexes
Integral of a 1-dimensional function using the Gauss-Kronrod method.
linear terminal swap rate model for cms coupon pricing
Definition: any.hpp:35
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.
Overnight index swap paying compounded overnight vs. fixed.
date schedule
simple quote class
Simple fixed-rate vs Libor swap.
Interest-rate term structure.