26#include <ql/cashflows/averagebmacoupon.hpp>
27#include <ql/cashflows/fixedratecoupon.hpp>
28#include <ql/cashflows/iborcoupon.hpp>
29#include <ql/pricingengines/blackformula.hpp>
31#include <boost/algorithm/string/join.hpp>
36 const Handle<SwaptionVolatilityStructure>& volatility)
37 : discountCurve_(discountCurve), volatility_(volatility) {}
45 const std::vector<Currency>& currency,
46 const QuantLib::ext::shared_ptr<Exercise>& exercise,
47 const Settlement::Type& settlementType,
48 const Settlement::Method& settlementMethod,
49 std::vector<std::string>& messages) {
51 bool isHandled =
true;
55 for (Size i = 1; i < currency.size(); ++i) {
56 if (currency[0] != currency[i]) {
57 messages.push_back(
"NumericLgmMultilegOptionEngine: can only handle single currency underlyings, got " +
58 currency[0].code() +
" on leg #1 and " + currency[i].code() +
" on leg #" +
59 std::to_string(i + 1));
64 for (Size i = 0; i < legs.size(); ++i) {
65 for (Size j = 0; j < legs[i].size(); ++j) {
66 if (
auto cpn = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(legs[i][j])) {
67 if (cpn->index()->currency() != currency[0]) {
68 messages.push_back(
"NumericLgmMultilegOptionEngine: can only handle indices (" +
69 cpn->index()->name() +
") with same currency as unqiue pay currency (" +
78 for (Size i = 0; i < legs.size(); ++i) {
79 for (Size j = 0; j < legs[i].size(); ++j) {
80 if (
auto c = QuantLib::ext::dynamic_pointer_cast<Coupon>(legs[i][j])) {
81 if (!(QuantLib::ext::dynamic_pointer_cast<IborCoupon>(c) || QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c) ||
82 QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(c) ||
83 QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(c) ||
84 QuantLib::ext::dynamic_pointer_cast<QuantLib::AverageBMACoupon>(c) ||
85 QuantLib::ext::dynamic_pointer_cast<QuantExt::SubPeriodsCoupon1>(c))) {
87 "BlackMultilegOptionEngine: coupon type not handled, supported coupon types: Fix, "
88 "Ibor, ON comp, ON avg, BMA/SIFMA, subperiod. leg = " +
89 std::to_string(i) +
" cf = " + std::to_string(j));
98 isHandled = isHandled && (exercise ==
nullptr || exercise->type() == Exercise::European);
105 std::vector<std::string> messages;
107 "BlackMultiLegOptionEngineBase::calculate(): instrument is not handled: "
108 << boost::algorithm::join(messages,
", "));
114 for (Size i = 0; i <
legs_.size(); ++i) {
115 for (Size j = 0; j <
legs_[i].size(); ++j) {
125 QL_REQUIRE(
exercise_->dates().size() == 1,
126 "BlackMultiLegOptionEngineBase: expected 1 exercise dates, got " <<
exercise_->dates().size());
128 Real exerciseTime =
volatility_->timeFromReference(exerciseDate);
134 Real fixedRate = Null<Real>();
135 Real floatingSpread = Null<Real>();
136 Real floatingGearing = Null<Real>();
137 Real nominal = Null<Real>();
138 Real accrual = Null<Real>();
139 Real amount = Null<Real>();
140 Real discountFactor = Null<Real>();
141 Date payDate = Null<Date>();
144 std::vector<CfInfo> cfInfo;
146 DayCounter fixedDayCount;
147 Date earliestAccrualStartDate = Date::maxDate();
148 Date latestAccrualEndDate = Date::minDate();
150 Size numFixed = 0, numFloat = 0;
152 for (Size i = 0; i <
legs_.size(); ++i) {
153 for (Size j = 0; j <
legs_[i].size(); ++j) {
154 Real payer =
payer_[i] ? -1.0 : 1.0;
155 if (
auto c = QuantLib::ext::dynamic_pointer_cast<Coupon>(
legs_[i][j])) {
156 auto fixedCoupon = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c);
158 fixedDayCount = c->dayCounter();
159 if (c->accrualStartDate() >= exerciseDate) {
160 cfInfo.push_back(CfInfo());
161 earliestAccrualStartDate = std::min(earliestAccrualStartDate, c->accrualStartDate());
162 latestAccrualEndDate = std::max(latestAccrualEndDate, c->accrualEndDate());
164 cfInfo.back().fixedRate = fixedCoupon->rate();
165 cfInfo.back().nominal = c->nominal() * payer;
167 }
else if (
auto f = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(c)) {
168 cfInfo.back().floatingSpread = f->spread();
169 cfInfo.back().floatingGearing = f->gearing();
170 cfInfo.back().nominal = f->nominal() * payer;
173 cfInfo.back().accrual = c->accrualPeriod();
174 cfInfo.back().discountFactor =
discountCurve_->discount(c->date());
175 cfInfo.back().amount = c->amount() * payer;
176 cfInfo.back().payDate = c->date();
178 }
else if (
legs_[i][j]->date() > exerciseDate) {
179 cfInfo.push_back(CfInfo());
181 cfInfo.back().amount =
legs_[i][j]->amount() * payer;
182 cfInfo.back().payDate =
legs_[i][j]->date();
189 if (numFloat > 0 && numFixed == 0) {
190 for (
auto const& c : cfInfo) {
191 if (c.floatingSpread != Null<Real>()) {
192 cfInfo.push_back(CfInfo());
193 cfInfo.back().fixedRate = 0.0;
194 cfInfo.back().nominal = c.nominal * -1;
195 cfInfo.back().accrual = c.accrual;
196 cfInfo.back().discountFactor = c.discountFactor;
197 cfInfo.back().amount = 0.0;
198 cfInfo.back().payDate = c.payDate;
203 QL_REQUIRE(!fixedDayCount.empty(),
"BlackMultiLegOptionEngineBase::calculate(): could not determine fixed day "
204 "counter - it appears no fixed coupons are present in the underlying.");
205 QL_REQUIRE(earliestAccrualStartDate != Date::maxDate() && latestAccrualEndDate != Date::minDate(),
206 "BlackMultiLegOptionEngineBase::calculate(): could not determine earliest / latest accrual start / end "
207 "dates. No coupons seems to be present in the underlying.");
211 if (
auto rebatedExercise = QuantLib::ext::dynamic_pointer_cast<QuantExt::RebatedExercise>(
exercise_)) {
212 cfInfo.push_back(CfInfo());
213 cfInfo.back().amount = rebatedExercise->rebate(0);
214 cfInfo.back().discountFactor =
discountCurve_->discount(rebatedExercise->rebatePaymentDate(0));
219 Real weightedFixedRateNom = 0.0, weightedFixedRateDenom = 0.0;
220 Real fixedBps = 0.0, fixedNpv = 0.0;
221 for (
auto const& c : cfInfo) {
222 if (c.fixedRate != Null<Real>()) {
223 Real w = c.nominal * c.accrual * c.discountFactor;
224 weightedFixedRateNom += c.fixedRate * w;
225 weightedFixedRateDenom += w;
227 fixedNpv += c.amount * c.discountFactor;
230 Real weightedFixedRate = weightedFixedRateNom / weightedFixedRateDenom;
234 Real weightedFloatingSpreadNom = 0.0, weightedFloatingSpreadDenom = 0.0;
235 Real floatingBps = 0.0, floatingNpv = 0.0;
236 for (
auto const& c : cfInfo) {
237 if (c.floatingSpread != Null<Real>()) {
238 Real w = c.nominal * c.accrual * c.discountFactor;
239 weightedFloatingSpreadNom += c.floatingSpread * w;
240 weightedFloatingSpreadDenom += w;
242 floatingNpv += c.amount * c.discountFactor;
245 Real weightedFloatingSpread = weightedFloatingSpreadNom / weightedFloatingSpreadDenom;
249 Real simpleCfNpv = 0.0;
250 for (
auto const& c : cfInfo) {
251 if (c.fixedRate == Null<Real>() && c.floatingSpread == Null<Real>()) {
252 simpleCfNpv += c.amount * c.discountFactor;
258 Real atmForward = -floatingNpv / fixedBps;
262 Real spreadCorrection = weightedFloatingSpread * std::abs(floatingBps / fixedBps);
264 Real effectiveAtmForward = atmForward - spreadCorrection;
265 Real effectiveFixedRate = weightedFixedRate - spreadCorrection;
269 Real simpleCfCorrection = 0.0;
270 for (
auto const& c : cfInfo) {
271 if (c.fixedRate == Null<Real>() && c.floatingSpread == Null<Real>()) {
272 simpleCfCorrection += c.amount * c.discountFactor / fixedBps;
276 effectiveFixedRate += simpleCfCorrection;
283 annuity = std::abs(fixedBps);
286 InterestRate sr(effectiveAtmForward, fixedDayCount, Compounded, Annual);
287 for (
auto const& c : cfInfo) {
288 if (c.fixedRate != Null<Real>()) {
289 annuity += std::abs(c.nominal) * c.accrual *
290 sr.discountFactor(std::min(earliestAccrualStartDate, c.payDate), c.payDate);
295 QL_FAIL(
"BlackMultiLegOptionEngine: invalid (settlementType, settlementMethod) pair");
300 Real swapLength =
volatility_->swapLength(earliestAccrualStartDate, latestAccrualEndDate);
304 swapLength = std::max(swapLength, 1.0 / 12.0);
311 Real delta = 0.0, vega = 0.0;
312 Option::Type optionType = fixedBps > 0 ? Option::Put : Option::Call;
316 }
else if (
volatility_->volatilityType() == ShiftedLognormal) {
317 npv_ = blackFormula(optionType, effectiveFixedRate, effectiveAtmForward, stdDev, annuity, displacement);
318 vega = std::sqrt(exerciseTime) *
319 blackFormulaStdDevDerivative(effectiveFixedRate, effectiveAtmForward, stdDev, annuity, displacement);
320 delta = blackFormulaForwardDerivative(optionType, effectiveFixedRate, effectiveAtmForward, stdDev, annuity,
323 npv_ = bachelierBlackFormula(optionType, effectiveFixedRate, effectiveAtmForward, stdDev, annuity);
324 vega = std::sqrt(exerciseTime) *
325 bachelierBlackFormulaStdDevDerivative(effectiveFixedRate, effectiveAtmForward, stdDev, annuity);
326 delta = bachelierBlackFormulaForwardDerivative(optionType, effectiveFixedRate, effectiveAtmForward, stdDev,
354 const Handle<SwaptionVolatilityStructure>& volatility)
377 const Handle<YieldTermStructure>& discountCurve,
const Handle<SwaptionVolatilityStructure>& volatility)
386 for (Size i = 0; i <
arguments_.payer.size(); ++i) {
402 const Handle<YieldTermStructure>& discountCurve,
const Handle<SwaptionVolatilityStructure>& volatility)
411 for (Size i = 0; i <
arguments_.payer.size(); ++i) {
coupon paying the weighted average of the daily overnight rate
Simple Black European swaption engine.
const Instrument::results * results_
Handle< YieldTermStructure > discountCurve_
BlackMultiLegOptionEngineBase(const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &volatility)
std::vector< Currency > currency_
QuantLib::ext::shared_ptr< Exercise > exercise_
std::map< std::string, boost::any > additionalResults_
Handle< SwaptionVolatilityStructure > volatility_
static bool instrumentIsHandled(const MultiLegOption &m, std::vector< std::string > &messages)
std::vector< bool > payer_
Settlement::Method settlementMethod_
Settlement::Type settlementType_
void calculate() const override
BlackMultiLegOptionEngine(const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &volatility)
BlackNonstandardSwaptionFromMultilegOptionEngine(const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &volatility)
void calculate() const override
void calculate() const override
BlackSwaptionFromMultilegOptionEngine(const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &volatility)
const std::vector< Leg > & legs() const
const Settlement::Type settlementType() const
const Settlement::Method settlementMethod() const
const QuantLib::ext::shared_ptr< Exercise > exercise() const
const std::vector< Currency > & currency() const
const std::vector< bool > & payer() const
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable variance(const RandomVariable &r)
coupon paying the compounded daily overnight rate, copy of QL class, added includeSpread flag
more flexible version of ql class
Coupon with a number of sub-periods.
Swap::arguments * arguments_