28#include <ql/errors.hpp>
29#include <ql/quotes/compositequote.hpp>
30#include <ql/quotes/derivedquote.hpp>
31#include <ql/quotes/simplequote.hpp>
33#include <boost/make_shared.hpp>
43std::pair<std::string, std::string> splitPair(
const std::string& pair) {
44 QL_REQUIRE(pair.size() == 6,
"FXTriangulation: Invalid currency pair '" << pair <<
"'");
45 return std::make_pair(pair.substr(0, 3), pair.substr(3));
48Handle<YieldTermStructure> getMarketDiscountCurve(
const Market* market,
const std::string& ccy,
49 const std::string& configuration) {
51 return market->discountCurve(ccy, configuration);
52 }
catch (
const std::exception&) {
53 WLOG(
"FXTriangulation: could not get market discount curve '"
54 << ccy <<
"' (requested for configuration '" << configuration
55 <<
"') - discounted fx spot rates will be replaced by non-discounted rates in future calculations, which "
56 "might lead to inaccuracies");
57 return Handle<YieldTermStructure>();
65 LOG(
"FXTriangulation: initializing");
69 std::set<std::string> ccys;
70 for (
auto const& q : quotes_) {
71 auto [ccy1, ccy2] = splitPair(q.first);
74 TLOG(
"FXTriangulation: adding quote " << q.first);
81 static vector<string> ccyOrder = {
"USD",
"EUR",
"GBP",
"CHF",
"JPY",
"AUD",
"CAD",
"ZAR"};
83 std::set<std::string> remainingCcys(ccys);
85 for (
auto const& c : ccyOrder) {
86 if (ccys.find(c) != ccys.end()) {
87 nodeToCcy_.push_back(c);
88 remainingCcys.erase(c);
92 for (
auto const& c : remainingCcys) {
93 nodeToCcy_.push_back(c);
98 for (Size i = 0; i < nodeToCcy_.size(); ++i)
99 ccyToNode_[nodeToCcy_[i]] = i;
103 neighbours_.resize(nodeToCcy_.size());
104 for (
auto const& q : quotes_) {
105 auto [ccy1, ccy2] = splitPair(q.first);
106 Size n1 = ccyToNode_[ccy1];
107 Size n2 = ccyToNode_[ccy2];
108 neighbours_[n1].insert(n2);
109 neighbours_[n2].insert(n1);
112 LOG(
"FXTriangulation: initialized with " << quotes_.size() <<
" quotes, " << ccys.size() <<
" currencies.");
115Handle<Quote> FXTriangulation::getQuote(
const std::string& pair)
const {
119 if (
auto it = quoteCache_.find(pair); it != quoteCache_.end())
124 Handle<Quote> result;
125 auto [ccy1, ccy2] = splitPair(pair);
130 return Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.0));
134 auto path = getPath(ccy1, ccy2);
136 if (path.size() == 2) {
140 result = getQuote(path[0], path[1]);
146 std::vector<Handle<Quote>> quotes;
150 for (Size i = 0; i < path.size() - 1; ++i) {
151 quotes.push_back(getQuote(path[i], path[i + 1]));
156 auto f = [](
const std::vector<Real>& quotes) {
157 return std::accumulate(quotes.begin(), quotes.end(), 1.0, std::multiplies());
159 result = Handle<Quote>(QuantLib::ext::make_shared<
CompositeVectorQuote<
decltype(f)>>(quotes, f));
164 quoteCache_[pair] = result;
168Handle<FxIndex> FXTriangulation::getIndex(
const std::string& indexOrPair,
const Market* market,
169 const std::string& configuration)
const {
173 if (
auto it = indexCache_.find(std::make_pair(indexOrPair, configuration)); it != indexCache_.end()) {
179 Handle<FxIndex> result;
181 std::string familyName;
187 familyName = ind->familyName();
188 forCcy = ind->sourceCurrency().code();
189 domCcy = ind->targetCurrency().code();
191 familyName =
"GENERIC";
192 std::tie(forCcy, domCcy) = splitPair(indexOrPair);
201 auto sourceYts = getMarketDiscountCurve(market, forCcy, configuration);
202 auto targetYts = getMarketDiscountCurve(market, domCcy, configuration);
206 auto path = getPath(forCcy, domCcy);
208 if (path.size() == 2) {
212 auto fxSpot = getQuote(path[0], path[1]);
213 result = Handle<FxIndex>(QuantLib::ext::make_shared<FxIndex>(familyName, fixingDays,
parseCurrency(forCcy),
221 std::vector<Handle<Quote>> quotes;
225 for (Size i = 0; i < path.size() - 1; ++i) {
227 auto q = getQuote(path[i], path[i + 1]);
232 auto s_yts = getMarketDiscountCurve(market, path[i], configuration);
233 auto t_yts = getMarketDiscountCurve(market, path[i + 1], configuration);
234 quotes.push_back(Handle<Quote>(QuantLib::ext::make_shared<FxRateQuote>(q, s_yts, t_yts, fd, fc)));
239 auto f = [](
const std::vector<Real>& quotes) {
240 return std::accumulate(quotes.begin(), quotes.end(), 1.0, std::multiplies());
242 Handle<Quote> compQuote(QuantLib::ext::make_shared<
CompositeVectorQuote<
decltype(f)>>(quotes, f));
246 Handle<Quote> spotQuote(
247 QuantLib::ext::make_shared<FxSpotQuote>(compQuote, sourceYts, targetYts, fixingDays, fixingCalendar));
251 result = Handle<FxIndex>(QuantLib::ext::make_shared<FxIndex>(familyName, fixingDays,
parseCurrency(forCcy),
253 sourceYts, targetYts));
258 indexCache_[std::make_pair(indexOrPair, configuration)] = result;
262std::vector<std::string> FXTriangulation::getPath(
const std::string& forCcy,
const std::string& domCcy)
const {
266 Size sourceNode, targetNode;
268 if (
auto it = ccyToNode_.find(forCcy); it != ccyToNode_.end()) {
269 sourceNode = it->second;
271 QL_FAIL(
"FXTriangulation: no conversion from '"
272 << forCcy <<
"' to '" << domCcy <<
"' possible, since '" << forCcy
273 <<
"' is not available as one of the currencies in any of the quotes (" << getAllQuotes() <<
")");
276 if (
auto it = ccyToNode_.find(domCcy); it != ccyToNode_.end()) {
277 targetNode = it->second;
279 QL_FAIL(
"FXTriangulation: no conversion from '"
280 << forCcy <<
"' to '" << domCcy <<
"' possible, since '" << domCcy
281 <<
"' is not available as one of the currencies in any of the quotes (" << getAllQuotes() <<
")");
285 std::vector<Size> prev(nodeToCcy_.size(), Null<Size>());
287 std::vector<Size> dist(nodeToCcy_.size(), std::numeric_limits<Size>::max());
289 std::vector<bool> visited(nodeToCcy_.size(),
false);
292 dist[sourceNode] = 0;
296 while (noVisited < nodeToCcy_.size()) {
297 Size u = Null<Size>(),
min = std::numeric_limits<Size>::max();
298 for (Size i = 0; i < dist.size(); ++i) {
299 if (!visited[i] && dist[i] <
min) {
304 QL_REQUIRE(u != Null<Size>(),
"FXTriangulation: internal error, no minimum found in dist array for '"
305 << forCcy <<
"' to '" << domCcy <<
"'. Quotes = " << getAllQuotes());
310 for (
auto const& n : neighbours_[u]) {
313 Size alt = dist[u] + 1;
322 if (dist[targetNode] < std::numeric_limits<Size>::max()) {
323 std::vector<std::string> result;
325 while (u != sourceNode) {
326 result.insert(result.begin(), nodeToCcy_[u]);
328 QL_REQUIRE(u != Null<Size>(),
"FXTriangulation: internal error u == null for '"
329 << forCcy <<
"' to '" << domCcy
330 <<
"'. Contact dev. Quotes = " << getAllQuotes() <<
".");
332 result.insert(result.begin(), nodeToCcy_[sourceNode]);
333 TLOG(
"FXTriangulation: found path of length "
334 << result.size() - 1 <<
" from '" << forCcy <<
"' to '" << domCcy <<
"': "
336 result.begin(), result.end(), std::string(),
337 [](
const std::string& s1,
const std::string& s2) { return s1.empty() ? s2 : s1 +
"-" + s2; }));
341 QL_FAIL(
"FXTriangulation: no path from '" << forCcy <<
"' to '" << domCcy
342 <<
"' found. Quotes = " << getAllQuotes());
345Handle<Quote> FXTriangulation::getQuote(
const std::string& forCcy,
const std::string& domCcy)
const {
346 if (
auto it = quotes_.find(forCcy + domCcy); it != quotes_.end()) {
349 if (
auto it = quotes_.find(domCcy + forCcy); it != quotes_.end()) {
350 auto f = [](Real x) {
return 1.0 / x; };
351 return Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<
decltype(f)>>(it->second, f));
354 "FXTriangulation::getQuote(" << forCcy << domCcy
355 <<
") - no such quote available. This is an internal error. Contact dev. Quotes = "
359std::string FXTriangulation::getAllQuotes()
const {
361 if (quotes_.size() > 0) {
362 for (
auto const& d : quotes_) {
363 result += d.first +
",";
365 result.erase(std::next(result.end(), -1));
Currency and instrument specific conventions/defaults.
Intelligent FX price repository.
QuantLib::ext::shared_ptr< FxIndex > parseFxIndex(const string &s, const Handle< Quote > &fxSpot, const Handle< YieldTermStructure > &sourceYts, const Handle< YieldTermStructure > &targetYts, const bool useConventions)
Convert std::string to QuantExt::FxIndex.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Map text representations to QuantLib/QuantExt types.
#define LOG(text)
Logging Macro (Level = Notice)
#define WLOG(text)
Logging Macro (Level = Warning)
#define TLOG(text)
Logging Macro (Level = Data)
market data related utilties
RandomVariable min(RandomVariable x, const RandomVariable &y)
bool isFxIndex(const std::string &indexName)
std::tuple< Natural, Calendar, BusinessDayConvention > getFxIndexConventions(const string &index)
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
string conversion utilities