19#include <ql/cashflows/iborcoupon.hpp>
20#include <ql/cashflows/overnightindexedcoupon.hpp>
21#include <ql/indexes/iborindex.hpp>
22#include <ql/math/solvers1d/brent.hpp>
25#include <boost/bind/bind.hpp>
32 const Handle<YieldTermStructure>& discountCurve,
34 : GenericEngine<Swaption::arguments, Swaption::results>(), p_(model->parametrization()),
35 c_(discountCurve.empty() ? p_->termStructure() : discountCurve), floatSpreadMapping_(floatSpreadMapping),
42 const Handle<YieldTermStructure>& discountCurve,
44 : GenericEngine<Swaption::arguments, Swaption::results>(), p_(model->irlgm1f(ccy)),
45 c_(discountCurve.empty() ? p_->termStructure() : discountCurve), floatSpreadMapping_(floatSpreadMapping),
52 const Handle<YieldTermStructure>& discountCurve,
54 : GenericEngine<Swaption::arguments, Swaption::results>(), p_(irlgm1f),
55 c_(discountCurve.empty() ? p_->termStructure() : discountCurve), floatSpreadMapping_(floatSpreadMapping),
74 Date reference =
p_->termStructure()->referenceDate();
75 QuantLib::ext::shared_ptr<IborIndex> index =
78 auto on = QuantLib::ext::dynamic_pointer_cast<QuantLib::OvernightIndexedCoupon>(
floatingLeg_[k]);
79 QL_REQUIRE(on,
"AnalyticalLgmSwaptionEngine::calculate(): internal error, could not cast to "
80 "QuantLib::OvernightIndexedCoupon.");
81 QL_REQUIRE(!on->valueDates().empty(),
82 "AnalyticalLgmSwaptionEngine::calculate(): internal error, no value dates in ois coupon.");
83 Date v1 = std::max(reference, on->valueDates().front());
84 Date v2 = std::max(v1 + 1, on->valueDates().back());
86 if (on->averagingMethod() == QuantLib::RateAveraging::Compound)
87 rate = (
c_->discount(v1) /
c_->discount(v2) - 1.0) / index->dayCounter().yearFraction(v1, v2);
89 rate = std::log(
c_->discount(v1) /
c_->discount(v2)) / index->dayCounter().yearFraction(v1, v2);
92 if (IborCoupon::Settings::instance().usingAtParCoupons()) {
96 Date fixingValueDate =
97 index->fixingCalendar().advance(
floatingLeg_[k]->fixingDate(), index->fixingDays(), Days);
98 fixingValueDate = std::max(fixingValueDate, reference);
99 auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(
floatingLeg_[k]);
100 QL_REQUIRE(cpn,
"AnalyticalLgmSwaptionEngine::calculate(): coupon expected on underlying swap "
101 "floating leg, could not cast");
102 Date nextFixingDate = index->fixingCalendar().advance(cpn->accrualEndDate(),
103 -
static_cast<Integer
>(index->fixingDays()), Days);
104 Date fixingEndDate = index->fixingCalendar().advance(nextFixingDate, index->fixingDays(), Days);
105 fixingEndDate = std::max(fixingEndDate, fixingValueDate + 1);
106 Real spanningTime = index->dayCounter().yearFraction(fixingValueDate, fixingEndDate);
107 DiscountFactor disc1 =
c_->discount(fixingValueDate);
108 DiscountFactor disc2 =
c_->discount(fixingEndDate);
109 Real fixing = (disc1 / disc2 - 1.0) / spanningTime;
114 auto flatIbor = QuantLib::ext::make_shared<IborIndex>(
115 index->familyName() +
" (no fixings)", index->tenor(), index->fixingDays(), index->currency(),
116 index->fixingCalendar(), index->businessDayConvention(), index->endOfMonth(), index->dayCounter(),
c_);
117 Date fixingDate = flatIbor->fixingCalendar().adjust(std::max(
floatingLeg_[k]->fixingDate(), reference));
125 QL_REQUIRE(
arguments_.settlementType == Settlement::Physical,
"cash-settled swaptions are not supported ...");
127 Date reference =
p_->termStructure()->referenceDate();
129 Date expiry =
arguments_.exercise->dates().back();
131 if (expiry <= reference) {
140 Option::Type type =
arguments_.type == VanillaSwap::Payer ? Option::Call : Option::Put;
144 "AnalyticalLgmSwaptionEngine::calculate(): internal error, expected either swap or swapOis to be set.");
145 const Schedule& fixedSchedule =
147 const Schedule& floatSchedule =
150 j1_ = std::lower_bound(fixedSchedule.dates().begin(), fixedSchedule.dates().end(), expiry) -
151 fixedSchedule.dates().begin();
152 k1_ = std::lower_bound(floatSchedule.dates().begin(), floatSchedule.dates().end(), expiry) -
153 floatSchedule.dates().begin();
160 fixedLeg_.push_back(QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c));
162 "AnalyticalLgmSwaptionEngine::calculate(): internal error, could not cast to FixedRateCoupon");
165 floatingLeg_.push_back(QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(c));
168 "AnalyticalLgmSwaptionEngine::calculate(): internal error, could not cast to FloatingRateRateCoupon");
180 for (Size i = 0; i <
S_.size(); ++i) {
185 static_cast<Size
>(
static_cast<Real
>(
floatingLeg_.size()) /
static_cast<Real
>(
fixedLeg_.size()) + 0.5);
186 QL_REQUIRE(ratio >= 1,
"floating leg's payment frequency must be equal or "
187 "higher than fixed leg's payment frequency in "
188 "analytic lgm swaption engine");
195 Real floatAmountMismatch = 0.0;
197 floatAmountMismatch +=
210 Real sum1 = 0.0, sum2 = 0.0;
211 for (Size rr = 0; rr < ratio && k <
floatingLeg_.size(); ++rr, ++k) {
212 Real amount = Null<Real>();
218 Real lambda1, lambda2;
222 lambda2 =
static_cast<Real
>(rr + 1) /
static_cast<Real
>(ratio);
223 lambda1 = 1.0 - lambda2;
228 lambda1 = lambda2 = 0.0;
230 if (amount != Null<Real>()) {
232 sum1 += lambda1 * correction;
233 sum2 += lambda2 * correction;
243 sum1 += lambda1 * correction;
244 sum2 += lambda2 * correction;
255 w_ = type == Option::Call ? -1.0 : 1.0;
266 u_ =
p_->Hprime(0.0) > 0.0 ? 1.0 : -1.0;
276 zetaex_ =
p_->zeta(
p_->termStructure()->timeFromReference(expiry));
284 }
catch (
const std::exception& e) {
285 std::ostringstream os;
286 os <<
"AnalyticLgmSwaptionEngine: failed to compute yStar (" << e.what() <<
"), parameter details: [";
287 Real tte =
p_->termStructure()->timeFromReference(expiry);
288 os <<
"tte=" << tte <<
", vol=" << std::sqrt(
zetaex_ / tte) <<
", nominal=" <<
nominal_
290 for (Size j = 0; j <
Dj_.size(); ++j)
291 os <<
", d" << j <<
"=" <<
Dj_[j];
293 for (Size j = 0; j <
Hj_.size(); ++j)
294 os <<
", h" << j <<
"=" <<
Hj_[j];
296 os <<
", cpn" << i <<
"=(" << QuantLib::io::iso_date(
fixedLeg_[i]->accrualStartDate()) <<
","
297 << QuantLib::io::iso_date(
fixedLeg_[i]->date()) <<
"," <<
fixedLeg_[i]->amount() <<
")";
299 os <<
", S=" <<
S_m1;
300 for (Size j = 0; j <
S_.size(); ++j) {
301 os <<
", S" << j <<
"=" <<
S_[j];
307 CumulativeNormalDistribution N;
308 Real sqrt_zetaex = std::sqrt(
zetaex_);
319 results_.additionalResults[
"fixedAmountCorrectionSettlement"] =
S_m1;
320 results_.additionalResults[
"fixedAmountCorrections"] =
S_;
analytic engine for european swaptions in the LGM model
const Instrument::results * results_
QuantLib::ext::shared_ptr< IrLgm1fParametrization > p_
std::vector< QuantLib::ext::shared_ptr< FixedRateCoupon > > fixedLeg_
AnalyticLgmSwaptionEngine(const QuantLib::ext::shared_ptr< LinearGaussMarkovModel > &model, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >(), const FloatSpreadMapping floatSpreadMapping=proRata)
void calculate() const override
FloatSpreadMapping floatSpreadMapping_
Real yStarHelper(const Real y) const
void enableCache(const bool lgm_H_constant=true, const bool lgm_alpha_constant=false)
std::vector< QuantLib::ext::shared_ptr< FloatingRateCoupon > > floatingLeg_
Real flatAmount(const Size k) const
Handle< YieldTermStructure > c_
Real sum(const Cash &c, const Cash &d)
Swap::arguments * arguments_