cointools 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0131dd2bc4e77dd203eacdfb8d13f31693f71394
4
+ data.tar.gz: dfd75a7b004577d0ec16cda14e8a3e896399b526
5
+ SHA512:
6
+ metadata.gz: 541daf33af1f794e931e1811bb4d25b1efd8d568f4f790e991142126cee37f9bcfcaf07acb1cfe51f86e230686ed22ee05f4c495152ac9a6f75dc708fde70cbe
7
+ data.tar.gz: 8f1e98132e55e483d4e4fe6db93315f1b4e6591f65fcbee30b666eb22db534d44c585cd341dfe3699eebacdf9a8bfdffe082c7c24598f306900fe75e82a250a5
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Cointools
2
+
3
+ This is a collection of Ruby scripts and library classes that let you check cryptocurrency prices on various services (currently Cryptowatch).
4
+
5
+ ## Installation
6
+
7
+ To use the scripts from the command line, install the gem:
8
+
9
+ ```
10
+ gem install cointools
11
+ ```
12
+
13
+ To use the code as a library, add it to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'cointools'
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### [Cryptowatch](https://cryptowat.ch)
22
+
23
+ To check past price of a given coin on a chosen exchange, pass the exchange and market name and a properly formatted timestamp:
24
+
25
+ ```
26
+ cryptowatch bitfinex btcusd "2017-12-17 13:00"
27
+ ```
28
+
29
+ To check the current price, skip the timestamp:
30
+
31
+ ```
32
+ cryptowatch bitfinex btcusd
33
+ ```
34
+
35
+ You can fetch a list of available exchanges and markets using these commands:
36
+
37
+ ```
38
+ cryptowatch --list-exchanges
39
+ cryptowatch --list-markets bithumb
40
+ ```
41
+
42
+ In code:
43
+
44
+ ```ruby
45
+ require 'cointools'
46
+ cryptowatch = CoinTools::Cryptowatch.new
47
+
48
+ exchange = cryptowatch.exchanges.first
49
+
50
+ list = cryptowatch.get_markets(exchange)
51
+ market = list.select { |x| x =~ /ltc/ && x !~ /btc/ }.first.upcase
52
+
53
+ result = cryptowatch.get_price(exchange, market, Time.now - 86400)
54
+ puts "#{market} yesterday: #{result.price}"
55
+
56
+ result = cryptowatch.get_current_price(exchange, market)
57
+ puts "#{market} today: #{result.price}"
58
+ ```
59
+
60
+ 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).
61
+
62
+
63
+ ## Credits & contributing
64
+
65
+ Copyright © 2018 [Kuba Suder](https://mackuba.eu). Licensed under [Very Simple Public License](https://github.com/mackuba/cointools/blob/master/VSPL-LICENSE.txt), my custom license that's basically a simplified version of the MIT license that fits in 3 lines.
66
+
67
+ If you'd like to help me extend the scripts with some additional features or add support for new services, [send me a pull request](https://github.com/mackuba/cointools/pulls).
data/VSPL-LICENSE.txt ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2018 Kuba Suder
2
+
3
+ You can modify, distribute and use this software for any purpose without any
4
+ restrictions as long as you keep this copyright notice intact. The software is
5
+ provided without any warranty.
data/bin/cryptowatch ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'cointools'
5
+
6
+ require 'optparse'
7
+ require 'time'
8
+
9
+ verbose = false
10
+
11
+ OptionParser.new do |opts|
12
+ opts.on('-v', '--verbose') { verbose = true }
13
+
14
+ opts.on('--list-exchanges') do
15
+ puts CoinTools::Cryptowatch.new.exchanges
16
+ exit 0
17
+ end
18
+
19
+ opts.on('--list-markets EXCHANGE') do |exchange|
20
+ begin
21
+ puts CoinTools::Cryptowatch.new.get_markets(exchange)
22
+ exit 0
23
+ rescue CoinTools::Cryptowatch::BadRequestException => e
24
+ $stderr.puts "Error: Incorrect exchange name: #{e}"
25
+ exit 3
26
+ end
27
+ end
28
+
29
+ opts.parse!
30
+ end
31
+
32
+ if ARGV.length < 2 || ARGV.length > 3
33
+ puts "Usage: #{$PROGRAM_NAME} <exchange> <market> [<date>] [-v/--verbose]"
34
+ puts " e.g.: #{$PROGRAM_NAME} gdax btcusd \"2017-06-30 15:27\""
35
+ puts
36
+ puts "* To print a list of available exchanges:"
37
+ puts " #{$PROGRAM_NAME} --list-exchanges"
38
+ puts "* To print a list of available markets on an exchange:"
39
+ puts " #{$PROGRAM_NAME} --list-markets kraken"
40
+ exit 1
41
+ end
42
+
43
+ exchange = ARGV[0].downcase
44
+ market = ARGV[1].downcase
45
+ date = Time.parse(ARGV[2]) if ARGV[2]
46
+
47
+ begin
48
+ result = CoinTools::Cryptowatch.new.get_price(exchange, market, date)
49
+
50
+ if verbose
51
+ puts "#{exchange}:#{market} @ #{result.time || Time.now} ==> #{result.price}"
52
+ puts
53
+ else
54
+ puts result.price
55
+ end
56
+ rescue CoinTools::Cryptowatch::InvalidDateException => e
57
+ $stderr.puts "Error: #{e}"
58
+ exit 2
59
+ rescue CoinTools::Cryptowatch::BadRequestException => e
60
+ $stderr.puts "Error: Incorrect exchange or market name: #{e}"
61
+ exit 3
62
+ rescue CoinTools::Cryptowatch::NoDataException => e
63
+ $stderr.puts "Error: #{e}: data not ready yet"
64
+ exit 4
65
+ end
@@ -0,0 +1,151 @@
1
+ require_relative 'version'
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module CoinTools
8
+ class Cryptowatch
9
+ BASE_URL = "https://api.cryptowat.ch"
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 NoDataException < StandardError
27
+ end
28
+
29
+ class InvalidDateException < StandardError
30
+ end
31
+
32
+ def exchanges
33
+ @exchanges ||= get_exchanges
34
+ end
35
+
36
+ def get_markets(exchange)
37
+ url = URI("#{BASE_URL}/markets/#{exchange}")
38
+
39
+ response = make_request(url)
40
+
41
+ case response
42
+ when Net::HTTPSuccess
43
+ json = JSON.load(response.body)
44
+ return json['result'].select { |m| m['active'] == true }.map { |m| m['pair'] }.sort
45
+ when Net::HTTPBadRequest
46
+ raise BadRequestException.new(response)
47
+ else
48
+ raise Exception.new(response)
49
+ end
50
+ end
51
+
52
+ def get_price(exchange, market, time = nil)
53
+ return get_current_price(exchange, market) if time.nil?
54
+
55
+ (time <= Time.now) or raise InvalidDateException.new('Future date was passed')
56
+ (time.year >= 2009) or raise InvalidDateException.new('Too early date was passed')
57
+
58
+ unixtime = time.to_i
59
+ current_time = Time.now.to_i
60
+ url = URI("#{BASE_URL}/markets/#{exchange}/#{market}/ohlc?after=#{unixtime}")
61
+
62
+ response = make_request(url)
63
+
64
+ case response
65
+ when Net::HTTPSuccess
66
+ json = JSON.load(response.body)
67
+ data = json['result']
68
+
69
+ timestamp, o, h, l, c, volume = best_matching_record(data, unixtime, current_time)
70
+ raise NoDataException.new('No data found for a given time') if timestamp.nil?
71
+
72
+ actual_time = Time.at(timestamp)
73
+ return DataPoint.new(o, actual_time)
74
+ when Net::HTTPBadRequest
75
+ raise BadRequestException.new(response)
76
+ else
77
+ raise Exception.new(response)
78
+ end
79
+ end
80
+
81
+ def get_current_price(exchange, market)
82
+ url = URI("#{BASE_URL}/markets/#{exchange}/#{market}/price")
83
+
84
+ response = make_request(url)
85
+
86
+ case response
87
+ when Net::HTTPSuccess
88
+ json = JSON.load(response.body)
89
+ price = json['result']['price']
90
+
91
+ return DataPoint.new(price, nil)
92
+ when Net::HTTPBadRequest
93
+ raise BadRequestException.new(response)
94
+ else
95
+ raise Exception.new(response)
96
+ end
97
+ end
98
+
99
+
100
+ private
101
+
102
+ def make_request(url)
103
+ Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
104
+ request = Net::HTTP::Get.new(url)
105
+ request['User-Agent'] = USER_AGENT
106
+
107
+ http.request(request)
108
+ end
109
+ end
110
+
111
+ def best_matching_record(data, unixtime, request_time)
112
+ candidates = []
113
+
114
+ data.keys.sort_by { |k| k.to_i }.each do |k|
115
+ records = data[k]
116
+ previous = nil
117
+
118
+ records.each do |record|
119
+ timestamp, o, h, l, c, volume = record
120
+
121
+ if timestamp >= unixtime
122
+ candidates.push(record) unless timestamp > request_time
123
+ break
124
+ else
125
+ previous = record
126
+ end
127
+ end
128
+
129
+ candidates.push(previous) if previous
130
+ end
131
+
132
+ candidates.sort_by { |record| (record[0] - unixtime).abs }.first
133
+ end
134
+
135
+ def get_exchanges
136
+ url = URI("#{BASE_URL}/exchanges")
137
+
138
+ response = make_request(url)
139
+
140
+ case response
141
+ when Net::HTTPSuccess
142
+ json = JSON.load(response.body)
143
+ return json['result'].select { |e| e['active'] == true }.map { |e| e['symbol'] }.sort
144
+ when Net::HTTPBadRequest
145
+ raise BadRequestException.new(response)
146
+ else
147
+ raise Exception.new(response)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,3 @@
1
+ module CoinTools
2
+ VERSION = "0.1.1"
3
+ end
data/lib/cointools.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'cointools/cryptowatch'
2
+ require 'cointools/version'
3
+
4
+ module CoinTools
5
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cointools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Kuba Suder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - jakub.suder@gmail.com
16
+ executables:
17
+ - cryptowatch
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - VSPL-LICENSE.txt
23
+ - bin/cryptowatch
24
+ - lib/cointools.rb
25
+ - lib/cointools/cryptowatch.rb
26
+ - lib/cointools/version.rb
27
+ homepage: https://github.com/mackuba/cointools
28
+ licenses:
29
+ - Nonstandard
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.5.1
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: A collection of scripts for checking cryptocurrency prices.
51
+ test_files: []