Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
knockoutswap.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2022 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17 */
18
21
26
27#include <boost/lexical_cast.hpp>
28
29namespace ore {
30namespace data {
31
32void KnockOutSwap::build(const QuantLib::ext::shared_ptr<EngineFactory>& factory) {
33
34 clear();
35
36 QL_REQUIRE(legData_.size() == 2, "Expected exactly two legs, got " << legData_.size());
37
38 std::set<std::string> legTypes;
39 for (auto const& ld : legData_) {
40 legTypes.insert(ld.legType());
41 }
42
43 QL_REQUIRE(legTypes.size() == 2 && *legTypes.begin() == "Fixed" && *std::next(legTypes.begin(), 1) == "Floating",
44 "Expected one 'Floating' and one 'Fixed' type");
45
46 const LegData& fixedLegData = legData_[0].legType() == "Fixed" ? legData_[0] : legData_[1];
47 const LegData& floatLegData = legData_[0].legType() == "Fixed" ? legData_[1] : legData_[0];
48
49 auto floatAddData = QuantLib::ext::dynamic_pointer_cast<FloatingLegData>(floatLegData.concreteLegData());
50 auto fixedAddData = QuantLib::ext::dynamic_pointer_cast<FixedLegData>(fixedLegData.concreteLegData());
51
52 QL_REQUIRE(floatAddData, "Internal error: could not cast to float additional data");
53 QL_REQUIRE(fixedAddData, "Internal error: could not cast to fixed additional data");
54
55 QL_REQUIRE(fixedLegData.isPayer() != floatLegData.isPayer(), "Expected one payer and one receiver leg");
56
57 if (fixedLegData.isPayer())
58 numbers_.emplace_back("Number", "Payer", "-1");
59 else
60 numbers_.emplace_back("Number", "Payer", "1");
61
62 QL_REQUIRE(fixedLegData.notionals().size() == 1,
63 "Expected one notional on fixed leg, got " << fixedLegData.notionals().size());
64 QL_REQUIRE(floatLegData.notionals().size() == 1,
65 "Expected one notional on floating leg, got " << floatLegData.notionals().size());
66 QL_REQUIRE(QuantLib::close_enough(fixedLegData.notionals().front(), floatLegData.notionals().front()),
67 "Expected same notional on fixed and floating leg, got " << fixedLegData.notionals().front() << " and "
68 << floatLegData.notionals().front());
69
70 QL_REQUIRE(fixedAddData->rates().size() == 1,
71 "Expected one rate on fixed leg, got " << fixedAddData->rates().size());
72 QL_REQUIRE(floatAddData->spreads().size() <= 1,
73 "Expected at most one spread on floating leg, got " << floatAddData->spreads().size());
74 QL_REQUIRE(floatAddData->gearings().size() <= 1,
75 "Expected at most one gearing on floating leg, got " << floatAddData->gearings().size());
76
77 numbers_.emplace_back("Number", "Notional", std::to_string(fixedLegData.notionals().front()));
78 numbers_.emplace_back("Number", "FixedRate", std::to_string(fixedAddData->rates().front()));
79 numbers_.emplace_back("Number", "FloatMargin",
80 floatAddData->spreads().empty() ? "0.0" : std::to_string(floatAddData->spreads().front()));
81 numbers_.emplace_back("Number", "FloatGearing",
82 floatAddData->gearings().empty() ? "1.0" : std::to_string(floatAddData->gearings().front()));
83
84 auto index = parseIborIndex(floatAddData->index());
85 Size fixingShift = floatAddData->fixingDays() == Null<Size>() ? index->fixingDays() : floatAddData->fixingDays();
86 std::string fixingCalendar = index->fixingCalendar().name();
87 events_.emplace_back("FloatFixingSchedule", "FloatSchedule", "-" + std::to_string(fixingShift) + "D", fixingCalendar, "P");
88
89 indices_.emplace_back("Index", "FloatIndex", floatAddData->index());
90
91 std::string floatDayCounter =
92 floatLegData.dayCounter().empty() ? index->dayCounter().name() : floatLegData.dayCounter();
93 std::string fixedDayCounter = fixedLegData.dayCounter().empty() ? floatDayCounter : fixedLegData.dayCounter();
94
95 daycounters_.emplace_back("Daycounter", "FixedDayCounter", fixedDayCounter);
96 daycounters_.emplace_back("Daycounter", "FloatDayCounter", floatDayCounter);
97
98 events_.emplace_back("FixedSchedule", fixedLegData.schedule());
99 events_.emplace_back("FloatSchedule", floatLegData.schedule());
100
101 QL_REQUIRE(!fixedLegData.currency().empty() && fixedLegData.currency() == floatLegData.currency(),
102 "Both legs must have the same currency, got '" << fixedLegData.currency() << "' on the fixed leg and '"
103 << floatLegData.currency() << "' on the floating leg.");
104 QL_REQUIRE(fixedLegData.currency() == index->currency().code(),
105 "Leg currency '" << fixedLegData.currency() << "' must match float index currency '"
106 << index->currency().code() << "' of index '" << index->name() << "'");
107
108 currencies_.emplace_back("Currency", "PayCurrency", fixedLegData.currency());
109
110 QL_REQUIRE(barrierData_.style().empty() || barrierData_.style() == "European",
111 "Expected European barrier style, got '" << barrierData_.style() << "'");
112
113 auto barrierType = parseBarrierType(barrierData_.type());
114 if (barrierType == QuantLib::Barrier::DownOut)
115 numbers_.emplace_back("Number", "KnockOutType", "3");
116 else if (barrierType == QuantLib::Barrier::UpOut)
117 numbers_.emplace_back("Number", "KnockOutType", "4");
118 else {
119 QL_FAIL("Expected BarrierType 'DownAndOut' or 'UpAndOut', got '" << barrierData_.type());
120 }
121
122 QL_REQUIRE(barrierData_.levels().size() == 1, "Expected exactly one barrier level");
123 QL_REQUIRE(barrierData_.levels().front().value() != Null<Real>(), "No barrier level specified.");
124
125 numbers_.emplace_back("Number", "KnockOutLevel", std::to_string(barrierData_.levels().front().value()));
126
127 events_.emplace_back("BarrierStartDate", barrierStartDate_);
128
129 // set product tag
130
131 productTag_ = "SingleUnderlyingIrOption";
132
133 // set script
134
135 bool isIborBased = QuantLib::ext::dynamic_pointer_cast<QuantLib::OvernightIndex>(index) == nullptr;
136
137 // clang-format off
138
139 // FloatFixingSchedule has one date more than needed at the back because it is derived from FloatSchedule
140 // there are two variants of the float payoff scripting depending on isIborBased
141
142 std::string mc_script = std::string(
143 "REQUIRE KnockOutType == 3 OR KnockOutType == 4;\n"
144 "NUMBER Alive[SIZE(FloatFixingSchedule)], aliveInd, lastFixedIndex, lastFloatIndex, d, j, fix;\n"
145
146 "aliveInd = 1;\n"
147
148 "FOR d IN (1, SIZE(FloatFixingSchedule), 1) DO\n"
149
150 " FOR j IN (lastFixedIndex + 1, SIZE(FixedSchedule) - 1, 1) DO\n"
151 " IF FixedSchedule[j] < FloatFixingSchedule[d] OR d == SIZE(FloatFixingSchedule) THEN\n"
152 " value = value + LOGPAY( Payer * aliveInd * Notional * FixedRate * dcf( FixedDayCounter, FixedSchedule[j], FixedSchedule[j+1]),\n"
153 " FixedSchedule[j], FixedSchedule[j+1], PayCurrency, 1, FixedLegCoupon );\n"
154 " lastFixedIndex = j;\n"
155 " END;\n"
156 " END;\n"
157
158 " FOR j IN (lastFloatIndex + 1, SIZE(FloatSchedule) - 1, 1) DO\n"
159 " IF FloatSchedule[j] < FloatFixingSchedule[d] OR d == SIZE(FloatFixingSchedule) THEN\n"
160 " value = value + LOGPAY( (-Payer) * aliveInd * Notional *\n") + std::string(
161 isIborBased ?
162 " ( FloatGearing * FloatIndex(FloatFixingSchedule[j]) + FloatMargin)\n"
163 :
164 " FWDCOMP(FloatIndex, FloatFixingSchedule[j], FloatSchedule[j], FloatSchedule[j+1], FloatMargin, FloatGearing)\n") + std::string(
165
166 " * dcf( FloatDayCounter, FloatSchedule[j], FloatSchedule[j+1]),\n"
167 " FloatFixingSchedule[j], FloatSchedule[j+1], PayCurrency, 2, FloatingLegCoupon );\n"
168 " lastFloatIndex = j;\n"
169 " END;\n"
170 " END;\n"
171
172 " IF d < SIZE(FloatFixingSchedule) THEN\n"
173 " fix = FloatIndex(FloatFixingSchedule[d]);\n"
174 " IF FloatFixingSchedule[d] >= BarrierStartDate AND\n"
175 " {{KnockOutType == 3 AND fix <= KnockOutLevel} OR\n"
176 " {KnockOutType == 4 AND fix >= KnockOutLevel}} THEN\n"
177 " aliveInd = 0;\n"
178 " END;\n"
179 " Alive[d] = aliveInd;\n"
180 " END;\n"
181
182 "END;\n");
183
184 // clang-format on
185
187 mc_script, "value", {{"currentNotional", "Notional"}, {"notionalCurrency", "PayCurrency"}, {"Alive", "Alive"}},
188 {}, {}, {}, {});
189
190 // build trade
191
192 ScriptedTrade::build(factory);
193}
194
196 Trade::fromXML(node);
197 XMLNode* dataNode = XMLUtils::getChildNode(node, tradeType() + "Data");
198 QL_REQUIRE(dataNode, tradeType() + "Data node not found");
199
200 barrierData_.fromXML(XMLUtils::getChildNode(dataNode, "BarrierData"));
201 barrierStartDate_ = XMLUtils::getChildValue(dataNode, "BarrierStartDate", true);
202
203 legData_.clear();
204 for (auto const n : XMLUtils::getChildrenNodes(dataNode, "LegData")) {
205 legData_.push_back(ore::data::LegData());
206 legData_.back().fromXML(n);
207 }
208}
209
211 XMLNode* node = Trade::toXML(doc);
212 XMLNode* dataNode = doc.allocNode(tradeType() + "Data");
214 XMLUtils::addChild(doc, dataNode, "BarrierStartDate", barrierStartDate_);
215 for (auto& ld : legData_) {
216 XMLUtils::appendNode(dataNode, ld.toXML(doc));
217 }
218 XMLUtils::appendNode(node, dataNode);
219 return node;
220}
221
222} // namespace data
223} // namespace ore
const std::string & type() const
Definition: barrierdata.hpp:46
virtual void fromXML(ore::data::XMLNode *node) override
Definition: barrierdata.cpp:25
virtual ore::data::XMLNode * toXML(ore::data::XMLDocument &doc) const override
Definition: barrierdata.cpp:49
std::vector< ore::data::TradeBarrier > levels() const
Definition: barrierdata.hpp:50
const std::string & style() const
Definition: barrierdata.hpp:51
std::vector< LegData > legData_
void fromXML(XMLNode *node) override
XMLNode * toXML(XMLDocument &doc) const override
std::string barrierStartDate_
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Serializable object holding leg data.
Definition: legdata.hpp:844
const string & currency() const
Definition: legdata.hpp:873
const ScheduleData & schedule() const
Definition: legdata.hpp:874
bool isPayer() const
Definition: legdata.hpp:872
const string & dayCounter() const
Definition: legdata.hpp:877
QuantLib::ext::shared_ptr< LegAdditionalData > concreteLegData() const
Definition: legdata.hpp:891
const vector< double > & notionals() const
Definition: legdata.hpp:875
std::vector< ScriptedTradeValueTypeData > daycounters_
std::vector< ScriptedTradeEventData > events_
std::vector< ScriptedTradeValueTypeData > currencies_
std::vector< ScriptedTradeValueTypeData > indices_
std::vector< ScriptedTradeValueTypeData > numbers_
std::map< std::string, ScriptedTradeScriptData > script_
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
virtual void fromXML(XMLNode *node) override
Definition: trade.cpp:34
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: trade.cpp:46
const string & tradeType() const
Definition: trade.hpp:133
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
static vector< XMLNode * > getChildrenNodes(XMLNode *node, const string &name)
Returns all the children with a given name.
Definition: xmlutils.cpp:428
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
Definition: xmlutils.cpp:277
static XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
Definition: xmlutils.cpp:181
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
Barrier::Type parseBarrierType(const std::string &s)
Convert std::string to QuantLib::BarrierType.
Definition: parsers.cpp:1042
knock out swap wrapper for scripted trade
leg data model and serialization
@ data
Definition: log.hpp:77
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
some utility functions
string conversion utilities