currency-rate 0.4.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -4
- data/README.md +41 -53
- data/VERSION +1 -1
- data/currency-rate.gemspec +92 -78
- data/lib/adapter.rb +37 -83
- data/lib/adapters/crypto/bitfinex_adapter.rb +17 -0
- data/lib/adapters/crypto/bitpay_adapter.rb +14 -0
- data/lib/adapters/crypto/bitstamp_adapter.rb +19 -0
- data/lib/adapters/crypto/btc_china_adapter.rb +11 -0
- data/lib/adapters/crypto/btc_e_adapter.rb +18 -0
- data/lib/adapters/crypto/coinbase_adapter.rb +14 -0
- data/lib/adapters/crypto/huobi_adapter.rb +17 -0
- data/lib/adapters/crypto/kraken_adapter.rb +29 -0
- data/lib/adapters/crypto/localbitcoins_adapter.rb +14 -0
- data/lib/adapters/crypto/okcoin_adapter.rb +19 -0
- data/lib/adapters/fiat/fixer_adapter.rb +16 -0
- data/lib/adapters/fiat/forge_adapter.rb +23 -0
- data/lib/adapters/fiat/yahoo_adapter.rb +35 -0
- data/lib/configuration.rb +21 -0
- data/lib/currency_rate.rb +52 -48
- data/lib/fetcher.rb +71 -0
- data/lib/storage/file_storage.rb +29 -0
- data/lib/storage/serializers/yaml_serializer.rb +15 -0
- data/lib/synchronizer.rb +31 -0
- data/spec/fixtures/adapters/bitfinex_adapter.yml +18 -0
- data/spec/fixtures/adapters/bitpay_adapter.yml +486 -0
- data/spec/fixtures/adapters/bitstamp_adapter.yml +30 -0
- data/spec/fixtures/adapters/btc_china_adapter.yml +11 -0
- data/spec/fixtures/adapters/btce_adapter.yml +60 -0
- data/spec/fixtures/adapters/coinbase_adapter.yml +680 -0
- data/spec/fixtures/adapters/fixer_adapter.yml +34 -0
- data/spec/fixtures/adapters/forge_adapter.yml +75 -0
- data/spec/fixtures/adapters/huobi_adapter.yml +22 -0
- data/spec/fixtures/adapters/kraken_adapter.yml +124 -0
- data/spec/fixtures/adapters/localbitcoins_adapter.yml +381 -0
- data/spec/fixtures/adapters/normalized/bitfinex_adapter.yml +2 -0
- data/spec/fixtures/adapters/normalized/bitpay_adapter.yml +161 -0
- data/spec/fixtures/adapters/normalized/bitstamp_adapter.yml +3 -0
- data/spec/fixtures/adapters/normalized/btc_china_adapter.yml +1 -0
- data/spec/fixtures/adapters/normalized/btce_adapter.yml +5 -0
- data/spec/fixtures/adapters/normalized/coinbase_adapter.yml +680 -0
- data/spec/fixtures/adapters/normalized/fixer_adapter.yml +32 -0
- data/spec/fixtures/adapters/normalized/forge_adapter.yml +16 -0
- data/spec/fixtures/adapters/normalized/huobi_adapter.yml +2 -0
- data/spec/fixtures/adapters/normalized/kraken_adapter.yml +4 -0
- data/spec/fixtures/adapters/normalized/localbitcoins_adapter.yml +57 -0
- data/spec/fixtures/adapters/normalized/okcoin_adapter.yml +4 -0
- data/spec/fixtures/adapters/normalized/yahoo_adapter.yml +160 -0
- data/spec/fixtures/adapters/okcoin_adapter.yml +40 -0
- data/spec/fixtures/adapters/yahoo_adapter.yml +1119 -0
- data/spec/lib/adapter_spec.rb +54 -0
- data/spec/lib/adapters/crypto/bitfinex_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/bitpay_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/bitstamp_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/btc_china_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/btc_e_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/coinbase_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/huobi_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/kraken_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/localbitcoins_adapter_spec.rb +13 -0
- data/spec/lib/adapters/crypto/okcoin_adapter_spec.rb +13 -0
- data/spec/lib/adapters/fiat/fixer_adapter_spec.rb +13 -0
- data/spec/lib/adapters/fiat/forge_adapter_spec.rb +23 -0
- data/spec/lib/adapters/fiat/yahoo_adapter_spec.rb +13 -0
- data/spec/lib/currency_rate_spec.rb +9 -0
- data/spec/lib/fetcher_spec.rb +156 -0
- data/spec/lib/storage/file_storage_spec.rb +38 -0
- data/spec/lib/synchronizer_spec.rb +65 -0
- data/spec/spec_helper.rb +23 -8
- data/spec/support/matchers/eq_any_of.rb +3 -0
- metadata +67 -64
- data/Gemfile.lock +0 -93
- data/lib/core_ext/deep_get.rb +0 -11
- data/lib/crypto_adapter.rb +0 -19
- data/lib/crypto_adapters/average_rate_adapter.rb +0 -54
- data/lib/crypto_adapters/bitfinex_adapter.rb +0 -40
- data/lib/crypto_adapters/bitpay_adapter.rb +0 -33
- data/lib/crypto_adapters/bitstamp_adapter.rb +0 -44
- data/lib/crypto_adapters/btcchina_adapter.rb +0 -19
- data/lib/crypto_adapters/btce_adapter.rb +0 -44
- data/lib/crypto_adapters/coinbase_adapter.rb +0 -23
- data/lib/crypto_adapters/huobi_adapter.rb +0 -40
- data/lib/crypto_adapters/kraken_adapter.rb +0 -42
- data/lib/crypto_adapters/localbitcoins_adapter.rb +0 -30
- data/lib/crypto_adapters/okcoin_adapter.rb +0 -43
- data/lib/fiat_adapter.rb +0 -17
- data/lib/fiat_adapters/fixer_adapter.rb +0 -38
- data/lib/fiat_adapters/yahoo_adapter.rb +0 -51
- data/lib/storage.rb +0 -20
- data/spec/currency_rate_spec.rb +0 -52
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/average_rate_adapter.yml +0 -567
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/bitfinex_adapter.yml +0 -121
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/bitpay_adapter.yml +0 -272
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/btcchina_adapter.yml +0 -55
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/btce_adapter.yml +0 -223
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/coinbase_adapter.yml +0 -96
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/huobi_adapter.yml +0 -83
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/kraken_adapter.yml +0 -179
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/localbitcoins_adapter.yml +0 -133
- data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/okcoin_adapter.yml +0 -223
- data/spec/fixtures/vcr/exchange_rate_adapters/fiat_adapters/fixer_adapter.yml +0 -85
- data/spec/fixtures/vcr/exchange_rate_adapters/fiat_adapters/yahoo_adapter.yml +0 -48
- data/spec/lib/crypto_adapter_spec.rb +0 -56
- data/spec/lib/crypto_adapters/average_rate_adapter_spec.rb +0 -46
- data/spec/lib/crypto_adapters/bitfinex_adapter_spec.rb +0 -38
- data/spec/lib/crypto_adapters/bitpay_adapter_spec.rb +0 -40
- data/spec/lib/crypto_adapters/bitstamp_adapter_spec.rb +0 -44
- data/spec/lib/crypto_adapters/btcchina_adapter_spec.rb +0 -36
- data/spec/lib/crypto_adapters/btce_adapter_spec.rb +0 -42
- data/spec/lib/crypto_adapters/coinbase_adapter_spec.rb +0 -39
- data/spec/lib/crypto_adapters/huobi_adapter_spec.rb +0 -38
- data/spec/lib/crypto_adapters/kraken_adapter_spec.rb +0 -38
- data/spec/lib/crypto_adapters/localbitcoins_adapter_spec.rb +0 -36
- data/spec/lib/crypto_adapters/okcoin_adapter_spec.rb +0 -43
- data/spec/lib/fiat_adapters/fixer_adapter_spec.rb +0 -25
- data/spec/lib/fiat_adapters/yahoo_adapter_spec.rb +0 -25
- data/spec/lib/storage_spec.rb +0 -32
@@ -0,0 +1,17 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class BitfinexAdapter < Adapter
|
3
|
+
FETCH_URL = {
|
4
|
+
"BTC_USD" => "https://api.bitfinex.com/v1/pubticker/btcusd",
|
5
|
+
"LTC_USD" => "https://api.bitfinex.com/v1/pubticker/ltcusd",
|
6
|
+
}
|
7
|
+
|
8
|
+
def normalize(data)
|
9
|
+
return nil unless super
|
10
|
+
data.reduce({}) do |result, (pair, value)|
|
11
|
+
result[pair] = BigDecimal.new(value["last_price"].to_s)
|
12
|
+
result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class BitpayAdapter < Adapter
|
3
|
+
FETCH_URL = "https://bitpay.com/api/rates"
|
4
|
+
|
5
|
+
def normalize(data)
|
6
|
+
return nil unless super
|
7
|
+
data.reject { |rate| rate["code"] == "BTC" }.reduce({}) do |result, rate|
|
8
|
+
result["BTC_#{rate['code'].upcase}"] = BigDecimal.new(rate["rate"].to_s)
|
9
|
+
result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class BitstampAdapter < Adapter
|
3
|
+
FETCH_URL = {
|
4
|
+
'BTC_USD' => 'https://www.bitstamp.net/api/v2/ticker/btcusd/',
|
5
|
+
'BTC_EUR' => 'https://www.bitstamp.net/api/v2/ticker/btceur/',
|
6
|
+
'LTC_USD' => 'https://www.bitstamp.net/api/v2/ticker/ltcusd/',
|
7
|
+
'LTC_EUR' => 'https://www.bitstamp.net/api/v2/ticker/ltceur/',
|
8
|
+
}
|
9
|
+
|
10
|
+
def normalize(data)
|
11
|
+
return nil unless super
|
12
|
+
data.reduce({}) do |result, (pair, value)|
|
13
|
+
result[pair] = BigDecimal.new(value["last"].to_s)
|
14
|
+
result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class BtcEAdapter < Adapter
|
3
|
+
FETCH_URL = {
|
4
|
+
'BTC_USD' => 'https://wex.nz/api/2/btc_usd/ticker',
|
5
|
+
'BTC_EUR' => 'https://wex.nz/api/2/btc_eur/ticker',
|
6
|
+
'BTC_RUB' => 'https://wex.nz/api/2/btc_rur/ticker',
|
7
|
+
}
|
8
|
+
|
9
|
+
def normalize(data)
|
10
|
+
return nil unless super
|
11
|
+
data.reduce({}) do |result, (pair, value)|
|
12
|
+
result[pair] = BigDecimal.new(value["ticker"]["last"].to_s)
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class CoinbaseAdapter < Adapter
|
3
|
+
FETCH_URL = 'https://coinbase.com/api/v1/currencies/exchange_rates'
|
4
|
+
|
5
|
+
def normalize(data)
|
6
|
+
return nil unless super
|
7
|
+
data.reduce({}) do |result, (pair, value)|
|
8
|
+
result[pair.gsub("_to_", "_").upcase] = BigDecimal.new(value.to_s)
|
9
|
+
result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class HuobiAdapter < Adapter
|
3
|
+
FETCH_URL = {
|
4
|
+
"BTC_CNY" => "http://api.huobi.com/staticmarket/ticker_btc_json.js",
|
5
|
+
"LTC_CNY" => "http://api.huobi.com/staticmarket/ticker_ltc_json.js"
|
6
|
+
}
|
7
|
+
|
8
|
+
def normalize(data)
|
9
|
+
return nil unless super
|
10
|
+
data.reduce({}) do |result, (pair, value)|
|
11
|
+
result[pair] = BigDecimal.new(value["ticker"]["last"].to_s)
|
12
|
+
result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class KrakenAdapter < Adapter
|
3
|
+
FETCH_URL = {
|
4
|
+
"BTC_USD" => "https://api.kraken.com/0/public/Ticker?pair=xbtusd",
|
5
|
+
"BTC_EUR" => "https://api.kraken.com/0/public/Ticker?pair=xbteur",
|
6
|
+
"LTC_USD" => "https://api.kraken.com/0/public/Ticker?pair=ltcusd",
|
7
|
+
"LTC_EUR" => "https://api.kraken.com/0/public/Ticker?pair=ltceur",
|
8
|
+
}
|
9
|
+
|
10
|
+
ASSET_MAP = {
|
11
|
+
"BTC" => "XBT",
|
12
|
+
}
|
13
|
+
|
14
|
+
def normalize(data)
|
15
|
+
return nil unless super
|
16
|
+
data.reduce({}) do |result, (pair, value)|
|
17
|
+
crypto, fiat = pair.split("_")
|
18
|
+
result[pair] = BigDecimal.new(value["result"]["X#{ta(crypto)}Z#{ta(fiat)}"]["c"].first.to_s)
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def translate_asset(asset)
|
24
|
+
ASSET_MAP[asset] || asset
|
25
|
+
end
|
26
|
+
alias_method :ta, :translate_asset
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class LocalbitcoinsAdapter < Adapter
|
3
|
+
FETCH_URL = 'https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/'
|
4
|
+
|
5
|
+
def normalize(data)
|
6
|
+
return nil unless super
|
7
|
+
data.reduce({}) do |result, (fiat, value)|
|
8
|
+
result["BTC_#{fiat.upcase}"] = BigDecimal.new(value["rates"]["last"].to_s)
|
9
|
+
result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class OkcoinAdapter < Adapter
|
3
|
+
FETCH_URL = {
|
4
|
+
'LTC_USD' => 'https://www.okcoin.com/api/v1/ticker.do?symbol=ltc_usd',
|
5
|
+
'BTC_USD' => 'https://www.okcoin.com/api/v1/ticker.do?symbol=btc_usd',
|
6
|
+
'LTC_CNY' => 'https://www.okcoin.cn/api/ticker.do?symbol=ltc_cny',
|
7
|
+
'BTC_CNY' => 'https://www.okcoin.cn/api/ticker.do?symbol=btc_cny'
|
8
|
+
}
|
9
|
+
|
10
|
+
def normalize(data)
|
11
|
+
return nil unless super
|
12
|
+
data.reduce({}) do |result, (pair, value)|
|
13
|
+
result[pair] = BigDecimal.new(value["ticker"]["last"].to_s)
|
14
|
+
result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class FixerAdapter < Adapter
|
3
|
+
ANCHOR_CURRENCY = "USD"
|
4
|
+
FETCH_URL = "http://api.fixer.io/latest?base=usd"
|
5
|
+
|
6
|
+
def normalize(data)
|
7
|
+
return nil unless super
|
8
|
+
rates = { "anchor" => ANCHOR_CURRENCY }
|
9
|
+
data["rates"].each do |k, v|
|
10
|
+
rates[k] = BigDecimal.new(v.to_s)
|
11
|
+
end
|
12
|
+
rates
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class ForgeAdapter < Adapter
|
3
|
+
SUPPORTED_CURRENCIES = %w(JPY CHF CAD SEK NOK MXN ZAR TRY CNH EUR GBP AUD NZD XAU XAG)
|
4
|
+
ANCHOR_CURRENCY = "USD"
|
5
|
+
FETCH_URL = "https://forex.1forge.com/1.0.2/quotes?pairs=" +
|
6
|
+
SUPPORTED_CURRENCIES.map { |c| "#{ANCHOR_CURRENCY}#{c}" }.join(",")
|
7
|
+
API_KEY_PARAM = "api_key"
|
8
|
+
|
9
|
+
def normalize(data)
|
10
|
+
return nil unless super
|
11
|
+
rates = { "anchor" => self.class::ANCHOR_CURRENCY }
|
12
|
+
data.each do |rate|
|
13
|
+
if rate["error"]
|
14
|
+
CurrencyRate.logger.error("Forge exchange returned error")
|
15
|
+
return nil
|
16
|
+
end
|
17
|
+
rates[rate["symbol"].sub(self.class::ANCHOR_CURRENCY, "")] = BigDecimal.new(rate["price"].to_s)
|
18
|
+
end
|
19
|
+
rates
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class YahooAdapter < Adapter
|
3
|
+
SUPPORTED_CURRENCIES = %w(
|
4
|
+
AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND
|
5
|
+
BRL BSD BTN BWP BYR BZD CAD CHF CLF CLP CNY COP CRC CUC CUP CVE CZK DEM
|
6
|
+
DJF DKK DOP DZD ECS EGP ERN ETB EUR FJD FKP FRF GBP GEL GHS GIP GMD GNF
|
7
|
+
GTQ GYD HKD HNL HRK HTG HUF IDR IEP ILS INR IQD ISK ITL JMD JOD JPY KES
|
8
|
+
KGS KHR KMF KWD KYD KZT LAK LBP LKR LRD LSL LTL LVL LYD MAD MGA MMK MNT
|
9
|
+
MOP MRO MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN
|
10
|
+
PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SLL SOS
|
11
|
+
SRD STD SVC SYP SZL THB TJS TMT TND TOP TRY TTD UAH UGX USD UYU UZS VND
|
12
|
+
VUV WST XAF XAG XAU XCD XDR XOF XPD XPF XPT YER ZAR ZMW ZWL
|
13
|
+
)
|
14
|
+
|
15
|
+
ANCHOR_CURRENCY = "USD"
|
16
|
+
|
17
|
+
FETCH_URL = "http://query.yahooapis.com/v1/public/yql?" + URI.encode_www_form(
|
18
|
+
format: 'json',
|
19
|
+
env: "store://datatables.org/alltableswithkeys",
|
20
|
+
q: "SELECT * FROM yahoo.finance.xchange WHERE pair IN" +
|
21
|
+
# The following line is building array string in SQL: '("USDJPY", "USDRUB", ...)'
|
22
|
+
"(#{SUPPORTED_CURRENCIES.map{|x| '"' + ANCHOR_CURRENCY + x.upcase + '"'}.join(',')})"
|
23
|
+
)
|
24
|
+
|
25
|
+
def normalize(data)
|
26
|
+
return nil unless super
|
27
|
+
rates = { "anchor" => self.class::ANCHOR_CURRENCY }
|
28
|
+
data["query"]["results"]["rate"].each do |rate|
|
29
|
+
rates[rate["Name"].split("/")[1]] = BigDecimal.new(rate["Rate"].to_s)
|
30
|
+
end
|
31
|
+
rates
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :api_keys
|
4
|
+
attr_accessor :file_storage
|
5
|
+
attr_accessor :logger
|
6
|
+
attr_accessor :crypto_adapters
|
7
|
+
attr_accessor :fiat_adapters
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@api_keys = { }
|
11
|
+
@file_storage = { path: "" }
|
12
|
+
@logger = {
|
13
|
+
device: $stdout,
|
14
|
+
level: :info,
|
15
|
+
formatter: nil,
|
16
|
+
}
|
17
|
+
@crypto_adapters = CurrencyRate.adapters :crypto
|
18
|
+
@fiat_adapters = CurrencyRate.adapters :fiat
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/currency_rate.rb
CHANGED
@@ -1,69 +1,73 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require "bigdecimal"
|
2
|
+
require "logger"
|
3
|
+
require "singleton"
|
4
|
+
require "json"
|
5
|
+
require "http"
|
6
6
|
|
7
|
+
require "configuration"
|
7
8
|
require "adapter"
|
8
|
-
require "
|
9
|
-
require "
|
9
|
+
require "fetcher"
|
10
|
+
require "synchronizer"
|
10
11
|
|
11
12
|
Dir["#{File.expand_path File.dirname(__FILE__)}/**/*.rb"].each { |f| require f }
|
12
13
|
|
13
14
|
module CurrencyRate
|
15
|
+
class << self
|
16
|
+
attr_writer :configuration
|
17
|
+
end
|
14
18
|
|
15
|
-
def self.
|
16
|
-
|
17
|
-
|
18
|
-
a.try_storage_on_fetching_failed = try_storage_on_fetching_failed
|
19
|
-
|
20
|
-
# Setting default values for anchor currency depending on
|
21
|
-
# which adapter type is un use.
|
22
|
-
anchor_currency ||= if a.kind_of?(CryptoAdapter)
|
23
|
-
a.class::SUPPORTED_CRYPTO_CURRENCIES.include?("BTC") ? "BTC" : a.class::SUPPORTED_CRYPTO_CURRENCIES.first
|
19
|
+
def self.method_missing(m, *args, &block)
|
20
|
+
if m.to_s.end_with? "_adapters"
|
21
|
+
self.send(:adapters, m[0..-10])
|
24
22
|
else
|
25
|
-
|
26
|
-
end
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
a.rate_for(from, to)
|
32
|
-
else
|
33
|
-
rate_from = get(adapter_name, anchor_currency, from, try_storage_on_fetching_failed: try_storage_on_fetching_failed)
|
34
|
-
rate_to = get(adapter_name, anchor_currency, to , try_storage_on_fetching_failed: try_storage_on_fetching_failed)
|
35
|
-
BigDecimal.new(rate_to.to_s)/BigDecimal.new(rate_from.to_s)
|
27
|
+
def self.adapters(type)
|
28
|
+
Dir[File.join self.root, "lib/adapters/#{type}/*"].map do |file|
|
29
|
+
File.basename(file, ".rb").split('_').map {|w| w.capitalize}.join
|
36
30
|
end
|
37
31
|
end
|
38
32
|
|
39
|
-
def self.
|
40
|
-
|
41
|
-
result = BigDecimal.new(amount.to_s)*BigDecimal.new(get(a, from, to, anchor_currency: anchor_currency, try_storage_on_fetching_failed: try_storage_on_fetching_failed).to_s)
|
42
|
-
result.round(a.kind_of?(CryptoAdapter) && a.class::SUPPORTED_CRYPTO_CURRENCIES.include?(to) ? a.class::DECIMAL_PRECISION : 2)
|
33
|
+
def self.configuration
|
34
|
+
@configuration ||= Configuration.new
|
43
35
|
end
|
44
36
|
|
45
|
-
def self.
|
46
|
-
|
37
|
+
def self.configure
|
38
|
+
yield(configuration)
|
47
39
|
end
|
48
40
|
|
49
|
-
|
41
|
+
def self.fetcher
|
42
|
+
@fetcher ||= Fetcher.new
|
43
|
+
end
|
50
44
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
45
|
+
def self.fetch_crypto(exchange, from, to)
|
46
|
+
fetcher.fetch_crypto(exchange, from, to)
|
47
|
+
end
|
55
48
|
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
def self.fetch_fiat(from, to)
|
50
|
+
fetcher.fetch_fiat(from, to)
|
51
|
+
end
|
59
52
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
53
|
+
def self.logger
|
54
|
+
return @logger if @logger
|
55
|
+
@logger = Logger.new(configuration.logger[:device])
|
56
|
+
@logger.progname = "CurrencyRate"
|
57
|
+
@logger.level = configuration.logger[:level]
|
58
|
+
@logger.formatter = configuration.logger[:formatter] if configuration.logger[:formatter]
|
59
|
+
@logger
|
60
|
+
end
|
68
61
|
|
62
|
+
def self.synchronizer
|
63
|
+
@synchronizer ||= Synchronizer.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.sync!
|
67
|
+
synchronizer.sync!
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.root
|
71
|
+
File.expand_path("../", File.dirname(__FILE__))
|
72
|
+
end
|
69
73
|
end
|
data/lib/fetcher.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module CurrencyRate
|
2
|
+
class Fetcher
|
3
|
+
attr_accessor :storage
|
4
|
+
attr_accessor :fiat_exchanges
|
5
|
+
|
6
|
+
def initialize(storage: nil)
|
7
|
+
@storage = storage || FileStorage.new
|
8
|
+
@fiat_exchanges = ["Yahoo", "Fixer", "Forge"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch_crypto(exchange, from, to)
|
12
|
+
crypto = from.upcase
|
13
|
+
fiat = to.upcase
|
14
|
+
pair = "#{from}_#{to}"
|
15
|
+
rates = @storage.read(exchange)
|
16
|
+
|
17
|
+
if rates.nil?
|
18
|
+
CurrencyRate.logger.warn("Fetcher#fetch_crypto: rates for #{exchange} not found in storage <#{@storage.class.name}>")
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
return BigDecimal.new(rates[pair]) if rates[pair]
|
22
|
+
|
23
|
+
supported_crypto, supported_fiat = rates.keys.reduce([[], []]) do |result, rate|
|
24
|
+
c, f = rate.split("_")
|
25
|
+
result[0] << c
|
26
|
+
result[1] << f
|
27
|
+
result
|
28
|
+
end.map { |x| x.uniq }
|
29
|
+
|
30
|
+
unless supported_crypto.include?(crypto)
|
31
|
+
CurrencyRate.logger.warn("Fetcher#fetch_crypto: #{exchange} doesn't support #{crypto}")
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# If requested pair not found and exchange supports requested cryptocurrency
|
36
|
+
# then we can convert using another supported fiat currency as an anchor
|
37
|
+
# USD has first priority since it's the most popular world currency
|
38
|
+
if fiat != "USD" && supported_fiat.delete("USD")
|
39
|
+
usd_fiat = fetch_fiat("USD", fiat)
|
40
|
+
return BigDecimal.new(rates["#{crypto}_USD"]) * BigDecimal.new(usd_fiat) if usd_fiat
|
41
|
+
end
|
42
|
+
|
43
|
+
# We don't have usd_fiat pair so try other supported currencies as convertation anchor
|
44
|
+
supported_fiat.each do |anchor|
|
45
|
+
anchor_fiat = fetch_fiat(anchor, fiat)
|
46
|
+
return BigDecimal.new(rates["#{crypto}_#{anchor}"]) * BigDecimal.new(anchor_fiat) if anchor_fiat
|
47
|
+
end
|
48
|
+
|
49
|
+
# We didn't find a way to fetch rate for requested pair
|
50
|
+
CurrencyRate.logger.warn("Fetcher#fetch_crypto: cannot fetch #{from}_#{to} from #{exchange}")
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch_fiat(from, to)
|
55
|
+
@fiat_exchanges.each do |exchange|
|
56
|
+
left = from.upcase
|
57
|
+
right = to.upcase
|
58
|
+
rates = @storage.read(exchange)
|
59
|
+
next if rates.nil?
|
60
|
+
anchor = rates.delete("anchor")
|
61
|
+
return BigDecimal.new(rates[right]) if anchor == left && rates[right]
|
62
|
+
return BigDecimal.new(1) / BigDecimal.new(rates[left]) if anchor == right && rates[left]
|
63
|
+
return BigDecimal.new(rates[left]) / BigDecimal.new(rates[right]) if rates[left] && rates[right]
|
64
|
+
end
|
65
|
+
CurrencyRate.logger.warn("Fetcher#fetch_fiat: rate for #{from}_#{to} not found")
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|