cointools 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '0219538fd4877d6e1ad63fa85d08bbb0acb478d7'
4
- data.tar.gz: 9d88ae1fb52ac943bb8558d061b3f5cdccabc5bf
3
+ metadata.gz: 0e581465b03a85e017f097f09f991294a9d87676
4
+ data.tar.gz: f9e6d0cfeab51850d8d858a50990838305b5a13c
5
5
  SHA512:
6
- metadata.gz: 85f565cae695326dbe75e7d492a0da2dd5fd85709319c0a02426f03e0dc0d87a53c23e4412c92572099dfcd3d3154b0ce949f339b191ad49c25f7eb636b9f02c
7
- data.tar.gz: 5f4018479cc8ccb897efc04b4c6ab66c86f7c801a2416c5202d13a9aa0783e4f215273709eac013e56cd1420a50fb4cdc052e6dbf1adb65b27485f8ac52fba8e
6
+ metadata.gz: 0f3237529fa8ad91074c3564ca1268bdbf52d2904e016e2744b4d714f829d3e37feb48614eb5fad7aa2833727f9168c5aa1f731ede1a71330b810f336215612d
7
+ data.tar.gz: 81833105049ee6fcc85a3ecd323fce79087fd75d25300486922e60031e102f997df4153db3696bbdbdbbe5fb9c64c8d23ac17a5efb0a3b613376a5116e13f5fa
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
+ #### Version 0.4.0 (23.05.2018)
2
+
3
+ * added CoinCap.io class and `coincap` command
4
+ * renamed CoinMarketCap command to `cmcap`
5
+ * removed BitBay class and command - BitBay is now supported on Cryptowatch (including past prices since March)
6
+ * fixed CoinMarketCap calls with currency conversion
7
+ * added "fast" method for Cryptowat.ch
8
+ * include API time allowance in Cryptowat.ch responses
9
+
1
10
  #### Version 0.3.0 (26.03.2018)
2
11
 
3
- * added BitBay class and bitbay-price command
12
+ * added BitBay class and `bitbay-price` command
4
13
 
5
14
  #### Version 0.2.1 (6.03.2018)
6
15
 
@@ -8,11 +17,11 @@
8
17
 
9
18
  #### Version 0.2.0 (29.01.2018)
10
19
 
11
- * added CoinMarketCap class & coinmcap binary
20
+ * added CoinMarketCap class & `coinmcap` binary
12
21
 
13
22
  #### Version 0.1.2 (24.01.2018)
14
23
 
15
- * fixed --help command
24
+ * fixed `--help` command
16
25
 
17
26
  #### Version 0.1.1 (24.01.2018)
18
27
 
data/README.md CHANGED
@@ -1,10 +1,15 @@
1
- # Cointools
1
+ # CoinTools
2
2
 
3
3
  This is a collection of Ruby scripts and library classes that let you check cryptocurrency prices on various services.
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/cointools.svg)](https://badge.fury.io/rb/cointools) [![Build Status](https://travis-ci.org/mackuba/cointools.svg?branch=master)](https://travis-ci.org/mackuba/cointools)
6
+
7
+
5
8
  ## Installation
6
9
 
7
- To use the scripts from the command line, install the gem:
10
+ CoinTools requires Ruby version 2.3 or newer.
11
+
12
+ To use the scripts from the command line, install the gem (depending on your configuration, you might need to add `sudo`):
8
13
 
9
14
  ```
10
15
  gem install cointools
@@ -16,7 +21,9 @@ To use the code as a library, add it to your Gemfile:
16
21
  gem 'cointools'
17
22
  ```
18
23
 
19
- ## [Cryptowatch](https://cryptowat.ch)
24
+ ## Cryptowatch
25
+
26
+ [Cryptowat.ch](https://cryptowat.ch) is a coin charts site owned by Kraken which tracks historical cryptocurrency prices directly on many exchanges. It provides data from specific markets on specific exchanges, but no general rankings or average market prices.
20
27
 
21
28
  To check past price of a given coin on a chosen exchange, pass the exchange and market name and a properly formatted timestamp:
22
29
 
@@ -57,19 +64,25 @@ puts "#{market} today: #{result.price}"
57
64
 
58
65
  The result object contains the requested price and (for historical prices) the actual timestamp of the found price, which might slightly differ from the timestamp passed in the argument (the earlier the date, the less precise the result).
59
66
 
67
+ The API is rate limited to 8 seconds of CPU time per hour - you can check the `api_time_spent` and `api_time_remaining` properties of the result object to see how much time allowance you have left.
68
+
69
+ If you need to do a large amount of lookups in a short period of time, try the alternative `#get_price_fast` method (or `-f` option on the command line). This method tries to guess which data set is the most appropriate for a given point in the past and requests only that set instead of all of them, which should use significantly less API time allowance. This however relies on an undocumented API behavior, so it's not guaranteed to return data and keep working in the future.
70
+
60
71
 
61
- ## [CoinMarketCap](https://coinmarketcap.com)
72
+ ## CoinMarketCap
62
73
 
63
- CoinMarketCap's API only returns current coin prices. To look up a coin's price, you need to pass its name as used on CoinMarketCap:
74
+ [CoinMarketCap](https://coinmarketcap.com) is by far the most popular site for checking coin rankings and BTC/USD prices for all coins available on the market. The API however only returns current average coin prices.
75
+
76
+ To look up a coin's price, you need to pass its name as used on CoinMarketCap:
64
77
 
65
78
  ```
66
- coinmcap power-ledger
79
+ cmcap power-ledger
67
80
  ```
68
81
 
69
82
  Alternatively, you can pass the cryptocurrency's symbol using the `-s` or `--symbol` parameter:
70
83
 
71
84
  ```
72
- coinmcap -s powr
85
+ cmcap -s powr
73
86
  ```
74
87
 
75
88
  However, this operation needs to download a complete ticker for all currencies and scan through the list, so it's recommended to use the name as in the example above.
@@ -77,16 +90,16 @@ However, this operation needs to download a complete ticker for all currencies a
77
90
  You can also use the `-b` or `--btc-price` flag to request a price in BTC instead of USD:
78
91
 
79
92
  ```
80
- coinmcap power-ledger -b
93
+ cmcap power-ledger -b
81
94
  ```
82
95
 
83
96
  Or you can request the price in one of the ~30 other supported fiat currencies with `-f` or `--fiat-currency`:
84
97
 
85
98
  ```
86
- coinmcap request-network -fEUR
99
+ cmcap request-network -fEUR
87
100
  ```
88
101
 
89
- You can print a list of supported fiat currencies with `coinmcap --list-fiat-currencies`.
102
+ You can print a list of supported fiat currencies with `cmcap --list-fiat-currencies`.
90
103
 
91
104
  Same things in code:
92
105
 
@@ -106,28 +119,51 @@ eth = cryptowatch.get_price('ethereum', convert_to: 'EUR')
106
119
  puts "ETH: #{eth.converted_price} EUR"
107
120
  ```
108
121
 
122
+ The soft rate limit for the API is 10 requests per minute (for the currently used v1 API).
123
+
124
+
125
+ ## CoinCap
126
+
127
+ [CoinCap.io](https://coincap.io) is a site similar to CoinMarketCap with an API that includes historical coin prices (with decreasing precision the further into the past you look).
128
+
129
+ To check past price of a given coin, pass the coin's symbol and a properly formatted timestamp:
130
+
131
+ ```
132
+ coincap xmr "2018-04-01 13:00"
133
+ ```
134
+
135
+ To check the current price, skip the timestamp:
109
136
 
110
- ## [BitBay](https://bitbay.net)
137
+ ```
138
+ coincap xmr
139
+ ```
111
140
 
112
- Returns ticker prices directly from the BitBay exchange, only current coin prices. Specify market name as coin symbol + currency.
141
+ You can also use the `-b` or `--btc-price` flag to request a price in BTC instead of USD, or `-e` or `--eur-price` for EUR:
113
142
 
114
143
  ```
115
- bitbay-price btcusd
144
+ coincap xmr -b
145
+ coincap xmr -e
116
146
  ```
117
147
 
148
+ These are however only supported for current prices - past prices are only listed in USD.
149
+
118
150
  In code:
119
151
 
120
152
  ```ruby
121
- require 'cointools' # or 'cointools/bitbay'
122
- bitbay = CoinTools::BitBay.new
153
+ require 'cointools' # or 'cointools/coincap'
154
+ coincap = CoinTools::CoinCap.new
123
155
 
124
- ltc = bitbay.get_price('ltcusd')
125
- puts "LTC: #{ltc.price} USD"
156
+ result = coincap.get_price('XMR', Time.now - 86400)
157
+ puts "XMR yesterday: $#{result.usd_price} (#{result.time})"
126
158
 
127
- btg = bitbay.get_price('btgbtc')
128
- puts "BTG: #{btg.price} BTC"
159
+ result = coincap.get_current_price('XMR')
160
+ puts "XMR today: $#{result.usd_price} / €#{result.eur_price} / ₿#{result.btc_price}"
129
161
  ```
130
162
 
163
+ The result object contains the requested price and (for historical prices) the actual timestamp of the found price, which might slightly differ from the timestamp passed in the argument (the earlier the date, the less precise the result).
164
+
165
+ At the moment there don't seem to be any official rate limits for the API.
166
+
131
167
 
132
168
  ## Credits & contributing
133
169
 
@@ -57,7 +57,7 @@ end
57
57
 
58
58
  if fiat_currency && btc_price
59
59
  puts "#{$PROGRAM_NAME}: --btc-price and --fiat-currency options cannot be used together"
60
- exit 2
60
+ exit 1
61
61
  end
62
62
 
63
63
  begin
@@ -84,13 +84,16 @@ begin
84
84
  else
85
85
  puts price
86
86
  end
87
- rescue CoinTools::CoinMarketCap::InvalidResponseException => e
87
+ rescue CoinTools::CoinMarketCap::BadRequestException => e
88
88
  $stderr.puts "Error: Incorrect coin name: #{coin_name} (#{e})"
89
- exit 3
89
+ exit 1
90
+ rescue CoinTools::CoinMarketCap::InvalidResponseException => e
91
+ $stderr.puts "Error: Something went wrong: #{e}"
92
+ exit 1
90
93
  rescue CoinTools::CoinMarketCap::NoDataException => e
91
94
  $stderr.puts "Error: #{e}"
92
- exit 4
95
+ exit 1
93
96
  rescue CoinTools::CoinMarketCap::InvalidFiatCurrencyException => e
94
97
  $stderr.puts "Error: Unsupported fiat currency: #{fiat_currency}"
95
- exit 5
98
+ exit 1
96
99
  end
data/bin/coincap ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'cointools'
5
+
6
+ require 'optparse'
7
+ require 'time'
8
+
9
+ def print_help
10
+ puts "Usage: #{$PROGRAM_NAME} <symbol> [<date>] [-b/--btc-price | -e/--eur-price] [-v/--verbose]"
11
+ puts " e.g.: #{$PROGRAM_NAME} NANO \"2017-12-20 15:00\""
12
+ puts
13
+ puts "* -b / --btc-price: returns the coin's price in BTC instead of USD (current prices only)"
14
+ puts "* -e / --eur-price: returns the coin's price in EUR (current prices only)"
15
+ end
16
+
17
+ verbose = false
18
+ btc_price = false
19
+ eur_price = false
20
+
21
+ OptionParser.new do |opts|
22
+ opts.on('-v', '--verbose') { verbose = true }
23
+ opts.on('-b', '--btc-price') { btc_price = true }
24
+ opts.on('-e', '--eur-price') { eur_price = true }
25
+
26
+ opts.on('-h', '--help') do
27
+ print_help
28
+ exit 0
29
+ end
30
+
31
+ opts.parse!
32
+ end
33
+
34
+ if ARGV.length < 1 || ARGV.length > 2
35
+ print_help
36
+ exit 1
37
+ end
38
+
39
+ symbol = ARGV[0]
40
+ date = Time.parse(ARGV[1]) if ARGV[1]
41
+
42
+ if eur_price && btc_price
43
+ puts "#{$PROGRAM_NAME}: --btc-price and --eur-price options cannot be used together"
44
+ exit 1
45
+ end
46
+
47
+ if date && (eur_price || btc_price)
48
+ puts "#{$PROGRAM_NAME}: --btc-price and --eur-price options cannot be used for historical prices"
49
+ exit 1
50
+ end
51
+
52
+ begin
53
+ result = CoinTools::CoinCap.new.get_price(symbol, date)
54
+
55
+ if btc_price
56
+ price = result.btc_price
57
+ unit = 'BTC'
58
+ elsif eur_price
59
+ price = result.eur_price
60
+ unit = 'EUR'
61
+ else
62
+ price = result.usd_price
63
+ unit = 'USD'
64
+ end
65
+
66
+ if verbose
67
+ puts "#{symbol.upcase} @ #{result.time || Time.now} ==> #{price} #{unit}"
68
+ puts
69
+ else
70
+ puts price
71
+ end
72
+ rescue CoinTools::CoinCap::BadRequestException => e
73
+ $stderr.puts "Error: Incorrect coin name: #{coin_name} (#{e})"
74
+ exit 1
75
+ rescue CoinTools::CoinCap::InvalidResponseException => e
76
+ $stderr.puts "Error: Something went wrong: #{e}"
77
+ exit 1
78
+ rescue CoinTools::CoinCap::NoDataException => e
79
+ $stderr.puts "Error: #{e}"
80
+ exit 1
81
+ end
data/bin/cryptowatch CHANGED
@@ -7,9 +7,10 @@ require 'optparse'
7
7
  require 'time'
8
8
 
9
9
  def print_help
10
- puts "Usage: #{$PROGRAM_NAME} <exchange> <market> [<date>] [-v/--verbose]"
10
+ puts "Usage: #{$PROGRAM_NAME} <exchange> <market> [<date>] [-v/--verbose] [-f/--fast]"
11
11
  puts " e.g.: #{$PROGRAM_NAME} gdax btcusd \"2017-06-30 15:27\""
12
12
  puts
13
+ puts "* --fast: tries to use less API time allowance at the cost of worse reliability"
13
14
  puts "* To print a list of available exchanges:"
14
15
  puts " #{$PROGRAM_NAME} --list-exchanges"
15
16
  puts "* To print a list of available markets on an exchange:"
@@ -17,6 +18,7 @@ def print_help
17
18
  end
18
19
 
19
20
  verbose = false
21
+ fast = false
20
22
 
21
23
  OptionParser.new do |opts|
22
24
  opts.on('-v', '--verbose') { verbose = true }
@@ -26,6 +28,8 @@ OptionParser.new do |opts|
26
28
  exit 0
27
29
  end
28
30
 
31
+ opts.on('-f', '--fast') { fast = true }
32
+
29
33
  opts.on('--list-exchanges') do
30
34
  puts CoinTools::Cryptowatch.new.exchanges
31
35
  exit 0
@@ -37,7 +41,7 @@ OptionParser.new do |opts|
37
41
  exit 0
38
42
  rescue CoinTools::Cryptowatch::BadRequestException => e
39
43
  $stderr.puts "Error: Incorrect exchange name: #{e}"
40
- exit 3
44
+ exit 1
41
45
  end
42
46
  end
43
47
 
@@ -54,7 +58,8 @@ market = ARGV[1].downcase
54
58
  date = Time.parse(ARGV[2]) if ARGV[2]
55
59
 
56
60
  begin
57
- result = CoinTools::Cryptowatch.new.get_price(exchange, market, date)
61
+ method = fast ? :get_price_fast : :get_price
62
+ result = CoinTools::Cryptowatch.new.send(method, exchange, market, date)
58
63
 
59
64
  if verbose
60
65
  puts "#{exchange}:#{market} @ #{result.time || Time.now} ==> #{result.price}"
@@ -64,11 +69,11 @@ begin
64
69
  end
65
70
  rescue CoinTools::Cryptowatch::InvalidDateException => e
66
71
  $stderr.puts "Error: #{e}"
67
- exit 2
72
+ exit 1
68
73
  rescue CoinTools::Cryptowatch::BadRequestException => e
69
74
  $stderr.puts "Error: Incorrect exchange or market name (#{e})"
70
- exit 3
75
+ exit 1
71
76
  rescue CoinTools::Cryptowatch::NoDataException => e
72
77
  $stderr.puts "Error: #{e}: data not ready yet"
73
- exit 4
78
+ exit 1
74
79
  end
@@ -0,0 +1,109 @@
1
+ require_relative 'version'
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module CoinTools
8
+ class CoinCap
9
+ BASE_URL = "https://coincap.io"
10
+ USER_AGENT = "cointools/#{CoinTools::VERSION}"
11
+
12
+ DataPoint = Struct.new(:time, :usd_price, :eur_price, :btc_price)
13
+
14
+ PERIODS = [1, 7, 30, 90, 180, 365]
15
+
16
+ class InvalidResponseException < StandardError
17
+ attr_reader :response
18
+
19
+ def initialize(response)
20
+ super("#{response.code} #{response.message}")
21
+ @response = response
22
+ end
23
+ end
24
+
25
+ class BadRequestException < InvalidResponseException
26
+ end
27
+
28
+ class InvalidDateException < StandardError
29
+ end
30
+
31
+ def get_price(symbol, time = nil)
32
+ return get_current_price(symbol) if time.nil?
33
+
34
+ (time <= Time.now) or raise InvalidDateException.new('Future date was passed')
35
+ (time.year >= 2009) or raise InvalidDateException.new('Too early date was passed')
36
+
37
+ period = period_for_time(time)
38
+
39
+ if period
40
+ url = URI("#{BASE_URL}/history/#{period}day/#{symbol.upcase}")
41
+ else
42
+ url = URI("#{BASE_URL}/history/#{symbol.upcase}")
43
+ end
44
+
45
+ unixtime = time.to_i
46
+
47
+ response = make_request(url)
48
+
49
+ case response
50
+ when Net::HTTPSuccess
51
+ json = JSON.load(response.body)
52
+ data = json['price']
53
+
54
+ timestamp, price = best_matching_record(data, unixtime)
55
+ actual_time = Time.at(timestamp / 1000)
56
+
57
+ return DataPoint.new(actual_time, price, nil, nil)
58
+ when Net::HTTPBadRequest
59
+ raise BadRequestException.new(response)
60
+ else
61
+ raise InvalidResponseException.new(response)
62
+ end
63
+ end
64
+
65
+ def get_current_price(symbol)
66
+ url = URI("#{BASE_URL}/page/#{symbol.upcase}")
67
+
68
+ response = make_request(url)
69
+
70
+ case response
71
+ when Net::HTTPSuccess
72
+ json = JSON.load(response.body)
73
+
74
+ usd_price = json['price_usd']
75
+ eur_price = json['price_eur']
76
+ btc_price = json['price_btc']
77
+
78
+ return DataPoint.new(nil, usd_price, eur_price, btc_price)
79
+ when Net::HTTPBadRequest
80
+ raise BadRequestException.new(response)
81
+ else
82
+ raise InvalidResponseException.new(response)
83
+ end
84
+ end
85
+
86
+
87
+ private
88
+
89
+ def make_request(url)
90
+ Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
91
+ request = Net::HTTP::Get.new(url)
92
+ request['User-Agent'] = USER_AGENT
93
+
94
+ http.request(request)
95
+ end
96
+ end
97
+
98
+ def best_matching_record(data, unixtime)
99
+ millitime = unixtime * 1000
100
+ data.sort_by { |record| (record[0] - millitime).abs }.first
101
+ end
102
+
103
+ def period_for_time(time)
104
+ now = Time.now
105
+
106
+ PERIODS.map { |p| [p, now - p * 86400 + 7200] }.detect { |p, t| t < time }&.first
107
+ end
108
+ end
109
+ end
@@ -73,7 +73,7 @@ module CoinTools
73
73
  end
74
74
 
75
75
  return DataPoint.new(timestamp, usd_price, btc_price, converted_price)
76
- when Net::HTTPBadRequest
76
+ when Net::HTTPNotFound
77
77
  raise BadRequestException.new(response)
78
78
  else
79
79
  raise InvalidResponseException.new(response)
@@ -86,7 +86,7 @@ module CoinTools
86
86
 
87
87
  if convert_to
88
88
  validate_fiat_currency(convert_to)
89
- url += "&convert=#{convert_to}"
89
+ url.query += "&convert=#{convert_to}"
90
90
  end
91
91
 
92
92
  response = make_request(url)
@@ -107,10 +107,10 @@ module CoinTools
107
107
  end
108
108
 
109
109
  return DataPoint.new(timestamp, usd_price, btc_price, converted_price)
110
- when Net::HTTPBadRequest
110
+ when Net::HTTPNotFound
111
111
  raise BadRequestException.new(response)
112
112
  else
113
- raise Exception.new(response)
113
+ raise InvalidResponseException.new(response)
114
114
  end
115
115
  end
116
116
 
@@ -9,7 +9,13 @@ module CoinTools
9
9
  BASE_URL = "https://api.cryptowat.ch"
10
10
  USER_AGENT = "cointools/#{CoinTools::VERSION}"
11
11
 
12
- DataPoint = Struct.new(:price, :time)
12
+ DataPoint = Struct.new(:price, :time, :api_time_spent, :api_time_remaining)
13
+
14
+ # we expect this many days worth of data for a given period precision (in seconds); NOT guaranteed by the API
15
+ DAYS_FOR_PERIODS = {
16
+ 60 => 3, 180 => 10, 300 => 15, 900 => 2 * 30, 1800 => 4 * 30, 3600 => 8 * 30,
17
+ 7200 => 365, 14400 => 1.5 * 365, 21600 => 2 * 365, 43200 => 3 * 365, 86400 => 4 * 365
18
+ }
13
19
 
14
20
  class InvalidResponseException < StandardError
15
21
  attr_reader :response
@@ -65,16 +71,17 @@ module CoinTools
65
71
  when Net::HTTPSuccess
66
72
  json = JSON.load(response.body)
67
73
  data = json['result']
74
+ allowance = json['allowance']
68
75
 
69
76
  timestamp, o, h, l, c, volume = best_matching_record(data, unixtime, current_time)
70
77
  raise NoDataException.new('No data found for a given time') if timestamp.nil?
71
78
 
72
79
  actual_time = Time.at(timestamp)
73
- return DataPoint.new(o, actual_time)
80
+ return DataPoint.new(o, actual_time, allowance['cost'], allowance['remaining'])
74
81
  when Net::HTTPBadRequest
75
82
  raise BadRequestException.new(response)
76
83
  else
77
- raise Exception.new(response)
84
+ raise InvalidResponseException.new(response)
78
85
  end
79
86
  end
80
87
 
@@ -87,12 +94,47 @@ module CoinTools
87
94
  when Net::HTTPSuccess
88
95
  json = JSON.load(response.body)
89
96
  price = json['result']['price']
97
+ allowance = json['allowance']
98
+
99
+ return DataPoint.new(price, nil, allowance['cost'], allowance['remaining'])
100
+ when Net::HTTPBadRequest
101
+ raise BadRequestException.new(response)
102
+ else
103
+ raise InvalidResponseException.new(response)
104
+ end
105
+ end
106
+
107
+ def get_price_fast(exchange, market, time)
108
+ (time <= Time.now) or raise InvalidDateException.new('Future date was passed')
109
+ (time.year >= 2009) or raise InvalidDateException.new('Too early date was passed')
110
+
111
+ period = precision_for_time(time)
112
+
113
+ if period.nil?
114
+ return get_price(exchange, market, time)
115
+ end
116
+
117
+ unixtime = time.to_i
118
+ current_time = Time.now.to_i
119
+ url = URI("#{BASE_URL}/markets/#{exchange}/#{market}/ohlc?after=#{unixtime}&periods=#{period}")
120
+
121
+ response = make_request(url)
90
122
 
91
- return DataPoint.new(price, nil)
123
+ case response
124
+ when Net::HTTPSuccess
125
+ json = JSON.load(response.body)
126
+ data = json['result']
127
+ allowance = json['allowance']
128
+
129
+ timestamp, o, h, l, c, volume = best_matching_record(data, unixtime, current_time)
130
+ raise NoDataException.new('No data found for a given time') if timestamp.nil?
131
+
132
+ actual_time = Time.at(timestamp)
133
+ return DataPoint.new(o, actual_time, allowance['cost'], allowance['remaining'])
92
134
  when Net::HTTPBadRequest
93
135
  raise BadRequestException.new(response)
94
136
  else
95
- raise Exception.new(response)
137
+ raise InvalidResponseException.new(response)
96
138
  end
97
139
  end
98
140
 
@@ -132,6 +174,21 @@ module CoinTools
132
174
  candidates.sort_by { |record| (record[0] - unixtime).abs }.first
133
175
  end
134
176
 
177
+ def precision_for_time(time)
178
+ now = Time.now
179
+
180
+ DAYS_FOR_PERIODS.keys.sort.each do |period|
181
+ days = DAYS_FOR_PERIODS[period]
182
+ earliest_date = now - days * 86400
183
+
184
+ if earliest_date < time
185
+ return period
186
+ end
187
+ end
188
+
189
+ nil
190
+ end
191
+
135
192
  def get_exchanges
136
193
  url = URI("#{BASE_URL}/exchanges")
137
194
 
@@ -141,10 +198,8 @@ module CoinTools
141
198
  when Net::HTTPSuccess
142
199
  json = JSON.load(response.body)
143
200
  return json['result'].select { |e| e['active'] == true }.map { |e| e['symbol'] }.sort
144
- when Net::HTTPBadRequest
145
- raise BadRequestException.new(response)
146
201
  else
147
- raise Exception.new(response)
202
+ raise InvalidResponseException.new(response)
148
203
  end
149
204
  end
150
205
  end
@@ -1,3 +1,3 @@
1
1
  module CoinTools
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/cointools.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'cointools/bitbay'
1
+ require 'cointools/coincap'
2
2
  require 'cointools/coinmarketcap'
3
3
  require 'cointools/cryptowatch'
4
4
  require 'cointools/version'
metadata CHANGED
@@ -1,33 +1,33 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cointools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-26 00:00:00.000000000 Z
11
+ date: 2018-05-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
15
15
  - jakub.suder@gmail.com
16
16
  executables:
17
- - bitbay-price
17
+ - coincap
18
18
  - cryptowatch
19
- - coinmcap
19
+ - cmcap
20
20
  extensions: []
21
21
  extra_rdoc_files: []
22
22
  files:
23
23
  - CHANGELOG.md
24
24
  - MIT-LICENSE.txt
25
25
  - README.md
26
- - bin/bitbay-price
27
- - bin/coinmcap
26
+ - bin/cmcap
27
+ - bin/coincap
28
28
  - bin/cryptowatch
29
29
  - lib/cointools.rb
30
- - lib/cointools/bitbay.rb
30
+ - lib/cointools/coincap.rb
31
31
  - lib/cointools/coinmarketcap.rb
32
32
  - lib/cointools/cryptowatch.rb
33
33
  - lib/cointools/version.rb
data/bin/bitbay-price DELETED
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'cointools'
5
-
6
- require 'optparse'
7
- require 'time'
8
-
9
- def print_help
10
- puts "Usage: #{$PROGRAM_NAME} <market> [-v/--verbose]"
11
- puts " e.g.: #{$PROGRAM_NAME} btcusd"
12
- end
13
-
14
- verbose = false
15
-
16
- OptionParser.new do |opts|
17
- opts.on('-v', '--verbose') { verbose = true }
18
-
19
- opts.on('-h', '--help') do
20
- print_help
21
- exit 0
22
- end
23
-
24
- opts.parse!
25
- end
26
-
27
- if ARGV.length != 1
28
- print_help
29
- exit 1
30
- end
31
-
32
- market = ARGV[0].downcase
33
-
34
- begin
35
- result = CoinTools::BitBay.new.get_price(market)
36
-
37
- if verbose
38
- puts "#{market.upcase} @ #{Time.now} ==> #{result.price}"
39
- puts
40
- else
41
- puts result.price
42
- end
43
- rescue CoinTools::BitBay::ErrorResponseException => e
44
- $stderr.puts "Error: #{e}"
45
- exit 2
46
- rescue CoinTools::BitBay::BadRequestException => e
47
- $stderr.puts "Error: Incorrect market name (#{e})"
48
- exit 3
49
- end
@@ -1,63 +0,0 @@
1
- require_relative 'version'
2
-
3
- require 'json'
4
- require 'net/http'
5
- require 'uri'
6
-
7
- module CoinTools
8
- class BitBay
9
- BASE_URL = "https://bitbay.net/API/Public"
10
- USER_AGENT = "cointools/#{CoinTools::VERSION}"
11
-
12
- DataPoint = Struct.new(:price, :time)
13
-
14
- class InvalidResponseException < StandardError
15
- attr_reader :response
16
-
17
- def initialize(response)
18
- super("#{response.code} #{response.message}")
19
- @response = response
20
- end
21
- end
22
-
23
- class BadRequestException < InvalidResponseException
24
- end
25
-
26
- class ErrorResponseException < StandardError
27
- end
28
-
29
- def get_price(market)
30
- url = URI("#{BASE_URL}/#{market}/ticker.json")
31
-
32
- response = make_request(url)
33
-
34
- case response
35
- when Net::HTTPSuccess
36
- json = JSON.load(response.body)
37
-
38
- if json['code']
39
- raise ErrorResponseException.new("#{json['code']} #{json['message']}")
40
- end
41
-
42
- price = json['last']
43
-
44
- return DataPoint.new(price, nil)
45
- when Net::HTTPBadRequest
46
- raise BadRequestException.new(response)
47
- else
48
- raise InvalidResponseException.new(response)
49
- end
50
- end
51
-
52
- private
53
-
54
- def make_request(url)
55
- Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
56
- request = Net::HTTP::Get.new(url)
57
- request['User-Agent'] = USER_AGENT
58
-
59
- http.request(request)
60
- end
61
- end
62
- end
63
- end