25#ifndef quantext_pricers_black_swaption_deltagamma_hpp
26#define quantext_pricers_black_swaption_deltagamma_hpp
30#include <ql/cashflows/cashflows.hpp>
31#include <ql/cashflows/fixedratecoupon.hpp>
32#include <ql/exercise.hpp>
33#include <ql/indexes/iborindex.hpp>
34#include <ql/instruments/swaption.hpp>
35#include <ql/math/distributions/normaldistribution.hpp>
36#include <ql/pricingengines/blackformula.hpp>
37#include <ql/pricingengines/swap/discountingswapengine.hpp>
38#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
39#include <ql/termstructures/volatility/swaption/swaptionvolstructure.hpp>
40#include <ql/time/calendars/nullcalendar.hpp>
42#include <boost/make_shared.hpp>
78 const DayCounter& dc = Actual365Fixed(), Real displacement = 0.0,
79 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
80 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
81 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
82 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
83 const bool linearInZero =
true);
85 const DayCounter& dc = Actual365Fixed(), Real displacement = 0.0,
86 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
87 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
88 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
89 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
90 const bool linearInZero =
true);
92 const Handle<SwaptionVolatilityStructure>& vol,
93 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
94 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
95 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
96 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
97 const bool linearInZero =
true);
104 Handle<SwaptionVolatilityStructure>
vol_;
112 static const VolatilityType
type = ShiftedLognormal;
113 Real
value(
const Option::Type
type,
const Real strike,
const Real atmForward,
const Real stdDev,
const Real annuity,
114 const Real displacement) {
115 return blackFormula(
type, strike, atmForward, stdDev, annuity, displacement);
117 Real
vega(
const Real strike,
const Real atmForward,
const Real stdDev,
const Real exerciseTime,
const Real annuity,
118 const Real displacement) {
119 return std::sqrt(exerciseTime) *
120 blackFormulaStdDevDerivative(strike, atmForward, stdDev, annuity, displacement);
122 Real
delta(
const Option::Type
type,
const Real strike,
const Real atmForward,
const Real stdDev,
const Real annuity,
123 const Real displacement) {
124 QuantLib::CumulativeNormalDistribution cnd;
125 Real d1 = std::log((atmForward + displacement) / (strike + displacement)) / stdDev + 0.5 * stdDev;
126 Real w =
type == Option::Call ? 1.0 : -1.0;
127 return annuity * w * cnd(w * d1);
129 Real
gamma(
const Real strike,
const Real atmForward,
const Real stdDev,
const Real annuity,
130 const Real displacement) {
131 QuantLib::CumulativeNormalDistribution cnd;
132 Real d1 = std::log((atmForward + displacement) / (strike + displacement)) / stdDev + 0.5 * stdDev;
133 return annuity * cnd.derivative(d1) / ((atmForward + displacement) * stdDev);
135 Real
theta(
const Real strike,
const Real atmForward,
const Real stdDev,
const Real exerciseTime,
const Real annuity,
136 const Real displacement) {
137 QuantLib::CumulativeNormalDistribution cnd;
138 Real d1 = std::log((atmForward + displacement) / (strike + displacement)) / stdDev + 0.5 * stdDev;
139 return -0.5 * annuity * cnd.derivative(d1) * (atmForward + displacement) * stdDev / exerciseTime;
145 static const VolatilityType
type = Normal;
146 Real
value(
const Option::Type
type,
const Real strike,
const Real atmForward,
const Real stdDev,
const Real annuity,
148 return bachelierBlackFormula(
type, strike, atmForward, stdDev, annuity);
150 Real
vega(
const Real strike,
const Real atmForward,
const Real stdDev,
const Real exerciseTime,
const Real annuity,
152 return std::sqrt(exerciseTime) * bachelierBlackFormulaStdDevDerivative(strike, atmForward, stdDev, annuity);
154 Real
delta(
const Option::Type
type,
const Real strike,
const Real atmForward,
const Real stdDev,
const Real annuity,
155 const Real displacement) {
156 QuantLib::CumulativeNormalDistribution cnd;
157 Real d1 = (atmForward - strike) / stdDev;
158 Real w =
type == Option::Call ? 1.0 : -1.0;
159 return annuity * w * cnd(w * d1);
161 Real
gamma(
const Real strike,
const Real atmForward,
const Real stdDev,
const Real annuity,
162 const Real displacement) {
163 QuantLib::CumulativeNormalDistribution cnd;
164 Real d1 = (atmForward - strike) / stdDev;
165 return annuity * cnd.derivative(d1) / stdDev;
167 Real
theta(
const Real strike,
const Real atmForward,
const Real stdDev,
const Real exerciseTime,
const Real annuity,
168 const Real displacement) {
169 QuantLib::CumulativeNormalDistribution cnd;
170 Real d1 = (atmForward - strike) / stdDev;
171 return -0.5 * annuity * cnd.derivative(d1) * stdDev / exerciseTime;
187 const DayCounter& dc = Actual365Fixed(), Real displacement = 0.0,
188 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
189 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
190 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
191 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
192 const bool linearInZero =
true);
194 const DayCounter& dc = Actual365Fixed(), Real displacement = 0.0,
195 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
196 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
197 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
198 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
199 const bool linearInZero =
true);
201 const Handle<SwaptionVolatilityStructure>& vol,
202 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
203 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
204 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
205 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
206 const bool linearInZero =
true);
219 const DayCounter& dc = Actual365Fixed(),
220 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
221 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
222 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
223 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
224 const bool linearInZero =
true);
226 const DayCounter& dc = Actual365Fixed(),
227 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
228 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
229 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
230 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
231 const bool linearInZero =
true);
233 const Handle<SwaptionVolatilityStructure>& vol,
234 const std::vector<Time>& bucketTimesDeltaGamma = std::vector<Time>(),
235 const std::vector<Time>& bucketTimesVegaOpt = std::vector<Time>(),
236 const std::vector<Time>& bucketTimesVegaUnd = std::vector<Time>(),
237 const bool computeDeltaVega =
false,
const bool computeGamma =
false,
238 const bool linearInZero =
true);
247 const Handle<YieldTermStructure>& discountCurve, Volatility vol,
const DayCounter& dc, Real displacement,
248 const std::vector<Time>& bucketTimesDeltaGamma,
const std::vector<Time>& bucketTimesVegaOpt,
249 const std::vector<Time>& bucketTimesVegaUnd,
const bool computeDeltaVega,
const bool computeGamma,
250 const bool linearInZero)
251 : discountCurve_(discountCurve),
vol_(
QuantLib::ext::shared_ptr<SwaptionVolatilityStructure>(new ConstantSwaptionVolatility(
252 0, NullCalendar(), Following, vol, dc, Spec().type, displacement))),
253 displacement_(displacement), bucketTimesDeltaGamma_(bucketTimesDeltaGamma),
254 bucketTimesVegaOpt_(bucketTimesVegaOpt), bucketTimesVegaUnd_(bucketTimesVegaUnd),
255 computeDeltaVega_(computeDeltaVega), computeGamma_(computeGamma), linearInZero_(linearInZero) {
258 (!computeDeltaVega && !computeGamma),
259 "bucket times are empty, although sensitivities have to be calculated");
264 const Handle<YieldTermStructure>& discountCurve,
const Handle<Quote>& vol,
const DayCounter& dc, Real displacement,
265 const std::vector<Time>& bucketTimesDeltaGamma,
const std::vector<Time>& bucketTimesVegaOpt,
266 const std::vector<Time>& bucketTimesVegaUnd,
const bool computeDeltaVega,
const bool computeGamma,
267 const bool linearInZero)
268 : discountCurve_(discountCurve),
vol_(
QuantLib::ext::shared_ptr<SwaptionVolatilityStructure>(new ConstantSwaptionVolatility(
269 0, NullCalendar(), Following, vol, dc, Spec().type, displacement))),
270 displacement_(displacement), bucketTimesDeltaGamma_(bucketTimesDeltaGamma),
271 bucketTimesVegaOpt_(bucketTimesVegaOpt), bucketTimesVegaUnd_(bucketTimesVegaUnd),
272 computeDeltaVega_(computeDeltaVega), computeGamma_(computeGamma), linearInZero_(linearInZero) {
276 (!computeDeltaVega && !computeGamma),
277 "bucket times are empty, although sensitivities have to be calculated");
282 const Handle<YieldTermStructure>& discountCurve,
const Handle<SwaptionVolatilityStructure>& volatility,
283 const std::vector<Time>& bucketTimesDeltaGamma,
const std::vector<Time>& bucketTimesVegaOpt,
284 const std::vector<Time>& bucketTimesVegaUnd,
const bool computeDeltaVega,
const bool computeGamma,
285 const bool linearInZero)
286 : discountCurve_(discountCurve),
vol_(volatility), displacement_(0.0),
287 bucketTimesDeltaGamma_(bucketTimesDeltaGamma), bucketTimesVegaOpt_(bucketTimesVegaOpt),
288 bucketTimesVegaUnd_(bucketTimesVegaUnd), computeDeltaVega_(computeDeltaVega), computeGamma_(computeGamma),
289 linearInZero_(linearInZero) {
293 (!computeDeltaVega && !computeGamma),
294 "bucket times are empty, although sensitivities have to be calculated");
298 Date exerciseDate =
arguments_.exercise->date(0);
300 Rate strike = swap.fixedRate();
302 std::vector<Leg> floatLeg(1, swap.leg(1)), fixedLeg(1, swap.leg(0));
303 std::vector<bool> payerFloat(1,
false), payerFixed(1,
false);
304 QuantLib::Swap swapFloatLeg(floatLeg, payerFloat), swapFixedLeg(fixedLeg, payerFixed);
306 QuantLib::ext::shared_ptr<PricingEngine> engine = QuantLib::ext::make_shared<DiscountingSwapEngine>(discountCurve_);
307 QuantLib::ext::shared_ptr<PricingEngine> engine1 = QuantLib::ext::make_shared<DiscountingSwapEngineDeltaGamma>(
308 discountCurve_, bucketTimesDeltaGamma_, computeDeltaVega_, computeGamma_,
false, linearInZero_);
309 QuantLib::ext::shared_ptr<PricingEngine> engine2 = QuantLib::ext::make_shared<DiscountingSwapEngineDeltaGamma>(
310 discountCurve_, bucketTimesDeltaGamma_, computeDeltaVega_, computeGamma_,
true, linearInZero_);
312 swap.setPricingEngine(engine);
313 swapFloatLeg.setPricingEngine(engine1);
314 swapFixedLeg.setPricingEngine(engine2);
316 Rate atmForward = swapFloatLeg.NPV() / swapFixedLeg.legBPS(0);
319 QL_REQUIRE(QuantLib::close_enough(swap.spread(), 0.0),
"BlackSwaptionEngineDeltaGamma requires zero spread");
333 results_.additionalResults[
"strike"] = strike;
334 results_.additionalResults[
"atmForward"] = atmForward;
336 swap.setPricingEngine(QuantLib::ext::shared_ptr<PricingEngine>(
new DiscountingSwapEngine(discountCurve_,
false)));
339 Real annuity = std::fabs(swap.fixedLegBPS()) / 1.0E-4;
340 results_.additionalResults[
"annuity"] = annuity;
342 Time swapLength =
vol_->swapLength(swap.floatingSchedule().dates().front(), swap.floatingSchedule().dates().back());
343 results_.additionalResults[
"swapLength"] = swapLength;
345 Real
variance =
vol_->blackVariance(exerciseDate, swapLength, strike);
347 results_.additionalResults[
"stdDev"] = stdDev;
348 Option::Type w = (
arguments_.type == VanillaSwap::Payer) ? Option::Call : Option::Put;
349 results_.value = Spec().value(w, strike, atmForward, stdDev, annuity, displacement_);
352 QL_REQUIRE(!computeGamma_ || computeDeltaVega_,
353 "BlackSwaptionEngineDeltaGamma, gamma can only be computed if delta is computed as well");
354 if (computeDeltaVega_) {
355 Time exerciseTime =
vol_->timeFromReference(exerciseDate);
357 int n1 = bucketTimesVegaOpt_.size();
358 int n2 = bucketTimesVegaUnd_.size();
360 static_cast<int>(std::upper_bound(bucketTimesVegaOpt_.begin(), bucketTimesVegaOpt_.end(), exerciseTime) -
361 bucketTimesVegaOpt_.begin());
362 int b2 =
static_cast<int>(std::upper_bound(bucketTimesVegaUnd_.begin(), bucketTimesVegaUnd_.end(), swapLength) -
363 bucketTimesVegaUnd_.begin());
369 }
else if (b1 == n1) {
373 w1 = (bucketTimesVegaOpt_[b1] - exerciseTime) / (bucketTimesVegaOpt_[b1] - bucketTimesVegaOpt_[b1 - 1]);
379 }
else if (b2 == n2) {
383 w2 = (bucketTimesVegaUnd_[b2] - swapLength) / (bucketTimesVegaUnd_[b2] - bucketTimesVegaUnd_[b2 - 1]);
386 Real singleVega = Spec().vega(strike, atmForward, stdDev, exerciseTime, annuity, displacement_);
387 Matrix vega(n1, n2, 0.0);
390 vega[i1][i2 + 1] = w1 * (1.0 - w2) * singleVega;
392 vega[i1][i2] = w1 * w2 * singleVega;
395 if (i2 >= 0 && i1 < n1) {
396 vega[i1 + 1][i2] = (1.0 - w1) * w2 * singleVega;
398 if (i1 < n1 && i2 < n2)
399 vega[i1 + 1][i2 + 1] = (1.0 - w1) * (1.0 - w2) * singleVega;
400 results_.additionalResults[
"vega"] = vega;
402 Real mu = swapFloatLeg.NPV();
404 Real blackDelta = Spec().delta(w, strike, atmForward, stdDev, 1.0, displacement_);
405 std::vector<Real> A_s_1 = swapFixedLeg.result<std::vector<std::vector<Real>>>(
"deltaBPS")[0];
406 Array A_s(A_s_1.begin(), A_s_1.end());
407 std::vector<Real> mu_sd_1 = swapFloatLeg.result<std::vector<Real>>(
"deltaDiscount");
408 std::vector<Real> mu_sf_1 = swapFloatLeg.result<std::vector<Real>>(
"deltaForward");
409 Array mu_sd(mu_sd_1.begin(), mu_sd_1.end());
410 Array mu_sf(mu_sf_1.begin(), mu_sf_1.end());
411 Array F_sd = 1.0 / annuity * mu_sd - 1.0 / (annuity * annuity) * mu * A_s;
412 Array F_sf = 1.0 / annuity * mu_sf;
413 Array deltaDiscount =
black * A_s + annuity * blackDelta * F_sd;
414 Array deltaForward = annuity * blackDelta * F_sf;
415 std::vector<Real> deltaDiscount1(deltaDiscount.begin(), deltaDiscount.end());
416 std::vector<Real> deltaForward1(deltaForward.begin(), deltaForward.end());
417 results_.additionalResults[
"deltaDiscount"] = deltaDiscount1;
418 results_.additionalResults[
"deltaForward"] = deltaForward1;
420 results_.additionalResults[
"theta"] =
421 annuity * Spec().theta(strike, atmForward, stdDev, exerciseTime, 1.0, displacement_);
424 Size n = bucketTimesDeltaGamma_.size();
425 Real blackGamma = Spec().gamma(strike, atmForward, stdDev, 1.0, displacement_);
426 Matrix A_ss = swapFixedLeg.result<std::vector<Matrix>>(
"gammaBPS")[0];
427 Matrix mu_ss = swapFloatLeg.result<Matrix>(
"gamma");
428 Matrix result(2 * n, 2 * n, 0.0);
429 for (Size i = 0; i < 2 * n; ++i) {
431 Real Fi = iIsDsc ? (mu_sd[i] / annuity - mu * A_s[i] / (annuity * annuity)) : (mu_sf[i - n] / annuity);
432 for (Size j = 0; j <= i; ++j) {
435 jIsDsc ? (mu_sd[j] / annuity - mu * A_s[j] / (annuity * annuity)) : (mu_sf[j - n] / annuity);
436 Real Fij = mu_ss[i][j] / annuity;
439 Fij += -mu * A_ss[i][j] / (annuity * annuity);
440 result[i][j] += A_ss[i][j] *
black;
442 Fij -= (jIsDsc ? mu_sd[j] : mu_sf[j - n]) * A_s[i] / (annuity * annuity);
443 result[i][j] += A_s[i] * blackDelta * Fj;
446 Fij += (iIsDsc ? mu_sd[i] : mu_sf[i - n]) * A_s[j] / (annuity * annuity);
447 Fij -= 2.0 * A_s[j] * (iIsDsc ? mu_sd[i] : mu_sf[i - n]) / (annuity * annuity);
449 Fij += 2.0 * A_s[j] * mu * A_s[i] / (annuity * annuity * annuity);
451 result[i][j] += A_s[j] * blackDelta * Fi;
453 result[i][j] += annuity * (blackGamma * Fi * Fj + blackDelta * Fij);
457 for (Size i = 0; i < 2 * n; ++i) {
458 for (Size j = i + 1; j < 2 * n; ++j) {
459 result[i][j] = result[j][i];
462 results_.additionalResults[
"gamma"] = result;
QuantLib::ext::shared_ptr< SimpleQuote > vol_
const Instrument::results * results_
Normal Bachelier-formula swaption engine.
Shifted Lognormal Black-formula swaption engine.
Handle< YieldTermStructure > discountCurve_
const bool computeDeltaVega_
Handle< SwaptionVolatilityStructure > volatility()
void calculate() const override
BlackStyleSwaptionEngineDeltaGamma(const Handle< YieldTermStructure > &discountCurve, Volatility vol, const DayCounter &dc=Actual365Fixed(), Real displacement=0.0, const std::vector< Time > &bucketTimesDeltaGamma=std::vector< Time >(), const std::vector< Time > &bucketTimesVegaOpt=std::vector< Time >(), const std::vector< Time > &bucketTimesVegaUnd=std::vector< Time >(), const bool computeDeltaVega=false, const bool computeGamma=false, const bool linearInZero=true)
const std::vector< Time > bucketTimesVegaOpt_
BlackStyleSwaptionEngineDeltaGamma(const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &vol, const std::vector< Time > &bucketTimesDeltaGamma=std::vector< Time >(), const std::vector< Time > &bucketTimesVegaOpt=std::vector< Time >(), const std::vector< Time > &bucketTimesVegaUnd=std::vector< Time >(), const bool computeDeltaVega=false, const bool computeGamma=false, const bool linearInZero=true)
BlackStyleSwaptionEngineDeltaGamma(const Handle< YieldTermStructure > &discountCurve, const Handle< Quote > &vol, const DayCounter &dc=Actual365Fixed(), Real displacement=0.0, const std::vector< Time > &bucketTimesDeltaGamma=std::vector< Time >(), const std::vector< Time > &bucketTimesVegaOpt=std::vector< Time >(), const std::vector< Time > &bucketTimesVegaUnd=std::vector< Time >(), const bool computeDeltaVega=false, const bool computeGamma=false, const bool linearInZero=true)
const std::vector< Time > bucketTimesVegaUnd_
Handle< SwaptionVolatilityStructure > vol_
Handle< YieldTermStructure > termStructure()
const std::vector< Time > bucketTimesDeltaGamma_
Swap engine providing analytical deltas and gammas for vanilla swaps.
RandomVariable variance(const RandomVariable &r)
RandomVariable black(const RandomVariable &omega, const RandomVariable &t, const RandomVariable &strike, const RandomVariable &forward, const RandomVariable &impliedVol)
Real vega(const Real strike, const Real atmForward, const Real stdDev, const Real exerciseTime, const Real annuity, const Real)
Real value(const Option::Type type, const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real)
Real theta(const Real strike, const Real atmForward, const Real stdDev, const Real exerciseTime, const Real annuity, const Real displacement)
static const VolatilityType type
Real gamma(const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real displacement)
Real delta(const Option::Type type, const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real displacement)
Real theta(const Real strike, const Real atmForward, const Real stdDev, const Real exerciseTime, const Real annuity, const Real displacement)
Real value(const Option::Type type, const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real displacement)
Real vega(const Real strike, const Real atmForward, const Real stdDev, const Real exerciseTime, const Real annuity, const Real displacement)
static const VolatilityType type
Real gamma(const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real displacement)
Real delta(const Option::Type type, const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real displacement)
Swap::arguments * arguments_