Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxforward.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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>
27#include <ql/cashflows/simplecashflow.hpp>
29
30namespace ore {
31namespace data {
32
33void FxForward::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
34
35 // ISDA taxonomy
36 additionalData_["isdaAssetClass"] = string("Foreign Exchange");
37 additionalData_["isdaBaseProduct"] = string(settlement_ == "Cash" ? "NDF" : "Forward");
38 additionalData_["isdaSubProduct"] = string("");
39 additionalData_["isdaTransaction"] = string("");
40
41 additionalData_["soldCurrency"] = soldCurrency_;
42 additionalData_["boughtCurrency"] = boughtCurrency_;
43 additionalData_["soldAmount"] = soldAmount_;
44 additionalData_["boughtAmount"] = boughtAmount_;
45 additionalData_["valueDate"] = maturityDate_;
46 additionalData_["settlement"] = settlement_;
47
48 // If you Buy EURUSD forward, then you buy EUR and sell USD.
49 // EUR = foreign, USD = Domestic.
50 // You pay in USD, so the Domestic / Sold ccy is the "payer" currency
51 Currency boughtCcy = parseCurrency(boughtCurrency_);
52 Currency soldCcy = parseCurrency(soldCurrency_);
53
54 Currency payCcy;
55 if (payCurrency_.empty()) {
56 // If settlement currency is not set, set it to the domestic currency.
57 payCcy = soldCcy;
58 } else {
59 payCcy = parseCurrency(payCurrency_);
60 QL_REQUIRE(payCcy == boughtCcy || payCcy == soldCcy,
61 "Settlement currency must be either " << boughtCcy.code() << " or " << soldCcy.code() << ".");
62 }
63
64 npvCurrency_ = payCcy.code();
65
66 // The notional and notional ccy will be set in the engine anyway,
67 // but we also set this here as a default/fallback in case the engine builder fails.
68 if (settlement_ == "Physical") {
69 notional_ = soldAmount_;
70 notionalCurrency_ = soldCurrency_;
71 } else {
72 // for cash settled forwards we take the notional from the settlement ccy leg
73 notional_ = soldCcy == payCcy ? soldAmount_ : boughtAmount_;
74 notionalCurrency_ = payCcy.code();
75 }
76
78
79 // Derive settlement date from payment data parameters
80 Date payDate;
81 if (payDate_.empty()) {
82 Natural conventionalLag = 0;
83 Calendar conventionalCalendar = NullCalendar();
84 BusinessDayConvention conventionalBdc = Unadjusted;
85 if (!fxIndex_.empty() && settlement_ == "Cash") {
86 std::tie(conventionalLag, conventionalCalendar, conventionalBdc) =
87 getFxIndexConventions(fxIndex_.empty() ? boughtCurrency_ + soldCurrency_ : fxIndex_);
88 }
89 PaymentLag paymentLag;
90 if (payLag_.empty())
91 paymentLag = conventionalLag;
92 else
93 paymentLag = parsePaymentLag(payLag_);
94 Period payLag = boost::apply_visitor(PaymentLagPeriod(), paymentLag);
95 Calendar payCalendar = payCalendar_.empty() ? conventionalCalendar : parseCalendar(payCalendar_);
96 BusinessDayConvention payConvention =
97 payConvention_.empty() ? conventionalBdc : parseBusinessDayConvention(payConvention_);
98 payDate = payCalendar.advance(maturityDate, payLag, payConvention);
99 } else {
101 QL_REQUIRE(payDate >= maturityDate, "FX Forward settlement date should equal or exceed the maturity date.");
102 }
103
104 additionalData_["payDate"] = payDate;
105 maturity_ = std::max(payDate, maturityDate);
106 QuantLib::ext::shared_ptr<QuantExt::FxIndex> fxIndex;
107
108 Date fixingDate;
109 if (settlement_ == "Cash") {
110 // We allow for an empty fxIndex if maturiytDate == payDate in order not to break trades that were previously
111 // pricing - we should really require fxIndex whenever cash settlement is specified. If the fxIndex is not
112 // given in this case, we assume that the current FX Spot rate is used to determine the settlement amount.
113 if (maturityDate <= payDate && !fxIndex_.empty()) {
114 Currency nonPayCcy = payCcy == boughtCcy ? soldCcy : boughtCcy;
115 fxIndex = buildFxIndex(fxIndex_, nonPayCcy.code(), payCcy.code(), engineFactory->market(),
116 engineFactory->configuration(MarketContext::pricing));
117 // We also allow for an effective fixing date > payDate in order not to break trades that were preivously
118 // pricing - this should be an error as well. If this is the case we assume that the current FX Spot
119 // rate is used to determine the settlement amount as above.
120 fixingDate = fxIndex->fixingCalendar().adjust(maturityDate);
121 if (fixingDate <= payDate) {
122 requiredFixings_.addFixingDate(fixingDate, fxIndex_, payDate);
123 }
124 } else {
125 QL_REQUIRE(maturityDate >= payDate,
126 "FX settlement index must be specified for non-deliverable forward if value date ("
127 << maturityDate << ") < payDate (" << payDate << ")");
128 }
129 }
130 if (fixingDate != Date()) {
131 if (fixingDate <= payDate)
132 additionalData_["fixingDate"] = fixingDate;
133 else
134 additionalData_["adjustedValueDate"] = fixingDate;
135 }
136
137 QL_REQUIRE(tradeActions().empty(), "TradeActions not supported for FxForward");
138
139 DLOG("Build FxForward with maturity date " << QuantLib::io::iso_date(maturityDate) << " and pay date "
140 << QuantLib::io::iso_date(payDate));
141
142 // get pricing engine builder
143 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder(tradeType_);
144 QL_REQUIRE(builder, "No builder found for " << tradeType_);
145 QuantLib::ext::shared_ptr<FxForwardEngineBuilderBase> fxBuilder =
146 QuantLib::ext::dynamic_pointer_cast<FxForwardEngineBuilderBase>(builder);
147
148 string tmp = fxBuilder->engineParameter("includeSettlementDateFlows", {}, false, "");
149 includeSettlementDateFlows_ = tmp == "" ? false : parseBool(tmp);
150
151 QuantLib::ext::shared_ptr<QuantLib::Instrument> instrument =
152 QuantLib::ext::make_shared<QuantExt::FxForward>(boughtAmount_, boughtCcy, soldAmount_, soldCcy, maturityDate, false,
153 settlement_ == "Physical", payDate, payCcy, fixingDate, fxIndex,
154 includeSettlementDateFlows_);
155 instrument_.reset(new VanillaInstrument(instrument));
156
157 // set pricing engine
158 instrument_->qlInstrument()->setPricingEngine(fxBuilder->engine(boughtCcy, soldCcy));
159 setSensitivityTemplate(*fxBuilder);
160
161 // Set up Legs
162 legs_ = {{QuantLib::ext::make_shared<SimpleCashFlow>(boughtAmount_, payDate)},
163 {QuantLib::ext::make_shared<SimpleCashFlow>(soldAmount_, payDate)}};
164 legCurrencies_ = {boughtCurrency_, soldCurrency_};
165 legPayers_ = {false, true};
166}
167
168bool FxForward::isExpired(const Date& date) {
169 if (includeSettlementDateFlows_)
170 return date > maturity_;
171 else
172 return date >= maturity_;
173}
174
175QuantLib::Real FxForward::notional() const {
176 // try to get the notional from the additional results of the instrument
177 try {
178 return instrument_->qlInstrument(true)->result<Real>("currentNotional");
179 } catch (const std::exception& e) {
180 if (strcmp(e.what(), "currentNotional not provided"))
181 ALOG("error when retrieving notional: " << e.what());
182 }
183 // if not provided, return original/fallback amount
184 return notional_;
185}
186
187std::string FxForward::notionalCurrency() const {
188 // try to get the notional ccy from the additional results of the instrument
189 try {
190 return instrument_->qlInstrument(true)->result<std::string>("notionalCurrency");
191 } catch (const std::exception& e) {
192 if (strcmp(e.what(), "notionalCurrency not provided"))
193 ALOG("error when retrieving notional ccy: " << e.what());
194 }
195 // if not provided, return original/fallback value
196 return notionalCurrency_;
197}
198
199void FxForward::fromXML(XMLNode* node) {
200 Trade::fromXML(node);
201 XMLNode* fxNode = XMLUtils::getChildNode(node, "FxForwardData");
202 QL_REQUIRE(fxNode, "No FxForwardData Node");
203 maturityDate_ = XMLUtils::getChildValue(fxNode, "ValueDate", true);
204 boughtCurrency_ = XMLUtils::getChildValue(fxNode, "BoughtCurrency", true);
205 soldCurrency_ = XMLUtils::getChildValue(fxNode, "SoldCurrency", true);
206 boughtAmount_ = XMLUtils::getChildValueAsDouble(fxNode, "BoughtAmount", true);
207 soldAmount_ = XMLUtils::getChildValueAsDouble(fxNode, "SoldAmount", true);
208 settlement_ = XMLUtils::getChildValue(fxNode, "Settlement", false);
209 if (settlement_ == "")
210 settlement_ = "Physical";
211
212 if (XMLNode* settlementDataNode = XMLUtils::getChildNode(fxNode, "SettlementData")) {
213 payCurrency_ = XMLUtils::getChildValue(settlementDataNode, "Currency", false);
214 fxIndex_ = XMLUtils::getChildValue(settlementDataNode, "FXIndex", false);
215 payDate_ = XMLUtils::getChildValue(settlementDataNode, "Date", false);
216
217 if (payDate_.empty()) {
218 if (XMLNode* rulesNode = XMLUtils::getChildNode(settlementDataNode, "Rules")) {
219 payLag_ = XMLUtils::getChildValue(rulesNode, "PaymentLag", false);
220 payCalendar_ = XMLUtils::getChildValue(rulesNode, "PaymentCalendar", false);
221 payConvention_ = XMLUtils::getChildValue(rulesNode, "PaymentConvention", false);
222 }
223 }
224 }
225}
226
227XMLNode* FxForward::toXML(XMLDocument& doc) const {
228 XMLNode* node = Trade::toXML(doc);
229 XMLNode* fxNode = doc.allocNode("FxForwardData");
230 XMLUtils::appendNode(node, fxNode);
231 XMLUtils::addChild(doc, fxNode, "ValueDate", maturityDate_);
232 XMLUtils::addChild(doc, fxNode, "BoughtCurrency", boughtCurrency_);
233 XMLUtils::addChild(doc, fxNode, "BoughtAmount", boughtAmount_);
234 XMLUtils::addChild(doc, fxNode, "SoldCurrency", soldCurrency_);
235 XMLUtils::addChild(doc, fxNode, "SoldAmount", soldAmount_);
236 XMLUtils::addChild(doc, fxNode, "Settlement", settlement_);
237
238 XMLNode* settlementDataNode = doc.allocNode("SettlementData");
239 XMLUtils::appendNode(fxNode, settlementDataNode);
240
241 if (!payCurrency_.empty())
242 XMLUtils::addChild(doc, settlementDataNode, "Currency", payCurrency_);
243 if (!fxIndex_.empty())
244 XMLUtils::addChild(doc, settlementDataNode, "FXIndex", fxIndex_);
245 if (!payDate_.empty()) {
246 XMLUtils::addChild(doc, settlementDataNode, "Date", payDate_);
247 } else {
248 XMLNode* rulesNode = doc.allocNode("Rules");
249 XMLUtils::appendNode(settlementDataNode, rulesNode);
250 if (!payLag_.empty())
251 XMLUtils::addChild(doc, rulesNode, "PaymentLag", payLag_);
252 if (!payCalendar_.empty())
253 XMLUtils::addChild(doc, rulesNode, "PaymentCalendar", payCalendar_);
254 if (!payConvention_.empty())
255 XMLUtils::addChild(doc, rulesNode, "PaymentConvention", payConvention_);
256 }
257
258 return node;
259}
260} // namespace data
261} // namespace ore
Engine builder for FX Forwards.
bool isExpired() const override
Date maturityDate() const
boost::shared_ptr< QuantExt::FxIndex > fxIndex() const
Date payDate() const
boost::shared_ptr< FxIndex > fxIndex_
Currency payCcy() const
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
Definition: fxforward.cpp:33
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
Pricing Engine Factory.
FX Forward 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
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Definition: parsers.cpp:173
bool parseBool(const string &s)
Convert text to bool.
Definition: parsers.cpp:144
PaymentLag parsePaymentLag(const string &s)
Convert text to PaymentLag.
Definition: parsers.cpp:628
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define ALOG(text)
Logging Macro (Level = Alert)
Definition: log.hpp:544
market data related utilties
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
std::tuple< Natural, Calendar, BusinessDayConvention > getFxIndexConventions(const string &index)
Definition: marketdata.cpp:160
boost::variant< QuantLib::Period, QuantLib::Natural > PaymentLag
Definition: types.hpp:32
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.
XML utility functions.