historical-bank 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +2 -0
- data/CHANGELOG.md +4 -0
- data/CONTRIBUTING.md +84 -0
- data/Gemfile +21 -0
- data/LICENSE +7 -0
- data/README.md +296 -0
- data/examples/add_get_rates.rb +110 -0
- data/examples/exchange.rb +75 -0
- data/historical-bank.gemspec +56 -0
- data/lib/money/bank/historical.rb +339 -0
- data/lib/money/rates_provider/open_exchange_rates.rb +123 -0
- data/lib/money/rates_store/historical_redis.rb +133 -0
- data/spec/bank/historical_spec.rb +503 -0
- data/spec/fixtures/time-series-2015-09.json +5199 -0
- data/spec/rates_provider/open_exchange_rates_spec.rb +201 -0
- data/spec/rates_store/historical_redis_spec.rb +176 -0
- data/spec/spec_helper.rb +29 -0
- metadata +209 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2017 Skyscanner Limited.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
require 'money/bank/historical'
|
20
|
+
|
21
|
+
redis_url = 'redis://localhost:6379'
|
22
|
+
namespace = 'currency_example'
|
23
|
+
|
24
|
+
Money::Bank::Historical.configure do |config|
|
25
|
+
# (required) your OpenExchangeRates App ID
|
26
|
+
config.oer_app_id = 'XXXXXXXXXXXXXXXXXXXXX'
|
27
|
+
|
28
|
+
# (optional) currency relative to which all the rates are stored (default: EUR)
|
29
|
+
config.base_currency = Money::Currency.new('USD')
|
30
|
+
|
31
|
+
# (optional) the URL of the Redis server (default: 'redis://localhost:6379')
|
32
|
+
config.redis_url = redis_url
|
33
|
+
|
34
|
+
# (optional) Redis namespace to prefix all keys (default: 'currency')
|
35
|
+
config.redis_namespace = namespace
|
36
|
+
|
37
|
+
# (optional) set a timeout for the OER calls (default: 15 seconds)
|
38
|
+
config.timeout = 20
|
39
|
+
end
|
40
|
+
|
41
|
+
bank = Money::Bank::Historical.instance
|
42
|
+
|
43
|
+
# In Rails, you can set this in the MoneyRails.configure block
|
44
|
+
Money.default_bank = bank
|
45
|
+
|
46
|
+
from_money = Money.new(100_00, 'EUR')
|
47
|
+
to_currency = 'GBP'
|
48
|
+
|
49
|
+
########## Exchange with the Bank object ##############
|
50
|
+
|
51
|
+
# exchange money normally as you do with normal banks (uses yesterday's closing rates)
|
52
|
+
bank.exchange_with(from_money, to_currency)
|
53
|
+
|
54
|
+
# exchange money with rates from December 10th 2016
|
55
|
+
bank.exchange_with_historical(from_money, to_currency, Date.new(2016, 12, 10))
|
56
|
+
# => #<Money fractional:8399 currency:GBP>
|
57
|
+
|
58
|
+
# can also pass a Time/DateTime object
|
59
|
+
bank.exchange_with_historical(from_money, to_currency, Time.utc(2016, 10, 2, 11, 0, 0))
|
60
|
+
# => #<Money fractional:8691 currency:GBP>
|
61
|
+
|
62
|
+
########## Exchange with the Money object ##############
|
63
|
+
|
64
|
+
# since it is set as the default bank, we can call Money#exchange_to (uses yesterday's closing rates)
|
65
|
+
from_money.exchange_to(to_currency)
|
66
|
+
|
67
|
+
# same result with a direct call on the Money object
|
68
|
+
from_money.exchange_to_historical(to_currency, Date.new(2016, 12, 10))
|
69
|
+
# => #<Money fractional:8399 currency:GBP>
|
70
|
+
|
71
|
+
########## Clean up Redis keys used here ##############
|
72
|
+
|
73
|
+
redis = Redis.new(url: redis_url)
|
74
|
+
keys = redis.keys("#{namespace}*")
|
75
|
+
redis.del(keys)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2017 Skyscanner Limited.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
Gem::Specification.new do |s|
|
20
|
+
s.name = 'historical-bank'
|
21
|
+
s.version = '0.1.0'
|
22
|
+
s.summary = 'Historical Bank'
|
23
|
+
s.description = 'A `Money::Bank::Base` with historical exchange rates'
|
24
|
+
s.authors = ['Kostis Dadamis', 'Emili Parreno']
|
25
|
+
s.email = ['kostis.dadamis@skyscanner.net']
|
26
|
+
s.homepage = 'https://github.com/Skyscanner/historical-bank-ruby'
|
27
|
+
s.license = 'Apache-2.0'
|
28
|
+
|
29
|
+
require 'rake'
|
30
|
+
s.files = FileList['lib/**/*.rb', 'Gemfile', 'examples/*.rb',
|
31
|
+
'historical-bank.gemspec', 'spec/**/*.rb'].to_a
|
32
|
+
s.files += ['README.md', 'LICENSE', 'CONTRIBUTING.md', 'AUTHORS',
|
33
|
+
'CHANGELOG.md', 'spec/fixtures/time-series-2015-09.json']
|
34
|
+
|
35
|
+
s.test_files = s.files.grep(%r{^spec/})
|
36
|
+
|
37
|
+
s.extra_rdoc_files = ['README.md']
|
38
|
+
|
39
|
+
s.requirements = 'redis'
|
40
|
+
|
41
|
+
s.require_path = 'lib'
|
42
|
+
|
43
|
+
s.required_ruby_version = '>= 2.0.0'
|
44
|
+
|
45
|
+
s.add_runtime_dependency 'money', '~> 6.7'
|
46
|
+
s.add_runtime_dependency 'httparty', '~> 0.14'
|
47
|
+
s.add_runtime_dependency 'redis', '~> 3.3'
|
48
|
+
|
49
|
+
s.add_development_dependency 'rspec', '~> 3.5'
|
50
|
+
s.add_development_dependency 'pry-byebug', '~> 3.4'
|
51
|
+
s.add_development_dependency 'rubocop', '~> 0.46'
|
52
|
+
s.add_development_dependency 'rack-test', '~> 0.6'
|
53
|
+
s.add_development_dependency 'webmock', '~> 2.3'
|
54
|
+
s.add_development_dependency 'faker', '~> 1.6'
|
55
|
+
s.add_development_dependency 'timecop', '~> 0.8'
|
56
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2017 Skyscanner Limited.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
require 'money'
|
20
|
+
require 'money/rates_provider/open_exchange_rates'
|
21
|
+
require 'money/rates_store/historical_redis'
|
22
|
+
|
23
|
+
class Money
|
24
|
+
module Bank
|
25
|
+
# Bank that serves historical exchange rates. Inherits from
|
26
|
+
# +Money::Bank::Base+
|
27
|
+
class Historical < Bank::Base
|
28
|
+
# Configuration class for +Money::Bank::Historical+
|
29
|
+
class Configuration
|
30
|
+
# +Money::Currency+ relative to which all exchange rates will be cached
|
31
|
+
attr_accessor :base_currency
|
32
|
+
# URL of the Redis server
|
33
|
+
attr_accessor :redis_url
|
34
|
+
# Redis namespace in which the exchange rates will be cached
|
35
|
+
attr_accessor :redis_namespace
|
36
|
+
# OpenExchangeRates app ID
|
37
|
+
attr_accessor :oer_app_id
|
38
|
+
# timeout to set in the OpenExchangeRates requests
|
39
|
+
attr_accessor :timeout
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@base_currency = Currency.new('EUR')
|
43
|
+
@redis_url = 'redis://localhost:6379'
|
44
|
+
@redis_namespace = 'currency'
|
45
|
+
@oer_app_id = nil
|
46
|
+
@timeout = 15
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the configuration (+Money::Bank::Historical::Configuration+)
|
51
|
+
def self.configuration
|
52
|
+
@configuration ||= Configuration.new
|
53
|
+
end
|
54
|
+
|
55
|
+
# Configures the bank. Parameters that can be configured:
|
56
|
+
# - +oer_app_id+ - (required) your OpenExchangeRates App ID
|
57
|
+
# - +base_currency+ - (optional) +Money::Currency+ relative to which all the rates are stored (default: EUR)
|
58
|
+
# - +redis_url+ - (optional) the URL of the Redis server (default: +redis://localhost:6379+)
|
59
|
+
# - +redis_namespace+ - (optional) Redis namespace to prefix all keys (default: +currency+)
|
60
|
+
# - +timeout+ - (optional) set a timeout for the OER calls (default: 15 seconds)
|
61
|
+
#
|
62
|
+
# ==== Examples
|
63
|
+
#
|
64
|
+
# Money::Bank::Historical.configure do |config|
|
65
|
+
# config.oer_app_id = 'XXXXXXXXXXXXXXXXXXXXX'
|
66
|
+
# config.base_currency = Money::Currency.new('USD')
|
67
|
+
# config.redis_url = 'redis://localhost:6379'
|
68
|
+
# config.redis_namespace = 'currency'
|
69
|
+
# config.timeout = 20
|
70
|
+
# end
|
71
|
+
def self.configure
|
72
|
+
yield(configuration)
|
73
|
+
instance.setup
|
74
|
+
end
|
75
|
+
|
76
|
+
# Called at the end of the superclass' +initialize+ and also when
|
77
|
+
# configuration changes. It initializes/resets all the instance variables.
|
78
|
+
def setup
|
79
|
+
@base_currency = Historical.configuration.base_currency
|
80
|
+
# Hash[iso_currency][iso_date]
|
81
|
+
@rates = {}
|
82
|
+
@store = RatesStore::HistoricalRedis.new(@base_currency,
|
83
|
+
Historical.configuration.redis_url,
|
84
|
+
Historical.configuration.redis_namespace)
|
85
|
+
@provider = RatesProvider::OpenExchangeRates.new(Historical.configuration.oer_app_id,
|
86
|
+
@base_currency,
|
87
|
+
Historical.configuration.timeout)
|
88
|
+
# for controlling access to @rates
|
89
|
+
@mutex = Mutex.new
|
90
|
+
end
|
91
|
+
|
92
|
+
# Adds historical rates in bulk to the Redis cache.
|
93
|
+
#
|
94
|
+
# ==== Parameters
|
95
|
+
#
|
96
|
+
# +currency_date_rate_hash+ - A +Hash+ of exchange rates, broken down by currency and date. See the example for details.
|
97
|
+
#
|
98
|
+
# ==== Examples
|
99
|
+
#
|
100
|
+
# Assuming USD is the base currency
|
101
|
+
#
|
102
|
+
# rates = {
|
103
|
+
# 'EUR' => {
|
104
|
+
# '2015-09-10' => 0.11, # 1 USD = 0.11 EUR
|
105
|
+
# '2015-09-11' => 0.22
|
106
|
+
# },
|
107
|
+
# 'GBP' => {
|
108
|
+
# '2015-09-10' => 0.44, # 1 USD = 0.44 GBP
|
109
|
+
# '2015-09-11' => 0.55
|
110
|
+
# }
|
111
|
+
# }
|
112
|
+
# bank.add_rates(rates)
|
113
|
+
|
114
|
+
def add_rates(currency_date_rate_hash)
|
115
|
+
@store.add_rates(currency_date_rate_hash)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Adds a single rate for a specific date to the Redis cache.
|
119
|
+
# If no datetime is passed, it defaults to yesterday (UTC).
|
120
|
+
# One of the passed currencies should match the base currency.
|
121
|
+
#
|
122
|
+
# ==== Parameters
|
123
|
+
#
|
124
|
+
# - +from_currency+ - Fixed currency of the +rate+ (https://en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String and +Money::Currency+ objects.
|
125
|
+
# - +to_currency+ - Variable currency of the +rate+ (https://en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String and +Money::Currency+ objects.
|
126
|
+
# - +rate+ - The price of 1 unit of +from_currency+ in +to_currency+.
|
127
|
+
# - +datetime+ - The +Date+ this +rate+ was observed. If +Time+ is passed instead, it's converted to the UTC +Date+. If no +datetime+ is passed, it defaults to yesterday (UTC).
|
128
|
+
#
|
129
|
+
# ==== Errors
|
130
|
+
#
|
131
|
+
# - Raises +ArgumentError+ when neither +from_currency+, nor +to_currency+ match the +base_currency+ given in the configuration.
|
132
|
+
#
|
133
|
+
# ==== Examples
|
134
|
+
#
|
135
|
+
# Assuming USD is the base currency
|
136
|
+
#
|
137
|
+
# from_money = Money.new(100_00, 'EUR')
|
138
|
+
# to_currency = 'GBP'
|
139
|
+
#
|
140
|
+
# date = Date.new(2016, 5, 18)
|
141
|
+
#
|
142
|
+
# # 1 EUR = 1.2 USD on May 18th 2016
|
143
|
+
# bank.add_rate('EUR', 'USD', 1.2, date)
|
144
|
+
# # 1 USD = 0.8 GBP on May 18th 2016
|
145
|
+
# bank.add_rate(Money::Currency.new('USD'), Money::Currency.new('GBP'), 0.8, date)
|
146
|
+
#
|
147
|
+
# # 100 EUR = 100 * 1.2 USD = 100 * 1.2 * 0.8 GBP = 96 GBP
|
148
|
+
# bank.exchange_with_historical(from_money, to_currency, date)
|
149
|
+
# # => #<Money fractional:9600 currency:GBP>
|
150
|
+
|
151
|
+
def add_rate(from_currency, to_currency, rate, datetime = yesterday_utc)
|
152
|
+
from_currency = Currency.wrap(from_currency)
|
153
|
+
to_currency = Currency.wrap(to_currency)
|
154
|
+
|
155
|
+
if from_currency != @base_currency && to_currency != @base_currency
|
156
|
+
raise ArgumentError, "`from_currency` (#{from_currency.iso_code}) or "\
|
157
|
+
"`to_currency` (#{to_currency.iso_code}) should "\
|
158
|
+
"match the base currency #{@base_currency.iso_code}"
|
159
|
+
end
|
160
|
+
|
161
|
+
date = datetime_to_date(datetime)
|
162
|
+
|
163
|
+
currency_date_rate_hash = if from_currency == @base_currency
|
164
|
+
{
|
165
|
+
to_currency.iso_code => {
|
166
|
+
date.iso8601 => rate
|
167
|
+
}
|
168
|
+
}
|
169
|
+
else
|
170
|
+
{
|
171
|
+
from_currency.iso_code => {
|
172
|
+
date.iso8601 => 1 / rate
|
173
|
+
}
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
add_rates(currency_date_rate_hash)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns the +BigDecimal+ rate for converting +from_currency+
|
181
|
+
# to +to_currency+ on a specific date. This is the price of 1 unit of
|
182
|
+
# +from_currency+ in +to_currency+ on that date.
|
183
|
+
# If rate is not found in the Redis cache, it is fetched from
|
184
|
+
# OpenExchangeRates.
|
185
|
+
# If no +datetime+ is passed, it defaults to yesterday (UTC).
|
186
|
+
#
|
187
|
+
# ==== Parameters
|
188
|
+
#
|
189
|
+
# - +from_currency+ - Fixed currency of the returned rate (https://en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String and +Money::Currency+ objects.
|
190
|
+
# - +to_currency+ - Variable currency of the returned rate (https://en.wikipedia.org/wiki/Exchange_rate#Quotations). Accepts ISO String and +Money::Currency+ objects.
|
191
|
+
# - +datetime+ - The +Date+ the returned rate was observed. If +Time+ is passed instead, it's converted to the UTC +Date+. If no +datetime+ is passed, it defaults to yesterday (UTC).
|
192
|
+
#
|
193
|
+
# ==== Examples
|
194
|
+
#
|
195
|
+
# bank.get_rate(Money::Currency.new('GBP'), 'CAD', Date.new(2016, 10, 1))
|
196
|
+
# # => #<BigDecimal:7fd39fd2cb78,'0.1703941289 451827243E1',27(45)>
|
197
|
+
|
198
|
+
def get_rate(from_currency, to_currency, datetime = yesterday_utc)
|
199
|
+
from_currency = Currency.wrap(from_currency)
|
200
|
+
to_currency = Currency.wrap(to_currency)
|
201
|
+
|
202
|
+
date = datetime_to_date(datetime)
|
203
|
+
|
204
|
+
rate_on_date(from_currency, to_currency, date)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Exchanges +from_money+ to +to_currency+ using yesterday's
|
208
|
+
# closing rates and returns a new +Money+ object.
|
209
|
+
#
|
210
|
+
# ==== Parameters
|
211
|
+
#
|
212
|
+
# - +from_money+ - The +Money+ object to exchange
|
213
|
+
# - +to_currency+ - The currency to exchange +from_money+ to. Accepts ISO String and +Money::Currency+ objects.
|
214
|
+
def exchange_with(from_money, to_currency)
|
215
|
+
exchange_with_historical(from_money, to_currency, yesterday_utc)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Exchanges +from_money+ to +to_currency+ using +datetime+'s
|
219
|
+
# closing rates and returns a new +Money+ object.
|
220
|
+
#
|
221
|
+
# ==== Parameters
|
222
|
+
#
|
223
|
+
# - +from_money+ - The +Money+ object to exchange
|
224
|
+
# - +to_currency+ - The currency to exchange +from_money+ to. Accepts ISO String and +Money::Currency+ objects.
|
225
|
+
# - +datetime+ - The +Date+ to get the exchange rate from. If +Time+ is passed instead, it's converted to the UTC +Date+.
|
226
|
+
def exchange_with_historical(from_money, to_currency, datetime)
|
227
|
+
date = datetime_to_date(datetime)
|
228
|
+
|
229
|
+
from_currency = from_money.currency
|
230
|
+
to_currency = Currency.wrap(to_currency)
|
231
|
+
|
232
|
+
rate = rate_on_date(from_currency, to_currency, date)
|
233
|
+
to_amount = from_money.amount * rate
|
234
|
+
|
235
|
+
Money.from_amount(to_amount, to_currency)
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def datetime_to_date(datetime)
|
241
|
+
datetime.is_a?(Date) ? datetime : datetime.utc.to_date
|
242
|
+
end
|
243
|
+
|
244
|
+
# rate for converting 1 unit of from_currency (e.g. USD) to to_currency (e.g. GBP).
|
245
|
+
# Comments below assume EUR is the base currency,
|
246
|
+
# 1 EUR = 1.21 USD, and 1 EUR = 0.83 GBP on given date
|
247
|
+
def rate_on_date(from_currency, to_currency, date)
|
248
|
+
return 1 if from_currency == to_currency
|
249
|
+
|
250
|
+
# 1 EUR = 1.21 USD => 1 USD = 1/1.21 EUR
|
251
|
+
from_base_to_from_rate = base_rate_on_date(from_currency, date)
|
252
|
+
# 1 EUR = 0.83 GBP
|
253
|
+
from_base_to_to_rate = base_rate_on_date(to_currency, date)
|
254
|
+
|
255
|
+
# 1 USD = 1/1.21 EUR = (1/1.21) * 0.83 GBP = 0.83/1.21 GBP
|
256
|
+
from_base_to_to_rate / from_base_to_from_rate
|
257
|
+
end
|
258
|
+
|
259
|
+
# rate for converting 1 unit of base currency to currency
|
260
|
+
def base_rate_on_date(currency, date)
|
261
|
+
return 1 if @base_currency == currency
|
262
|
+
|
263
|
+
rate = get_base_rate(currency, date) ||
|
264
|
+
fetch_stored_base_rate(currency, date) ||
|
265
|
+
fetch_provider_base_rate(currency, date)
|
266
|
+
|
267
|
+
if rate.nil?
|
268
|
+
raise UnknownRate, "Rate from #{currency} to #{@base_currency} "\
|
269
|
+
"on #{date} not found"
|
270
|
+
end
|
271
|
+
|
272
|
+
rate
|
273
|
+
end
|
274
|
+
|
275
|
+
def fetch_stored_base_rate(currency, date)
|
276
|
+
date_rate_hash = @store.get_rates(currency)
|
277
|
+
|
278
|
+
if date_rate_hash && !date_rate_hash.empty?
|
279
|
+
rate = date_rate_hash[date.iso8601]
|
280
|
+
set_base_rates(currency, date_rate_hash)
|
281
|
+
|
282
|
+
rate
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def fetch_provider_base_rate(currency, date)
|
287
|
+
currency_date_rate_hash = @provider.fetch_month_rates(date)
|
288
|
+
|
289
|
+
date_rate_hash = currency_date_rate_hash[currency.iso_code]
|
290
|
+
rate = date_rate_hash && date_rate_hash[date.iso8601]
|
291
|
+
|
292
|
+
if currency_date_rate_hash && !currency_date_rate_hash.empty?
|
293
|
+
@store.add_rates(currency_date_rate_hash)
|
294
|
+
end
|
295
|
+
|
296
|
+
if date_rate_hash && !date_rate_hash.empty?
|
297
|
+
set_base_rates(currency, date_rate_hash)
|
298
|
+
end
|
299
|
+
|
300
|
+
rate
|
301
|
+
end
|
302
|
+
|
303
|
+
def set_base_rates(currency, date_rate_hash)
|
304
|
+
iso_currency = currency.iso_code
|
305
|
+
@mutex.synchronize do
|
306
|
+
@rates[iso_currency] = {} if @rates[iso_currency].nil?
|
307
|
+
@rates[iso_currency].merge!(date_rate_hash)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def get_base_rate(currency, date)
|
312
|
+
@mutex.synchronize do
|
313
|
+
rates = @rates[currency.iso_code]
|
314
|
+
rates[date] if rates
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# yesterday in UTC timezone
|
319
|
+
def yesterday_utc
|
320
|
+
Time.now.utc.to_date - 1
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Exchanges to +other_currency+ using +datetime+'s
|
326
|
+
# closing rates and returns a new +Money+ object.
|
327
|
+
# +rounding_method+ is ignored in this version of the gem.
|
328
|
+
#
|
329
|
+
# ==== Parameters
|
330
|
+
#
|
331
|
+
# - +other_currency+ - The currency to exchange to. Accepts ISO String and +Money::Currency+ objects.
|
332
|
+
# - +datetime+ - The +Date+ to get the exchange rate from. If +Time+ is passed instead, it's converted to the UTC +Date+.
|
333
|
+
# - +rounding_method+ - This parameter is ignored in this version of the gem.
|
334
|
+
|
335
|
+
def exchange_to_historical(other_currency, datetime, &rounding_method)
|
336
|
+
Bank::Historical.instance.exchange_with_historical(self, other_currency,
|
337
|
+
datetime, &rounding_method)
|
338
|
+
end
|
339
|
+
end
|