crypto_arbitrer 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +83 -0
- data/Rakefile +1 -0
- data/crypto_arbitrer.gemspec +26 -0
- data/lib/crypto_arbitrer.rb +174 -0
- data/lib/crypto_arbitrer/version.rb +3 -0
- data/spec/base_spec.rb +172 -0
- data/spec/conversions.csv +238 -0
- data/spec/mocks/btce_btc_usd.html +569 -0
- data/spec/mocks/btce_cnc_btc.html +569 -0
- data/spec/mocks/btce_ftc_btc.html +569 -0
- data/spec/mocks/btce_ltc_btc.html +569 -0
- data/spec/mocks/btce_mtgox.json +47 -0
- data/spec/mocks/btce_nmc_btc.html +569 -0
- data/spec/mocks/btce_nvc_btc.html +569 -0
- data/spec/mocks/btce_ppc_btc.html +569 -0
- data/spec/mocks/btce_trc_btc.html +569 -0
- data/spec/mocks/dolarblue.json +1 -0
- data/spec/mocks/dolarparalelo.html +201 -0
- data/spec/mocks/mtgox.json +47 -0
- data/spec/mocks/rate_exchange_usd_brl.json +1 -0
- data/spec/mocks/rate_exchange_usd_clp.json +1 -0
- data/spec/mocks/rate_exchange_usd_eur.json +1 -0
- data/spec/mocks/rate_exchange_usd_sgd.json +1 -0
- data/spec/mocks/rate_exchange_usd_uyu.json +1 -0
- metadata +177 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Nubis
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
![Travis Build Status](https://secure.travis-ci.org/nubis/crypto_arbitrer.png)
|
2
|
+
|
3
|
+
# CryptoArbitrer
|
4
|
+
|
5
|
+
Provides currency conversions across several fiat currencies and crypto currencies.
|
6
|
+
Exchange rates are fetch (and in some cases scraped)
|
7
|
+
from mtgox.com, btc-e.com, eldolarblue.net, rate-exchange.appspot.com and dolarparalelo.org.
|
8
|
+
|
9
|
+
Given some particular situations in both Venezuela and Argentina, an unofficial but de-facto exchange rate
|
10
|
+
is used for each country's currency (from dolarparalelo.org and eldolarblue.net respectively).
|
11
|
+
|
12
|
+
The supported fiat currencies are:
|
13
|
+
usd ars uyu brl clp sgd eur vef
|
14
|
+
|
15
|
+
The supported crypto currencies are:
|
16
|
+
btc ltc nmc nvc trc ppc ftc cnc
|
17
|
+
|
18
|
+
If you're just looking to use these rates quickly, they are available as a JSON api at [http://cryptocueva.com](http://cryptocueva.com)
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'crypto_arbitrer'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install crypto_arbitrer
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
To get the exchange rate from United States Dollars to Argentine Pesos:
|
37
|
+
|
38
|
+
irb > CryptoArbitrer::Base.fetch('usd', 'ars')
|
39
|
+
=> {'sell' => 7.9, 'buy' => 8.0}
|
40
|
+
|
41
|
+
Prices are returned as 'sell' and 'buy' hashes, where 'sell' is the price at which you can sell the given currency (highest bid price) and 'buy' is the price at which is being offered to you by others (lowest ask price).
|
42
|
+
|
43
|
+
If you were to buy 10 Bitcoin paying with US Dollars, this is how much you would spend in US Dollars.
|
44
|
+
|
45
|
+
irb > 10 * CryptoArbitrer::Base.fetch('btc', 'usd')['buy']
|
46
|
+
=> 1107.7001 # USD
|
47
|
+
|
48
|
+
|
49
|
+
### Caching and Rails
|
50
|
+
|
51
|
+
There is basic support for plugging in your own caching mechanism. Caching in Rails is as easy as creating an initializer
|
52
|
+
with the following code:
|
53
|
+
|
54
|
+
CryptoArbitrer::Base.cache_backend = lambda do |from, to, block|
|
55
|
+
Rails.cache.fetch([from, to], &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
Essentially, you just make a lambda that would receive the currencies to convert from and to, and a block that would return the
|
59
|
+
exchange rate when called.
|
60
|
+
|
61
|
+
Keeping an updated cache is also rather simple, I have this short rake task running every 10 minutes:
|
62
|
+
|
63
|
+
namespace :exchange_rates do
|
64
|
+
desc "Recaches the exchange rates"
|
65
|
+
task recache: :environment do
|
66
|
+
CryptoArbitrer::Base.supported_conversions.each do |from, to|
|
67
|
+
# Notice the third argument to 'fetch', it forces the lookup, ignoring the existing cache.
|
68
|
+
rate = CryptoArbitrer::Base.fetch(from, to, true) rescue next
|
69
|
+
Rails.cache.delete([from, to])
|
70
|
+
Rails.cache.write([from, to], rate)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Docs available at: [http://rubydoc.info/github/nubis/crypto_arbitrer/master/frames](http://rubydoc.info/github/nubis/crypto_arbitrer/master/frames)
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
1. Fork it
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
83
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'crypto_arbitrer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "crypto_arbitrer"
|
8
|
+
spec.version = CryptoArbitrer::VERSION
|
9
|
+
spec.authors = ["Nubis"]
|
10
|
+
spec.email = ["yo@nubis.im"]
|
11
|
+
spec.description = "Arbitrage calculator for crypto currencies. Currently focused on Argentina moving funds from btc-e to mt.gox, then into an asian bank account and finally into Argentina as USD which may be sold at the unofficial price."
|
12
|
+
spec.summary = "Argentine arbitrage calculator for crypto currencies"
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "json"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "webmock"
|
26
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require "crypto_arbitrer/version"
|
2
|
+
require "json"
|
3
|
+
require "open-uri"
|
4
|
+
|
5
|
+
module CryptoArbitrer
|
6
|
+
class Base
|
7
|
+
# Quasi constant, all supported fiat currencies.
|
8
|
+
def self.supported_fiat
|
9
|
+
%w(usd ars uyu brl clp sgd eur vef)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Quasi constant, all supported crypto currencies.
|
13
|
+
def self.supported_cryptos
|
14
|
+
%w(btc ltc nmc nvc trc ppc ftc cnc)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Quasi constant, all supported currencies.
|
18
|
+
def self.supported_currencies
|
19
|
+
supported_fiat + supported_cryptos
|
20
|
+
end
|
21
|
+
|
22
|
+
# Quasi constant, a list of iso code pairs representing all supported exchange rates.
|
23
|
+
# [['ars','usd'],['usd','btc'],['ltc','cnc'],...]
|
24
|
+
def self.supported_conversions
|
25
|
+
supported_currencies.product(supported_currencies)
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# If you want to cache calls done to third party api's you should use this.
|
30
|
+
# Pass in a lambda that will receive the cache key name and the block for fetching it's value.
|
31
|
+
# If you're using rails you can configure CryptoArbitrer in an initializer to use rails caching
|
32
|
+
# just forwarding the cache key and the block to it.
|
33
|
+
# You can set the cache_backend to nil to prevent caching (not recommended, unless you're testing)
|
34
|
+
def cache_backend=(backend)
|
35
|
+
@cache_backend = backend
|
36
|
+
end
|
37
|
+
|
38
|
+
def cache_backend
|
39
|
+
@cache_backend
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# For existing known exchange rates, we want to derive the reverse lookup,
|
44
|
+
# for example: We derive ars_usd from usd_ars
|
45
|
+
def self.derive_reversal(from, to)
|
46
|
+
define_method("fetch_#{to}_#{from}") do
|
47
|
+
rate = send("fetch_#{from}_#{to}")
|
48
|
+
{'sell' => 1/rate['sell'], 'buy' => 1/rate['buy']}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Uses eldolarblue.net API for checking Argentine unofficial US dolar prices.
|
53
|
+
# The returned hash has 'sell' and 'buy' keys for the different prices.
|
54
|
+
# 'buy' is the price at which you can buy USD from agents and 'sell' is the price
|
55
|
+
# at which you can sell USD to agents. Notice this is the opposite to argentina's
|
56
|
+
# convention for 'buy' and 'sell' (where buy is the price in which agents would buy from you)
|
57
|
+
# The ugly regex is to fix the service's response which is
|
58
|
+
# not valid json but a javascript object literal.
|
59
|
+
def fetch_usd_ars
|
60
|
+
response = open('http://www.eldolarblue.net/getDolarBlue.php?as=json').read
|
61
|
+
rate = JSON.parse(response.gsub(/([{,])([^:]*)/, '\1"\2"') )['exchangerate']
|
62
|
+
{'sell' => rate['buy'], 'buy' => rate['sell']}
|
63
|
+
end
|
64
|
+
derive_reversal(:usd, :ars)
|
65
|
+
|
66
|
+
# Uses mt.gox API for checking latest bitcoin sell and buy prices.
|
67
|
+
# The returned hash has 'sell' and 'buy' keys for the different prices,
|
68
|
+
# the prices mean at which price you could buy and sell from them, respectively.
|
69
|
+
def fetch_btc_usd
|
70
|
+
response = open('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast').read
|
71
|
+
json = JSON.parse(response)['data']
|
72
|
+
{'sell' => json['sell']['value'].to_f, 'buy' => json['buy']['value'].to_f}
|
73
|
+
end
|
74
|
+
derive_reversal(:btc, :usd)
|
75
|
+
|
76
|
+
# Goes to dolarparalelo.org to grab the actual price for usd to vef
|
77
|
+
# The returned hash has 'sell' and 'buy' keys for the different prices,
|
78
|
+
# the prices mean at which price you could buy and sell from them, respectively.
|
79
|
+
def fetch_usd_vef
|
80
|
+
response = open('http://www.dolarparalelo.org').read
|
81
|
+
response =~ /<p><font.*?>Dolar:<\/font>.*?<font.*?>(.*?)</
|
82
|
+
rate = $1.to_f
|
83
|
+
{'sell' => rate, 'buy' => rate}
|
84
|
+
end
|
85
|
+
derive_reversal(:usd, :vef)
|
86
|
+
|
87
|
+
# All prices for non-btc cryptocurrencies are fetch from btc-e, prices are expressed in BTC.
|
88
|
+
# We do that by parsing their pages since they don't provide an API.
|
89
|
+
# The prices returned mean at which price you could buy and sell from them, respectively.
|
90
|
+
# We don't use btc-e pricing for bitcoin since the common arbitrage path is to
|
91
|
+
# move bitcoin from btc-e to mt.gox when converting any cryptocurrency to fiat.
|
92
|
+
non_btc_cryptos = supported_cryptos - ['btc']
|
93
|
+
non_btc_cryptos.each do |currency|
|
94
|
+
define_method("fetch_#{currency}_btc") do
|
95
|
+
response = open("https://btc-e.com/exchange/#{currency}_btc").read
|
96
|
+
response =~ /<span id=.max_price..(.*?)..span./
|
97
|
+
sell = $1.to_f
|
98
|
+
response =~ /<span id=.min_price..(.*?)..span./
|
99
|
+
buy = $1.to_f
|
100
|
+
{'sell' => sell, 'buy' => buy}
|
101
|
+
end
|
102
|
+
derive_reversal(currency, :btc)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Fiat prices are fetch from rate-exchange.appspot.com but there are no buy/sell prices.
|
106
|
+
# We use USD as the common denominator for all fiat currencies.
|
107
|
+
# @returns [{'sell' => Float, 'buy' => Float}]
|
108
|
+
%w(uyu brl clp sgd eur).each do |currency|
|
109
|
+
define_method("fetch_usd_#{currency}") do
|
110
|
+
response = open("http://rate-exchange.appspot.com/currency?from=usd&to=#{currency}").read
|
111
|
+
json = JSON.parse(response)
|
112
|
+
{'sell' => json['rate'].to_f, 'buy' => json['rate'].to_f}
|
113
|
+
end
|
114
|
+
derive_reversal(:usd, currency)
|
115
|
+
end
|
116
|
+
|
117
|
+
# All non usd fiat can be converted to btc through their dollar price.
|
118
|
+
non_usd_fiat = supported_fiat - ['usd']
|
119
|
+
non_usd_fiat.each do |currency|
|
120
|
+
define_method("fetch_btc_#{currency}") do
|
121
|
+
btc_rate = fetch_btc_usd
|
122
|
+
usd_rate = fetch('usd', currency)
|
123
|
+
{'sell' => btc_rate['sell'] * usd_rate['sell'], 'buy' => btc_rate['buy'] * usd_rate['buy']}
|
124
|
+
end
|
125
|
+
derive_reversal(:btc, currency)
|
126
|
+
end
|
127
|
+
|
128
|
+
# All fiat currencies can be converted to any non-btc cryptocurrency by using their
|
129
|
+
# rate to btc as common denominator.
|
130
|
+
non_usd_to_non_btc = (non_usd_fiat + non_btc_cryptos).product(non_usd_fiat + non_btc_cryptos)
|
131
|
+
usd_to_non_btc = (non_btc_cryptos+[:usd]).product(non_btc_cryptos+[:usd])
|
132
|
+
(non_usd_to_non_btc + usd_to_non_btc).each do |from, to|
|
133
|
+
define_method("fetch_#{from}_#{to}") do
|
134
|
+
from_rate = fetch(from, 'btc')
|
135
|
+
to_rate = fetch(to, 'btc')
|
136
|
+
{'sell' => from_rate['sell']/to_rate['sell'], 'buy' => from_rate['buy']/to_rate['buy']}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# From and to the same currency is always 1, but we include the methods just for robustness
|
141
|
+
supported_currencies.each do |c|
|
142
|
+
define_method("fetch_#{c}_#{c}"){ {'sell' => 1, 'buy' => 1 } }
|
143
|
+
end
|
144
|
+
|
145
|
+
def fetch(from, to, force = false)
|
146
|
+
if force
|
147
|
+
send("fetch_#{from}_#{to}")
|
148
|
+
else
|
149
|
+
cached(from, to){ send("fetch_#{from}_#{to}") }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Fetch a given conversion caching the result if a backend is set.
|
154
|
+
# This should be the preferred way to fetch a conversion by users.
|
155
|
+
# Check {#supported_currencies} for a list of possible values for 'from' and 'to'
|
156
|
+
# @param from [String] a three letter currency code to convert from.
|
157
|
+
# @param to [String] a three letter currency code to convert to.
|
158
|
+
# @param to [String] a three letter currency code to convert to.
|
159
|
+
# @param force [Boolean] Ignore the cache (does not read from it, and does not write to it). Defaults to false.
|
160
|
+
# @return [{'buy' => Float, 'sell' => Float}] The buy and sell prices
|
161
|
+
def self.fetch(from,to, force = false)
|
162
|
+
new.fetch(from, to, force)
|
163
|
+
end
|
164
|
+
|
165
|
+
protected
|
166
|
+
def cached(from, to, &block)
|
167
|
+
if self.class.cache_backend
|
168
|
+
self.class.cache_backend.call(from, to, block)
|
169
|
+
else
|
170
|
+
block.call
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
require_relative '../lib/crypto_arbitrer.rb'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
describe CryptoArbitrer::Base do
|
6
|
+
def stub_get(url, mock_name)
|
7
|
+
stub_request(:get, url)
|
8
|
+
.to_return(body: open(File.expand_path("../mocks/#{mock_name}", __FILE__)).read)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'when matching corpus of conversion rates' do
|
12
|
+
before :each do
|
13
|
+
stub_get("http://www.eldolarblue.net/getDolarBlue.php?as=json", 'dolarblue.json')
|
14
|
+
stub_get('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast', 'mtgox.json')
|
15
|
+
stub_get('http://www.dolarparalelo.org/', 'dolarparalelo.html')
|
16
|
+
%w(ltc nmc nvc trc ppc ftc cnc).each do |crypto|
|
17
|
+
stub_get("https://btc-e.com/exchange/#{crypto}_btc", "btce_#{crypto}_btc.html")
|
18
|
+
end
|
19
|
+
%w(uyu brl clp sgd eur).each do |currency|
|
20
|
+
stub_get("http://rate-exchange.appspot.com/currency?from=usd&to=#{currency}",
|
21
|
+
"rate_exchange_usd_#{currency}.json")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
CSV.read(File.expand_path('../conversions.csv', __FILE__), 'r').each do |from, to, buy, sell|
|
26
|
+
it "converts from #{from} to #{to}" do
|
27
|
+
rate = subject.fetch(from, to)
|
28
|
+
rate['buy'].should == buy.to_f
|
29
|
+
rate['sell'].should == sell.to_f
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# As a sanity check, we make sure all conversions advertised as supported at least exists.
|
34
|
+
# You should always increase the conversions.csv corpus when adding a new currency though.
|
35
|
+
CryptoArbitrer::Base.supported_conversions.each do |from, to|
|
36
|
+
it "Conversion from #{from} to #{to} exists" do
|
37
|
+
subject.send("fetch_#{from}_#{to}").tap do |rate|
|
38
|
+
# When adding a new currency, you may want to print out the rates returned by the engine
|
39
|
+
# to check them manually and then add them to the corpus
|
40
|
+
#puts "#{from},#{to},#{rate['buy']},#{rate['sell']}"
|
41
|
+
rate['buy'].should_not be_nil
|
42
|
+
rate['sell'].should_not be_nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'supports conversions from and to the same currency' do
|
48
|
+
subject.class.supported_currencies.each do |c|
|
49
|
+
subject.fetch(c,c).should == {'buy' => 1, 'sell' => 1}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'hits dolarblue for usd to ars rate' do
|
55
|
+
stub = stub_get("http://www.eldolarblue.net/getDolarBlue.php?as=json", 'dolarblue.json')
|
56
|
+
subject.fetch_usd_ars
|
57
|
+
stub.should have_been_requested
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'hits mt.gox for the btc to usd rate' do
|
61
|
+
stub = stub_get('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast', 'mtgox.json')
|
62
|
+
subject.fetch_btc_usd
|
63
|
+
stub.should have_been_requested
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'hits dolarparalelo for venezuelan bolivar' do
|
67
|
+
stub = stub_get('http://www.dolarparalelo.org/', 'dolarparalelo.html')
|
68
|
+
subject.fetch_usd_vef
|
69
|
+
stub.should have_been_requested
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'hits mt.gox and dolarblue for the btc to ars rate' do
|
73
|
+
stub_ars = stub_get("http://www.eldolarblue.net/getDolarBlue.php?as=json", 'dolarblue.json')
|
74
|
+
stub_btc = stub_get('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast', 'mtgox.json')
|
75
|
+
subject.fetch_btc_ars
|
76
|
+
stub_ars.should have_been_requested
|
77
|
+
stub_btc.should have_been_requested
|
78
|
+
end
|
79
|
+
|
80
|
+
%w(ltc nmc nvc trc ppc ftc cnc).each do |currency|
|
81
|
+
it "hits btc-e for the #{currency} to btc rate" do
|
82
|
+
stub = stub_get("https://btc-e.com/exchange/#{currency}_btc", "btce_#{currency}_btc.html")
|
83
|
+
subject.fetch(currency, 'btc')
|
84
|
+
subject.fetch('btc', currency)
|
85
|
+
stub.should have_been_requested.twice
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
%w(uyu brl clp sgd eur).each do |currency|
|
90
|
+
it "hits rate-exchange for the #{currency} to usd rate" do
|
91
|
+
stub = stub_get("http://rate-exchange.appspot.com/currency?from=usd&to=#{currency}",
|
92
|
+
"rate_exchange_usd_#{currency}.json")
|
93
|
+
subject.fetch('usd', currency)
|
94
|
+
subject.fetch(currency, 'usd')
|
95
|
+
stub.should have_been_requested.twice
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'when caching remote calls' do
|
100
|
+
it 'does not cache anything by default' do
|
101
|
+
stub = stub_get("http://www.eldolarblue.net/getDolarBlue.php?as=json", 'dolarblue.json')
|
102
|
+
2.times{ subject.fetch('usd', 'ars') }
|
103
|
+
stub.should have_been_requested.twice
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'caches when a cache_backend has been set, stops caching afterwards' do
|
107
|
+
cache = {}
|
108
|
+
subject.class.cache_backend = lambda {|from, to, block| cache[[from,to]] ||= block.call }
|
109
|
+
stub = stub_get('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast', 'mtgox.json')
|
110
|
+
subject.fetch('btc', 'usd')
|
111
|
+
cache[['btc','usd']].should be_a(Hash)
|
112
|
+
subject.fetch('btc', 'usd')
|
113
|
+
stub.should have_been_requested.once
|
114
|
+
subject.class.cache_backend = nil
|
115
|
+
subject.fetch('btc', 'usd')
|
116
|
+
stub.should have_been_requested.twice
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'does not cache for low level calls' do
|
120
|
+
cache = {}
|
121
|
+
subject.class.cache_backend = lambda {|from, to, block| cache[key] ||= block.call }
|
122
|
+
stub = stub_get('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast', 'mtgox.json')
|
123
|
+
subject.fetch_btc_usd
|
124
|
+
subject.fetch_btc_usd
|
125
|
+
stub.should have_been_requested.twice
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'does not cache when using a force argument' do
|
129
|
+
cache = {}
|
130
|
+
subject.class.cache_backend = lambda {|from, to, block| cache[key] ||= block.call }
|
131
|
+
stub = stub_get('http://data.mtgox.com/api/2/BTCUSD/money/ticker_fast', 'mtgox.json')
|
132
|
+
subject.fetch('btc', 'usd', true)
|
133
|
+
subject.fetch('btc', 'usd', true)
|
134
|
+
stub.should have_been_requested.twice
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "Service is either down or changed their API at:", slow: true do
|
139
|
+
before(:all){ WebMock.allow_net_connect! }
|
140
|
+
after(:all){ WebMock.disable_net_connect! }
|
141
|
+
|
142
|
+
def check_service(method)
|
143
|
+
%w(buy sell).each do |price|
|
144
|
+
price = subject.send(method)[price]
|
145
|
+
price.should be_a Float
|
146
|
+
price.should >= 0
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'eldolarblue' do
|
151
|
+
check_service(:fetch_usd_ars)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'mtgox' do
|
155
|
+
check_service(:fetch_btc_usd)
|
156
|
+
end
|
157
|
+
|
158
|
+
%w(ltc_btc nmc_btc nvc_btc trc_btc ppc_btc ftc_btc cnc_btc).each do |exchange|
|
159
|
+
it "btce #{exchange}" do
|
160
|
+
check_service("fetch_#{exchange}")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'rate-exchange' do
|
165
|
+
check_service('fetch_usd_sgd')
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'dolarparalelo' do
|
169
|
+
check_service('fetch_usd_vef')
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|