49 {
50
51 DLOG(
"CapFloor::build() called for trade " <<
id() <<
", leg type is " <<
legData_.
legType());
52
53
58
62 "CapFloor build error, LegType must be Floating, CMS, DurationAdjustedCMS, CMSSpread, CPI or YY");
63
64 QL_REQUIRE(
caps_.size() > 0 ||
floors_.size() > 0,
"CapFloor build error, no cap rates or floor rates provided");
65 QuantLib::CapFloor::Type capFloorType;
67 capFloorType = QuantLib::CapFloor::Cap;
68 }
else if (
caps_.size() == 0) {
69 capFloorType = QuantLib::CapFloor::Floor;
70 } else {
71 capFloorType = QuantLib::CapFloor::Collar;
72 }
73
75 QuantLib::ext::shared_ptr<EngineBuilder> builder;
76 std::string underlyingIndex;
77 QuantLib::ext::shared_ptr<QuantLib::Instrument> qlInstrument;
78
79
80
81
82
84
86
87 QuantLib::ext::shared_ptr<FloatingLegData> floatData =
89 QL_REQUIRE(floatData,
"Wrong LegType, expected Floating, got " <<
legData_.
legType());
90 underlyingIndex = floatData->index();
91 Handle<IborIndex> hIndex =
93 QL_REQUIRE(!hIndex.empty(), "Could not find ibor index " << underlyingIndex << " in market.");
94 QuantLib::ext::shared_ptr<IborIndex> index = hIndex.currentLink();
95 bool isBma = QuantLib::ext::dynamic_pointer_cast<QuantExt::BMAIndexWrapper>(index) != nullptr;
96 bool isOis = QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndex>(index) != nullptr;
97
98 QL_REQUIRE(floatData->caps().empty() && floatData->floors().empty(),
99 "CapFloor build error, Floating leg section must not have caps and floors");
100
101 if (!floatData->hasSubPeriods() || isOis || isBma) {
102
103
104
105
107 QuantLib::ext::shared_ptr<FloatingLegData> tmpFloatData = QuantLib::ext::make_shared<FloatingLegData>(*floatData);
108 tmpFloatData->floors() =
floors_;
109 tmpFloatData->caps() =
caps_;
110 tmpFloatData->nakedOption() = true;
111 tmpLegData.concreteLegData() = tmpFloatData;
112 legs_.push_back(engineFactory->legBuilder(tmpLegData.legType())
115
116
117
118 qlInstrument =
119 QuantLib::ext::make_shared<QuantLib::Swap>(
legs_, std::vector<bool>{!
floors_.empty() && !
caps_.empty()});
120 if (engineFactory->engineData()->hasProduct("Swap")) {
121 builder = engineFactory->builder("Swap");
122 QuantLib::ext::shared_ptr<SwapEngineBuilderBase> swapBuilder =
123 QuantLib::ext::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
124 QL_REQUIRE(swapBuilder, "No Builder found for Swap " << id());
127 } else {
128 qlInstrument->setPricingEngine(
129 QuantLib::ext::make_shared<DiscountingSwapEngine>(engineFactory->market()->discountCurve(
legData_.
currency())));
130 }
132 } else {
133
134
135
136
137 ALOG(
"CapFloor trade " <<
id() <<
" on sub periods Ibor (index = '" << underlyingIndex
138 << "') built, will ignore sub periods feature");
141
142
145 "The number of floor rates provided does not match the number of schedule periods");
146 }
147
148 if (
caps_.size() > 1) {
150 "The number of cap rates provided does not match the number of schedule periods");
151 }
152
153
156
157 if (
caps_.size() == 1)
159
160
161 qlInstrument = QuantLib::ext::make_shared<QuantLib::CapFloor>(capFloorType,
legs_[0],
caps_,
floors_);
162
163 QuantLib::ext::shared_ptr<CapFloorEngineBuilder> capFloorBuilder =
164 QuantLib::ext::dynamic_pointer_cast<CapFloorEngineBuilder>(builder);
165 qlInstrument->setPricingEngine(capFloorBuilder->engine(underlyingIndex));
167
168 maturity_ = QuantLib::ext::dynamic_pointer_cast<QuantLib::CapFloor>(qlInstrument)->maturityDate();
169 }
170
172 builder = engineFactory->builder("Swap");
173
174 QuantLib::ext::shared_ptr<CMSLegData> cmsData = QuantLib::ext::dynamic_pointer_cast<CMSLegData>(
legData_.
concreteLegData());
175 QL_REQUIRE(cmsData, "Wrong LegType, expected CMS");
176
177 underlyingIndex = cmsData->swapIndex();
178 Handle<SwapIndex> hIndex =
180 QL_REQUIRE(!hIndex.empty(), "Could not find swap index " << underlyingIndex << " in market.");
181
182 QuantLib::ext::shared_ptr<SwapIndex> index = hIndex.currentLink();
183
185 QuantLib::ext::shared_ptr<CMSLegData> tmpFloatData = QuantLib::ext::make_shared<CMSLegData>(*cmsData);
186 tmpFloatData->floors() =
floors_;
187 tmpFloatData->caps() =
caps_;
188 tmpFloatData->nakedOption() = true;
189 tmpLegData.concreteLegData() = tmpFloatData;
190 legs_.push_back(engineFactory->legBuilder(tmpLegData.legType())
193
194
195
196 qlInstrument = QuantLib::ext::make_shared<QuantLib::Swap>(
legs_, std::vector<bool>{!
floors_.empty() && !
caps_.empty()});
197 if (engineFactory->engineData()->hasProduct("Swap")) {
198 builder = engineFactory->builder("Swap");
199 QuantLib::ext::shared_ptr<SwapEngineBuilderBase> swapBuilder =
200 QuantLib::ext::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
201 QL_REQUIRE(swapBuilder, "No Builder found for Swap " << id());
204 } else {
205 qlInstrument->setPricingEngine(
206 QuantLib::ext::make_shared<DiscountingSwapEngine>(engineFactory->market()->discountCurve(
legData_.
currency())));
207 }
209
212 QL_REQUIRE(cmsData, "Wrong LegType, expected DurationAdjustedCmsLegData");
214 auto tmpCmsData = QuantLib::ext::make_shared<DurationAdjustedCmsLegData>(*cmsData);
215 tmpCmsData->floors() =
floors_;
216 tmpCmsData->caps() =
caps_;
217 tmpCmsData->nakedOption() = true;
218 tmpLegData.concreteLegData() = tmpCmsData;
219 legs_.push_back(engineFactory->legBuilder(tmpLegData.legType())
222
223
224
225 qlInstrument = QuantLib::ext::make_shared<QuantLib::Swap>(
legs_, std::vector<bool>{!
floors_.empty() && !
caps_.empty()});
226 if (engineFactory->engineData()->hasProduct("Swap")) {
227 builder = engineFactory->builder("Swap");
228 QuantLib::ext::shared_ptr<SwapEngineBuilderBase> swapBuilder =
229 QuantLib::ext::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
230 QL_REQUIRE(swapBuilder, "No Builder found for Swap " << id());
233 } else {
234 qlInstrument->setPricingEngine(
235 QuantLib::ext::make_shared<DiscountingSwapEngine>(engineFactory->market()->discountCurve(
legData_.
currency())));
236 }
239 builder = engineFactory->builder("Swap");
240 QuantLib::ext::shared_ptr<CMSSpreadLegData> cmsSpreadData =
242 QL_REQUIRE(cmsSpreadData, "Wrong LegType, expected CMSSpread");
244 QuantLib::ext::shared_ptr<CMSSpreadLegData> tmpFloatData = QuantLib::ext::make_shared<CMSSpreadLegData>(*cmsSpreadData);
245 tmpFloatData->floors() =
floors_;
246 tmpFloatData->caps() =
caps_;
247 tmpFloatData->nakedOption() = true;
248 tmpLegData.concreteLegData() = tmpFloatData;
249 legs_.push_back(engineFactory->legBuilder(tmpLegData.legType())
252
253
254
255 qlInstrument = QuantLib::ext::make_shared<QuantLib::Swap>(
legs_, std::vector<bool>{!
floors_.empty() && !
caps_.empty()});
256 if (engineFactory->engineData()->hasProduct("Swap")) {
257 builder = engineFactory->builder("Swap");
258 QuantLib::ext::shared_ptr<SwapEngineBuilderBase> swapBuilder =
259 QuantLib::ext::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
260 QL_REQUIRE(swapBuilder, "No Builder found for Swap " << id());
263 } else {
264 qlInstrument->setPricingEngine(
265 QuantLib::ext::make_shared<DiscountingSwapEngine>(engineFactory->market()->discountCurve(
legData_.
currency())));
266 }
269 DLOG(
"CPI CapFloor Type " << capFloorType <<
" ID " <<
id());
270
271 builder = engineFactory->builder("CpiCapFloor");
272
273 QuantLib::ext::shared_ptr<CPILegData> cpiData = QuantLib::ext::dynamic_pointer_cast<CPILegData>(
legData_.
concreteLegData());
274 QL_REQUIRE(cpiData, "Wrong LegType, expected CPI");
275
276 underlyingIndex = cpiData->index();
277 Handle<ZeroInflationIndex> zeroIndex = engineFactory->market()->zeroInflationIndex(
279
280
281
282
283 Date startDate;
285
286 const string& start = cpiData->startDate();
287 if (schedule.size() < 2) {
288 QL_REQUIRE(!start.empty(), "Only one schedule date, a 'StartDate' must be given.");
290 } else if (!start.empty()) {
291 DLOG(
"Schedule with more than 2 dates was provided. The first schedule date "
292 << io::iso_date(schedule.dates().front()) << " is used as the start date. The 'StartDate' of " << start
293 << " is not used.");
294 startDate = schedule.dates().front();
295 }
296
297 Real baseCPI = cpiData->baseCPI();
298
299 QuantLib::ext::shared_ptr<InflationSwapConvention> cpiSwapConvention = nullptr;
300
301 auto inflationConventions = InstrumentConventions::instance().conventions()->get(
303
304 if (inflationConventions.first)
305 cpiSwapConvention = QuantLib::ext::dynamic_pointer_cast<InflationSwapConvention>(inflationConventions.second);
306
307 Period observationLag;
308 if (cpiData->observationLag().empty()) {
309 QL_REQUIRE(cpiSwapConvention, "observationLag is not specified in legData and couldn't find convention for "
310 << underlyingIndex
311 << ". Please add field to trade xml or add convention");
312 DLOG(
"Build CPI Leg and use observation lag from standard inflationswap convention");
313 observationLag = cpiSwapConvention->observationLag();
314 } else {
315 observationLag =
parsePeriod(cpiData->observationLag());
316 }
317
318 CPI::InterpolationType interpolationMethod;
319 if (cpiData->interpolation().empty()) {
320 QL_REQUIRE(cpiSwapConvention, "observationLag is not specified in legData and couldn't find convention for "
321 << underlyingIndex
322 << ". Please add field to trade xml or add convention");
323 DLOG(
"Build CPI Leg and use observation lag from standard inflationswap convention");
324 interpolationMethod = cpiSwapConvention->interpolated() ? CPI::Linear : CPI::Flat;
325 } else {
327 }
328
329 Calendar cal = zeroIndex->fixingCalendar();
330 BusinessDayConvention conv = Unadjusted;
331
332 QL_REQUIRE(!zeroIndex.empty(), "Zero Inflation Index is empty");
333
335
336
339 "The number of floor rates provided does not match the number of schedule periods");
340 }
341
342 if (
caps_.size() > 1) {
344 "The number of cap rates provided does not match the number of schedule periods");
345 }
346
347
350
351 if (
caps_.size() == 1)
353
354 QuantLib::ext::shared_ptr<CpiCapFloorEngineBuilder> capFloorBuilder =
355 QuantLib::ext::dynamic_pointer_cast<CpiCapFloorEngineBuilder>(builder);
356
357
358 qlInstrument = QuantLib::ext::make_shared<CompositeInstrument>();
360 for (Size i = 0; i <
legs_[0].size(); ++i) {
361 DLOG(
"Create composite " << i);
362 Real nominal, gearing;
363 Date paymentDate;
364 QuantLib::ext::shared_ptr<CPICoupon> coupon = QuantLib::ext::dynamic_pointer_cast<CPICoupon>(
legs_[0][i]);
365 QuantLib::ext::shared_ptr<CPICashFlow> cashflow = QuantLib::ext::dynamic_pointer_cast<CPICashFlow>(
legs_[0][i]);
366 if (coupon) {
367 nominal = coupon->nominal();
368 gearing = coupon->fixedRate() * coupon->accrualPeriod();
369 paymentDate = coupon->date();
370 } else if (cashflow) {
371 nominal = cashflow->notional();
372 gearing = 1.0;
373 paymentDate = cashflow->date();
374 } else {
375 QL_FAIL("Failed to interpret CPI flow");
376 }
377
378 if (capFloorType == QuantLib::CapFloor::Cap || capFloorType == QuantLib::CapFloor::Collar) {
379 QuantLib::ext::shared_ptr<CPICapFloor> capfloor = QuantLib::ext::make_shared<CPICapFloor>(
380 Option::Call, nominal, startDate, baseCPI, paymentDate, cal, conv, cal, conv,
caps_[i], zeroIndex,
381 observationLag, interpolationMethod);
382 capfloor->setPricingEngine(capFloorBuilder->engine(underlyingIndex));
384 QuantLib::ext::dynamic_pointer_cast<QuantLib::CompositeInstrument>(qlInstrument)->add(capfloor, gearing);
386 }
387
388 if (capFloorType == QuantLib::CapFloor::Floor || capFloorType == QuantLib::CapFloor::Collar) {
389
390 Real sign = capFloorType == QuantLib::CapFloor::Floor ? 1.0 : -1.0;
391 QuantLib::ext::shared_ptr<CPICapFloor> capfloor = QuantLib::ext::make_shared<CPICapFloor>(
392 Option::Put, nominal, startDate, baseCPI, paymentDate, cal, conv, cal, conv,
floors_[i], zeroIndex,
393 observationLag, interpolationMethod);
394 capfloor->setPricingEngine(capFloorBuilder->engine(underlyingIndex));
396 QuantLib::ext::dynamic_pointer_cast<QuantLib::CompositeInstrument>(qlInstrument)->add(capfloor, sign * gearing);
398 }
399 }
400
402 builder = engineFactory->builder("YYCapFloor");
403
404 QuantLib::ext::shared_ptr<YoYLegData> yyData = QuantLib::ext::dynamic_pointer_cast<YoYLegData>(
legData_.
concreteLegData());
405 QL_REQUIRE(yyData, "Wrong LegType, expected YY");
406
407 underlyingIndex = yyData->index();
408 Handle<YoYInflationIndex> yoyIndex;
409
410 yoyIndex =
411 engineFactory->market()->yoyInflationIndex(underlyingIndex, builder->configuration(
MarketContext::pricing));
412
413
414
415 if (yoyIndex.empty()) {
416 Handle<ZeroInflationIndex> zeroIndex = engineFactory->market()->zeroInflationIndex(
418 QL_REQUIRE(!zeroIndex.empty(), "Could not find inflation index (of type either zero or yoy) "
419 << underlyingIndex << " in market.");
420 yoyIndex = Handle<YoYInflationIndex>(QuantLib::ext::make_shared<QuantExt::YoYInflationIndexWrapper>(
421 zeroIndex.currentLink(), false));
422 }
423
425
426
429 "The number of floor rates provided does not match the number of schedule periods");
430 }
431
432 if (
caps_.size() > 1) {
434 "The number of cap rates provided does not match the number of schedule periods");
435 }
436
437
440
441 if (
caps_.size() == 1)
443
444
445 if (capFloorType == QuantLib::CapFloor::Cap) {
446 qlInstrument = QuantLib::ext::shared_ptr<YoYInflationCapFloor>(
new YoYInflationCap(
legs_[0],
caps_));
447 } else if (capFloorType == QuantLib::CapFloor::Floor) {
448 qlInstrument = QuantLib::ext::shared_ptr<YoYInflationCapFloor>(
new YoYInflationFloor(
legs_[0],
floors_));
449 } else if (capFloorType == QuantLib::CapFloor::Collar) {
450 qlInstrument = QuantLib::ext::shared_ptr<YoYInflationCapFloor>(
451 new YoYInflationCapFloor(QuantLib::YoYInflationCapFloor::Collar,
legs_[0],
caps_,
floors_));
452 } else {
453 QL_FAIL("unknown YoYInflation cap/floor type");
454 }
455
456 QuantLib::ext::shared_ptr<YoYCapFloorEngineBuilder> capFloorBuilder =
457 QuantLib::ext::dynamic_pointer_cast<YoYCapFloorEngineBuilder>(builder);
458 qlInstrument->setPricingEngine(capFloorBuilder->engine(underlyingIndex));
460
461
462
463 maturity_ = QuantLib::ext::dynamic_pointer_cast<QuantLib::YoYInflationCapFloor>(qlInstrument)->maturityDate();
464 } else {
466 }
467
468
469
470 QL_REQUIRE(
legs_.size() == 1,
"internal error, expected one leg in cap floor builder, got " <<
legs_.size());
471
477
478
479
480 std::vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
481 std::vector<Real> additionalMultipliers;
485
486
488 QuantLib::ext::make_shared<VanillaInstrument>(qlInstrument, multiplier, additionalInstruments, additionalMultipliers);
489
490
492 for (
auto const& l :
legs_)
494
495 Date startDate = Date::maxDate();
496 for (
auto const& l :
legs_) {
497 if (!l.empty()) {
498 startDate = std::min(startDate, l.front()->date());
499 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(l.front());
500 if (coupon)
501 startDate = std::min(startDate, coupon->accrualStartDate());
502 }
503 }
504
506}
const string & currency() const
const ScheduleData & schedule() const
const string & legType() const
QuantLib::ext::shared_ptr< LegAdditionalData > concreteLegData() const
std::vector< bool > legPayers_
std::vector< string > legCurrencies_
std::vector< QuantLib::Leg > legs_
Date addPremiums(std::vector< QuantLib::ext::shared_ptr< Instrument > > &instruments, std::vector< Real > &multipliers, const Real tradeMultiplier, const PremiumData &premiumData, const Real premiumMultiplier, const Currency &tradeCurrency, const QuantLib::ext::shared_ptr< EngineFactory > &factory, const string &configuration)
void setSensitivityTemplate(const EngineBuilder &builder)
RequiredFixings requiredFixings_
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
std::map< std::string, boost::any > additionalData_
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
QuantLib::CPI::InterpolationType parseObservationInterpolation(const std::string &s)
Convert string to observation interpolation.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
#define DLOG(text)
Logging Macro (Level = Debug)
#define ALOG(text)
Logging Macro (Level = Alert)
Real currentNotional(const Leg &leg)
void addToRequiredFixings(const QuantLib::Leg &leg, const QuantLib::ext::shared_ptr< FixingDateGetter > &fixingDateGetter)
Size size(const ValueType &v)
Leg makeYoYLeg(const LegData &data, const QuantLib::ext::shared_ptr< InflationIndex > &index, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, const QuantLib::Date &openEndDateReplacement)
std::string to_string(const LocationInfo &l)
Leg makeCPILeg(const LegData &data, const QuantLib::ext::shared_ptr< ZeroInflationIndex > &index, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, const QuantLib::Date &openEndDateReplacement)
Leg makeIborLeg(const LegData &data, const QuantLib::ext::shared_ptr< IborIndex > &index, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, const bool attachPricer, const QuantLib::Date &openEndDateReplacement)
Schedule makeSchedule(const ScheduleDates &data)