| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2004 Decillion Pty(Ltd) |
| 5 | Copyright (C) 2007 StatPro Italia srl |
| 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 | <http://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/money.hpp> |
| 22 | #include <ql/currencies/exchangeratemanager.hpp> |
| 23 | #include <ql/math/comparison.hpp> |
| 24 | |
| 25 | #include <boost/format.hpp> |
| 26 | |
| 27 | namespace QuantLib { |
| 28 | |
| 29 | namespace { |
| 30 | |
| 31 | void convertTo(Money& m, const Currency& target) { |
| 32 | if (m.currency() != target) { |
| 33 | ExchangeRate rate = |
| 34 | ExchangeRateManager::instance().lookup(source: m.currency(), |
| 35 | target); |
| 36 | m = rate.exchange(amount: m).rounded(); |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | void convertToBase(Money& m) { |
| 41 | const auto & base_currency = |
| 42 | Money::Settings::instance().baseCurrency(); |
| 43 | QL_REQUIRE(!base_currency.empty(), "no base currency set" ); |
| 44 | convertTo(m, target: base_currency); |
| 45 | } |
| 46 | |
| 47 | } |
| 48 | |
| 49 | Money& Money::operator+=(const Money& m) { |
| 50 | const auto & conversion_type = Settings::instance().conversionType(); |
| 51 | if (currency_ == m.currency_) { |
| 52 | value_ += m.value_; |
| 53 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 54 | convertToBase(m&: *this); |
| 55 | Money tmp = m; |
| 56 | convertToBase(m&: tmp); |
| 57 | *this += tmp; |
| 58 | } else if (conversion_type == Money::AutomatedConversion) { |
| 59 | Money tmp = m; |
| 60 | convertTo(m&: tmp, target: currency_); |
| 61 | *this += tmp; |
| 62 | } else { |
| 63 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 64 | } |
| 65 | return *this; |
| 66 | } |
| 67 | |
| 68 | Money& Money::operator-=(const Money& m) { |
| 69 | const auto & conversion_type = Settings::instance().conversionType(); |
| 70 | if (currency_ == m.currency_) { |
| 71 | value_ -= m.value_; |
| 72 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 73 | convertToBase(m&: *this); |
| 74 | Money tmp = m; |
| 75 | convertToBase(m&: tmp); |
| 76 | *this -= tmp; |
| 77 | } else if (conversion_type == Money::AutomatedConversion) { |
| 78 | Money tmp = m; |
| 79 | convertTo(m&: tmp, target: currency_); |
| 80 | *this -= tmp; |
| 81 | } else { |
| 82 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 83 | } |
| 84 | return *this; |
| 85 | } |
| 86 | |
| 87 | Decimal operator/(const Money& m1, const Money& m2) { |
| 88 | const auto & conversion_type = |
| 89 | Money::Settings::instance().conversionType(); |
| 90 | if (m1.currency() == m2.currency()) { |
| 91 | return m1.value()/m2.value(); |
| 92 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 93 | Money tmp1 = m1; |
| 94 | convertToBase(m&: tmp1); |
| 95 | Money tmp2 = m2; |
| 96 | convertToBase(m&: tmp2); |
| 97 | return tmp1/tmp2; |
| 98 | } else if (conversion_type == Money::AutomatedConversion) { |
| 99 | Money tmp = m2; |
| 100 | convertTo(m&: tmp, target: m1.currency()); |
| 101 | return m1/tmp; |
| 102 | } else { |
| 103 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | bool operator==(const Money& m1, const Money& m2) { |
| 108 | const auto & conversion_type = |
| 109 | Money::Settings::instance().conversionType(); |
| 110 | if (m1.currency() == m2.currency()) { |
| 111 | return m1.value() == m2.value(); |
| 112 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 113 | Money tmp1 = m1; |
| 114 | convertToBase(m&: tmp1); |
| 115 | Money tmp2 = m2; |
| 116 | convertToBase(m&: tmp2); |
| 117 | return tmp1 == tmp2; |
| 118 | } else if (conversion_type == Money::AutomatedConversion) { |
| 119 | Money tmp = m2; |
| 120 | convertTo(m&: tmp, target: m1.currency()); |
| 121 | return m1 == tmp; |
| 122 | } else { |
| 123 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | bool operator<(const Money& m1, const Money& m2) { |
| 128 | const auto & conversion_type = |
| 129 | Money::Settings::instance().conversionType(); |
| 130 | if (m1.currency() == m2.currency()) { |
| 131 | return m1.value() < m2.value(); |
| 132 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 133 | Money tmp1 = m1; |
| 134 | convertToBase(m&: tmp1); |
| 135 | Money tmp2 = m2; |
| 136 | convertToBase(m&: tmp2); |
| 137 | return tmp1 < tmp2; |
| 138 | } else if (conversion_type == Money::AutomatedConversion) { |
| 139 | Money tmp = m2; |
| 140 | convertTo(m&: tmp, target: m1.currency()); |
| 141 | return m1 < tmp; |
| 142 | } else { |
| 143 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | bool operator<=(const Money& m1, const Money& m2) { |
| 148 | const auto & conversion_type = |
| 149 | Money::Settings::instance().conversionType(); |
| 150 | if (m1.currency() == m2.currency()) { |
| 151 | return m1.value() <= m2.value(); |
| 152 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 153 | Money tmp1 = m1; |
| 154 | convertToBase(m&: tmp1); |
| 155 | Money tmp2 = m2; |
| 156 | convertToBase(m&: tmp2); |
| 157 | return tmp1 <= tmp2; |
| 158 | } else if (conversion_type == Money::AutomatedConversion) { |
| 159 | Money tmp = m2; |
| 160 | convertTo(m&: tmp, target: m1.currency()); |
| 161 | return m1 <= tmp; |
| 162 | } else { |
| 163 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | bool close(const Money& m1, const Money& m2, Size n) { |
| 168 | const auto & conversion_type = |
| 169 | Money::Settings::instance().conversionType(); |
| 170 | if (m1.currency() == m2.currency()) { |
| 171 | return close(x: m1.value(),y: m2.value(),n); |
| 172 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 173 | Money tmp1 = m1; |
| 174 | convertToBase(m&: tmp1); |
| 175 | Money tmp2 = m2; |
| 176 | convertToBase(m&: tmp2); |
| 177 | return close(m1: tmp1,m2: tmp2,n); |
| 178 | } else if (conversion_type == Money::AutomatedConversion) { |
| 179 | Money tmp = m2; |
| 180 | convertTo(m&: tmp, target: m1.currency()); |
| 181 | return close(m1,m2: tmp,n); |
| 182 | } else { |
| 183 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | bool close_enough(const Money& m1, const Money& m2, Size n) { |
| 188 | const auto & conversion_type = |
| 189 | Money::Settings::instance().conversionType(); |
| 190 | if (m1.currency() == m2.currency()) { |
| 191 | return close_enough(x: m1.value(),y: m2.value(),n); |
| 192 | } else if (conversion_type == Money::BaseCurrencyConversion) { |
| 193 | Money tmp1 = m1; |
| 194 | convertToBase(m&: tmp1); |
| 195 | Money tmp2 = m2; |
| 196 | convertToBase(m&: tmp2); |
| 197 | return close_enough(m1: tmp1,m2: tmp2,n); |
| 198 | } else if (conversion_type == Money::AutomatedConversion) { |
| 199 | Money tmp = m2; |
| 200 | convertTo(m&: tmp, target: m1.currency()); |
| 201 | return close_enough(m1,m2: tmp,n); |
| 202 | } else { |
| 203 | QL_FAIL("currency mismatch and no conversion specified" ); |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | |
| 208 | std::ostream& operator<<(std::ostream& out, const Money& m) { |
| 209 | boost::format fmt(m.currency().format()); |
| 210 | fmt.exceptions(newexcept: boost::io::all_error_bits ^ |
| 211 | boost::io::too_many_args_bit); |
| 212 | return out << fmt % m.rounded().value() |
| 213 | % m.currency().code() |
| 214 | % m.currency().symbol(); |
| 215 | } |
| 216 | |
| 217 | |
| 218 | const Money::ConversionType & Money::Settings::conversionType() const |
| 219 | { |
| 220 | return conversionType_; |
| 221 | } |
| 222 | |
| 223 | Money::ConversionType & Money::Settings::conversionType() |
| 224 | { |
| 225 | return conversionType_; |
| 226 | } |
| 227 | |
| 228 | const Currency & Money::Settings::baseCurrency() const |
| 229 | { |
| 230 | return baseCurrency_; |
| 231 | } |
| 232 | |
| 233 | Currency & Money::Settings::baseCurrency() |
| 234 | { |
| 235 | return baseCurrency_; |
| 236 | } |
| 237 | |
| 238 | Money::BaseCurrencyProxy& Money::BaseCurrencyProxy::operator=(const Currency& c) { |
| 239 | Money::Settings::instance().baseCurrency() = c; |
| 240 | return *this; |
| 241 | } |
| 242 | |
| 243 | Money::BaseCurrencyProxy::operator Currency() const { |
| 244 | return Money::Settings::instance().baseCurrency(); |
| 245 | } |
| 246 | |
| 247 | Money::ConversionTypeProxy& Money::ConversionTypeProxy::operator=(ConversionType t) { |
| 248 | Money::Settings::instance().conversionType() = t; |
| 249 | return *this; |
| 250 | } |
| 251 | |
| 252 | Money::ConversionTypeProxy::operator Money::ConversionType() const { |
| 253 | return Money::Settings::instance().conversionType(); |
| 254 | } |
| 255 | |
| 256 | } |
| 257 | |