historical-bank 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|