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>
28#include <boost/accumulators/accumulators.hpp>
29#include <boost/accumulators/statistics/mean.hpp>
30#include <boost/accumulators/statistics/stats.hpp>
32#include <boost/make_shared.hpp>
34using namespace boost::accumulators;
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)
47 cmsPricers_(cmsPricers), couponDiscountCurve_(couponDiscountCurve), samples_(samples), seed_(seed),
48 useSobol_(useSobol), salvaging_(salvaging) {
51 registerWith(p.second);
53 registerWith(p.second);
54 registerWith(couponDiscountCurve);
67 QL_FAIL(
"MCGaussianFormulaBasedCouponPricer::capletPrice(): not provided");
71 QL_FAIL(
"MCGaussianFormulaBasedCouponPricer::capletRate(): not provided");
75 QL_FAIL(
"MCGaussianFormulaBasedCouponPricer::floorletPrice(): not provided");
79 QL_FAIL(
"MCGaussianFormulaBasedCouponPricer::floorletRate(): not provided");
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);
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);
96 QL_FAIL(
"No correlation between " << key1 <<
" and " << key2 <<
" is given!");
104 QL_REQUIRE(
coupon_,
"MCGaussianFormulaBasedCouponPricer::initialize(): FormulaBasedCoupon excepted");
106 "MCGaussianFormulaBasedCouponPricer::initialize(): coupon discount curve is empty");
108 "MCGaussianFormulaBasedCouponPricer::initialize(): coupon payment currency ("
112 today_ = Settings::instance().evaluationDate();
123 if (fixingDate_ <= today_ || index_->indices().empty())
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;
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(),
150 c->setPricer(iborPricer->second);
153 volType_[i] = iborPricer->second->capletVolatility()->volatilityType();
154 if (
volType_[i] == ShiftedLognormal) {
155 volShift_[i] = iborPricer->second->capletVolatility()->displacement();
160 auto cmsPricer =
cmsPricers_.find(cms->iborIndex()->name());
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(),
168 c->setPricer(cmsPricer->second);
170 vol[i] = cmsPricer->second->swaptionVolatility()->volatility(
fixingDate_, cms->tenor(),
atmRate_[i]);
171 volType_[i] = cmsPricer->second->swaptionVolatility()->volatilityType();
172 if (
volType_[i] == ShiftedLognormal) {
178 QL_FAIL(
"MCGaussianFormulaBasedCouponPricer::initialize(): index not recognised, must be IBOR or CMS");
180 Real adjRate = c->adjustedFixing();
181 if (
volType_[i] == ShiftedLognormal) {
191 Real quantoCorrelation = getCorrelation(
index_->indices()[i]->name(),
"FX",
correlation_);
192 auto ccy =
index_->indices()[i]->currency().code();
194 QL_REQUIRE(volIt !=
fxVolatilities_.end(),
"MCGaussianFormulaBasedCouponPricer::initialize(): need fx vol "
197 Real fxVol = volIt->second->blackVol(
fixingDate_, Null<Real>());
198 mean_[i] += vol[i] * fxVol * quantoCorrelation * fixingTime;
204 for (Size i = 0; i <
n_; ++i) {
205 for (Size j = 0; j < i; ++j) {
225 accumulator_set<double, stats<tag::mean>> acc;
228 InverseCumulativeNormal icn;
231 MersenneTwisterUniformRng mt_(
seed_);
234 for (Size i = 0; i <
samples_; ++i) {
236 auto seq = sb_.nextSequence().value;
237 std::transform(seq.begin(), seq.end(), w.begin(), icn);
239 for (Size j = 0; j <
n_; ++j) {
240 w[j] = icn(mt_.nextReal());
244 for (Size j = 0; j <
n_; ++j) {
245 if (
volType_[j] == ShiftedLognormal) {
249 acc(formula(z.begin(), z.end()));