currency 0.4.6 → 0.4.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|