Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxtouchoption.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>
30#include <ql/errors.hpp>
31#include <ql/exercise.hpp>
32#include <ql/instruments/barrieroption.hpp>
33#include <ql/instruments/compositeinstrument.hpp>
34#include <ql/instruments/swap.hpp>
35#include <ql/instruments/vanillaoption.hpp>
37
38using namespace QuantLib;
39
40namespace ore {
41namespace data {
42
43FxTouchOption::FxTouchOption(Envelope& env, OptionData option, BarrierData barrier, string foreignCurrency,
44 string domesticCurrency, string payoffCurrency, double payoffAmount, string startDate,
45 string calendar, string fxIndex)
46 : ore::data::Trade("FxTouchOption", env),
47 FxSingleAssetDerivative("", env, foreignCurrency, domesticCurrency), option_(option),
48 barrier_(barrier), startDate_(startDate), calendar_(calendar), fxIndex_(fxIndex), payoffAmount_(payoffAmount),
49 payoffCurrency_(payoffCurrency) {
50 Barrier::Type barrierType = parseBarrierType(barrier_.type());
51 switch (barrierType) {
52 case Barrier::DownIn:
53 case Barrier::UpIn:
54 type_ = "One-Touch";
55 break;
56 case Barrier::DownOut:
57 case Barrier::UpOut:
58 type_ = "No-Touch";
59 break;
60 default:
61 QL_FAIL("unknown barrier type");
62 }
63}
64
65void FxTouchOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
66
67 // ISDA taxonomy
68 additionalData_["isdaAssetClass"] = string("Foreign Exchange");
69 additionalData_["isdaBaseProduct"] = string("Simple Exotic");
70 additionalData_["isdaSubProduct"] = string("Barrier");
71 additionalData_["isdaTransaction"] = string("");
72
73 additionalData_["payoffAmount"] = payoffAmount_;
74 additionalData_["payoffCurrency"] = payoffCurrency_;
75
79
80 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
81
82 QL_REQUIRE(tradeActions().empty(), "TradeActions not supported for FxOption");
83 QL_REQUIRE(option_.exerciseDates().size() == 1, "Invalid number of exercise dates");
84 QL_REQUIRE(barrier_.levels().size() == 1, "Double barriers not supported for FxTouchOptions");
85 QL_REQUIRE(barrier_.style().empty() || barrier_.style() == "American", "Only american barrier style suppported");
86
87 // Parse trade data
88 Currency fgnCcy = parseCurrency(foreignCurrency_);
89 Currency domCcy = parseCurrency(domesticCurrency_);
90 Real level = barrier_.levels()[0].value();
91 Date expiryDate = parseDate(option_.exerciseDates().front());
92
93 Natural payLag = 0;
94 BusinessDayConvention payConvention = Unadjusted;
95 Calendar payCalendar = NullCalendar();
96 Date payDate = expiryDate;
97 const boost::optional<OptionPaymentData>& opd = option_.paymentData();
98 if (opd) {
99 if (opd->rulesBased()) {
100 payLag = opd->lag();
101 payConvention = opd->convention();
102 payCalendar = opd->calendar();
103 payDate = payCalendar.advance(expiryDate, opd->lag(), Days, opd->convention());
104 } else {
105 if (opd->dates().size() > 1)
107 "Found more than 1 payment date. The first one will be used.")
108 .log();
109 payDate = opd->dates().front();
110 }
111 }
112 QL_REQUIRE(payDate >= expiryDate, "Settlement date cannot be earlier than expiry date");
113
114 Barrier::Type barrierType = parseBarrierType(barrier_.type());
115 Option::Type type;
116 if (barrierType == Barrier::DownIn || barrierType == Barrier::DownOut)
117 type = Option::Type::Put;
118 else
119 type = Option::Type::Call;
121 bool payoffAtExpiry = option_.payoffAtExpiry();
122 Real rebate = barrier_.rebate();
123 Position::Type positionType = parsePositionType(option_.longShort());
124 Date start = ore::data::parseDate(startDate_);
125
126 QL_REQUIRE(rebate == 0, "Rebates not supported for FxTouchOptions");
127 QL_REQUIRE(payoffAtExpiry == true || barrierType == Barrier::Type::DownIn || barrierType == Barrier::Type::UpIn,
128 "Payoff at hit not supported for FxNoTouchOptions");
129 if ((barrierType == Barrier::Type::DownIn || barrierType == Barrier::Type::UpIn) && payoffAtExpiry == false)
130 QL_REQUIRE(
131 !opd || (opd->rulesBased() && opd->relativeTo() == OptionPaymentData::RelativeTo::Exercise),
132 "Option payment data must be rules-based and relative to Exercise for FxOneTouchOption with payoff at hit");
133
134 // Handle PayoffCurrency, we might have to flip the trade here
135 bool flipResults = false;
137 // Invert the trade, switch dom and for and flip Put/Call
138 level = 1.0 / level;
139 std::swap(fgnCcy, domCcy);
140 type = type == Option::Call ? Option::Put : Option::Call;
141 switch (barrierType) {
142 case Barrier::DownIn:
143 barrierType = Barrier::UpIn;
144 break;
145 case Barrier::UpIn:
146 barrierType = Barrier::DownIn;
147 break;
148 case Barrier::DownOut:
149 barrierType = Barrier::UpOut;
150 break;
151 case Barrier::UpOut:
152 barrierType = Barrier::DownOut;
153 break;
154 }
155 flipResults = true;
156 } else if (payoffCurrency_ != domesticCurrency_) {
157 QL_FAIL("Invalid Payoff currency (" << payoffCurrency_ << ") for FxTouchOption " << foreignCurrency_
159 }
160 DLOG("Setting up FxTouchOption with level " << level << " foreign/bought " << fgnCcy << " domestic/sold "
161 << domCcy);
162 // from this point on it's important not to use domesticCurrency_, foreignCurrency_, barrier_.level(), etc
163 // rather the local variables (fgnCcy, domCcy, level, etc) should be used as they may have been flipped.
164
165 QuantLib::ext::shared_ptr<QuantExt::FxIndex> fxIndex;
166 if (!fxIndex_.empty())
167 fxIndex = buildFxIndex(fxIndex_, domCcy.code(), fgnCcy.code(), engineFactory->market(),
168 engineFactory->configuration(MarketContext::pricing));
169 Calendar cal = ore::data::parseCalendar(calendar_);
170
171 auto buildBarrierOptionWrapperInstr = [this, type, level, engineFactory, domCcy, fgnCcy, flipResults, positionType,
172 market, barrierType, rebate, fxIndex, cal,
173 start](const Date& expiryDate, const Date& payDate) {
174 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoff(new CashOrNothingPayoff(type, level, 1.0));
175 Leg leg;
176
177 leg.push_back(QuantLib::ext::shared_ptr<CashFlow>(new SimpleCashFlow(1.0, payDate)));
178 // Hard code payoff at expiry to true - we ignore in pricing; QPR-10669
179 bool payoffFlag = true;
180
181 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<AmericanExercise>(expiryDate, payoffFlag);
182
183 QuantLib::ext::shared_ptr<Instrument> barrier = QuantLib::ext::make_shared<VanillaOption>(payoff, exercise);
184 QuantLib::ext::shared_ptr<Instrument> underlying = QuantLib::ext::make_shared<Swap>(Leg(), leg);
185
186 // set pricing engines
187 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder(tradeType_);
188 QL_REQUIRE(builder, "No builder found for " << tradeType_);
189 QuantLib::ext::shared_ptr<FxTouchOptionEngineBuilder> fxTouchOptBuilder =
190 QuantLib::ext::dynamic_pointer_cast<FxTouchOptionEngineBuilder>(builder);
191 barrier->setPricingEngine(fxTouchOptBuilder->engine(fgnCcy, domCcy, type_, payDate, flipResults));
192 setSensitivityTemplate(*fxTouchOptBuilder);
193 if (type_ == "One-Touch") {
194 // if a one-touch option is triggered it becomes a simple forward cashflow
195 // which we price as a swap
196 builder = engineFactory->builder("Swap");
197 QL_REQUIRE(builder, "No builder found for Swap");
198 QuantLib::ext::shared_ptr<SwapEngineBuilderBase> swapBuilder =
199 QuantLib::ext::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
200 underlying->setPricingEngine(swapBuilder->engine(domCcy, std::string(), std::string()));
201 }
202
203 bool isLong = (positionType == Position::Long) ? true : false;
204
205 std::vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
206 std::vector<Real> additionalMultipliers;
207 Date lastPremiumDate =
208 addPremiums(additionalInstruments, additionalMultipliers, (isLong ? 1.0 : -1.0) * payoffAmount_,
209 option_.premiumData(), isLong ? -1.0 : 1.0, parseCurrency(payoffCurrency_), engineFactory,
210 builder->configuration(MarketContext::pricing));
211
212 Handle<Quote> spot = market->fxRate(fgnCcy.code() + domCcy.code());
213
214 auto barrierOptionWrapper = QuantLib::ext::make_shared<SingleBarrierOptionWrapper>(
215 barrier, isLong, expiryDate, false, underlying, barrierType, spot, level, rebate, domCcy, start, fxIndex,
216 cal, payoffAmount_, payoffAmount_, additionalInstruments, additionalMultipliers);
217
218 maturity_ = std::max(lastPremiumDate, payDate);
219
220 return barrierOptionWrapper;
221 };
222
223 auto barrierOptionWrapper = buildBarrierOptionWrapperInstr(expiryDate, payDate);
224
225 // We make sure to add required fixings before checking for modifying the instrument's expiry date, to make sure the
226 // portfolio-analyzer gets all the fixings needed for the instrument at the first evaluation.
227 Calendar fixingCal = fxIndex ? fxIndex->fixingCalendar() : cal;
228 if (start != Date()) {
229 for (Date d = start; d <= expiryDate; d = fixingCal.advance(d, 1 * Days))
231 }
232
233 // Check if the barrier has been triggered already. If payoff-at-hit, and barrier was touched in the past, then
234 // create instrument again, with expiry date and pay date corresponding to that past barrier exercise date
235 if (auto rt = engineFactory->engineData()->globalParameters().find("RunType");
236 rt != engineFactory->engineData()->globalParameters().end() && rt->second != "PortfolioAnalyser" &&
237 barrierOptionWrapper->exercise()) {
238 QL_REQUIRE(barrierOptionWrapper->exerciseDate() != Date(), "Option is exercised but exercise date was not defined");
239 expiryDate = barrierOptionWrapper->exerciseDate();
240 additionalData_["exerciseDate"] = expiryDate;
241
242 if (!payoffAtExpiry && type_ == "One-Touch") {
243 payDate = payCalendar.advance(expiryDate, payLag, Days, payConvention);
244 barrierOptionWrapper = buildBarrierOptionWrapperInstr(expiryDate, payDate);
245 additionalData_["settlementDate"] = payDate;
246 }
247 }
248
249 instrument_ = barrierOptionWrapper;
250
251 // maturity_ is set in buildBarrierOptionWrapperInstr()
252}
253
254bool FxTouchOption::checkBarrier(Real spot, Barrier::Type type, Real barrier) {
255 switch (type) {
256 case Barrier::DownIn:
257 case Barrier::DownOut:
258 return spot <= barrier;
259 case Barrier::UpIn:
260 case Barrier::UpOut:
261 return spot >= barrier;
262 default:
263 QL_FAIL("unknown barrier type " << type);
264 }
265}
266
268 Trade::fromXML(node);
269 XMLNode* fxNode = XMLUtils::getChildNode(node, "FxTouchOptionData");
270 QL_REQUIRE(fxNode, "No FxOptionData Node");
271 option_.fromXML(XMLUtils::getChildNode(fxNode, "OptionData"));
272 barrier_.fromXML(XMLUtils::getChildNode(fxNode, "BarrierData"));
273 Barrier::Type barrierType = parseBarrierType(barrier_.type());
274 switch (barrierType) {
275 case Barrier::DownIn:
276 case Barrier::UpIn:
277 type_ = "One-Touch";
278 break;
279 case Barrier::DownOut:
280 case Barrier::UpOut:
281 type_ = "No-Touch";
282 break;
283 default:
284 QL_FAIL("unknown barrier type");
285 }
286
287 foreignCurrency_ = XMLUtils::getChildValue(fxNode, "ForeignCurrency", true);
288 domesticCurrency_ = XMLUtils::getChildValue(fxNode, "DomesticCurrency", true);
289 payoffCurrency_ = XMLUtils::getChildValue(fxNode, "PayoffCurrency", true);
290 startDate_ = XMLUtils::getChildValue(fxNode, "StartDate", false);
291 calendar_ = XMLUtils::getChildValue(fxNode, "Calendar", false);
292 fxIndex_ = XMLUtils::getChildValue(fxNode, "FXIndex", false);
293 payoffAmount_ = XMLUtils::getChildValueAsDouble(fxNode, "PayoffAmount", true);
294}
295
297 XMLNode* node = Trade::toXML(doc);
298 XMLNode* fxNode = doc.allocNode("FxTouchOptionData");
299 XMLUtils::appendNode(node, fxNode);
300 XMLUtils::appendNode(fxNode, option_.toXML(doc));
301 XMLUtils::appendNode(fxNode, barrier_.toXML(doc));
302 XMLUtils::addChild(doc, fxNode, "ForeignCurrency", foreignCurrency_);
303 XMLUtils::addChild(doc, fxNode, "DomesticCurrency", domesticCurrency_);
304 XMLUtils::addChild(doc, fxNode, "PayoffCurrency", payoffCurrency_);
305 XMLUtils::addChild(doc, fxNode, "PayoffAmount", payoffAmount_);
306 if (startDate_ != "")
307 XMLUtils::addChild(doc, fxNode, "StartDate", startDate_);
308 if (fxIndex_ != "")
309 XMLUtils::addChild(doc, fxNode, "FXIndex", fxIndex_);
310 if (calendar_ != "")
311 XMLUtils::addChild(doc, fxNode, "Calendar", calendar_);
312
313 return node;
314}
315
316} // namespace data
317} // namespace oreplus
Wrapper for option instruments, tracks whether option has been exercised or not.
Engine builder for Swaps.
Serializable obejct holding barrier data.
Definition: barrierdata.hpp:34
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
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Base class for all single asset FX Derivaties.
const BarrierData & barrier() const
virtual void fromXML(XMLNode *node) override
FxTouchOption()
Default constructor.
virtual XMLNode * toXML(XMLDocument &doc) const override
const string & type() const
const string & fxIndex() const
bool checkBarrier(Real spot, Barrier::Type type, Real level)
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
void log() const
generate Boost log record to pass to corresponding sinks
Definition: log.cpp:491
Serializable object holding option data.
Definition: optiondata.hpp:42
void setCallPut(const string &callPut)
Definition: optiondata.hpp:102
const string & longShort() const
Definition: optiondata.hpp:70
virtual void fromXML(XMLNode *node) override
Definition: optiondata.cpp:32
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: optiondata.cpp:86
const bool & payoffAtExpiry() const
Definition: optiondata.hpp:75
const boost::optional< OptionPaymentData > & paymentData() const
Definition: optiondata.hpp:93
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)
Utility classes for Structured warnings, contains the Trade ID and Type.
Trade base class.
Definition: trade.hpp:55
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
string tradeType_
Definition: trade.hpp:196
RequiredFixings requiredFixings_
Definition: trade.hpp:223
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
string notionalCurrency_
Definition: trade.hpp:203
const string & tradeType() const
Definition: trade.hpp:133
std::map< std::string, boost::any > additionalData_
Definition: trade.hpp:224
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 One-Touch/No-Touch Option data model and serialization.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
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
Map text representations to QuantLib/QuantExt types.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
Calendar calendar
Definition: utilities.cpp:441
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
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.
Classes for structured trade warnings.
string conversion utilities