Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/currencies/exchangeratemanager.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2004, 2005, 2006, 2007, 2008 StatPro Italia srl
5
 Copyright (C) 2004 Decillion Pty(Ltd)
6
7
 This file is part of QuantLib, a free-software/open-source library
8
 for financial quantitative analysts and developers - http://quantlib.org/
9
10
 QuantLib is free software: you can redistribute it and/or modify it
11
 under the terms of the QuantLib license.  You should have received a
12
 copy of the license along with this program; if not, please email
13
 <quantlib-dev@lists.sf.net>. The license is also available online at
14
 <https://www.quantlib.org/license.shtml>.
15
16
 This program is distributed in the hope that it will be useful, but WITHOUT
17
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 FOR A PARTICULAR PURPOSE.  See the license for more details.
19
*/
20
21
#include <ql/currencies/exchangeratemanager.hpp>
22
#include <ql/currencies/europe.hpp>
23
#include <ql/currencies/america.hpp>
24
#include <ql/settings.hpp>
25
#include <algorithm>
26
27
namespace QuantLib {
28
29
    namespace {
30
31
        struct valid_at {
32
            Date d;
33
0
            explicit valid_at(const Date& d) : d(d) {}
34
0
            bool operator()(const ExchangeRateManager::Entry& e) const {
35
0
                return d >= e.startDate && d <= e.endDate;
36
0
            }
37
        };
38
39
    }
40
41
0
    ExchangeRateManager::ExchangeRateManager() {
42
0
        addKnownRates();
43
0
    }
44
45
    void ExchangeRateManager::add(const ExchangeRate& rate,
46
                                  const Date& startDate,
47
0
                                  const Date& endDate) {
48
0
        Key k = hash(rate.source(), rate.target());
49
0
        data_[k].emplace_front(rate,startDate,endDate);
50
0
    }
51
52
    ExchangeRate ExchangeRateManager::lookup(const Currency& source,
53
                                             const Currency& target,
54
                                             Date date,
55
0
                                             ExchangeRate::Type type) const {
56
57
0
        if (source == target)
58
0
            return ExchangeRate(source,target,1.0);
59
60
0
        if (date == Date())
61
0
            date = Settings::instance().evaluationDate();
62
63
0
        if (type == ExchangeRate::Direct) {
64
0
            return directLookup(source,target,date);
65
0
        } else if (!source.triangulationCurrency().empty()) {
66
0
            const Currency& link = source.triangulationCurrency();
67
0
            if (link == target)
68
0
                return directLookup(source,link,date);
69
0
            else
70
0
                return ExchangeRate::chain(directLookup(source,link,date),
71
0
                                           lookup(link,target,date));
72
0
        } else if (!target.triangulationCurrency().empty()) {
73
0
            const Currency& link = target.triangulationCurrency();
74
0
            if (source == link)
75
0
                return directLookup(link,target,date);
76
0
            else
77
0
                return ExchangeRate::chain(lookup(source,link,date),
78
0
                                           directLookup(link,target,date));
79
0
        } else {
80
0
            return smartLookup(source,target,date);
81
0
        }
82
0
    }
83
84
0
    void ExchangeRateManager::clear() {
85
0
        data_.clear();
86
0
        addKnownRates();
87
0
    }
88
89
    ExchangeRateManager::Key ExchangeRateManager::hash(
90
0
                               const Currency& c1, const Currency& c2) const {
91
0
        return Key(std::min(c1.numericCode(),c2.numericCode()))*1000
92
0
             + Key(std::max(c1.numericCode(),c2.numericCode()));
93
0
    }
94
95
    bool ExchangeRateManager::hashes(ExchangeRateManager::Key k,
96
0
                                     const Currency& c) const {
97
0
        return c.numericCode() == k % 1000 || c.numericCode() == k/1000;
98
0
    }
99
100
0
    void ExchangeRateManager::addKnownRates() {
101
        // currencies obsoleted by Euro
102
0
        add(ExchangeRate(EURCurrency(), ATSCurrency(), 13.7603),
103
0
            Date(1,January,1999), Date::maxDate());
104
0
        add(ExchangeRate(EURCurrency(), BEFCurrency(), 40.3399),
105
0
            Date(1,January,1999), Date::maxDate());
106
0
        add(ExchangeRate(EURCurrency(), DEMCurrency(), 1.95583),
107
0
            Date(1,January,1999), Date::maxDate());
108
0
        add(ExchangeRate(EURCurrency(), ESPCurrency(), 166.386),
109
0
            Date(1,January,1999), Date::maxDate());
110
0
        add(ExchangeRate(EURCurrency(), FIMCurrency(), 5.94573),
111
0
            Date(1,January,1999), Date::maxDate());
112
0
        add(ExchangeRate(EURCurrency(), FRFCurrency(), 6.55957),
113
0
            Date(1,January,1999), Date::maxDate());
114
0
        add(ExchangeRate(EURCurrency(), GRDCurrency(), 340.750),
115
0
            Date(1,January,2001), Date::maxDate());
116
0
        add(ExchangeRate(EURCurrency(), IEPCurrency(), 0.787564),
117
0
            Date(1,January,1999), Date::maxDate());
118
0
        add(ExchangeRate(EURCurrency(), ITLCurrency(), 1936.27),
119
0
            Date(1,January,1999), Date::maxDate());
120
0
        add(ExchangeRate(EURCurrency(), LUFCurrency(), 40.3399),
121
0
            Date(1,January,1999), Date::maxDate());
122
0
        add(ExchangeRate(EURCurrency(), NLGCurrency(), 2.20371),
123
0
            Date(1,January,1999), Date::maxDate());
124
0
        add(ExchangeRate(EURCurrency(), PTECurrency(), 200.482),
125
0
            Date(1,January,1999), Date::maxDate());
126
        // other obsoleted currencies
127
0
        add(ExchangeRate(TRYCurrency(), TRLCurrency(), 1000000.0),
128
0
            Date(1,January,2005), Date::maxDate());
129
0
        add(ExchangeRate(RONCurrency(), ROLCurrency(), 10000.0),
130
0
            Date(1,July,2005), Date::maxDate());
131
0
        add(ExchangeRate(PENCurrency(), PEICurrency(), 1000000.0),
132
0
            Date(1,July,1991), Date::maxDate());
133
0
        add(ExchangeRate(PEICurrency(), PEHCurrency(), 1000.0),
134
0
            Date(1,February,1985), Date::maxDate());
135
0
    }
136
137
    ExchangeRate ExchangeRateManager::directLookup(const Currency& source,
138
                                                   const Currency& target,
139
0
                                                   const Date& date) const {
140
0
        if (const ExchangeRate* rate = fetch(source,target,date))
141
0
            return *rate;
142
0
        else
143
0
            QL_FAIL("no direct conversion available from "
144
0
                    << source.code() << " to " << target.code()
145
0
                    << " for " << date);
146
0
    }
147
148
    ExchangeRate ExchangeRateManager::smartLookup(
149
                                         const Currency& source,
150
                                         const Currency& target,
151
                                         const Date& date,
152
0
                                         std::list<Integer> forbidden) const {
153
        // direct exchange rates are preferred.
154
0
        if (const ExchangeRate* direct = fetch(source,target,date))
155
0
            return *direct;
156
157
        // if none is found, turn to smart lookup. The source currency
158
        // is forbidden to subsequent lookups in order to avoid cycles.
159
0
        forbidden.push_back(source.numericCode());
160
0
        std::map<Key, std::list<Entry> >::const_iterator i;
161
0
        for (i = data_.begin(); i != data_.end(); ++i) {
162
            // we look for exchange-rate data which involve our source
163
            // currency...
164
0
            if (hashes(i->first, source) && !(i->second.empty())) {
165
                // ...whose other currency is not forbidden...
166
0
                const Entry& e = i->second.front();
167
0
                const Currency& other =
168
0
                    source == e.rate.source() ?
169
0
                        e.rate.target() : e.rate.source();
170
0
                if (std::find(forbidden.begin(),forbidden.end(),
171
0
                              other.numericCode()) == forbidden.end()) {
172
                    // ...and which carries information for the requested date.
173
0
                    if (const ExchangeRate* head = fetch(source,other,date)) {
174
                        // if we can get to the target from here...
175
0
                        try {
176
0
                            ExchangeRate tail = smartLookup(other,target,date,
177
0
                                                            forbidden);
178
                            // ..we're done.
179
0
                            return ExchangeRate::chain(*head,tail);
180
0
                        } catch (Error&) {
181
                            // otherwise, we just discard this rate.
182
0
                            ;
183
0
                        }
184
0
                    }
185
0
                }
186
0
            }
187
0
        }
188
        // if the loop completed, we have no way to return the requested rate.
189
0
        QL_FAIL("no conversion available from "
190
0
                << source.code() << " to " << target.code()
191
0
                << " for " << date);
192
0
    }
193
194
    const ExchangeRate* ExchangeRateManager::fetch(const Currency& source,
195
                                                   const Currency& target,
196
0
                                                   const Date& date) const {
197
0
        const std::list<Entry>& rates = data_[hash(source,target)];
198
0
        auto i = std::find_if(rates.begin(), rates.end(), valid_at(date));
199
0
        return i == rates.end() ? (const ExchangeRate*)nullptr : &(i->rate);
200
0
    }
201
202
}
203