Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxeuropeanbarrieroption.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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
19#include <boost/make_shared.hpp>
28#include <ql/errors.hpp>
29#include <ql/exercise.hpp>
30#include <ql/instruments/barriertype.hpp>
31#include <ql/instruments/compositeinstrument.hpp>
32#include <ql/instruments/vanillaoption.hpp>
34
35using namespace QuantLib;
36using namespace QuantExt;
37
38namespace ore {
39namespace data {
40
41void FxEuropeanBarrierOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
42
43 // ISDA taxonomy
44 additionalData_["isdaAssetClass"] = string("Foreign Exchange");
45 additionalData_["isdaBaseProduct"] = string("Simple Exotic");
46 additionalData_["isdaSubProduct"] = string("Barrier");
47 additionalData_["isdaTransaction"] = string("");
48
49 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
50
51 // Only European Single Barrier supported for now
52 QL_REQUIRE(option_.style() == "European", "Option Style unknown: " << option_.style());
53 QL_REQUIRE(option_.exerciseDates().size() == 1, "Invalid number of exercise dates");
54 QL_REQUIRE(barrier_.levels().size() == 1, "Invalid number of barrier levels");
55 QL_REQUIRE(barrier_.style().empty() || barrier_.style() == "European", "Only european barrier style suppported");
56 QL_REQUIRE(tradeActions().empty(), "TradeActions not supported for FxEuropeanBarrierOption");
57
58 Currency boughtCcy = parseCurrency(boughtCurrency_);
59 Currency soldCcy = parseCurrency(soldCurrency_);
60 Real level = barrier_.levels()[0].value();
61 Real rebate = barrier_.rebate();
62 QL_REQUIRE(rebate >= 0, "Rebate must be non-negative");
63
64 // Replicate the payoff of European Barrier Option (with strike K and barrier B) using combinations of options
65
66 // Call
67 // Up
68 // In
69 // Long Up&Out Digital Option with barrier B payoff rebate
70 // B > K
71 // Long European Call Option with strike B
72 // Long Up&In Digital Option with barrier B payoff B - K
73 // B <= K
74 // Long European Call Option with strike K
75 // Out
76 // Long Up&In Digital Option with barrier B payoff rebate
77 // B > K
78 // Long European Call Option with strike K
79 // Short European Call Option with strike B
80 // Short Up&In Digital Option with barrier B payoff B - K
81 // B <= K
82 // 0
83 // Down
84 // In
85 // Long Down&Out Digital Option with barrier B payoff rebate
86 // B > K
87 // Long European Call Option with strike K
88 // B <= K
89 // 0
90 // Out
91 // Long Down&In Digital Option with barrier B payoff rebate
92 // B > K
93 // Long European Call Option with strike B
94 // Long Down&Out Digital Option with barrier B payoff B - K
95 // B <= K
96 // Long European Call Option with strike K
97
98 // Put
99 // Up
100 // In
101 // Long Up&Out Digital Option with barrier B payoff rebate
102 // B > K
103 // 0
104 // B <= K
105 // Long European Put Option with strike K
106 // Short European Put Option with strike B
107 // Short Up&Out Digital Option with barrier B payoff K - B
108 // Out
109 // Long Up&In Digital Option with barrier B payoff rebate
110 // B > K
111 // Long European Put Option with strike K
112 // B <= K
113 // Long European Put Option with strike B
114 // Long Up&Out Digital Option with barrier B payoff K - B
115 // Down
116 // In
117 // Long Down&Out Digital Option with barrier B payoff rebate
118 // B > K
119 // Long European Put Option with strike K
120 // B <= K
121 // Long European Put Option with strike B
122 // Long Down&In Digital Option with barrier B payoff K - B
123 // Out
124 // Long Down&In Digital Option with barrier B payoff rebate
125 // B > K
126 // 0
127 // B <= K
128 // Long European Put Option with strike K
129
130 Real strike = this->strike();
131 Option::Type type = parseOptionType(option_.callPut());
132
133 // Exercise
134 Date expiryDate = parseDate(option_.exerciseDates().front());
135 Date paymentDate = expiryDate;
136
137 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<EuropeanExercise>(expiryDate);
138
139 const boost::optional<OptionPaymentData>& opd = option_.paymentData();
140 if (opd) {
141 if (opd->rulesBased()) {
142 const Calendar& cal = opd->calendar();
143 QL_REQUIRE(cal != Calendar(), "Need a non-empty calendar for rules based payment date.");
144 paymentDate = cal.advance(expiryDate, opd->lag(), Days, opd->convention());
145 } else {
146 const vector<Date>& dates = opd->dates();
147 QL_REQUIRE(dates.size() == 1, "Need exactly one payment date for cash settled European option.");
148 paymentDate = dates[0];
149 }
150 QL_REQUIRE(paymentDate >= expiryDate, "Payment date must be greater than or equal to expiry date.");
151 }
152
153 // delayed pay date is only affecting the maturity
154 maturity_ = std::max({option_.premiumData().latestPremiumDate(), paymentDate});
155
156 QuantLib::ext::shared_ptr<Instrument> digital;
157 QuantLib::ext::shared_ptr<Instrument> vanillaK;
158 QuantLib::ext::shared_ptr<Instrument> vanillaB;
159 QuantLib::ext::shared_ptr<Instrument> rebateInstrument;
160
161 bool exercised = false;
162 Real exercisePrice = Null<Real>();
163 Barrier::Type barrierType = parseBarrierType(barrier_.type());
164
165 Option::Type rebateType;
166 if (barrierType == Barrier::Type::UpIn || barrierType == Barrier::Type::DownOut) {
167 // Payoff - Up&Out / Down&In Digital Option with barrier B payoff rebate
168 rebateType = Option::Put;
169 } else {
170 // Payoff - Up&In / Down&Out Digital Option with barrier B payoff rebate
171 rebateType = Option::Call;
172 }
173
174 if (paymentDate > expiryDate) {
175
176 // Has the option been marked as exercised
177 const boost::optional<OptionExerciseData>& oed = option_.exerciseData();
178 if (oed) {
179 QL_REQUIRE(oed->date() == expiryDate, "The supplied exercise date ("
180 << io::iso_date(oed->date())
181 << ") should equal the option's expiry date ("
182 << io::iso_date(expiryDate) << ").");
183 exercised = true;
184 exercisePrice = oed->price();
185 }
186
187 QuantLib::ext::shared_ptr<FxIndex> fxIndex;
189 QL_REQUIRE(!fxIndex_.empty(), "FX european barrier option trade with delay payment "
190 << id() << ": the FXIndex node needs to be populated.");
191 fxIndex = buildFxIndex(fxIndex_, soldCcy.code(), boughtCcy.code(), engineFactory->market(),
192 engineFactory->configuration(MarketContext::pricing));
193 requiredFixings_.addFixingDate(expiryDate, fxIndex_, paymentDate);
194 }
195
196 vanillaK = QuantLib::ext::make_shared<CashSettledEuropeanOption>(
197 type, strike, expiryDate, paymentDate, option_.isAutomaticExercise(), fxIndex, exercised, exercisePrice);
198 vanillaB = QuantLib::ext::make_shared<CashSettledEuropeanOption>(
199 type, level, expiryDate, paymentDate, option_.isAutomaticExercise(), fxIndex, exercised, exercisePrice);
200 digital = QuantLib::ext::make_shared<CashSettledEuropeanOption>(type, level, fabs(level - strike), expiryDate,
201 paymentDate, option_.isAutomaticExercise(), fxIndex,
202 exercised, exercisePrice);
203 rebateInstrument = QuantLib::ext::make_shared<CashSettledEuropeanOption>(rebateType, level, rebate, expiryDate,
204 paymentDate, option_.isAutomaticExercise(), fxIndex,
205 exercised, exercisePrice);
206 } else {
207 // Payoff - European Option with strike K
208 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoffVanillaK(new PlainVanillaPayoff(type, strike));
209 // Payoff - European Option with strike B
210 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoffVanillaB(new PlainVanillaPayoff(type, level));
211 // Payoff - Digital Option with barrier B payoff abs(B - K)
212 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoffDigital(new CashOrNothingPayoff(type, level, fabs(level - strike)));
213 QuantLib::ext::shared_ptr<StrikedTypePayoff> rebatePayoff(new CashOrNothingPayoff(rebateType, level, rebate));
214
215 vanillaK = QuantLib::ext::make_shared<VanillaOption>(payoffVanillaK, exercise);
216 vanillaB = QuantLib::ext::make_shared<VanillaOption>(payoffVanillaB, exercise);
217 digital = QuantLib::ext::make_shared<VanillaOption>(payoffDigital, exercise);
218 rebateInstrument = QuantLib::ext::make_shared<VanillaOption>(rebatePayoff, exercise);
219 }
220
221 // This is for when/if a PayoffCurrency is added to the instrument,
222 // which would require flipping the underlying currency pair
223 const bool flipResults = false;
224
225 // set pricing engines
226 QuantLib::ext::shared_ptr<EngineBuilder> builder;
227 QuantLib::ext::shared_ptr<EngineBuilder> digitalBuilder;
228 QuantLib::ext::shared_ptr<VanillaOptionEngineBuilder> fxOptBuilder;
229
230 if (paymentDate > expiryDate) {
231 builder = engineFactory->builder("FxOptionEuropeanCS");
232 QL_REQUIRE(builder, "No builder found for FxOptionEuropeanCS");
233 fxOptBuilder = QuantLib::ext::dynamic_pointer_cast<FxEuropeanCSOptionEngineBuilder>(builder);
234
235 digitalBuilder = engineFactory->builder("FxDigitalOptionEuropeanCS");
236 QL_REQUIRE(digitalBuilder, "No builder found for FxDigitalOptionEuropeanCS");
237 auto fxDigitalOptBuilder = QuantLib::ext::dynamic_pointer_cast<FxDigitalCSOptionEngineBuilder>(digitalBuilder);
238 digital->setPricingEngine(fxDigitalOptBuilder->engine(boughtCcy, soldCcy));
239 rebateInstrument->setPricingEngine(fxDigitalOptBuilder->engine(boughtCcy, soldCcy));
240 setSensitivityTemplate(*fxDigitalOptBuilder);
241 } else {
242 builder = engineFactory->builder("FxOption");
243 QL_REQUIRE(builder, "No builder found for FxOption");
244 fxOptBuilder = QuantLib::ext::dynamic_pointer_cast<FxEuropeanOptionEngineBuilder>(builder);
245
246 digitalBuilder = engineFactory->builder("FxDigitalOption");
247 QL_REQUIRE(digitalBuilder, "No builder found for FxDigitalOption");
248 auto fxDigitalOptBuilder = QuantLib::ext::dynamic_pointer_cast<FxDigitalOptionEngineBuilder>(digitalBuilder);
249 digital->setPricingEngine(fxDigitalOptBuilder->engine(boughtCcy, soldCcy, flipResults));
250 rebateInstrument->setPricingEngine(fxDigitalOptBuilder->engine(boughtCcy, soldCcy, flipResults));
251 setSensitivityTemplate(*fxDigitalOptBuilder);
252 }
253
254 vanillaK->setPricingEngine(fxOptBuilder->engine(boughtCcy, soldCcy, paymentDate));
255 vanillaB->setPricingEngine(fxOptBuilder->engine(boughtCcy, soldCcy, paymentDate));
256 setSensitivityTemplate(*fxOptBuilder);
257
258 QuantLib::ext::shared_ptr<CompositeInstrument> qlInstrument = QuantLib::ext::make_shared<CompositeInstrument>();
259 qlInstrument->add(rebateInstrument);
260 if (type == Option::Call) {
261 if (barrierType == Barrier::Type::UpIn || barrierType == Barrier::Type::DownOut) {
262 if (level > strike) {
263 qlInstrument->add(vanillaB);
264 qlInstrument->add(digital);
265 } else {
266 qlInstrument->add(vanillaK);
267 }
268 } else if (barrierType == Barrier::Type::UpOut || barrierType == Barrier::Type::DownIn) {
269 if (level > strike) {
270 qlInstrument->add(vanillaK);
271 qlInstrument->add(vanillaB, -1);
272 qlInstrument->add(digital, -1);
273 } else {
274 // empty
275 }
276 } else {
277 QL_FAIL("Unknown Barrier Type: " << barrierType);
278 }
279 } else if (type == Option::Put) {
280 if (barrierType == Barrier::Type::UpIn || barrierType == Barrier::Type::DownOut) {
281 if (level > strike) {
282 // empty
283 } else {
284 qlInstrument->add(vanillaK);
285 qlInstrument->add(vanillaB, -1);
286 qlInstrument->add(digital, -1);
287 }
288 } else if (barrierType == Barrier::Type::UpOut || barrierType == Barrier::Type::DownIn) {
289 if (level > strike) {
290 qlInstrument->add(vanillaK);
291 } else {
292 qlInstrument->add(vanillaB);
293 qlInstrument->add(digital);
294 }
295 } else {
296 QL_FAIL("Unknown Barrier Type: " << barrierType);
297 }
298 }
299
300 // Add additional premium payments
301 Position::Type positionType = parsePositionType(option_.longShort());
302 Real bsInd = (positionType == QuantLib::Position::Long ? 1.0 : -1.0);
303 Real mult = boughtAmount_ * bsInd;
304
305 std::vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
306 std::vector<Real> additionalMultipliers;
307 addPremiums(additionalInstruments, additionalMultipliers, mult, option_.premiumData(), -bsInd, soldCcy,
308 engineFactory, fxOptBuilder->configuration(MarketContext::pricing));
309
310 instrument_ = QuantLib::ext::shared_ptr<InstrumentWrapper>(
311 new VanillaInstrument(qlInstrument, mult, additionalInstruments, additionalMultipliers));
312
313 npvCurrency_ = soldCurrency_; // sold is the domestic
316
317 additionalData_["boughtCurrency"] = boughtCurrency_;
318 additionalData_["boughtAmount"] = boughtAmount_;
319 additionalData_["soldCurrency"] = soldCurrency_;
320 additionalData_["soldAmount"] = soldAmount_;
321 if (!fxIndex_.empty())
322 additionalData_["FXIndex"] = fxIndex_;
323}
324
326 Trade::fromXML(node);
327 XMLNode* fxNode = XMLUtils::getChildNode(node, "FxEuropeanBarrierOptionData");
328 QL_REQUIRE(fxNode, "No FxEuropeanBarrierOptionData Node");
329 option_.fromXML(XMLUtils::getChildNode(fxNode, "OptionData"));
330 barrier_.fromXML(XMLUtils::getChildNode(fxNode, "BarrierData"));
331 boughtCurrency_ = XMLUtils::getChildValue(fxNode, "BoughtCurrency", true);
332 soldCurrency_ = XMLUtils::getChildValue(fxNode, "SoldCurrency", true);
333 boughtAmount_ = XMLUtils::getChildValueAsDouble(fxNode, "BoughtAmount", true);
334 soldAmount_ = XMLUtils::getChildValueAsDouble(fxNode, "SoldAmount", true);
335 fxIndex_ = XMLUtils::getChildValue(fxNode, "FXIndex", false, "");
336}
337
339 XMLNode* node = Trade::toXML(doc);
340 XMLNode* fxNode = doc.allocNode("FxEuropeanBarrierOptionData");
341 XMLUtils::appendNode(node, fxNode);
342
343 XMLUtils::appendNode(fxNode, option_.toXML(doc));
344 XMLUtils::appendNode(fxNode, barrier_.toXML(doc));
345 XMLUtils::addChild(doc, fxNode, "BoughtCurrency", boughtCurrency_);
346 XMLUtils::addChild(doc, fxNode, "BoughtAmount", boughtAmount_);
347 XMLUtils::addChild(doc, fxNode, "SoldCurrency", soldCurrency_);
348 XMLUtils::addChild(doc, fxNode, "SoldAmount", soldAmount_);
349
350 if (!fxIndex_.empty())
351 XMLUtils::addChild(doc, fxNode, "FXIndex", fxIndex_);
352
353 return node;
354}
355
357 return soldAmount_ / boughtAmount_;
358}
359} // namespace data
360} // namespace ore
Engine builder for FX Options.
const std::string & type() const
Definition: barrierdata.hpp:46
virtual void fromXML(ore::data::XMLNode *node) override
Definition: barrierdata.cpp:25
double rebate() const
Definition: barrierdata.hpp:47
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
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
std::string fxIndex_
If the option has automatic exercise (i.e. cash settled after maturity), need an FX index for settlem...
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
const string & callPut() const
Definition: optiondata.hpp:71
const string & longShort() const
Definition: optiondata.hpp:70
const string & style() const
Definition: optiondata.hpp:74
virtual void fromXML(XMLNode *node) override
Definition: optiondata.cpp:32
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: optiondata.cpp:86
bool isAutomaticExercise() const
Automatic exercise assumed false if not explicitly provided.
Definition: optiondata.hpp:117
const boost::optional< OptionPaymentData > & paymentData() const
Definition: optiondata.hpp:93
const boost::optional< OptionExerciseData > & exerciseData() const
Definition: optiondata.hpp:92
const PremiumData & premiumData() const
Definition: optiondata.hpp:83
const vector< string > & exerciseDates() const
Definition: optiondata.hpp:76
void addFixingDate(const QuantLib::Date &fixingDate, const std::string &indexName, const QuantLib::Date &payDate=Date::maxDate(), const bool alwaysAddIfPaysOnSettlement=false, const bool mandatoryFixing=true)
TradeActions & tradeActions()
Set the trade actions.
Definition: trade.hpp:126
string npvCurrency_
Definition: trade.hpp:201
QuantLib::Real notional_
Definition: trade.hpp:202
virtual void fromXML(XMLNode *node) override
Definition: trade.cpp:34
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)
Definition: trade.cpp:58
void setSensitivityTemplate(const EngineBuilder &builder)
Definition: trade.cpp:295
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: trade.cpp:46
RequiredFixings requiredFixings_
Definition: trade.hpp:223
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
string notionalCurrency_
Definition: trade.hpp:203
std::map< std::string, boost::any > additionalData_
Definition: trade.hpp:224
Vanilla Instrument Wrapper.
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
static Real getChildValueAsDouble(XMLNode *node, const string &name, bool mandatory=false, double defaultValue=0.0)
Definition: xmlutils.cpp:286
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
Pricing Engine Factory.
FX European Barrier Option data model and serialization.
FX Option data model and serialization.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
Definition: parsers.cpp:404
Barrier::Type parseBarrierType(const std::string &s)
Convert std::string to QuantLib::BarrierType.
Definition: parsers.cpp:1042
Option::Type parseOptionType(const std::string &s)
Convert text to QuantLib::Option::Type.
Definition: parsers.cpp:481
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
market data related utilties
QuantLib::ext::shared_ptr< QuantExt::FxIndex > buildFxIndex(const string &fxIndex, const string &domestic, const string &foreign, const QuantLib::ext::shared_ptr< Market > &market, const string &configuration, bool useXbsCurves)
Definition: marketdata.cpp:137
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.