bitsor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +61 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.rubocop.yml +45 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +21 -0
- data/README.md +74 -0
- data/Rakefile +9 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/bitsor.gemspec +33 -0
- data/lib/bitsor/client/account_status.rb +14 -0
- data/lib/bitsor/client/available_books.rb +14 -0
- data/lib/bitsor/client/balance.rb +14 -0
- data/lib/bitsor/client/bitcoin_withdrawal.rb +12 -0
- data/lib/bitsor/client/debit_card_withdrawal.rb +12 -0
- data/lib/bitsor/client/ether_withdrawal.rb +12 -0
- data/lib/bitsor/client/fees.rb +14 -0
- data/lib/bitsor/client/funding.rb +14 -0
- data/lib/bitsor/client/funding_destination.rb +14 -0
- data/lib/bitsor/client/kyc_documents.rb +12 -0
- data/lib/bitsor/client/ledger.rb +38 -0
- data/lib/bitsor/client/mx_bank_codes.rb +12 -0
- data/lib/bitsor/client/open_orders.rb +14 -0
- data/lib/bitsor/client/order_book.rb +12 -0
- data/lib/bitsor/client/order_trades.rb +12 -0
- data/lib/bitsor/client/orders.rb +22 -0
- data/lib/bitsor/client/phone_number.rb +12 -0
- data/lib/bitsor/client/phone_number_withdrawal.rb +12 -0
- data/lib/bitsor/client/phone_verification.rb +12 -0
- data/lib/bitsor/client/spei_withdrawal.rb +12 -0
- data/lib/bitsor/client/ticker.rb +14 -0
- data/lib/bitsor/client/trades.rb +14 -0
- data/lib/bitsor/client/user_trades.rb +14 -0
- data/lib/bitsor/client/withdrawals.rb +12 -0
- data/lib/bitsor/client.rb +86 -0
- data/lib/bitsor/concerns/authorizable.rb +10 -0
- data/lib/bitsor/concerns/configurable.rb +41 -0
- data/lib/bitsor/concerns/connection.rb +86 -0
- data/lib/bitsor/concerns/rate_limit.rb +18 -0
- data/lib/bitsor/default.rb +32 -0
- data/lib/bitsor/error.rb +96 -0
- data/lib/bitsor/normalizer.rb +138 -0
- data/lib/bitsor/version.rb +20 -0
- data/lib/bitsor.rb +33 -0
- metadata +148 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitsor
|
4
|
+
class Client
|
5
|
+
module Trades
|
6
|
+
def trades(book:, marker: nil, sort: :desc, limit: 25)
|
7
|
+
normalize_response.with(:trade) do
|
8
|
+
get('/v3/trades/', book: book, marker: marker, sort: sort, limit: limit)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitsor
|
4
|
+
class Client
|
5
|
+
module UserTrades
|
6
|
+
def user_trades(book:, marker: nil, sort: :desc, limit: 25)
|
7
|
+
normalize_response.with(:user_trade) do
|
8
|
+
get('/v3/user_trades/', book: book, marker: marker, sort: sort, limit: limit)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bitsor/error'
|
4
|
+
require 'bitsor/normalizer'
|
5
|
+
|
6
|
+
require 'bitsor/concerns/configurable'
|
7
|
+
require 'bitsor/concerns/rate_limit'
|
8
|
+
require 'bitsor/concerns/connection'
|
9
|
+
require 'bitsor/concerns/authorizable'
|
10
|
+
|
11
|
+
require 'bitsor/client/account_status'
|
12
|
+
require 'bitsor/client/available_books'
|
13
|
+
require 'bitsor/client/balance'
|
14
|
+
require 'bitsor/client/bitcoin_withdrawal'
|
15
|
+
require 'bitsor/client/debit_card_withdrawal'
|
16
|
+
require 'bitsor/client/ether_withdrawal'
|
17
|
+
require 'bitsor/client/fees'
|
18
|
+
require 'bitsor/client/funding'
|
19
|
+
require 'bitsor/client/funding_destination'
|
20
|
+
require 'bitsor/client/kyc_documents'
|
21
|
+
require 'bitsor/client/ledger'
|
22
|
+
require 'bitsor/client/mx_bank_codes'
|
23
|
+
require 'bitsor/client/open_orders'
|
24
|
+
require 'bitsor/client/order_book'
|
25
|
+
require 'bitsor/client/order_trades'
|
26
|
+
require 'bitsor/client/orders'
|
27
|
+
require 'bitsor/client/phone_number'
|
28
|
+
require 'bitsor/client/phone_number_withdrawal'
|
29
|
+
require 'bitsor/client/phone_verification'
|
30
|
+
require 'bitsor/client/spei_withdrawal'
|
31
|
+
require 'bitsor/client/ticker'
|
32
|
+
require 'bitsor/client/trades'
|
33
|
+
require 'bitsor/client/user_trades'
|
34
|
+
require 'bitsor/client/withdrawals'
|
35
|
+
|
36
|
+
module Bitsor
|
37
|
+
class Client
|
38
|
+
include Bitsor::Configurable
|
39
|
+
include Bitsor::Connection
|
40
|
+
include Bitsor::Authorizable
|
41
|
+
|
42
|
+
include Bitsor::Client::AccountStatus
|
43
|
+
include Bitsor::Client::AvailableBooks
|
44
|
+
include Bitsor::Client::Balance
|
45
|
+
include Bitsor::Client::BitcoinWithdrawal
|
46
|
+
include Bitsor::Client::DebitCardWithdrawal
|
47
|
+
include Bitsor::Client::EtherWithdrawal
|
48
|
+
include Bitsor::Client::Fees
|
49
|
+
include Bitsor::Client::Funding
|
50
|
+
include Bitsor::Client::FundingDestination
|
51
|
+
include Bitsor::Client::KycDocuments
|
52
|
+
include Bitsor::Client::Ledger
|
53
|
+
include Bitsor::Client::MxBankCodes
|
54
|
+
include Bitsor::Client::OpenOrders
|
55
|
+
include Bitsor::Client::OrderBook
|
56
|
+
include Bitsor::Client::OrderTrades
|
57
|
+
include Bitsor::Client::Orders
|
58
|
+
include Bitsor::Client::PhoneNumber
|
59
|
+
include Bitsor::Client::PhoneNumberWithdrawal
|
60
|
+
include Bitsor::Client::PhoneVerification
|
61
|
+
include Bitsor::Client::SpeiWithdrawal
|
62
|
+
include Bitsor::Client::Ticker
|
63
|
+
include Bitsor::Client::Trades
|
64
|
+
include Bitsor::Client::UserTrades
|
65
|
+
include Bitsor::Client::Withdrawals
|
66
|
+
|
67
|
+
attr_writer :client_id
|
68
|
+
attr_writer :api_key
|
69
|
+
attr_writer :api_secret
|
70
|
+
|
71
|
+
def initialize(options = {})
|
72
|
+
Bitsor::Configurable.keys.each do |key|
|
73
|
+
instance_variable_set(:"@#{key}", options[key] || Bitsor.instance_variable_get(:"@#{key}"))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def inspect
|
78
|
+
"Bitsor::Client(client_id: ****#{@client_id[4..-1]} api_key: ******#{@api_key[6..-1]}, object_id: #{format('0x00%x', (object_id << 1))})"
|
79
|
+
end
|
80
|
+
|
81
|
+
def normalize_response
|
82
|
+
@normalizer ||= Normalizer.new
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitsor
|
4
|
+
module Configurable
|
5
|
+
attr_accessor :client_id, :api_key, :api_secret
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def keys
|
9
|
+
@keys ||= [
|
10
|
+
:client_id,
|
11
|
+
:api_key,
|
12
|
+
:api_secret,
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def configure
|
18
|
+
yield self
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset!
|
22
|
+
Bitsor::Configurable.keys.each do |key|
|
23
|
+
instance_variable_set(:"@#{key}", Bitsor::Default.options[key])
|
24
|
+
end
|
25
|
+
@last_response = nil
|
26
|
+
self
|
27
|
+
end
|
28
|
+
alias setup reset!
|
29
|
+
|
30
|
+
def api_endpoint
|
31
|
+
File.join(@api_endpoint, '')
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def options
|
37
|
+
Hash[Bitsor::Configurable.keys.map { |key| [key, instance_variable_get(:"@#{key}")] }]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'json'
|
5
|
+
require 'openssl'
|
6
|
+
require 'typhoeus'
|
7
|
+
|
8
|
+
require 'bitsor/error'
|
9
|
+
|
10
|
+
module Bitsor
|
11
|
+
module Connection
|
12
|
+
def get(url, options = {})
|
13
|
+
request :get, url, nil, parse_query(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(url, options = {})
|
17
|
+
request :post, url, parse_body(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def put(url, options = {})
|
21
|
+
request :put, url, parse_body(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def patch(url, options = {})
|
25
|
+
request :patch, url, parse_body(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete(url, options = {})
|
29
|
+
request :delete, url, parse_body(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def last_response
|
33
|
+
@last_response if defined? @last_response
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def endpoint
|
39
|
+
Bitsor::Default.api_endpoint
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def request(method, path, body = nil, params = nil)
|
45
|
+
nonce = DateTime.now.strftime('%Q')
|
46
|
+
message = nonce + method.to_s.upcase + path + params.to_s + body.to_s
|
47
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), api_secret, message)
|
48
|
+
|
49
|
+
url = "#{endpoint}#{path}#{params}"
|
50
|
+
request_options = {
|
51
|
+
method: method,
|
52
|
+
headers: {
|
53
|
+
Authorization: "Bitso #{api_key}:#{nonce}:#{signature}",
|
54
|
+
'Content-Type': 'application/json',
|
55
|
+
},
|
56
|
+
}
|
57
|
+
request_options[:body] = body
|
58
|
+
response = Typhoeus::Request.new(url, request_options).run
|
59
|
+
@last_response = response
|
60
|
+
|
61
|
+
complete_request(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
def complete_request(response)
|
65
|
+
if error = Bitsor::Error.from_response(response)
|
66
|
+
raise error
|
67
|
+
end
|
68
|
+
|
69
|
+
JSON.parse(response.body, symbolize_names: true)[:payload]
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_query(options)
|
73
|
+
return nil if options.empty? || !options
|
74
|
+
|
75
|
+
options = options.select { |_key, value| !value.nil? || (value && !value.empty?) }
|
76
|
+
"?#{URI.encode_www_form(options)}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_body(options)
|
80
|
+
return '' if options.nil? || options.empty?
|
81
|
+
options = (options || {}).delete_if { |_k, v| v.nil? }
|
82
|
+
options.to_json
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitsor
|
4
|
+
class RateLimit < Struct.new(:limit, :remaining, :resets_at, :resets_in)
|
5
|
+
def self.from_response(response)
|
6
|
+
info = new
|
7
|
+
unless response&.headers.nil?
|
8
|
+
info.limit = (response.headers['X-RateLimit-Limit'] || 1).to_i
|
9
|
+
info.remaining = (response.headers['X-RateLimit-Remaining'] || 1).to_i
|
10
|
+
info.resets_at = Time.at((response.headers['X-RateLimit-Reset'] || Time.now).to_i)
|
11
|
+
info.resets_in = [(info.resets_at - Time.now).to_i, 0].max
|
12
|
+
end
|
13
|
+
|
14
|
+
info
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bitsor/version'
|
4
|
+
|
5
|
+
module Bitsor
|
6
|
+
module Default
|
7
|
+
API_ENDPOINT = 'https://api.bitso.com'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def options
|
11
|
+
Hash[Bitsor::Configurable.keys.map { |key| [key, send(key)] }]
|
12
|
+
end
|
13
|
+
|
14
|
+
def client_id
|
15
|
+
ENV['CLIENT_ID']
|
16
|
+
end
|
17
|
+
|
18
|
+
def api_key
|
19
|
+
ENV['API_KEY']
|
20
|
+
end
|
21
|
+
|
22
|
+
def api_secret
|
23
|
+
ENV['API_SECRET']
|
24
|
+
end
|
25
|
+
|
26
|
+
def api_endpoint
|
27
|
+
ENV['API_ENDPOINT'] || API_ENDPOINT
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/lib/bitsor/error.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitsor
|
4
|
+
class Error < StandardError
|
5
|
+
def self.from_response(response)
|
6
|
+
status = response.response_code
|
7
|
+
|
8
|
+
if klass = case status
|
9
|
+
when 400 then Bitsor::BadRequest
|
10
|
+
when 401 then Bitsor::Forbidden
|
11
|
+
when 403 then Bitsor::Forbidden
|
12
|
+
when 404 then Bitsor::NotFound
|
13
|
+
when 405 then Bitsor::MethodNotAllowed
|
14
|
+
when 406 then Bitsor::NotAcceptable
|
15
|
+
when 422 then Bitsor::UnprocessableEntity
|
16
|
+
when 400..499 then Bitsor::ClientError
|
17
|
+
when 500 then Bitsor::InternalServerError
|
18
|
+
when 501 then Bitsor::NotImplemented
|
19
|
+
when 502 then Bitsor::BadGateway
|
20
|
+
when 503 then Bitsor::ServiceUnavailable
|
21
|
+
when 500..599 then Bitsor::ServerError
|
22
|
+
end
|
23
|
+
klass.new(response)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(response = nil)
|
28
|
+
@response = response
|
29
|
+
@request = response.request
|
30
|
+
@body = { error: {} }
|
31
|
+
|
32
|
+
begin
|
33
|
+
if response.body && !response.body.empty?
|
34
|
+
@body = JSON.parse(response.body, symbolize_names: true)
|
35
|
+
end
|
36
|
+
rescue JSON::ParserError => e
|
37
|
+
@body = { error: { code: response.response_code, message: 'Internal Server Error: An Error Was Encountered' } }
|
38
|
+
end
|
39
|
+
|
40
|
+
super(build_error_message)
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_error_message
|
44
|
+
return nil if @response.nil?
|
45
|
+
message = ["#{@request.options[:method].to_s.upcase} "]
|
46
|
+
message << @response.options[:effective_url].to_s + "\n"
|
47
|
+
message << "Code #{@body[:error][:code]}: #{@body[:error][:message]} \n"
|
48
|
+
message.join
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_accessor :response, :request, :body
|
52
|
+
end
|
53
|
+
|
54
|
+
# Raised on errors in the 400-499 range
|
55
|
+
class ClientError < Error; end
|
56
|
+
|
57
|
+
# Raised when 400 HTTP status code
|
58
|
+
class BadRequest < ClientError; end
|
59
|
+
|
60
|
+
# Raised when 401 HTTP status code
|
61
|
+
class Unauthorized < ClientError; end
|
62
|
+
|
63
|
+
# Raised when 403 HTTP status code
|
64
|
+
class Forbidden < ClientError; end
|
65
|
+
|
66
|
+
# Raised when 403 HTTP status code
|
67
|
+
class TooManyRequests < Forbidden; end
|
68
|
+
|
69
|
+
# Raised when 404 HTTP status code
|
70
|
+
class NotFound < ClientError; end
|
71
|
+
|
72
|
+
# Raised when 405 HTTP status code
|
73
|
+
class MethodNotAllowed < ClientError; end
|
74
|
+
|
75
|
+
# Raised when 406 HTTP status code
|
76
|
+
class NotAcceptable < ClientError; end
|
77
|
+
|
78
|
+
# Raised when 422 HTTP status code
|
79
|
+
class UnprocessableEntity < ClientError; end
|
80
|
+
|
81
|
+
# Raised on errors in the 500-599 range
|
82
|
+
class ServerError < Error; end
|
83
|
+
|
84
|
+
# Raised when 500 HTTP status code
|
85
|
+
class InternalServerError < ServerError; end
|
86
|
+
|
87
|
+
# Raised when 501 HTTP status code
|
88
|
+
class NotImplemented < ServerError; end
|
89
|
+
|
90
|
+
# Raised when 502 HTTP status code
|
91
|
+
class BadGateway < ServerError; end
|
92
|
+
|
93
|
+
# Raised when 503 HTTP status code
|
94
|
+
class ServiceUnavailable < ServerError; end
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Bitsor
|
6
|
+
class Normalizer
|
7
|
+
SCHEMAS = {
|
8
|
+
account_balances: 'normalize_account_balances',
|
9
|
+
account_fees: 'normalize_account_fees',
|
10
|
+
account_status: 'normalize_account',
|
11
|
+
book: 'normalize_book',
|
12
|
+
funding: 'normalize_funding',
|
13
|
+
ledger: 'normalize_ledger',
|
14
|
+
order: 'normalize_order',
|
15
|
+
ticker: 'normalize_ticker',
|
16
|
+
trade: 'normalize_trade',
|
17
|
+
user_trade: 'normalize_user_trade',
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def with(type)
|
21
|
+
method = SCHEMAS[type.to_sym] || :null_normalizer
|
22
|
+
|
23
|
+
response = yield
|
24
|
+
|
25
|
+
if response.class == Array
|
26
|
+
return response.map do |response_element|
|
27
|
+
send method, response_element
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
send method, response
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def null_normalizer(response)
|
37
|
+
response
|
38
|
+
end
|
39
|
+
|
40
|
+
def normalize_account(response_object)
|
41
|
+
response_object[:client_id] = response_object[:client_id].to_i
|
42
|
+
response_object[:daily_limit] = response_object[:daily_limit].to_i
|
43
|
+
response_object[:monthly_limit] = response_object[:monthly_limit].to_i
|
44
|
+
response_object[:daily_remaining] = response_object[:daily_remaining].to_f
|
45
|
+
response_object[:monthly_remaining] = response_object[:monthly_remaining].to_f
|
46
|
+
response_object
|
47
|
+
end
|
48
|
+
|
49
|
+
def normalize_account_balances(response_object)
|
50
|
+
response_object[:balances] = response_object[:balances].map do |balance|
|
51
|
+
balance[:available] = balance[:available].to_f
|
52
|
+
balance[:locked] = balance[:locked].to_f
|
53
|
+
balance[:total] = balance[:total].to_f
|
54
|
+
balance[:pending_deposit] = balance[:pending_deposit].to_f
|
55
|
+
balance[:pending_withdrawal] = balance[:pending_withdrawal].to_f
|
56
|
+
balance
|
57
|
+
end
|
58
|
+
response_object
|
59
|
+
end
|
60
|
+
|
61
|
+
def normalize_book(response_object)
|
62
|
+
response_object[:minimum_price] = response_object[:minimum_price].to_f
|
63
|
+
response_object[:maximum_price] = response_object[:maximum_price].to_f
|
64
|
+
response_object[:minimum_amount] = response_object[:minimum_amount].to_f
|
65
|
+
response_object[:maximum_amount] = response_object[:maximum_amount].to_f
|
66
|
+
response_object[:minimum_value] = response_object[:minimum_value].to_f
|
67
|
+
response_object[:maximum_value] = response_object[:maximum_value].to_f
|
68
|
+
response_object
|
69
|
+
end
|
70
|
+
|
71
|
+
def normalize_account_fees(response_object)
|
72
|
+
response_object[:fees] = response_object[:fees].map do |fee|
|
73
|
+
fee[:fee_percent] = fee[:fee_percent].to_f
|
74
|
+
fee[:fee_decimal] = fee[:fee_decimal].to_f
|
75
|
+
fee
|
76
|
+
end
|
77
|
+
response_object[:withdrawal_fees][:btc] = response_object[:withdrawal_fees][:btc].to_f
|
78
|
+
response_object[:withdrawal_fees][:eth] = response_object[:withdrawal_fees][:eth].to_f
|
79
|
+
response_object
|
80
|
+
end
|
81
|
+
|
82
|
+
def normalize_funding(response_object)
|
83
|
+
response_object[:created_at] = DateTime.parse(response_object[:created_at])
|
84
|
+
response_object[:amount] = response_object[:amount].to_f
|
85
|
+
response_object
|
86
|
+
end
|
87
|
+
|
88
|
+
def normalize_order(response_object)
|
89
|
+
response_object[:original_amount] = response_object[:original_amount].to_f
|
90
|
+
response_object[:unfilled_amount] = response_object[:unfilled_amount].to_f
|
91
|
+
response_object[:original_value] = response_object[:original_value].to_f
|
92
|
+
response_object[:price] = response_object[:price].to_f
|
93
|
+
response_object[:created_at] = DateTime.parse(response_object[:created_at])
|
94
|
+
response_object[:updated_at] = DateTime.parse(response_object[:updated_at])
|
95
|
+
response_object
|
96
|
+
end
|
97
|
+
|
98
|
+
def normalize_ledger(response_object)
|
99
|
+
response_object[:created_at] = DateTime.parse(response_object[:created_at])
|
100
|
+
response_object[:balance_updates] = response_object[:balance_updates].map do |balance|
|
101
|
+
balance[:amount] = balance[:amount].to_f
|
102
|
+
balance
|
103
|
+
end
|
104
|
+
response_object
|
105
|
+
end
|
106
|
+
|
107
|
+
def normalize_ticker(response_object)
|
108
|
+
response_object[:volume] = response_object[:volume].to_f
|
109
|
+
response_object[:high] = response_object[:high].to_f
|
110
|
+
response_object[:last] = response_object[:last].to_f
|
111
|
+
response_object[:low] = response_object[:low].to_f
|
112
|
+
response_object[:vwap] = response_object[:vwap].to_f
|
113
|
+
response_object[:ask] = response_object[:ask].to_f
|
114
|
+
response_object[:bid] = response_object[:bid].to_f
|
115
|
+
response_object[:created_at] = DateTime.parse(response_object[:created_at])
|
116
|
+
response_object
|
117
|
+
end
|
118
|
+
|
119
|
+
def normalize_trade(response_object)
|
120
|
+
response_object[:amount] = response_object[:amount].to_f
|
121
|
+
response_object[:price] = response_object[:price].to_f
|
122
|
+
response_object[:created_at] = DateTime.parse(response_object[:created_at])
|
123
|
+
response_object
|
124
|
+
end
|
125
|
+
|
126
|
+
def normalize_user_trade(response_object)
|
127
|
+
response_object[:major] = response_object[:major].to_f
|
128
|
+
response_object[:minor] = response_object[:minor].to_f
|
129
|
+
response_object[:amount] = response_object[:amount].to_f
|
130
|
+
response_object[:fees_amount] = response_object[:fees_amount].to_f
|
131
|
+
response_object[:price] = response_object[:price].to_f
|
132
|
+
response_object[:tid] = response_object[:tid].to_i
|
133
|
+
response_object[:created_at] = DateTime.parse(response_object[:created_at])
|
134
|
+
response_object
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitsor
|
4
|
+
# Current major release.
|
5
|
+
# @return [Integer]
|
6
|
+
MAJOR = 0
|
7
|
+
|
8
|
+
# Current minor release.
|
9
|
+
# @return [Integer]
|
10
|
+
MINOR = 1
|
11
|
+
|
12
|
+
# Current patch level.
|
13
|
+
# @return [Integer]
|
14
|
+
PATCH = 0
|
15
|
+
|
16
|
+
# Full release version.
|
17
|
+
# @return [String]
|
18
|
+
VERSION = [MAJOR, MINOR, PATCH].join('.').freeze
|
19
|
+
end
|
20
|
+
|
data/lib/bitsor.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bitsor/client'
|
4
|
+
require 'bitsor/default'
|
5
|
+
require 'bitsor/error'
|
6
|
+
|
7
|
+
module Bitsor
|
8
|
+
class << self
|
9
|
+
include Bitsor::Configurable
|
10
|
+
|
11
|
+
def client
|
12
|
+
return @client if defined?(@client)
|
13
|
+
@client = Bitsor::Client.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def respond_to_missing?(method_name, include_private = false)
|
19
|
+
client.respond_to?(method_name, include_private)
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(method_name, *args, &block)
|
23
|
+
if client.respond_to?(method_name)
|
24
|
+
return client.send(method_name, *args, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Bitsor.setup
|
33
|
+
|