Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxindex.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2015 Peter Caspers
3 This file is part of QuantLib, a free-software/open-source library
4 for financial quantitative analysts and developers - http://quantlib.org/
5 QuantLib is free software: you can redistribute it and/or modify it
6 under the terms of the QuantLib license. You should have received a
7 copy of the license along with this program.
8 <quantlib-dev@lists.sf.net>. The license is also available online at
9 <http://quantlib.org/license.shtml>.
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the license for more details.
13*/
14
15/*
16 Copyright (C) 2016 Quaternion Risk Management Ltd
17 All rights reserved.
18
19 This file is part of ORE, a free-software/open-source library
20 for transparent pricing and risk analysis - http://opensourcerisk.org
21
22 ORE is free software: you can redistribute it and/or modify it
23 under the terms of the Modified BSD License. You should have received a
24 copy of the license along with this program.
25 The license is also available online at <http://opensourcerisk.org>
26
27 This program is distributed on the basis that it will form a useful
28 contribution to risk analytics and model standardisation, but WITHOUT
29 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
30 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
31*/
32
33#include <boost/algorithm/string.hpp>
34#include <ql/currencies/exchangeratemanager.hpp>
35#include <ql/math/functional.hpp>
36#include <ql/quotes/derivedquote.hpp>
37#include <ql/quotes/simplequote.hpp>
39
40using namespace std;
41using namespace QuantLib;
42
43namespace QuantExt {
44
45FxRateQuote::FxRateQuote(Handle<Quote> spotQuote, const Handle<YieldTermStructure>& sourceYts,
46 const Handle<YieldTermStructure>& targetYts, Natural fixingDays,
47 const Calendar& fixingCalendar)
48 : spotQuote_(spotQuote), sourceYts_(sourceYts), targetYts_(targetYts), fixingDays_(fixingDays),
49 fixingCalendar_(fixingCalendar) {
50 registerWith(spotQuote_);
51 registerWith(sourceYts_);
52 registerWith(targetYts_);
53}
54
55Real FxRateQuote::value() const {
56 QL_ENSURE(isValid(), "invalid FxRateQuote");
57 if (fixingDays_ == 0)
58 return spotQuote_->value();
59 else {
60 if (sourceYts_.empty() || targetYts_.empty())
61 return spotQuote_->value();
62 Date today = sourceYts_->referenceDate();
63 Date refValueDate = fixingCalendar_.advance(today, fixingDays_, Days);
64 return spotQuote_->value() * targetYts_->discount(refValueDate) / sourceYts_->discount(refValueDate);
65 }
66}
67
68bool FxRateQuote::isValid() const { return !spotQuote_.empty() && spotQuote_->isValid(); }
69
70void FxRateQuote::update() { notifyObservers(); }
71
72FxSpotQuote::FxSpotQuote(Handle<Quote> todaysQuote, const Handle<YieldTermStructure>& sourceYts,
73 const Handle<YieldTermStructure>& targetYts, Natural fixingDays,
74 const Calendar& fixingCalendar)
75 : todaysQuote_(todaysQuote), sourceYts_(sourceYts), targetYts_(targetYts), fixingDays_(fixingDays),
76 fixingCalendar_(fixingCalendar) {
77 registerWith(todaysQuote_);
78 registerWith(sourceYts_);
79 registerWith(targetYts_);
80}
81
82Real FxSpotQuote::value() const {
83 QL_ENSURE(isValid(), "invalid FxSpotQuote");
84 if (fixingDays_ == 0)
85 return todaysQuote_->value();
86 else {
87 if(sourceYts_.empty() || targetYts_.empty())
88 return todaysQuote_->value();
89 Date today = sourceYts_->referenceDate();
90 Date refValueDate = fixingCalendar_.advance(today, fixingDays_, Days);
91 return todaysQuote_->value() / targetYts_->discount(refValueDate) * sourceYts_->discount(refValueDate);
92 }
93}
94
96 return !todaysQuote_.empty() && todaysQuote_->isValid();
97}
98
99void FxSpotQuote::update() { notifyObservers(); }
100
101FxIndex::FxIndex(const std::string& familyName, Natural fixingDays, const Currency& source, const Currency& target,
102 const Calendar& fixingCalendar, const Handle<YieldTermStructure>& sourceYts,
103 const Handle<YieldTermStructure>& targetYts, bool fixingTriangulation)
104 : familyName_(familyName), fixingDays_(fixingDays), sourceCurrency_(source), targetCurrency_(target),
105 sourceYts_(sourceYts), targetYts_(targetYts), useQuote_(false), fixingCalendar_(fixingCalendar),
106 fixingTriangulation_(fixingTriangulation) {
107
108 initialise();
109}
110
111FxIndex::FxIndex(const std::string& familyName, Natural fixingDays, const Currency& source, const Currency& target,
112 const Calendar& fixingCalendar, const Handle<Quote> fxSpot,
113 const Handle<YieldTermStructure>& sourceYts, const Handle<YieldTermStructure>& targetYts,
114 bool fixingTriangulation)
115 : familyName_(familyName), fixingDays_(fixingDays), sourceCurrency_(source), targetCurrency_(target),
116 sourceYts_(sourceYts), targetYts_(targetYts), fxSpot_(fxSpot), useQuote_(true), fixingCalendar_(fixingCalendar),
117 fixingTriangulation_(fixingTriangulation) {
118
119 initialise();
120}
121
123 std::ostringstream tmp;
124 tmp << familyName_ << " " << sourceCurrency_.code() << "/" << targetCurrency_.code();
125 name_ = tmp.str();
126
127 oreName_ = "FX-" + familyName_ + "-" + sourceCurrency_.code() + "-" + targetCurrency_.code();
128
129 registerWith(IndexManager::instance().notifier(name()));
130 registerWith(fxSpot_);
131 registerWith(sourceYts_);
132 registerWith(targetYts_);
133}
134
135const Handle<Quote> FxIndex::fxQuote(bool withSettlementLag) const {
136 Handle<Quote> quote;
137 if (withSettlementLag || fixingDays_ == 0)
138 quote = fxSpot_;
139
140 if (quote.empty()) {
141 if (fxRate_.empty()) {
142 Handle<Quote> tmpQuote;
143 if (!useQuote_)
144 tmpQuote = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(
145 ExchangeRateManager::instance().lookup(sourceCurrency_, targetCurrency_).rate()));
146 else
147 tmpQuote = fxSpot_;
148
149 // adjust for spot
150 fxRate_ = Handle<Quote>(
151 QuantLib::ext::make_shared<FxRateQuote>(tmpQuote, sourceYts_, targetYts_, fixingDays_, fixingCalendar_));
152 }
153 quote = fxRate_;
154 }
155 return quote;
156}
157
158Real FxIndex::fixing(const Date& fixingDate, bool forecastTodaysFixing) const {
159
160 Date adjustedFixingDate = fixingCalendar().adjust(fixingDate, Preceding);
161
162 Date today = Settings::instance().evaluationDate();
163
164 Real result = Null<Real>();
165
166 if (adjustedFixingDate > today || (adjustedFixingDate == today && forecastTodaysFixing))
167 result = forecastFixing(adjustedFixingDate);
168
169 if (result == Null<Real>()) {
170 if (adjustedFixingDate < today || Settings::instance().enforcesTodaysHistoricFixings()) {
171 // must have been fixed
172 // do not catch exceptions
173 result = pastFixing(adjustedFixingDate);
174 QL_REQUIRE(result != Null<Real>(), "Missing " << name() << " fixing for " << adjustedFixingDate);
175 } else {
176 try {
177 // might have been fixed
178 result = pastFixing(adjustedFixingDate);
179 } catch (Error&) {
180 ; // fall through and forecast
181 }
182 if (result == Null<Real>())
183 result = forecastFixing(adjustedFixingDate);
184 }
185 }
186
187 return result;
188}
189
190Real FxIndex::forecastFixing(const Time& fixingTime) const {
191 QL_REQUIRE(!sourceYts_.empty() && !targetYts_.empty(),
192 "FxIndex::forecastFixing(): null term structure set to this instance of " << name());
193
194 // we base the forecast always on the exchange rate (and not on today's fixing)
195 Real rate;
196 if (!useQuote_) {
197 rate = ExchangeRateManager::instance().lookup(sourceCurrency_, targetCurrency_).rate();
198 } else {
199 QL_REQUIRE(!fxSpot_.empty(), "FxIndex::forecastFixing(): fx quote required for " << name());
200 rate = fxSpot_->value();
201 }
202
203 // TODO: do we need to add this a class variable?
204 DayCounter dc = Actual365Fixed();
205
206 // to make the spot adjustment we get the time to spot, and also add this to fixing time
207 Date refDate = sourceYts_->referenceDate();
208 Date spotValueDate = valueDate(fixingCalendar().adjust(refDate));
209
210 // time from ref to spot date
211 Real spotTime = dc.yearFraction(refDate, spotValueDate);
212 Real forwardTime = spotTime + fixingTime;
213
214 QL_REQUIRE(forwardTime > 0.0 || close_enough(forwardTime, 0.0),
215 "FxIndex::forecastFixing(" << fixingTime << "): forwardTime (" << forwardTime << ") is negative for "
216 << name());
217
218 // compute the forecast applying the usual no arbitrage principle
219 Real forward = rate * sourceYts_->discount(forwardTime) * targetYts_->discount(spotTime) /
220 (targetYts_->discount(forwardTime) * sourceYts_->discount(spotTime));
221 return forward;
222}
223
224Real FxIndex::forecastFixing(const Date& fixingDate) const {
225 QL_REQUIRE(!sourceYts_.empty() && !targetYts_.empty(), "null term structure set to this instance of " << name());
226
227 // we base the forecast always on the exchange rate (and not on today's
228 // fixing)
229 Real rate;
230 if (!useQuote_) {
231 rate = ExchangeRateManager::instance().lookup(sourceCurrency_, targetCurrency_).rate();
232 } else {
233 QL_REQUIRE(!fxSpot_.empty(), "FxIndex::forecastFixing(): fx quote required for " << name());
234 rate = fxSpot_->value();
235 }
236
237 // the exchange rate is interpreted as the spot rate w.r.t. the index's
238 // settlement date
239 Date refValueDate = valueDate(fixingCalendar().adjust(sourceYts_->referenceDate()));
240
241 // the fixing is obeying the settlement delay as well
242 Date fixingValueDate = valueDate(fixingDate);
243
244 // we can assume fixingValueDate >= valueDate
245 QL_REQUIRE(fixingValueDate >= refValueDate, "value date for requested fixing as of "
246 << fixingDate << " (" << fixingValueDate
247 << ") must be greater or equal to today's fixing value date ("
248 << refValueDate << ") for " << name());
249
250 // compute the forecast applying the usual no arbitrage principle
251 Real forward = rate * sourceYts_->discount(fixingValueDate) * targetYts_->discount(refValueDate) /
252 (sourceYts_->discount(refValueDate) * targetYts_->discount(fixingValueDate));
253
254 return forward;
255}
256
257QuantLib::ext::shared_ptr<FxIndex> FxIndex::clone(const Handle<Quote> fxQuote, const Handle<YieldTermStructure>& sourceYts,
258 const Handle<YieldTermStructure>& targetYts, const string& familyName) {
259 Handle<Quote> quote = fxQuote.empty() ? fxSpot_ : fxQuote;
260 Handle<YieldTermStructure> source = sourceYts.empty() ? sourceYts_ : sourceYts;
261 Handle<YieldTermStructure> target = targetYts.empty() ? targetYts_ : targetYts;
262 string famName = familyName.empty() ? familyName_ : familyName;
263 return QuantLib::ext::make_shared<FxIndex>(famName, fixingDays_, sourceCurrency_, targetCurrency_, fixingCalendar_, quote,
264 source, target);
265}
266
267std::string FxIndex::name() const { return name_; }
268
269Calendar FxIndex::fixingCalendar() const { return fixingCalendar_; }
270
271bool FxIndex::isValidFixingDate(const Date& d) const { return fixingCalendar().isBusinessDay(d); }
272
274 fxRate_ = Handle<Quote>();
275 notifyObservers();
276}
277
278Date FxIndex::fixingDate(const Date& valueDate) const {
279 Date fixingDate = fixingCalendar().advance(valueDate, -static_cast<Integer>(fixingDays_), Days);
280 return fixingDate;
281}
282
283Date FxIndex::valueDate(const Date& fixingDate) const {
284 QL_REQUIRE(isValidFixingDate(fixingDate),
285 "FxIndex::valueDate(): " << fixingDate << " is not a valid fixing date for " << name()
286 << " (calendar is " << fixingCalendar().name() << ")");
287 return fixingCalendar().advance(fixingDate, fixingDays_, Days);
288}
289
290Real FxIndex::pastFixing(const Date& fixingDate) const {
291 QL_REQUIRE(isValidFixingDate(fixingDate), fixingDate << "FxIndex::pastFixing(): is not a valid fixing date for "
292 << name() << " (calendar is " << fixingCalendar().name()
293 << ")");
294
295 Real fixing = timeSeries()[fixingDate];
296 if (fixing != Null<Real>())
297 return fixing;
298
300 // check reverse
301 string revName = familyName_ + " " + targetCurrency_.code() + "/" + sourceCurrency_.code();
302 if (IndexManager::instance().hasHistoricalFixing(revName, fixingDate))
303 return 1.0 / IndexManager::instance().getHistory(revName)[fixingDate];
304
305 // Now we search for a pair of quotes that we can combine to construct the quote required.
306 // We only search for a pair of quotes a single step apart.
307 //
308 // Suppose we want a USDJPY quote and we have EUR based data, there are 4 combinations to
309 // consider:
310 // EURUSD, EURJPY => we want EURJPY / EURUSD [Triangulation]
311 // EURUSD, JPYEUR => we want 1 / (EURUSD * JPYEUR) [InverseProduct]
312 // USDEUR, EURJPY => we want USDEUR * EURJPY [Product]
313 // USDEUR, JPYEUR => we want USDEUR / JPYEUR [Triangulation (but in the reverse order)]
314 //
315 // Loop over the map, look for domestic then use the map to find the other side of the pair.
316
317 // Loop over all the possible Indexes
318 vector<string> availIndexes = IndexManager::instance().histories();
319 for (auto i : availIndexes) {
320 if (boost::starts_with(i, familyName_)) {
321 // check for a fixing
322 Real fixing = IndexManager::instance().getHistory(i)[fixingDate];
323 if (fixing != Null<Real>()) {
324
325 Size l = i.size();
326 string keyDomestic = i.substr(l - 7, 3);
327 string keyForeign = i.substr(l - 3);
328 string domestic = sourceCurrency_.code();
329 string foreign = targetCurrency_.code();
330
331 if (domestic == keyDomestic) {
332 // we have domestic, now look for foreign/keyForeign
333 // USDEUR, JPYEUR => we want USDEUR / JPYEUR [Triangulation (but in the reverse order)]
334 string forName = familyName_ + " " + foreign + "/" + keyForeign;
335 if (IndexManager::instance().hasHistoricalFixing(forName, fixingDate)) {
336 Real forFixing = IndexManager::instance().getHistory(forName)[fixingDate];
337 return fixing / forFixing;
338 }
339
340 // USDEUR, EURJPY => we want USDEUR * EURJPY [Product]
341 forName = familyName_ + " " + keyForeign + "/" + foreign;
342 if (IndexManager::instance().hasHistoricalFixing(forName, fixingDate)) {
343 Real forFixing = IndexManager::instance().getHistory(forName)[fixingDate];
344 return fixing * forFixing;
345 }
346 }
347
348 if (domestic == keyForeign) {
349 // we have fixing, now look for foreign/keyDomestic
350 // EURUSD, JPYEUR => we want 1 / (EURUSD * JPYEUR) [InverseProduct]
351 string forName = familyName_ + " " + foreign + "/" + keyDomestic;
352 if (IndexManager::instance().hasHistoricalFixing(forName, fixingDate)) {
353 Real forFixing = IndexManager::instance().getHistory(forName)[fixingDate];
354 return 1 / (fixing * forFixing);
355 }
356
357 // EURUSD, EURJPY => we want EURJPY / EURUSD [Triangulation]
358 forName = familyName_ + " " + keyDomestic + "/" + foreign;
359 if (IndexManager::instance().hasHistoricalFixing(forName, fixingDate)) {
360 Real forFixing = IndexManager::instance().getHistory(forName)[fixingDate];
361 return forFixing / fixing;
362 }
363 }
364 }
365 }
366 }
367 }
368 return fixing;
369}
370
371} // namespace QuantExt
Handle< Quote > fxRate_
Definition: fxindex.hpp:163
const Handle< Quote > fxQuote(bool withSettlementLag=false) const
fxQuote returns instantaneous Quote by default, otherwise settlement after fixingDays
Definition: fxindex.cpp:135
QuantLib::ext::shared_ptr< FxIndex > clone(const Handle< Quote > fxQuote=Handle< Quote >(), const Handle< YieldTermStructure > &sourceYts=Handle< YieldTermStructure >(), const Handle< YieldTermStructure > &targetYts=Handle< YieldTermStructure >(), const std::string &familyName=std::string())
clone the index, the clone will be linked to the provided handles
Definition: fxindex.cpp:257
virtual Real forecastFixing(const Time &fixingTime) const override
It can be overridden to implement particular conventions.
Definition: fxindex.cpp:190
void update() override
Definition: fxindex.cpp:273
std::string oreName_
Definition: fxindex.hpp:155
Calendar fixingCalendar() const override
Definition: fxindex.cpp:269
std::string name_
Definition: fxindex.hpp:159
const Handle< Quote > fxSpot_
Definition: fxindex.hpp:161
Currency sourceCurrency_
Definition: fxindex.hpp:157
bool fixingTriangulation_
Definition: fxindex.hpp:168
Natural fixingDays_
Definition: fxindex.hpp:156
const Handle< YieldTermStructure > targetYts_
Definition: fxindex.hpp:158
std::string name() const override
Definition: fxindex.cpp:267
FxIndex(const std::string &familyName, Natural fixingDays, const Currency &source, const Currency &target, const Calendar &fixingCalendar, const Handle< YieldTermStructure > &sourceYts=Handle< YieldTermStructure >(), const Handle< YieldTermStructure > &targetYts=Handle< YieldTermStructure >(), bool fixingTriangulation=false)
Definition: fxindex.cpp:101
virtual Date valueDate(const Date &fixingDate) const
Definition: fxindex.cpp:283
std::string familyName_
Definition: fxindex.hpp:155
bool isValidFixingDate(const Date &fixingDate) const override
Definition: fxindex.cpp:271
const Handle< YieldTermStructure > sourceYts_
Definition: fxindex.hpp:158
Currency targetCurrency_
Definition: fxindex.hpp:157
Real pastFixing(const Date &fixingDate) const override
returns a past fixing at the given date
Definition: fxindex.cpp:290
Calendar fixingCalendar_
Definition: fxindex.hpp:167
Date fixingDate(const Date &valueDate) const
Definition: fxindex.cpp:278
std::string familyName() const
Definition: fxindex.hpp:125
Real fixing(const Date &fixingDate, bool forecastTodaysFixing=false) const override
Definition: fxindex.cpp:158
FxRateQuote(Handle< Quote > spotQuote, const Handle< YieldTermStructure > &sourceYts, const Handle< YieldTermStructure > &targetYts, Natural fixingDays, const Calendar &fixingCalendar)
if sourceYts, targetYts are not given, the non-discounted spot quote will be returned as a fallback
Definition: fxindex.cpp:45
void update() override
Definition: fxindex.cpp:70
const Handle< Quote > spotQuote_
Definition: fxindex.hpp:66
const Handle< YieldTermStructure > targetYts_
Definition: fxindex.hpp:67
const Handle< YieldTermStructure > sourceYts_
Definition: fxindex.hpp:67
Real value() const override
Definition: fxindex.cpp:55
Calendar fixingCalendar_
Definition: fxindex.hpp:69
bool isValid() const override
Definition: fxindex.cpp:68
void update() override
Definition: fxindex.cpp:99
FxSpotQuote(Handle< Quote > todaysQuote, const Handle< YieldTermStructure > &sourceYts, const Handle< YieldTermStructure > &targetYts, Natural fixingDays, const Calendar &fixingCalendar)
Definition: fxindex.cpp:72
const Handle< YieldTermStructure > targetYts_
Definition: fxindex.hpp:87
const Handle< Quote > todaysQuote_
Definition: fxindex.hpp:86
const Handle< YieldTermStructure > sourceYts_
Definition: fxindex.hpp:87
Real value() const override
Definition: fxindex.cpp:82
Calendar fixingCalendar_
Definition: fxindex.hpp:89
bool isValid() const override
Definition: fxindex.cpp:95
FX index class.
Filter close_enough(const RandomVariable &x, const RandomVariable &y)