currency 0.4.6 → 0.4.7
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.
- data/Manifest.txt +2 -0
- data/Releases.txt +16 -0
- data/TODO.txt +2 -0
- data/lib/currency/currency_version.rb +1 -1
- data/lib/currency/exchange.rb +2 -0
- data/lib/currency/exchange/rate.rb +13 -2
- data/lib/currency/exchange/rate/source/federal_reserve.rb +160 -0
- data/lib/currency/exchange/rate/source/historical/rate.rb +20 -18
- data/lib/currency/exchange/rate/source/new_york_fed.rb +16 -0
- data/lib/currency/exchange/rate/source/provider.rb +8 -6
- data/test/federal_reserve_test.rb +75 -0
- metadata +4 -2
data/Manifest.txt
CHANGED
@@ -22,6 +22,7 @@ lib/currency/exchange/rate/deriver.rb
|
|
22
22
|
lib/currency/exchange/rate/source.rb
|
23
23
|
lib/currency/exchange/rate/source/base.rb
|
24
24
|
lib/currency/exchange/rate/source/failover.rb
|
25
|
+
lib/currency/exchange/rate/source/federal_reserve.rb
|
25
26
|
lib/currency/exchange/rate/source/historical.rb
|
26
27
|
lib/currency/exchange/rate/source/historical/rate.rb
|
27
28
|
lib/currency/exchange/rate/source/historical/writer.rb
|
@@ -42,6 +43,7 @@ test/ar_simple_test.rb
|
|
42
43
|
test/ar_test_base.rb
|
43
44
|
test/ar_test_core.rb
|
44
45
|
test/config_test.rb
|
46
|
+
test/federal_reserve_test.rb
|
45
47
|
test/formatter_test.rb
|
46
48
|
test/historical_writer_test.rb
|
47
49
|
test/macro_test.rb
|
data/Releases.txt
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
= Currency Release History
|
2
2
|
|
3
|
+
== Release 0.4.7: 2007/06/25
|
4
|
+
|
5
|
+
CRITICAL FIXES
|
6
|
+
|
7
|
+
* NewYorkFed: CRITICAL FIX:
|
8
|
+
|
9
|
+
Rates for USDAUD, USDEUR, USDNZD and USDGBP were reciprocals.
|
10
|
+
See http://www.newyorkfed.org/markets/fxrates/noon.cfm
|
11
|
+
|
12
|
+
* FederalReserve: Added support for historical rates from http://www.federalreserve.gov/releases/H10/hist
|
13
|
+
* Provider#get_rate(): Return first rate immediately.
|
14
|
+
* Rate::Writable for rate summary computation support.
|
15
|
+
* Rate: collect_rate(): fixed rate_date_1.
|
16
|
+
* Historical::Rate: to_rate(): takes optional class.
|
17
|
+
* Historical::Rate: from_rate(): fixed rate_samples.
|
18
|
+
|
3
19
|
== Release 0.4.6: 2007/05/29
|
4
20
|
|
5
21
|
* NewYorkFed: changed FEXtime to FEXTIME.
|
data/TODO.txt
CHANGED
data/lib/currency/exchange.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
2
|
# See LICENSE.txt for details.
|
3
3
|
|
4
|
+
require 'currency/exchange'
|
5
|
+
|
4
6
|
# Represents a convertion rate between two currencies at a given period of time.
|
5
7
|
class Currency::Exchange::Rate
|
6
8
|
# The first Currency.
|
@@ -132,7 +134,6 @@ class Currency::Exchange::Rate
|
|
132
134
|
@source ||= rate.source
|
133
135
|
@c1 ||= rate.c1
|
134
136
|
@c2 ||= rate.c2
|
135
|
-
@date ||= rate.date
|
136
137
|
|
137
138
|
# Reciprocal?
|
138
139
|
if @c1 == rate.c2 && @c2 == rate.c1
|
@@ -170,7 +171,7 @@ class Currency::Exchange::Rate
|
|
170
171
|
d = rate.date_1 || rate.date
|
171
172
|
unless @date_1 && @date_1 > d
|
172
173
|
@date_1 = d
|
173
|
-
@
|
174
|
+
@rate_date_1 = r
|
174
175
|
end
|
175
176
|
|
176
177
|
@date ||= rate.date || rate.date0 || rate.date1
|
@@ -190,3 +191,13 @@ class Currency::Exchange::Rate
|
|
190
191
|
end # class
|
191
192
|
|
192
193
|
|
194
|
+
class Currency::Exchange::Rate::Writable < Currency::Exchange::Rate
|
195
|
+
attr_writer :source
|
196
|
+
attr_writer :derived
|
197
|
+
attr_writer :rate
|
198
|
+
def initialize(c1 = nil, c2 = nil, rate = nil, *opts)
|
199
|
+
super(c1, c2, 0.0, *opts)
|
200
|
+
@rate = rate
|
201
|
+
end
|
202
|
+
end # class
|
203
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
# See LICENSE.txt for details.
|
3
|
+
|
4
|
+
require 'currency/exchange/rate/source/provider'
|
5
|
+
|
6
|
+
require 'net/http'
|
7
|
+
require 'open-uri'
|
8
|
+
|
9
|
+
|
10
|
+
# Connects to http://www.federalreserve.gov/releases/H10/hist/dat00_<country>.txtb
|
11
|
+
# Parses all known currency files.
|
12
|
+
#
|
13
|
+
class Currency::Exchange::Rate::Source::FederalReserve < ::Currency::Exchange::Rate::Source::Provider
|
14
|
+
# Defines the pivot currency for http://www.federalreserve.gov/releases/H10/hist/dat00_#{country_code}.txt data files.
|
15
|
+
PIVOT_CURRENCY = :USD
|
16
|
+
|
17
|
+
# Arbitrary currency code used by www.federalreserve.gov for
|
18
|
+
# naming historical data files.
|
19
|
+
# Used internally.
|
20
|
+
attr_accessor :country_code
|
21
|
+
|
22
|
+
def initialize(*opt)
|
23
|
+
self.uri = 'http://www.federalreserve.gov/releases/H10/hist/dat00_#{country_code}.txt'
|
24
|
+
self.country_code = ''
|
25
|
+
@raw_rates = nil
|
26
|
+
super(*opt)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# Returns 'federalreserve.gov'.
|
31
|
+
def name
|
32
|
+
'federalreserve.gov'
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# FIXME?
|
37
|
+
#def available?(time = nil)
|
38
|
+
# time ||= Time.now
|
39
|
+
# ! [0, 6].include?(time.wday) ? true : false
|
40
|
+
#end
|
41
|
+
|
42
|
+
|
43
|
+
def clear_rates
|
44
|
+
@raw_rates = nil
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def raw_rates
|
50
|
+
rates
|
51
|
+
@raw_rates
|
52
|
+
end
|
53
|
+
|
54
|
+
# Maps bizzare federalreserve.gov country codes to ISO currency codes.
|
55
|
+
# May only work for the dat00_XX.txt data files.
|
56
|
+
# See http://www.jhall.demon.co.uk/currency/by_country.html
|
57
|
+
#
|
58
|
+
# Some data files list reciprocal rates!
|
59
|
+
@@country_to_currency =
|
60
|
+
{
|
61
|
+
'al' => [ :AUD, :USD ],
|
62
|
+
# 'au' => :ASH, # AUSTRIAN SHILLING: pre-EUR?
|
63
|
+
'bz' => [ :USD, :BRL ],
|
64
|
+
'ca' => [ :USD, :CAD ],
|
65
|
+
'ch' => [ :USD, :CNY ],
|
66
|
+
'dn' => [ :USD, :DKK ],
|
67
|
+
'eu' => [ :EUR, :USD ],
|
68
|
+
# 'gr' => :XXX, # Greece Drachma: pre-EUR?
|
69
|
+
'hk' => [ :USD, :HKD ],
|
70
|
+
'in' => [ :USD, :INR ],
|
71
|
+
'ja' => [ :USD, :JPY ],
|
72
|
+
'ma' => [ :USD, :MYR ],
|
73
|
+
'mx' => [ :USD, :MXN ], # OR MXP?
|
74
|
+
'nz' => [ :NZD, :USD ],
|
75
|
+
'no' => [ :USD, :NOK ],
|
76
|
+
'ko' => [ :USD, :KRW ],
|
77
|
+
'sf' => [ :USD, :ZAR ],
|
78
|
+
'sl' => [ :USD, :LKR ],
|
79
|
+
'sd' => [ :USD, :SEK ],
|
80
|
+
'sz' => [ :USD, :CHF ],
|
81
|
+
'ta' => [ :USD, :TWD ], # New Taiwan Dollar.
|
82
|
+
'th' => [ :USD, :THB ],
|
83
|
+
'uk' => [ :GBP, :USD ],
|
84
|
+
've' => [ :USD, :VEB ],
|
85
|
+
}
|
86
|
+
|
87
|
+
|
88
|
+
# Parses text file for rates.
|
89
|
+
def parse_rates(data = nil)
|
90
|
+
data = get_page_content unless data
|
91
|
+
|
92
|
+
rates = [ ]
|
93
|
+
|
94
|
+
@raw_rates ||= { }
|
95
|
+
|
96
|
+
$stderr.puts "#{self}: parse_rates: data =\n#{data}" if @verbose
|
97
|
+
|
98
|
+
# Rates are USD/currency so
|
99
|
+
# c1 = currency
|
100
|
+
# c2 = :USD
|
101
|
+
c1, c2 = @@country_to_currency[country_code]
|
102
|
+
|
103
|
+
unless c1 && c2
|
104
|
+
raise(::Currency::Exception::UnavailableRates, "Cannot determine currency code for federalreserve.gov country code #{country_code.inspect}")
|
105
|
+
end
|
106
|
+
|
107
|
+
data.split(/\r?\n\r?/).each do | line |
|
108
|
+
# day month yy rate
|
109
|
+
m = /^\s*(\d\d?)-([A-Z][a-z][a-z])-(\d\d)\s+([\d\.]+)/.match(line)
|
110
|
+
next unless m
|
111
|
+
|
112
|
+
day = m[1].to_i
|
113
|
+
month = m[2]
|
114
|
+
year = m[3].to_i
|
115
|
+
if year >= 50 and year < 100
|
116
|
+
year += 1900
|
117
|
+
elsif year < 50
|
118
|
+
year += 2000
|
119
|
+
end
|
120
|
+
|
121
|
+
date = Time.parse("#{day}-#{month}-#{year} 12:00:00 -05:00") # USA NY => EST
|
122
|
+
|
123
|
+
rate = m[4].to_f
|
124
|
+
|
125
|
+
STDERR.puts "#{c1} #{c2} #{rate} #{date}" if @verbose
|
126
|
+
|
127
|
+
rates << new_rate(c1, c2, rate, date)
|
128
|
+
|
129
|
+
((@raw_rates[date] ||= { })[c1] ||= { })[c2] ||= rate
|
130
|
+
((@raw_rates[date] ||= { })[c2] ||= { })[c1] ||= 1.0 / rate
|
131
|
+
end
|
132
|
+
|
133
|
+
# Put most recent rate first.
|
134
|
+
# See Provider#get_rate.
|
135
|
+
rates.reverse!
|
136
|
+
|
137
|
+
# $stderr.puts "rates = #{rates.inspect}"
|
138
|
+
raise ::Currency::Exception::UnavailableRates, "No rates found in #{get_uri.inspect}" if rates.empty?
|
139
|
+
|
140
|
+
rates
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Return a list of known base rates.
|
145
|
+
def load_rates(time = nil)
|
146
|
+
# $stderr.puts "#{self}: load_rates(#{time})" if @verbose
|
147
|
+
self.date = time
|
148
|
+
rates = [ ]
|
149
|
+
@@country_to_currency.keys.each do | cc |
|
150
|
+
self.country_code = cc
|
151
|
+
rates.push(*parse_rates)
|
152
|
+
end
|
153
|
+
rates
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
end # class
|
158
|
+
|
159
|
+
|
160
|
+
|
@@ -56,6 +56,7 @@ class Currency::Exchange::Rate::Source::Historical::Rate < ::ActiveRecord::Base
|
|
56
56
|
self.c2 = rate.c2.code.to_s
|
57
57
|
self.rate = rate.rate
|
58
58
|
self.rate_avg = rate.rate_avg
|
59
|
+
self.rate_samples = rate.rate_samples
|
59
60
|
self.rate_lo = rate.rate_lo
|
60
61
|
self.rate_hi = rate.rate_hi
|
61
62
|
self.rate_date_0 = rate.rate_date_0
|
@@ -78,24 +79,25 @@ class Currency::Exchange::Rate::Source::Historical::Rate < ::ActiveRecord::Base
|
|
78
79
|
|
79
80
|
|
80
81
|
# Creates a new Currency::Exchange::Rate object.
|
81
|
-
def to_rate
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
82
|
+
def to_rate(cls = ::Currency::Exchange::Rate)
|
83
|
+
cls.
|
84
|
+
new(
|
85
|
+
::Currency::Currency.get(self.c1),
|
86
|
+
::Currency::Currency.get(self.c2),
|
87
|
+
self.rate,
|
88
|
+
"historical #{self.source}",
|
89
|
+
self.date,
|
90
|
+
self.derived,
|
91
|
+
{
|
92
|
+
:rate_avg => self.rate_avg,
|
93
|
+
:rate_samples => self.rate_samples,
|
94
|
+
:rate_lo => self.rate_lo,
|
95
|
+
:rate_hi => self.rate_hi,
|
96
|
+
:rate_date_0 => self.rate_date_0,
|
97
|
+
:rate_date_1 => self.rate_date_1,
|
98
|
+
:date_0 => self.date_0,
|
99
|
+
:date_1 => self.date_1
|
100
|
+
})
|
99
101
|
end
|
100
102
|
|
101
103
|
|
@@ -47,6 +47,17 @@ class Currency::Exchange::Rate::Source::NewYorkFed < ::Currency::Exchange::Rate:
|
|
47
47
|
rates
|
48
48
|
@raw_rates
|
49
49
|
end
|
50
|
+
|
51
|
+
|
52
|
+
# The fed swaps rates on some currency pairs!
|
53
|
+
# See http://www.newyorkfed.org/markets/fxrates/noon.cfm (LISTS AUD!)
|
54
|
+
# http://www.newyorkfed.org/xml/fx.html (DOES NOT LIST AUD!)
|
55
|
+
@@swap_units = {
|
56
|
+
:AUD => true,
|
57
|
+
:EUR => true,
|
58
|
+
:NZD => true,
|
59
|
+
:GBP => true,
|
60
|
+
}
|
50
61
|
|
51
62
|
|
52
63
|
# Parses XML for rates.
|
@@ -80,6 +91,11 @@ class Currency::Exchange::Rate::Source::NewYorkFed < ::Currency::Exchange::Rate:
|
|
80
91
|
date = date.text
|
81
92
|
date = Time.parse("#{date} 12:00:00 -05:00") # USA NY => EST
|
82
93
|
|
94
|
+
# Handle arbitrary rate reciprocals!
|
95
|
+
if @@swap_units[c1] || @@swap_units[c2]
|
96
|
+
c1, c2 = c2, c1
|
97
|
+
end
|
98
|
+
|
83
99
|
rates << new_rate(c1, c2, rate, date)
|
84
100
|
|
85
101
|
(@raw_rates[c1] ||= { })[c2] ||= rate
|
@@ -94,14 +94,16 @@ class Currency::Exchange::Rate::Source::Provider < Currency::Exchange::Rate::Sou
|
|
94
94
|
end
|
95
95
|
|
96
96
|
|
97
|
-
# Return a matching base rate
|
97
|
+
# Return a matching base rate.
|
98
98
|
def get_rate(c1, c2, time)
|
99
|
-
|
100
|
-
rate
|
101
|
-
|
102
|
-
|
99
|
+
rates.each do | rate |
|
100
|
+
return rate if
|
101
|
+
rate.c1 == c1 &&
|
102
|
+
rate.c2 == c2 &&
|
103
|
+
(! time || normalize_time(rate.date) == time)
|
103
104
|
end
|
104
|
-
|
105
|
+
|
106
|
+
nil
|
105
107
|
end
|
106
108
|
|
107
109
|
alias :get_rate_base :get_rate
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
|
2
|
+
# See LICENSE.txt for details.
|
3
|
+
|
4
|
+
require 'test/test_base'
|
5
|
+
|
6
|
+
require 'currency' # For :type => :money
|
7
|
+
require 'currency/exchange/rate/source/federal_reserve'
|
8
|
+
|
9
|
+
module Currency
|
10
|
+
|
11
|
+
class FederalReserveTest < TestBase
|
12
|
+
def setup
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
@@available = nil
|
18
|
+
|
19
|
+
# New York Fed rates are not available on Saturday and Sunday.
|
20
|
+
def available?
|
21
|
+
if @@available == nil
|
22
|
+
@@available = @source.available?
|
23
|
+
STDERR.puts "Warning: FederalReserve unavailable on Saturday and Sunday, skipping tests." unless @@available
|
24
|
+
end
|
25
|
+
@@available
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def get_rate_source
|
30
|
+
# Force FederalReserve Exchange.
|
31
|
+
verbose = false
|
32
|
+
source = @source = Exchange::Rate::Source::FederalReserve.new(:verbose => verbose)
|
33
|
+
deriver = Exchange::Rate::Deriver.new(:source => source, :verbose => source.verbose)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
def test_usd_cad
|
39
|
+
return unless available?
|
40
|
+
|
41
|
+
# yesterday = Time.now.to_date - 1
|
42
|
+
|
43
|
+
assert_not_nil rates = Exchange::Rate::Source.default.source.raw_rates
|
44
|
+
#assert_not_nil rates[:USD]
|
45
|
+
#assert_not_nil usd_cad = rates[:USD][:CAD]
|
46
|
+
|
47
|
+
assert_not_nil usd = Money.new(123.45, :USD)
|
48
|
+
assert_not_nil cad = usd.convert(:CAD)
|
49
|
+
|
50
|
+
# assert_kind_of Numeric, m = (cad.to_f / usd.to_f)
|
51
|
+
# $stderr.puts "m = #{m}"
|
52
|
+
# assert_equal_float usd_cad, m, 0.001
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def test_cad_eur
|
57
|
+
return unless available?
|
58
|
+
|
59
|
+
assert_not_nil rates = Exchange::Rate::Source.default.source.raw_rates
|
60
|
+
#assert_not_nil rates[:USD]
|
61
|
+
#assert_not_nil usd_cad = rates[:USD][:CAD]
|
62
|
+
#assert_not_nil usd_eur = rates[:USD][:EUR]
|
63
|
+
|
64
|
+
assert_not_nil cad = Money.new(123.45, :CAD)
|
65
|
+
assert_not_nil eur = cad.convert(:EUR)
|
66
|
+
|
67
|
+
#assert_kind_of Numeric, m = (eur.to_f / cad.to_f)
|
68
|
+
# $stderr.puts "m = #{m}"
|
69
|
+
#assert_equal_float (1.0 / usd_cad) * usd_eur, m, 0.001
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end # module
|
75
|
+
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: currency
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.4.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.4.7
|
7
|
+
date: 2007-06-25 00:00:00 -04:00
|
8
8
|
summary: "Currency models currencies, monetary values, foreign exchanges rates. Pulls live and historical rates from http://xe.com/, http://newyorkfed.org/, http://thefinancials.com/. Can store/retrieve historical rate data from database using ActiveRecord. Can store/retrieve Money values using ActiveRecord. For more details, see: http://rubyforge.org/projects/currency/ http://currency.rubyforge.org/ http://currency.rubyforge.org/files/README_txt.html"
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- lib/currency/exchange/rate/source.rb
|
55
55
|
- lib/currency/exchange/rate/source/base.rb
|
56
56
|
- lib/currency/exchange/rate/source/failover.rb
|
57
|
+
- lib/currency/exchange/rate/source/federal_reserve.rb
|
57
58
|
- lib/currency/exchange/rate/source/historical.rb
|
58
59
|
- lib/currency/exchange/rate/source/historical/rate.rb
|
59
60
|
- lib/currency/exchange/rate/source/historical/writer.rb
|
@@ -74,6 +75,7 @@ files:
|
|
74
75
|
- test/ar_test_base.rb
|
75
76
|
- test/ar_test_core.rb
|
76
77
|
- test/config_test.rb
|
78
|
+
- test/federal_reserve_test.rb
|
77
79
|
- test/formatter_test.rb
|
78
80
|
- test/historical_writer_test.rb
|
79
81
|
- test/macro_test.rb
|