oanda_exchange 0.7.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b1d160d29dac99403a86bc2394f350213463605
4
+ data.tar.gz: 19361f24d1c532b5e7ad41423da6e21e7d2cd342
5
+ SHA512:
6
+ metadata.gz: 6f2818ace9293814792533b89598f1b1f2cb021cf707ad406e1f23290757b727132f456bbdf6ad3e43f30564083c21330c56f391162e6388c98973330ccaf39b
7
+ data.tar.gz: 994f549e9dcc730a400d72799ef50a44ed0aa82283f3f6d59aa737bfe84680cfde3c7c47d958305796003c2bd811461d8aa71d6a7691d1f09b1951d14827675a
@@ -0,0 +1,47 @@
1
+ require "logger"
2
+ require "active_support/core_ext/hash"
3
+
4
+ module OandaExchange
5
+ class Config
6
+
7
+ def self.get
8
+ @@config ||= YAML::load_file(File.join(root, "config", "oanda_api.yml")).symbolize_keys or raise Exception.new("Error on loading configuration file for OANDA API")
9
+ end
10
+
11
+ def self.root
12
+ rails_root || "."
13
+ end
14
+
15
+ def self.logger
16
+ @@logger ||= rails_logger || Logger.new(STDOUT)
17
+ end
18
+
19
+ def self.env
20
+ rails_env || ENV["ENV"] || "development"
21
+ end
22
+
23
+ def self.rails_env
24
+ if defined?(Rails)
25
+ Rails.env
26
+ elsif defined?(RAILS_ENV)
27
+ RAILS_ENV
28
+ end
29
+ end
30
+
31
+ def self.rails_root
32
+ if defined?(Rails)
33
+ Rails.root
34
+ elsif defined?(RAILS_ENV)
35
+ RAILS_ROOT
36
+ end
37
+ end
38
+
39
+ def self.rails_logger
40
+ if defined?(Rails)
41
+ Rails.logger
42
+ elsif defined?(RAILS_DEFAULT_LOGGER)
43
+ RAILS_DEFAULT_LOGGER
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,115 @@
1
+ require "rest-client"
2
+ require "active_support/core_ext/object/blank"
3
+ require "active_support/core_ext/enumerable"
4
+ require "active_support/core_ext/big_decimal"
5
+ require "active_support/core_ext/hash/conversions"
6
+
7
+ class Oanda
8
+
9
+ attr_accessor :base_currency, :quote_currency, :date, :days, :amount, :response, :resp_hash, :interbank, :price
10
+
11
+ PRECISION = 8
12
+
13
+ def self.exchange(base_currency, options = {})
14
+ new.exchange(base_currency, options)
15
+ end
16
+
17
+ def self.currencies
18
+ new.currencies
19
+ end
20
+
21
+ def exchange(base_currency, options = {})
22
+ raise Exception.new("no base currency specified") if base_currency.blank?
23
+ raise Exception.new("no quote currency specified") if options[:to].blank?
24
+ self.price = options[:price] == :midpoint ? [:ask, :bid] : [options[:price] || :bid]
25
+ self.base_currency = base_currency.to_s.upcase
26
+ self.quote_currency = options[:to].to_s.upcase
27
+ self.date = options[:date] || Date.today
28
+ self.days = options[:days] || 1
29
+ self.amount = options[:amount] || 1
30
+ self.interbank = options[:interbank] || 0
31
+ self.response = exchange_request(build_request)
32
+ self.resp_hash = Hash.from_xml(response)["RESPONSE"]
33
+ average_rate = calculate_average_rate
34
+ self.interbank = average_rate * interbank.to_f / 100
35
+ base_currency == quote_currency ? average_rate : average_rate - interbank
36
+ end
37
+
38
+ def currencies
39
+ self.response = currencies_request
40
+ self.resp_hash = Hash.from_xml response
41
+ extract_currencies
42
+ end
43
+
44
+
45
+ def api_request(request_string, &block)
46
+ OandaExchange::Config.logger.debug "OANDA API: request body\n#{request_string}"
47
+ resp = yield request_string
48
+ OandaExchange::Config.logger.debug "OANDA API: response #{resp.code}\n#{resp.body}"
49
+ raise Exception.new("Unexpected response code from OANDA API. See log entries with debug level to get more information.") unless resp.code == 200
50
+ resp.body
51
+ end
52
+
53
+ def exchange_request(request_string)
54
+ api_request request_string do
55
+ OandaExchange::Config.logger.debug "OANDA API: request \n#{config[:exchange_service_url]}"
56
+ OandaExchange::Config.logger.info "OANDA API: requesting #{base_currency} conversion into #{quote_currency}"
57
+ RestClient.get "#{config[:exchange_service_url]}", :params => {:fxmlrequest => request_string}
58
+ end
59
+ end
60
+
61
+ def currencies_request
62
+ api_request "" do
63
+ OandaExchange::Config.logger.debug "OANDA API: request \n#{config[:currencies_url]}"
64
+ RestClient.get "#{config[:currencies_url]}"
65
+ end
66
+ end
67
+
68
+
69
+ protected
70
+
71
+ def extract_currencies
72
+ resp_hash['CURRENCYCODES']['CURRENCY'].inject({}) do |hash, node|
73
+ hash.merge node['CODE'] => node['COUNTRY']
74
+ end
75
+ end
76
+
77
+ def calculate_average_rate
78
+ average extract_exchange_rates
79
+ end
80
+
81
+ def extract_exchange_rates
82
+ extract_rates.reject{|values| values.include?("na")}.map do |values|
83
+ average values.map{|v| BigDecimal.new(v, PRECISION)}
84
+ end
85
+ end
86
+
87
+ def average(values)
88
+ (values.sum / values.size).round(PRECISION) rescue BigDecimal.new('0', PRECISION)
89
+ end
90
+
91
+ def extract_rates
92
+ rates = resp_hash["CONVERSION"].is_a?(Array) ? resp_hash["CONVERSION"] : [resp_hash["CONVERSION"]]
93
+ rates.map{|hash| price_value(hash)}
94
+ end
95
+
96
+ def price_value(hash)
97
+ price.map{|p| hash[p.to_s.upcase]}
98
+ end
99
+
100
+ def build_request
101
+ {
102
+ :date => date.strftime('%m/%d/%Y'),
103
+ :client_id => config[:client_id],
104
+ :exch => base_currency,
105
+ :expr => quote_currency,
106
+ :nprices => days,
107
+ :amount => amount
108
+ }.to_xml(:dasherize => false, :skip_types => true, :root => :convert)
109
+ end
110
+
111
+ def config
112
+ OandaExchange::Config.get
113
+ end
114
+
115
+ end
@@ -0,0 +1,9 @@
1
+ require "money"
2
+
3
+ class OandaBank < Money::Bank::Base
4
+
5
+ def exchange_with(from, to_currency)
6
+ Money.new(Oanda.exchange(from.currency.to_s, :to => to_currency.to_s, :amount => from) * 100, to_currency.to_s)
7
+ end
8
+
9
+ end
@@ -0,0 +1,15 @@
1
+ module OandaExchange::Stubs
2
+ require 'rspec/mocks/standalone' if defined? RSpec
3
+
4
+ DEFAULT_CURRENCIES = {"USD" => "US Dollar", "EUR" => "Euro"}
5
+ DEFAULT_RATES = {"USD" => 1, "other" => 1.5}
6
+
7
+ def self.stub!(options = {})
8
+ raise "RSpec stub library required" unless Object.respond_to? :stub!
9
+ rates = options[:rates] || DEFAULT_RATES
10
+ Oanda.stub!(:exchange) do |cur, opts|
11
+ BigDecimal.new((opts[:amount] || 0).to_s, 5) * (rates[cur] || rates["other"] || 1)
12
+ end
13
+ Oanda.stub!(:currencies).and_return(options[:currencies] || DEFAULT_CURRENCIES)
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module OandaExchange
2
+ VERSION = "0.7.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "oanda_exchange/config"
2
+ require "oanda_exchange/oanda"
3
+ require "oanda_exchange/oanda_bank"
4
+ require "oanda_exchange/stubs"
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oanda_exchange
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.2
5
+ platform: ruby
6
+ authors:
7
+ - Dieter Pisarewski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: builder
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: money
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ description: OANDA Exchange API
70
+ email:
71
+ - dieter.pisarewski@arvatosystems.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/oanda_exchange/config.rb
77
+ - lib/oanda_exchange/oanda.rb
78
+ - lib/oanda_exchange/oanda_bank.rb
79
+ - lib/oanda_exchange/stubs.rb
80
+ - lib/oanda_exchange/version.rb
81
+ - lib/oanda_exchange.rb
82
+ homepage: http://www.arvatosystems-us.com/
83
+ licenses: []
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.0.14
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: API for OANDA Exchange Services
105
+ test_files: []