28#include <ql/cashflows/fixedratecoupon.hpp>
29#include <ql/cashflows/floatingratecoupon.hpp>
38 LOG(
"FlexiSwap::build() for id \"" <<
id() <<
"\" called.");
41 additionalData_[
"isdaAssetClass"] = string(
"Interest Rate");
42 additionalData_[
"isdaBaseProduct"] = string(
"Exotic");
43 additionalData_[
"isdaSubProduct"] = string(
"");
44 additionalData_[
"isdaTransaction"] = string(
"");
46 QL_REQUIRE(swap_.size() == 2,
"swap must have 2 legs");
47 QL_REQUIRE(swap_[0].currency() == swap_[1].currency(),
"swap must be single currency");
49 string ccy_str = swap_[0].currency();
52 Size fixedLegIndex, floatingLegIndex;
53 if (swap_[0].legType() ==
"Floating" && swap_[1].legType() ==
"Fixed") {
56 }
else if (swap_[1].legType() ==
"Floating" && swap_[0].legType() ==
"Fixed") {
60 QL_FAIL(
"Invalid leg types " << swap_[0].legType() <<
" + " << swap_[1].legType());
63 QuantLib::ext::shared_ptr<FixedLegData> fixedLegData =
64 QuantLib::ext::dynamic_pointer_cast<FixedLegData>(swap_[fixedLegIndex].concreteLegData());
65 QuantLib::ext::shared_ptr<FloatingLegData> floatingLegData =
66 QuantLib::ext::dynamic_pointer_cast<FloatingLegData>(swap_[floatingLegIndex].concreteLegData());
68 QL_REQUIRE(fixedLegData !=
nullptr,
"expected fixed leg data");
69 QL_REQUIRE(floatingLegData !=
nullptr,
"expected floating leg data");
71 QuantLib::ext::shared_ptr<EngineBuilder> tmp = engineFactory->builder(
"FlexiSwap");
72 auto builder = QuantLib::ext::dynamic_pointer_cast<FlexiSwapBGSEngineBuilderBase>(tmp);
73 QL_REQUIRE(builder,
"No Flexi-Swap Builder found for \"" <<
id() <<
"\"");
78 swap_[fixedLegIndex].notionals(), swap_[fixedLegIndex].notionalDates(),
fixedSchedule, 0.0);
80 swap_[floatingLegIndex].notionals(), swap_[floatingLegIndex].notionalDates(),
floatingSchedule, 0.0);
91 floatingIndex_ = floatingLegData->index();
92 DayCounter fixedDayCounter =
parseDayCounter(swap_[fixedLegIndex].dayCounter());
93 Handle<IborIndex> index =
94 engineFactory->market()->iborIndex(floatingIndex_, builder->configuration(MarketContext::pricing));
95 DayCounter floatingDayCounter =
parseDayCounter(swap_[floatingLegIndex].dayCounter());
97 VanillaSwap::Type
type = swap_[fixedLegIndex].isPayer() ? VanillaSwap::Payer : VanillaSwap::Receiver;
104 QL_REQUIRE(lowerNotionalBounds_.empty() || exerciseDates_.empty(),
105 "can not have lower notional bounds and exercise dates / types / values specified at the same time");
109 if (!lowerNotionalBounds_.empty()) {
110 lowerNotionalBounds =
112 DLOG(
"optionality is given by lower notional bounds");
121 if (!exerciseDates_.empty()) {
122 DLOG(
"optionality is given by exercise dates, types, values");
131 Date previousExerciseDate = Null<Date>();
132 for (Size i = 0; i < exerciseDates_.size(); ++i) {
134 QL_REQUIRE(exerciseValues_[i] > 0.0 ||
close_enough(exerciseValues_[i], 0.0),
135 "exercise value #" << i <<
" (" << exerciseValues_[i] <<
") must be non-negative");
136 QL_REQUIRE(i == 0 || previousExerciseDate < d,
"exercise dates must be strictly increasing, got "
137 << QuantLib::io::iso_date(previousExerciseDate)
138 <<
" and " << QuantLib::io::iso_date(d) <<
" as #" << i
139 <<
" and #" << i + 1);
140 previousExerciseDate = d;
145 DLOG(
"exercise date "
146 << QuantLib::io::iso_date(d)
147 <<
" ignored since there is no whole fixed leg period with accrual start >= exercise date");
151 if (exerciseTypes_[i] ==
"ReductionUpToLowerBound") {
152 for (Size j = exerciseIdx; j < lowerNotionalBounds.size(); ++j) {
153 lowerNotionalBounds[j] = std::min(lowerNotionalBounds[j], exerciseValues_[i]);
155 }
else if (exerciseTypes_[i] ==
"ReductionByAbsoluteAmount" ||
156 exerciseTypes_[i] ==
"ReductionUpToAbsoluteAmount") {
160 for (Size j = exerciseIdx; j < lowerNotionalBounds.size(); ++j) {
161 lowerNotionalBounds[j] = std::max(lowerNotionalBounds[j] - exerciseValues_[i], 0.0);
164 QL_FAIL(
"exercise type '" << exerciseTypes_[i]
165 <<
"' unknown, expected ReductionUpToLowerBound, ReductionByAbsoluteAmount, "
166 "ReductionUpToAbsoluteAmount");
171 DLOG(
"fixedPeriod#,notional,lowerNotionalBound,canBeReduced");
172 for (Size i = 0; i < lowerNotionalBounds.size(); ++i) {
173 DLOG(i <<
"," <<
fixedNominal.at(i) <<
"," << lowerNotionalBounds[i] <<
"," << std::boolalpha
181 auto flexiSwap = QuantLib::ext::make_shared<QuantExt::FlexiSwap>(
186 auto fixLeg = flexiSwap->leg(0);
187 auto fltLeg = flexiSwap->leg(1);
191 bool hasCapsFloors =
false;
192 for (
auto const& k : caps) {
193 if (k != Null<Real>())
194 hasCapsFloors =
true;
196 for (
auto const& k : floors) {
197 if (k != Null<Real>())
198 hasCapsFloors =
true;
201 QuantLib::ext::shared_ptr<EngineBuilder> cfBuilder = engineFactory->builder(
"CapFlooredIborLeg");
202 QL_REQUIRE(cfBuilder,
"No builder found for CapFlooredIborLeg");
203 QuantLib::ext::shared_ptr<CapFlooredIborLegEngineBuilder> cappedFlooredIborBuilder =
204 QuantLib::ext::dynamic_pointer_cast<CapFlooredIborLegEngineBuilder>(cfBuilder);
205 QL_REQUIRE(cappedFlooredIborBuilder !=
nullptr,
"expected CapFlooredIborLegEngineBuilder");
206 QuantLib::ext::shared_ptr<FloatingRateCouponPricer> couponPricer =
207 cappedFlooredIborBuilder->engine(IndexNameTranslator::instance().oreName(index->name()));
208 QuantLib::setCouponPricer(fltLeg, couponPricer);
212 std::vector<Date> expiryDates;
214 Date today = Settings::instance().evaluationDate();
215 Size legRatio = fltLeg.size() / fixLeg.size();
216 for (Size i = 0; i < fltLeg.size(); ++i) {
217 auto fltcpn = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(fltLeg[i]);
218 if (fltcpn !=
nullptr && fltcpn->fixingDate() > today && i % legRatio == 0) {
219 expiryDates.push_back(fltcpn->fixingDate());
220 auto fixcpn = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(fixLeg[i / legRatio]);
221 QL_REQUIRE(fixcpn !=
nullptr,
"FlexiSwap Builder: expected fixed rate coupon");
222 strikes.push_back(fixcpn->rate() - fltcpn->spread());
228 flexiSwap->setPricingEngine(
229 builder->engine(
id(),
"", index.empty() ? ccy_str : IndexNameTranslator::instance().oreName(index->name()),
230 expiryDates, flexiSwap->maturityDate(),
strikes));
231 setSensitivityTemplate(*builder);
234 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(flexiSwap);
236 npvCurrency_ = ccy_str;
238 notionalCurrency_ = ccy_str;
239 legCurrencies_ = vector<string>(2, ccy_str);
240 legs_ = {fixLeg, fltLeg};
241 legPayers_ = {swap_[fixedLegIndex].isPayer(), swap_[floatingLegIndex].isPayer()};
242 maturity_ = flexiSwap->maturityDate();
247 Trade::fromXML(node);
248 XMLNode* swapNode = XMLUtils::getChildNode(node,
"FlexiSwapData");
249 QL_REQUIRE(swapNode,
"FlexiSwap::fromXML(): FlexiSwapData not found");
251 lowerNotionalBounds_ = XMLUtils::getChildrenValuesWithAttributes<Real>(
252 swapNode,
"LowerNotionalBounds",
"Notional",
"startDate", lowerNotionalBoundsDates_, &
parseReal);
254 noticePeriod_ = noticeCalendar_ = noticeConvention_ =
"";
255 exerciseDates_.clear();
256 exerciseTypes_.clear();
257 exerciseValues_.clear();
258 XMLNode* prepayNode = XMLUtils::getChildNode(swapNode,
"Prepayment");
260 noticePeriod_ = XMLUtils::getChildValue(prepayNode,
"NoticePeriod",
false);
261 noticeCalendar_ = XMLUtils::getChildValue(prepayNode,
"NoticeCalendar",
false);
262 noticeConvention_ = XMLUtils::getChildValue(prepayNode,
"NoticeConvention",
false);
263 XMLNode* optionsNode = XMLUtils::getChildNode(prepayNode,
"PrepaymentOptions");
265 auto prepayOptionNodes = XMLUtils::getChildrenNodes(optionsNode,
"PrepaymentOption");
266 for (
auto const n : prepayOptionNodes) {
267 exerciseDates_.push_back(XMLUtils::getChildValue(n,
"ExerciseDate",
true));
268 exerciseTypes_.push_back(XMLUtils::getChildValue(n,
"Type",
true));
269 exerciseValues_.push_back(
parseReal(XMLUtils::getChildValue(n,
"Value",
true)));
274 optionLongShort_ = XMLUtils::getChildValue(swapNode,
"OptionLongShort",
true);
277 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(swapNode,
"LegData");
278 for (Size i = 0; i < nodes.size(); i++) {
286 XMLNode* node = Trade::toXML(doc);
288 XMLUtils::appendNode(node, swapNode);
290 if (!lowerNotionalBounds_.empty()) {
291 XMLUtils::addChildrenWithOptionalAttributes(doc, swapNode,
"LowerNotionalBounds",
"Notional",
292 lowerNotionalBounds_,
"startDate", lowerNotionalBoundsDates_);
295 if (!exerciseDates_.empty()) {
297 XMLUtils::appendNode(swapNode, prepayNode);
298 if (!noticePeriod_.empty())
299 XMLUtils::addChild(doc, prepayNode,
"NoticePeriod", noticePeriod_);
300 if (!noticeCalendar_.empty())
301 XMLUtils::addChild(doc, prepayNode,
"NoticeCalendar", noticeCalendar_);
302 if (!noticeConvention_.empty())
303 XMLUtils::addChild(doc, prepayNode,
"NoticeConvention", noticeConvention_);
305 XMLUtils::appendNode(prepayNode, optionsNode);
306 for (Size i = 0; i < exerciseDates_.size(); ++i) {
308 XMLUtils::appendNode(optionsNode, exerciseNode);
309 XMLUtils::addChild(doc, exerciseNode,
"ExerciseDate", exerciseDates_.at(i));
310 XMLUtils::addChild(doc, exerciseNode,
"Type", exerciseTypes_.at(i));
311 XMLUtils::addChild(doc, exerciseNode,
"Value", exerciseValues_.at(i));
315 XMLUtils::addChild(doc, swapNode,
"OptionLongShort", optionLongShort_);
317 for (Size i = 0; i < swap_.size(); i++)
318 XMLUtils::appendNode(swapNode, swap_[i].toXML(doc));
builder that returns an engine to price capped floored ibor legs
const std::vector< bool > & notionalCanBeDecreased() const
const std::vector< Real > & fixedNominal() const
BusinessDayConvention paymentConvention() const
const Schedule & fixedSchedule() const
const Schedule & floatingSchedule() const
VanillaSwap::Type type() const
const std::vector< Real > & fixedRate() const
void build(const QuantLib::ext::shared_ptr< ore::data::EngineFactory > &) override
Serializable object holding leg data.
virtual void fromXML(XMLNode *node) override
Small XML Document wrapper class.
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Logic for calculating required fixing dates on legs.
Flexi-Swap data model and serialization.
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.
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Real parseReal(const string &s)
Convert text to Real.
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
translates between QuantLib::Index::name() and ORE names
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
vector< T > buildScheduledVectorNormalised(const vector< T > &values, const vector< string > &dates, const Schedule &schedule, const T &defaultValue, const bool checkAllValuesAppearInResult=false)
Real currentNotional(const Leg &leg)
void addToRequiredFixings(const QuantLib::Leg &leg, const QuantLib::ext::shared_ptr< FixingDateGetter > &fixingDateGetter)
Schedule makeSchedule(const ScheduleDates &data)
Serializable Credit Default Swap.