Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
mcgaussianformulabasedcouponpricer.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <ql/cashflows/cmscoupon.hpp>
22#include <ql/cashflows/iborcoupon.hpp>
23#include <ql/indexes/swapindex.hpp>
24#include <ql/math/distributions/normaldistribution.hpp>
25#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
26#include <ql/math/randomnumbers/sobolrsg.hpp>
27
28#include <boost/accumulators/accumulators.hpp>
29#include <boost/accumulators/statistics/mean.hpp>
30#include <boost/accumulators/statistics/stats.hpp>
31
32#include <boost/make_shared.hpp>
33using namespace QuantLib;
34using namespace boost::accumulators;
35
36namespace QuantExt {
37
39 const std::string& paymentCurrencyCode,
40 const std::map<std::string, QuantLib::ext::shared_ptr<IborCouponPricer>>& iborPricers,
41 const std::map<std::string, QuantLib::ext::shared_ptr<CmsCouponPricer>>& cmsPricers,
42 const std::map<std::string, Handle<BlackVolTermStructure>>& fxVolatilities,
43 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlation,
44 const Handle<YieldTermStructure>& couponDiscountCurve, const Size samples, const Size seed, const bool useSobol,
45 SalvagingAlgorithm::Type salvaging)
46 : FormulaBasedCouponPricer(paymentCurrencyCode, fxVolatilities, correlation), iborPricers_(iborPricers),
47 cmsPricers_(cmsPricers), couponDiscountCurve_(couponDiscountCurve), samples_(samples), seed_(seed),
48 useSobol_(useSobol), salvaging_(salvaging) {
49 // registering with fx vol and correlations is done in the base class already
50 for (auto const& p : iborPricers_)
51 registerWith(p.second);
52 for (auto const& p : cmsPricers_)
53 registerWith(p.second);
54 registerWith(couponDiscountCurve);
55}
56
58 compute();
59 return rateEstimate_;
60}
61
63 return coupon_->accrualPeriod() * discount_ * swapletRate();
64}
65
67 QL_FAIL("MCGaussianFormulaBasedCouponPricer::capletPrice(): not provided");
68}
69
71 QL_FAIL("MCGaussianFormulaBasedCouponPricer::capletRate(): not provided");
72}
73
75 QL_FAIL("MCGaussianFormulaBasedCouponPricer::floorletPrice(): not provided");
76}
77
78Rate MCGaussianFormulaBasedCouponPricer::floorletRate(Rate effectiveFloor) const {
79 QL_FAIL("MCGaussianFormulaBasedCouponPricer::floorletRate(): not provided");
80}
81
82namespace {
83Real getCorrelation(
84 const std ::string& key1, const std::string& key2,
85 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlation) {
86 auto k1 = std::make_pair(key1, key2);
87 auto k2 = std::make_pair(key2, key1);
88
89 Real corr;
90 if (correlation.find(k1) != correlation.end()) {
91 corr = correlation.at(k1)->correlation(0.0, 1.0);
92 } else if (correlation.find(k2) != correlation.end()) {
93 corr = correlation.at(k2)->correlation(0.0, 1.0);
94 } else {
95 // missing correlation entry in the map
96 QL_FAIL("No correlation between " << key1 << " and " << key2 << " is given!");
97 }
98 return corr;
99}
100} // namespace
101
103 coupon_ = dynamic_cast<const FormulaBasedCoupon*>(&coupon);
104 QL_REQUIRE(coupon_, "MCGaussianFormulaBasedCouponPricer::initialize(): FormulaBasedCoupon excepted");
105 QL_REQUIRE(!couponDiscountCurve_.empty(),
106 "MCGaussianFormulaBasedCouponPricer::initialize(): coupon discount curve is empty");
107 QL_REQUIRE(coupon_->paymentCurrency().code() == paymentCurrencyCode_,
108 "MCGaussianFormulaBasedCouponPricer::initialize(): coupon payment currency ("
109 << coupon_->paymentCurrency().code() << ") does not match pricer's payment currency ("
110 << paymentCurrencyCode_ << ")");
111
112 today_ = Settings::instance().evaluationDate();
113 fixingDate_ = coupon_->fixingDate();
114 Real fixingTime =
115 couponDiscountCurve_->timeFromReference(fixingDate_); // date to time conversion via discount curve dc
116 paymentDate_ = coupon_->date();
118 discount_ =
119 paymentDate_ > couponDiscountCurve_->referenceDate() ? couponDiscountCurve_->discount(paymentDate_) : 1.0;
120
121 // for past fixing we are done, also if there are actually no indices on which the formula depends
122
123 if (fixingDate_ <= today_ || index_->indices().empty())
124 return;
125
126 // loop over source indices, compute mean and variance for the MC simulation
127
128 n_ = index_->indices().size();
129 volType_.resize(n_);
130 volShift_.resize(n_);
131 atmRate_.resize(n_);
132 mean_ = Array(n_);
133 Array vol(n_);
134
135 covariance_ = Matrix(n_, n_, 0.0);
136
137 for (Size i = 0; i < n_; ++i) {
138 auto ibor = QuantLib::ext::dynamic_pointer_cast<IborIndex>(index_->indices()[i]);
139 auto cms = QuantLib::ext::dynamic_pointer_cast<SwapIndex>(index_->indices()[i]);
140 QuantLib::ext::shared_ptr<FloatingRateCoupon> c;
141 if (ibor) {
142 auto iborPricer = iborPricers_.find(ibor->name());
143 QL_REQUIRE(iborPricer != iborPricers_.end(),
144 "MCGaussianFormulaBasedCouponPricer::initialize(): need ibor coupon pricer for key '"
145 << ibor->name() << "'");
146 c = QuantLib::ext::make_shared<IborCoupon>(coupon_->date(), coupon_->nominal(), coupon_->accrualStartDate(),
147 coupon_->accrualEndDate(), coupon_->fixingDays(), ibor, 1.0, 0.0,
148 coupon_->referencePeriodStart(), coupon_->referencePeriodEnd(),
149 coupon_->dayCounter(), coupon_->isInArrears());
150 c->setPricer(iborPricer->second);
151 atmRate_[i] = c->indexFixing();
152 vol[i] = iborPricer->second->capletVolatility()->volatility(fixingDate_, atmRate_[i]);
153 volType_[i] = iborPricer->second->capletVolatility()->volatilityType();
154 if (volType_[i] == ShiftedLognormal) {
155 volShift_[i] = iborPricer->second->capletVolatility()->displacement();
156 } else {
157 volShift_[i] = 0.0;
158 }
159 } else if (cms) {
160 auto cmsPricer = cmsPricers_.find(cms->iborIndex()->name());
161 QL_REQUIRE(cmsPricer != cmsPricers_.end(),
162 "MCGaussianFormulaBasedCouponPricer::initialize(): need cms coupon pricer for key '"
163 << cms->iborIndex()->name() << "'");
164 c = QuantLib::ext::make_shared<CmsCoupon>(coupon_->date(), coupon_->nominal(), coupon_->accrualStartDate(),
165 coupon_->accrualEndDate(), coupon_->fixingDays(), cms, 1.0, 0.0,
166 coupon_->referencePeriodStart(), coupon_->referencePeriodEnd(),
167 coupon_->dayCounter(), coupon_->isInArrears());
168 c->setPricer(cmsPricer->second);
169 atmRate_[i] = c->indexFixing();
170 vol[i] = cmsPricer->second->swaptionVolatility()->volatility(fixingDate_, cms->tenor(), atmRate_[i]);
171 volType_[i] = cmsPricer->second->swaptionVolatility()->volatilityType();
172 if (volType_[i] == ShiftedLognormal) {
173 volShift_[i] = cmsPricer->second->swaptionVolatility()->shift(fixingDate_, cms->tenor());
174 } else {
175 volShift_[i] = 0.0;
176 }
177 } else {
178 QL_FAIL("MCGaussianFormulaBasedCouponPricer::initialize(): index not recognised, must be IBOR or CMS");
179 }
180 Real adjRate = c->adjustedFixing();
181 if (volType_[i] == ShiftedLognormal) {
182 mean_[i] =
183 std::log((adjRate + volShift_[i]) / (atmRate_[i] + volShift_[i])) - 0.5 * vol[i] * vol[i] * fixingTime;
184 } else {
185 mean_[i] = adjRate;
186 }
187
188 // incorporate Quanto adjustment into mean, if applicable
189
190 if (index_->indices()[i]->currency().code() != paymentCurrencyCode_) {
191 Real quantoCorrelation = getCorrelation(index_->indices()[i]->name(), "FX", correlation_);
192 auto ccy = index_->indices()[i]->currency().code();
193 auto volIt = fxVolatilities_.find(ccy);
194 QL_REQUIRE(volIt != fxVolatilities_.end(), "MCGaussianFormulaBasedCouponPricer::initialize(): need fx vol "
195 << ccy << " vs " << paymentCurrencyCode_);
196 // TODO we rely on the fx vol structure to return the atm vol for strike = null, see warning in the header
197 Real fxVol = volIt->second->blackVol(fixingDate_, Null<Real>());
198 mean_[i] += vol[i] * fxVol * quantoCorrelation * fixingTime;
199 }
200
201 } // for indices
202
203 // populate covariance matrix
204 for (Size i = 0; i < n_; ++i) {
205 for (Size j = 0; j < i; ++j) {
206 Real corr = getCorrelation(index_->indices()[i]->name(), index_->indices()[j]->name(), correlation_);
207 covariance_(i, j) = covariance_(j, i) = vol[i] * vol[j] * corr * fixingTime;
208 }
209 covariance_(i, i) = vol[i] * vol[i] * fixingTime;
210 }
211
212} // initialize
213
215
216 // is the rate determined?
217
218 if (fixingDate_ <= today_) {
220 return;
221 }
222
223 // the actual MC simulation
224
225 accumulator_set<double, stats<tag::mean>> acc;
226
227 Matrix C = pseudoSqrt(covariance_, salvaging_);
228 InverseCumulativeNormal icn;
229 const CompiledFormula& formula = index_->formula();
230
231 MersenneTwisterUniformRng mt_(seed_);
232 SobolRsg sb_(n_, seed_); // default direction integers
233 Array w(n_), z(n_);
234 for (Size i = 0; i < samples_; ++i) {
235 if (useSobol_) {
236 auto seq = sb_.nextSequence().value;
237 std::transform(seq.begin(), seq.end(), w.begin(), icn);
238 } else {
239 for (Size j = 0; j < n_; ++j) {
240 w[j] = icn(mt_.nextReal());
241 }
242 }
243 z = C * w + mean_;
244 for (Size j = 0; j < n_; ++j) {
245 if (volType_[j] == ShiftedLognormal) {
246 z[j] = (atmRate_[j] + volShift_[j]) * std::exp(z[j]) - volShift_[j];
247 }
248 }
249 acc(formula(z.begin(), z.end()));
250 }
251
252 rateEstimate_ = mean(acc);
253} // compute
254
255} // namespace QuantExt
helper class representing a formula with variables given by an id v
formula based coupon class
const QuantLib::ext::shared_ptr< FormulaBasedIndex > & formulaBasedIndex() const
const Currency & paymentCurrency() const
base pricer for formula based coupons
const std::map< std::string, Handle< BlackVolTermStructure > > fxVolatilities_
std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > correlation_
virtual Real capletPrice(Rate effectiveCap) const override
virtual Rate floorletRate(Rate effectiveFloor) const override
const std::map< std::string, QuantLib::ext::shared_ptr< CmsCouponPricer > > cmsPricers_
const std::map< std::string, QuantLib::ext::shared_ptr< IborCouponPricer > > iborPricers_
void initialize(const FloatingRateCoupon &coupon) override
QuantLib::ext::shared_ptr< FormulaBasedIndex > index_
MCGaussianFormulaBasedCouponPricer(const std::string &paymentCurrencyCode, const std::map< std::string, QuantLib::ext::shared_ptr< IborCouponPricer > > &iborPricers, const std::map< std::string, QuantLib::ext::shared_ptr< CmsCouponPricer > > &cmsPricers, const std::map< std::string, Handle< BlackVolTermStructure > > &fxVolatilities, const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > &correlation, const Handle< YieldTermStructure > &couponDiscountCurve, const Size samples=10000, const Size seed=42, const bool useSobol=true, SalvagingAlgorithm::Type salvaging=SalvagingAlgorithm::None)
virtual Real floorletPrice(Rate effectiveFloor) const override
virtual Rate capletRate(Rate effectiveCap) const override
formula based coupon pricer