1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2007 Ferdinando Ametrano
5 Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
6 Copyright (C) 2006 StatPro Italia srl
7 Copyright (C) 2015, 2016, 2017 Peter Caspers
8 Copyright (C) 2017 Paul Giltinan
9 Copyright (C) 2017 Werner Kuerzinger
10 Copyright (C) 2020 Marcin Rybacki
11
12 This file is part of QuantLib, a free-software/open-source library
13 for financial quantitative analysts and developers - http://quantlib.org/
14
15 QuantLib is free software: you can redistribute it and/or modify it
16 under the terms of the QuantLib license. You should have received a
17 copy of the license along with this program; if not, please email
18 <quantlib-dev@lists.sf.net>. The license is also available online at
19 <http://quantlib.org/license.shtml>.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the license for more details.
24*/
25
26/*! \file blackswaptionengine.hpp
27 \brief Black-formula swaption engine
28*/
29
30#ifndef quantlib_pricers_black_swaption_hpp
31#define quantlib_pricers_black_swaption_hpp
32
33#include <ql/cashflows/cashflows.hpp>
34#include <ql/cashflows/fixedratecoupon.hpp>
35#include <ql/exercise.hpp>
36#include <ql/indexes/iborindex.hpp>
37#include <ql/instruments/swaption.hpp>
38#include <ql/pricingengines/blackformula.hpp>
39#include <ql/pricingengines/swap/discountingswapengine.hpp>
40#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
41#include <ql/termstructures/volatility/swaption/swaptionvolstructure.hpp>
42#include <ql/time/calendars/nullcalendar.hpp>
43#include <utility>
44
45namespace QuantLib {
46
47 class Quote;
48
49 namespace detail {
50
51 /*! Generic Black-style-formula swaption engine
52 This is the base class for the Black and Bachelier swaption engines */
53 template<class Spec>
54 class BlackStyleSwaptionEngine : public Swaption::engine {
55 public:
56 enum CashAnnuityModel { SwapRate, DiscountCurve };
57 BlackStyleSwaptionEngine(Handle<YieldTermStructure> discountCurve,
58 Volatility vol,
59 const DayCounter& dc = Actual365Fixed(),
60 Real displacement = 0.0,
61 CashAnnuityModel model = DiscountCurve);
62 BlackStyleSwaptionEngine(Handle<YieldTermStructure> discountCurve,
63 const Handle<Quote>& vol,
64 const DayCounter& dc = Actual365Fixed(),
65 Real displacement = 0.0,
66 CashAnnuityModel model = DiscountCurve);
67 BlackStyleSwaptionEngine(Handle<YieldTermStructure> discountCurve,
68 Handle<SwaptionVolatilityStructure> vol,
69 CashAnnuityModel model = DiscountCurve);
70 void calculate() const override;
71 Handle<YieldTermStructure> termStructure() { return discountCurve_; }
72 Handle<SwaptionVolatilityStructure> volatility() { return vol_; }
73
74 private:
75 Handle<YieldTermStructure> discountCurve_;
76 Handle<SwaptionVolatilityStructure> vol_;
77 CashAnnuityModel model_;
78 };
79
80 // shifted lognormal type engine
81 struct Black76Spec {
82 static const VolatilityType type = ShiftedLognormal;
83 Real value(const Option::Type type, const Real strike,
84 const Real atmForward, const Real stdDev, const Real annuity,
85 const Real displacement) {
86 return blackFormula(optionType: type, strike, forward: atmForward, stdDev, discount: annuity,
87 displacement);
88 }
89 Real vega(const Real strike, const Real atmForward, const Real stdDev,
90 const Real exerciseTime, const Real annuity,
91 const Real displacement) {
92 return std::sqrt(x: exerciseTime) *
93 blackFormulaStdDevDerivative(strike, forward: atmForward, stdDev,
94 discount: annuity, displacement);
95 }
96 Real delta(const Option::Type type, const Real strike,
97 const Real atmForward, const Real stdDev, const Real annuity,
98 const Real displacement) {
99 return blackFormulaForwardDerivative(optionType: type, strike, forward: atmForward, stdDev,
100 discount: annuity, displacement);
101 }
102 };
103
104 // normal type engine
105 struct BachelierSpec {
106 static const VolatilityType type = Normal;
107 Real value(const Option::Type type, const Real strike,
108 const Real atmForward, const Real stdDev, const Real annuity,
109 const Real) {
110 return bachelierBlackFormula(optionType: type, strike, forward: atmForward, stdDev,
111 discount: annuity);
112 }
113 Real vega(const Real strike, const Real atmForward, const Real stdDev,
114 const Real exerciseTime, const Real annuity, const Real) {
115 return std::sqrt(x: exerciseTime) *
116 bachelierBlackFormulaStdDevDerivative(
117 strike, forward: atmForward, stdDev, discount: annuity);
118 }
119 Real delta(const Option::Type type, const Real strike,
120 const Real atmForward, const Real stdDev, const Real annuity,
121 const Real) {
122 return bachelierBlackFormulaForwardDerivative(
123 optionType: type, strike, forward: atmForward, stdDev, discount: annuity);
124 }
125 };
126
127 } // namespace detail
128
129 //! Shifted Lognormal Black-formula swaption engine
130 /*! \ingroup swaptionengines
131
132 \warning The engine assumes that the exercise date lies before the
133 start date of the passed swap.
134 */
135
136 class BlackSwaptionEngine
137 : public detail::BlackStyleSwaptionEngine<detail::Black76Spec> {
138 public:
139 BlackSwaptionEngine(const Handle<YieldTermStructure>& discountCurve,
140 Volatility vol,
141 const DayCounter& dc = Actual365Fixed(),
142 Real displacement = 0.0,
143 CashAnnuityModel model = DiscountCurve);
144 BlackSwaptionEngine(const Handle<YieldTermStructure>& discountCurve,
145 const Handle<Quote>& vol,
146 const DayCounter& dc = Actual365Fixed(),
147 Real displacement = 0.0,
148 CashAnnuityModel model = DiscountCurve);
149 BlackSwaptionEngine(const Handle<YieldTermStructure>& discountCurve,
150 const Handle<SwaptionVolatilityStructure>& vol,
151 CashAnnuityModel model = DiscountCurve);
152 };
153
154 //! Normal Bachelier-formula swaption engine
155 /*! \ingroup swaptionengines
156
157 \warning The engine assumes that the exercise date lies before the
158 start date of the passed swap.
159 */
160
161 class BachelierSwaptionEngine
162 : public detail::BlackStyleSwaptionEngine<detail::BachelierSpec> {
163 public:
164 BachelierSwaptionEngine(const Handle<YieldTermStructure>& discountCurve,
165 Volatility vol,
166 const DayCounter& dc = Actual365Fixed(),
167 CashAnnuityModel model = DiscountCurve);
168 BachelierSwaptionEngine(const Handle<YieldTermStructure>& discountCurve,
169 const Handle<Quote>& vol,
170 const DayCounter& dc = Actual365Fixed(),
171 CashAnnuityModel model = DiscountCurve);
172 BachelierSwaptionEngine(const Handle<YieldTermStructure>& discountCurve,
173 const Handle<SwaptionVolatilityStructure>& vol,
174 CashAnnuityModel model = DiscountCurve);
175 };
176
177 // implementation
178
179 namespace detail {
180
181 template <class Spec>
182 BlackStyleSwaptionEngine<Spec>::BlackStyleSwaptionEngine(
183 Handle<YieldTermStructure> discountCurve,
184 Volatility vol,
185 const DayCounter& dc,
186 Real displacement,
187 CashAnnuityModel model)
188 : discountCurve_(std::move(discountCurve)),
189 vol_(ext::shared_ptr<SwaptionVolatilityStructure>(new ConstantSwaptionVolatility(
190 0, NullCalendar(), Following, vol, dc, Spec().type, displacement))),
191 model_(model) {
192 registerWith(h: discountCurve_);
193 }
194
195 template <class Spec>
196 BlackStyleSwaptionEngine<Spec>::BlackStyleSwaptionEngine(
197 Handle<YieldTermStructure> discountCurve,
198 const Handle<Quote>& vol,
199 const DayCounter& dc,
200 Real displacement,
201 CashAnnuityModel model)
202 : discountCurve_(std::move(discountCurve)),
203 vol_(ext::shared_ptr<SwaptionVolatilityStructure>(new ConstantSwaptionVolatility(
204 0, NullCalendar(), Following, vol, dc, Spec().type, displacement))),
205 model_(model) {
206 registerWith(h: discountCurve_);
207 registerWith(h: vol_);
208 }
209
210 template <class Spec>
211 BlackStyleSwaptionEngine<Spec>::BlackStyleSwaptionEngine(
212 Handle<YieldTermStructure> discountCurve,
213 Handle<SwaptionVolatilityStructure> volatility,
214 CashAnnuityModel model)
215 : discountCurve_(std::move(discountCurve)), vol_(std::move(volatility)), model_(model) {
216 registerWith(h: discountCurve_);
217 registerWith(h: vol_);
218 }
219
220 template<class Spec>
221 void BlackStyleSwaptionEngine<Spec>::calculate() const {
222 static const Spread basisPoint = 1.0e-4;
223
224 Date exerciseDate = arguments_.exercise->date(index: 0);
225
226 // the part of the swap preceding exerciseDate should be truncated
227 // to avoid taking into account unwanted cashflows
228 // for the moment we add a check avoiding this situation
229 VanillaSwap swap = *arguments_.swap;
230 const Leg& fixedLeg = swap.fixedLeg();
231 ext::shared_ptr<FixedRateCoupon> firstCoupon =
232 ext::dynamic_pointer_cast<FixedRateCoupon>(r: fixedLeg[0]);
233 QL_REQUIRE(firstCoupon->accrualStartDate() >= exerciseDate,
234 "swap start (" << firstCoupon->accrualStartDate() << ") before exercise date ("
235 << exerciseDate << ") not supported in Black swaption engine");
236
237 Rate strike = swap.fixedRate();
238
239 // using the discounting curve
240 // swap.iborIndex() might be using a different forwarding curve
241 swap.setPricingEngine(ext::shared_ptr<PricingEngine>(new
242 DiscountingSwapEngine(discountCurve_, false)));
243 Rate atmForward = swap.fairRate();
244
245 // Volatilities are quoted for zero-spreaded swaps.
246 // Therefore, any spread on the floating leg must be removed
247 // with a corresponding correction on the fixed leg.
248 if (swap.spread()!=0.0) {
249 Spread correction = swap.spread() *
250 std::fabs(x: swap.floatingLegBPS()/swap.fixedLegBPS());
251 strike -= correction;
252 atmForward -= correction;
253 results_.additionalResults["spreadCorrection"] = correction;
254 } else {
255 results_.additionalResults["spreadCorrection"] = Real(0.0);
256 }
257 results_.additionalResults["strike"] = strike;
258 results_.additionalResults["atmForward"] = atmForward;
259
260 // using the discounting curve
261 swap.setPricingEngine(ext::shared_ptr<PricingEngine>(
262 new DiscountingSwapEngine(discountCurve_, false)));
263 Real annuity;
264 if (arguments_.settlementType == Settlement::Physical ||
265 (arguments_.settlementType == Settlement::Cash &&
266 arguments_.settlementMethod ==
267 Settlement::CollateralizedCashPrice)) {
268 annuity = std::fabs(x: swap.fixedLegBPS()) / basisPoint;
269 } else if (arguments_.settlementType == Settlement::Cash &&
270 arguments_.settlementMethod == Settlement::ParYieldCurve) {
271 DayCounter dayCount = firstCoupon->dayCounter();
272 // we assume that the cash settlement date is equal
273 // to the swap start date
274 Date discountDate = model_ == DiscountCurve
275 ? firstCoupon->accrualStartDate()
276 : discountCurve_->referenceDate();
277 Real fixedLegCashBPS = CashFlows::bps(
278 leg: fixedLeg,
279 yield: InterestRate(atmForward, dayCount, Compounded, Annual), includeSettlementDateFlows: false,
280 settlementDate: discountDate);
281 annuity = std::fabs(x: fixedLegCashBPS / basisPoint) *
282 discountCurve_->discount(d: discountDate);
283 } else {
284 QL_FAIL("invalid (settlementType, settlementMethod) pair");
285 }
286 results_.additionalResults["annuity"] = annuity;
287
288 Time swapLength = vol_->swapLength(start: swap.floatingSchedule().dates().front(),
289 end: swap.floatingSchedule().dates().back());
290
291 // swapLength is rounded to whole months. To ensure we can read a variance
292 // and a shift from vol_ we floor swapLength at 1/12 here therefore.
293 swapLength = std::max(a: swapLength, b: 1.0 / 12.0);
294 results_.additionalResults["swapLength"] = swapLength;
295
296 Real variance = vol_->blackVariance(optionDate: exerciseDate, swapLength, strike);
297
298 Real displacement =
299 vol_->volatilityType() == ShiftedLognormal ?
300 vol_->shift(optionDate: exerciseDate, swapLength) : 0.0;
301
302 Real stdDev = std::sqrt(x: variance);
303 results_.additionalResults["stdDev"] = stdDev;
304 Option::Type w = (arguments_.type==Swap::Payer) ? Option::Call : Option::Put;
305 results_.value = Spec().value(w, strike, atmForward, stdDev, annuity, displacement);
306
307 Time exerciseTime = vol_->timeFromReference(d: exerciseDate);
308 results_.additionalResults["vega"] = Spec().vega(
309 strike, atmForward, stdDev, exerciseTime, annuity, displacement);
310 results_.additionalResults["delta"] = Spec().delta(
311 w, strike, atmForward, stdDev, annuity, displacement);
312 results_.additionalResults["timeToExpiry"] = exerciseTime;
313 results_.additionalResults["impliedVolatility"] = Real(stdDev / std::sqrt(x: exerciseTime));
314 }
315
316 } // namespace detail
317
318}
319
320#endif
321

source code of quantlib/ql/pricingengines/swaption/blackswaptionengine.hpp