class Money::Bank::Historical
Bank
that serves historical exchange rates. Inherits from Money::Bank::Base
Public Class Methods
Returns the configuration (Money::Bank::Historical::Configuration
)
# File lib/money/bank/historical.rb, line 55 def self.configuration @configuration ||= Configuration.new end
Configures the bank. Parameters that can be configured:
-
oer_app_id
- (required) your OpenExchangeRates App ID -
oer_account_type
- (optional) your OpenExchangeRates account type. Choose one of the values in theMoney::RatesProvider::OpenExchangeRates::AccountType
module (default:Money::RatesProvider::OpenExchangeRates::AccountType::ENTERPRISE
) -
base_currency
- (optional)Money::Currency
relative to which all the rates are stored (default: EUR) -
redis_url
- (optional) the URL of the Redis server (default:redis://localhost:6379
) -
redis_namespace
- (optional) Redis namespace to prefix all keys (default:currency
) -
timeout
- (optional) set a timeout for the OER calls (default: 15 seconds)
Examples¶ ↑
Money::Bank::Historical.configure do |config| config.oer_app_id = 'XXXXXXXXXXXXXXXXXXXXX' config.oer_account_type = Money::RatesProvider::OpenExchangeRates::AccountType::FREE config.base_currency = Money::Currency.new('USD') config.redis_url = 'redis://localhost:6379' config.redis_namespace = 'currency' config.timeout = 20 end
# File lib/money/bank/historical.rb, line 77 def self.configure yield(configuration) instance.setup end
Public Instance Methods
Adds a single rate for a specific date to the Redis cache. If no datetime is passed, it defaults to yesterday (UTC). One of the passed currencies should match the base currency.
Parameters¶ ↑
-
from_currency
- Fixed currency of therate
(en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String andMoney::Currency
objects. -
to_currency
- Variable currency of therate
(en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String andMoney::Currency
objects. -
rate
- The price of 1 unit offrom_currency
into_currency
. -
datetime
- TheDate
thisrate
was observed. IfTime
is passed instead, it's converted to the UTCDate
. If nodatetime
is passed, it defaults to yesterday (UTC).
Errors¶ ↑
-
Raises
ArgumentError
when neitherfrom_currency
, norto_currency
match thebase_currency
given in the configuration.
Examples¶ ↑
Assuming USD is the base currency
from_money = Money.new(100_00, 'EUR') to_currency = 'GBP' date = Date.new(2016, 5, 18) # 1 EUR = 1.2 USD on May 18th 2016 bank.add_rate('EUR', 'USD', 1.2, date) # 1 USD = 0.8 GBP on May 18th 2016 bank.add_rate(Money::Currency.new('USD'), Money::Currency.new('GBP'), 0.8, date) # 100 EUR = 100 * 1.2 USD = 100 * 1.2 * 0.8 GBP = 96 GBP bank.exchange_with_historical(from_money, to_currency, date) # => #<Money fractional:9600 currency:GBP>
# File lib/money/bank/historical.rb, line 158 def add_rate(from_currency, to_currency, rate, datetime = yesterday_utc) from_currency = Currency.wrap(from_currency) to_currency = Currency.wrap(to_currency) if from_currency != @base_currency && to_currency != @base_currency raise ArgumentError, "`from_currency` (#{from_currency.iso_code}) or "\ "`to_currency` (#{to_currency.iso_code}) should "\ "match the base currency #{@base_currency.iso_code}" end date = datetime_to_date(datetime) currency_date_rate_hash = if from_currency == @base_currency { to_currency.iso_code => { date.iso8601 => rate } } else { from_currency.iso_code => { date.iso8601 => 1 / rate } } end add_rates(currency_date_rate_hash) end
Adds historical rates in bulk to the Redis cache.
Parameters¶ ↑
currency_date_rate_hash
- A Hash
of exchange rates, broken down by currency and date. See the example for details.
Examples¶ ↑
Assuming USD is the base currency
rates = { 'EUR' => { '2015-09-10' => 0.11, # 1 USD = 0.11 EUR '2015-09-11' => 0.22 }, 'GBP' => { '2015-09-10' => 0.44, # 1 USD = 0.44 GBP '2015-09-11' => 0.55 } } bank.add_rates(rates)
# File lib/money/bank/historical.rb, line 121 def add_rates(currency_date_rate_hash) @store.add_rates(currency_date_rate_hash) end
Exchanges from_money
to to_currency
using yesterday's closing rates and returns a new Money
object.
Parameters¶ ↑
-
from_money
- TheMoney
object to exchange -
to_currency
- The currency to exchangefrom_money
to. Accepts ISO String andMoney::Currency
objects.
# File lib/money/bank/historical.rb, line 221 def exchange_with(from_money, to_currency) exchange_with_historical(from_money, to_currency, yesterday_utc) end
Exchanges from_money
to to_currency
using datetime
's closing rates and returns a new Money
object.
Parameters¶ ↑
-
from_money
- TheMoney
object to exchange -
to_currency
- The currency to exchangefrom_money
to. Accepts ISO String andMoney::Currency
objects. -
datetime
- TheDate
to get the exchange rate from. IfTime
is passed instead, it's converted to the UTCDate
.
# File lib/money/bank/historical.rb, line 233 def exchange_with_historical(from_money, to_currency, datetime) date = datetime_to_date(datetime) from_currency = from_money.currency to_currency = Currency.wrap(to_currency) rate = rate_on_date(from_currency, to_currency, date) to_amount = from_money.amount * rate Money.from_amount(to_amount, to_currency) end
Returns the BigDecimal
rate for converting from_currency
to to_currency
on a specific date. This is the price of 1 unit of from_currency
in to_currency
on that date. If rate is not found in the Redis cache, it is fetched from OpenExchangeRates. If no datetime
is passed, it defaults to yesterday (UTC).
Parameters¶ ↑
-
from_currency
- Fixed currency of the returned rate (en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String andMoney::Currency
objects. -
to_currency
- Variable currency of the returned rate (en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String andMoney::Currency
objects. -
datetime
- TheDate
the returned rate was observed. IfTime
is passed instead, it's converted to the UTCDate
. If nodatetime
is passed, it defaults to yesterday (UTC).
Examples¶ ↑
bank.get_rate(Money::Currency.new('GBP'), 'CAD', Date.new(2016, 10, 1)) # => #<BigDecimal:7fd39fd2cb78,'0.1703941289 451827243E1',27(45)>
# File lib/money/bank/historical.rb, line 205 def get_rate(from_currency, to_currency, datetime = yesterday_utc) from_currency = Currency.wrap(from_currency) to_currency = Currency.wrap(to_currency) date = datetime_to_date(datetime) rate_on_date(from_currency, to_currency, date) end
Called at the end of the superclass' initialize
and also when configuration changes. It initializes/resets all the instance variables.
# File lib/money/bank/historical.rb, line 84 def setup @base_currency = Historical.configuration.base_currency # Hash[iso_currency][iso_date] @rates = {} @store = RatesStore::HistoricalRedis.new(@base_currency, Historical.configuration.redis_url, Historical.configuration.redis_namespace) @provider = RatesProvider::OpenExchangeRates.new(Historical.configuration.oer_app_id, @base_currency, Historical.configuration.timeout, Historical.configuration.oer_account_type) # for controlling access to @rates @mutex = Mutex.new end
Private Instance Methods
rate for converting 1 unit of base currency to currency
# File lib/money/bank/historical.rb, line 267 def base_rate_on_date(currency, date) return 1 if @base_currency == currency rate = get_base_rate(currency, date) || fetch_stored_base_rate(currency, date) || fetch_provider_base_rate(currency, date) if rate.nil? raise UnknownRate, "Rate from #{currency} to #{@base_currency} "\ "on #{date} not found" end rate end
# File lib/money/bank/historical.rb, line 247 def datetime_to_date(datetime) datetime.is_a?(Date) ? datetime : datetime.utc.to_date end
# File lib/money/bank/historical.rb, line 293 def fetch_provider_base_rate(currency, date) currency_date_rate_hash = @provider.fetch_rates(date) date_rate_hash = currency_date_rate_hash[currency.iso_code] rate = date_rate_hash && date_rate_hash[date.iso8601] if currency_date_rate_hash && !currency_date_rate_hash.empty? @store.add_rates(currency_date_rate_hash) end if date_rate_hash && !date_rate_hash.empty? set_base_rates(currency, date_rate_hash) end rate end
# File lib/money/bank/historical.rb, line 282 def fetch_stored_base_rate(currency, date) date_rate_hash = @store.get_rates(currency) if date_rate_hash && !date_rate_hash.empty? rate = date_rate_hash[date.iso8601] set_base_rates(currency, date_rate_hash) rate end end
# File lib/money/bank/historical.rb, line 318 def get_base_rate(currency, date) @mutex.synchronize do rates = @rates[currency.iso_code] rates[date] if rates end end
rate for converting 1 unit of from_currency (e.g. USD) to to_currency (e.g. GBP). Comments below assume EUR is the base currency, 1 EUR = 1.21 USD, and 1 EUR = 0.83 GBP on given date
# File lib/money/bank/historical.rb, line 254 def rate_on_date(from_currency, to_currency, date) return 1 if from_currency == to_currency # 1 EUR = 1.21 USD => 1 USD = 1/1.21 EUR from_base_to_from_rate = base_rate_on_date(from_currency, date) # 1 EUR = 0.83 GBP from_base_to_to_rate = base_rate_on_date(to_currency, date) # 1 USD = 1/1.21 EUR = (1/1.21) * 0.83 GBP = 0.83/1.21 GBP from_base_to_to_rate / from_base_to_from_rate end
# File lib/money/bank/historical.rb, line 310 def set_base_rates(currency, date_rate_hash) iso_currency = currency.iso_code @mutex.synchronize do @rates[iso_currency] = {} if @rates[iso_currency].nil? @rates[iso_currency].merge!(date_rate_hash) end end
yesterday in UTC timezone
# File lib/money/bank/historical.rb, line 326 def yesterday_utc Time.now.utc.to_date - 1 end