bitmex-api 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +28 -13
- data/LICENSE +21 -0
- data/README.md +201 -24
- data/TODOs.org +10 -2
- data/bin/chat.rb +1 -1
- data/bitmex.gemspec +22 -23
- data/lib/bitmex.rb +15 -0
- data/lib/bitmex/apikey.rb +6 -6
- data/lib/bitmex/base.rb +13 -7
- data/lib/bitmex/chat.rb +18 -20
- data/lib/bitmex/client.rb +67 -196
- data/lib/bitmex/instrument.rb +10 -15
- data/lib/bitmex/order.rb +12 -13
- data/lib/bitmex/position.rb +20 -11
- data/lib/bitmex/quote.rb +19 -10
- data/lib/bitmex/rest.rb +103 -0
- data/lib/bitmex/stats.rb +4 -4
- data/lib/bitmex/trade.rb +24 -13
- data/lib/bitmex/user.rb +26 -48
- data/lib/bitmex/version.rb +1 -1
- data/lib/bitmex/websocket.rb +80 -30
- metadata +19 -17
data/TODOs.org
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
CLOSED: [2019-01-14] SCHEDULED: <2019-01-14 Fri>
|
6
6
|
** DONE auth for REST-API endpoints
|
7
7
|
CLOSED: [2019-01-16 Wed] SCHEDULED: <2019-01-15 Tue> DEADLINE: <2019-01-16 Wed>
|
8
|
-
**
|
8
|
+
** DONE auth for private subscription topics
|
9
|
+
CLOSED: [2019-02-01 Fri] SCHEDULED: <2019-02-01 Fri>
|
9
10
|
** move from rspec to minitest
|
10
11
|
** configure autorun
|
11
12
|
** hearbeat, ping/pong
|
@@ -18,8 +19,15 @@
|
|
18
19
|
** DONE orderbook, order resource
|
19
20
|
CLOSED: [2019-01-29 Tue] SCHEDULED: <2019-01-29 Tue>
|
20
21
|
** refactoring: use class methods when working with multiple entities
|
21
|
-
**
|
22
|
+
** bug: {"error":{"message":"Signature not valid.","name":"HTTPError"}} in user#executions
|
22
23
|
** DONE liquidation, leaderboard, insurance, instrument, funding, execution, chat, announcement
|
23
24
|
CLOSED: [2019-01-30 Wed] SCHEDULED: <2019-01-30 Wed>
|
24
25
|
** DONE refactoring: extract websocket common logic
|
25
26
|
CLOSED: [2019-01-31 Thu] SCHEDULED: <2019-01-31 Thu>
|
27
|
+
** DONE refactoring: extract REST API implemenration
|
28
|
+
CLOSED: [2019-02-08 Fri] SCHEDULED: <2019-02-08 Fri>
|
29
|
+
** DONE add idiomatic websocket support for all resources
|
30
|
+
CLOSED: [2019-02-10 Sun 16:57] SCHEDULED: <2019-02-10 Sun>
|
31
|
+
** refactoring: make user model to extend base class as well
|
32
|
+
** DONE Release 1.0: API endpoints, examples in README
|
33
|
+
CLOSED: [2019-02-11 Mon] SCHEDULED: <2019-02-11 Mon>
|
data/bin/chat.rb
CHANGED
data/bitmex.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require 'bitmex/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = 'bitmex-api'
|
@@ -9,45 +8,45 @@ Gem::Specification.new do |spec|
|
|
9
8
|
spec.authors = ['Iulian Costan']
|
10
9
|
spec.email = ['iulian.costan@gmail.com']
|
11
10
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
11
|
+
spec.summary = 'Fully-featured, idiomatic Ruby library for BitMEX API'
|
12
|
+
spec.description = 'Fully-featured, idiomatic Ruby library for BitMEX API'
|
14
13
|
spec.homepage = 'https://github.com/icostan/bitmex-api-ruby'
|
15
14
|
|
16
15
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
16
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
17
|
if spec.respond_to?(:metadata)
|
19
|
-
spec.metadata[
|
18
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
20
19
|
|
21
|
-
spec.metadata[
|
22
|
-
spec.metadata[
|
23
|
-
spec.metadata[
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
21
|
+
spec.metadata['source_code_uri'] = 'https://github.com/icostan/bitmex-api-ruby.git'
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/icostan/bitmex-api-ruby/blob/master/CHANGELOG.md'
|
24
23
|
else
|
25
|
-
raise
|
26
|
-
|
24
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
25
|
+
'public gem pushes.'
|
27
26
|
end
|
28
27
|
|
29
28
|
# Specify which files should be added to the gem when it is released.
|
30
29
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
31
|
-
spec.files
|
30
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
32
31
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
32
|
end
|
34
|
-
spec.bindir =
|
33
|
+
spec.bindir = 'exe'
|
35
34
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
-
spec.require_paths = [
|
35
|
+
spec.require_paths = ['lib']
|
37
36
|
|
38
|
-
spec.add_dependency 'httparty'
|
39
|
-
spec.add_dependency 'hashie'
|
40
|
-
spec.add_dependency 'faye-websocket'
|
41
37
|
spec.add_dependency 'eventmachine'
|
38
|
+
spec.add_dependency 'faye-websocket'
|
39
|
+
spec.add_dependency 'hashie'
|
40
|
+
spec.add_dependency 'httparty'
|
42
41
|
|
43
|
-
spec.add_development_dependency 'bundler'
|
44
|
-
spec.add_development_dependency 'rake'
|
45
|
-
spec.add_development_dependency 'rspec'
|
46
42
|
spec.add_development_dependency 'bump'
|
43
|
+
spec.add_development_dependency 'bundler'
|
44
|
+
spec.add_development_dependency 'dotenv'
|
47
45
|
spec.add_development_dependency 'pry'
|
48
46
|
spec.add_development_dependency 'pry-doc'
|
49
|
-
spec.add_development_dependency '
|
50
|
-
spec.add_development_dependency 'dotenv'
|
51
|
-
spec.add_development_dependency 'rubocop'
|
47
|
+
spec.add_development_dependency 'rake'
|
52
48
|
spec.add_development_dependency 'reek'
|
49
|
+
spec.add_development_dependency 'rspec'
|
50
|
+
spec.add_development_dependency 'rubocop'
|
51
|
+
spec.add_development_dependency 'simplecov'
|
53
52
|
end
|
data/lib/bitmex.rb
CHANGED
@@ -16,10 +16,14 @@ require 'bitmex/instrument'
|
|
16
16
|
require 'bitmex/user'
|
17
17
|
require 'bitmex/apikey'
|
18
18
|
require 'bitmex/websocket'
|
19
|
+
require 'bitmex/rest'
|
19
20
|
|
20
21
|
# Bitmex module
|
21
22
|
module Bitmex
|
23
|
+
# Bitmex standard error
|
22
24
|
class Error < StandardError; end
|
25
|
+
# 403 Forbidden
|
26
|
+
class ForbiddenError < Error; end
|
23
27
|
|
24
28
|
TESTNET_HOST = 'testnet.bitmex.com'.freeze
|
25
29
|
MAINNET_HOST = 'www.bitmex.com'.freeze
|
@@ -31,4 +35,15 @@ module Bitmex
|
|
31
35
|
data = verb + path + expires.to_s + params
|
32
36
|
OpenSSL::HMAC.hexdigest 'SHA256', api_secret, data
|
33
37
|
end
|
38
|
+
|
39
|
+
def self.headers(api_key, api_secret, verb, path, body)
|
40
|
+
return {} unless api_key || api_secret
|
41
|
+
|
42
|
+
expires = Time.now.utc.to_i + 60
|
43
|
+
{
|
44
|
+
'api-expires' => expires.to_s,
|
45
|
+
'api-key' => api_key,
|
46
|
+
'api-signature' => Bitmex.signature(api_secret, verb, path, expires, body)
|
47
|
+
}
|
48
|
+
end
|
34
49
|
end
|
data/lib/bitmex/apikey.rb
CHANGED
@@ -5,17 +5,17 @@ module Bitmex
|
|
5
5
|
attr_reader :api_key
|
6
6
|
|
7
7
|
# Create new Apikey
|
8
|
-
# @param
|
8
|
+
# @param rest [Bitmex::Rest] the rest client
|
9
9
|
# @param api_key [String] public apikey
|
10
|
-
def initialize(
|
11
|
-
super
|
10
|
+
def initialize(rest, api_key = nil)
|
11
|
+
super rest
|
12
12
|
@api_key = api_key
|
13
13
|
end
|
14
14
|
|
15
15
|
# Get your API Keys
|
16
16
|
# @return [Array] the api keys
|
17
17
|
def all
|
18
|
-
|
18
|
+
rest.get apikey_path, auth: true do |response|
|
19
19
|
response_handler response
|
20
20
|
end
|
21
21
|
end
|
@@ -23,7 +23,7 @@ module Bitmex
|
|
23
23
|
# NOT SUPPORTED
|
24
24
|
# #return 403 Access Denied
|
25
25
|
def enable
|
26
|
-
|
26
|
+
rest.post apikey_path(:enable) do |response|
|
27
27
|
response_handler response
|
28
28
|
end
|
29
29
|
end
|
@@ -31,7 +31,7 @@ module Bitmex
|
|
31
31
|
# NOT SUPPORTED
|
32
32
|
# @return 403 Access Denied
|
33
33
|
def disable
|
34
|
-
|
34
|
+
rest.post apikey_path(:disable) do |response|
|
35
35
|
response_handler response
|
36
36
|
end
|
37
37
|
end
|
data/lib/bitmex/base.rb
CHANGED
@@ -2,25 +2,31 @@ module Bitmex
|
|
2
2
|
# Base class for all Bitmex models
|
3
3
|
# @author Iulian Costan
|
4
4
|
class Base
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :rest, :websocket
|
6
6
|
|
7
|
-
# @param
|
8
|
-
|
9
|
-
|
7
|
+
# @param rest [Bitmex::Rest] the rest implementation
|
8
|
+
# @param websocket [Bitmex::Websocket] the websocket implementation
|
9
|
+
def initialize(rest, websocket = nil)
|
10
|
+
@rest = rest
|
11
|
+
@websocket = websocket
|
10
12
|
end
|
11
13
|
|
12
14
|
protected
|
13
15
|
|
16
|
+
def check_binsize(bin_size)
|
17
|
+
raise ArgumentError, 'invalid bin_size' unless %w[1m 5m 1h 1d].include? bin_size.to_s
|
18
|
+
end
|
19
|
+
|
14
20
|
def response_handler(response)
|
15
|
-
|
21
|
+
rest.response_handler response
|
16
22
|
end
|
17
23
|
|
18
|
-
def requires
|
24
|
+
def requires(arg, args)
|
19
25
|
raise ArgumentError, "argument '#{arg}' is required" unless args.include? arg
|
20
26
|
end
|
21
27
|
|
22
28
|
def base_path(resource, action)
|
23
|
-
|
29
|
+
rest.base_path resource, action
|
24
30
|
end
|
25
31
|
end
|
26
32
|
end
|
data/lib/bitmex/chat.rb
CHANGED
@@ -4,48 +4,46 @@ module Bitmex
|
|
4
4
|
class Chat < Base
|
5
5
|
# Get chat messages
|
6
6
|
# @example Get last 10 messages for channel 1
|
7
|
-
# messages = client.chat.messages
|
7
|
+
# messages = client.chat.messages channelID: 1, count: 10, reverse: true
|
8
8
|
# @param options [Hash] options to filter by
|
9
9
|
# @option options [Integer] :count (100) number of results to fetch.
|
10
10
|
# @option options [Integer] :start starting ID for results
|
11
11
|
# @option options [Boolean] :reverse If true, will sort results newest first
|
12
12
|
# @option options [Integer] :channelID Channel id. GET /chat/channels for ids. Leave blank for all.
|
13
13
|
# @return [Array] the messages
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
response_handler response
|
14
|
+
# @yield [Hash] the message
|
15
|
+
def messages(options = { count: 100, reverse: true }, &ablock)
|
16
|
+
if block_given?
|
17
|
+
websocket.listen chat: options[:channelID], &ablock
|
18
|
+
else
|
19
|
+
rest.get chat_path, params: options
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
23
|
# Get available channels
|
25
24
|
# @return [Array] the available channels
|
26
25
|
def channels
|
27
|
-
|
28
|
-
response_handler response
|
29
|
-
end
|
26
|
+
rest.get chat_path(:channels)
|
30
27
|
end
|
31
28
|
|
32
29
|
# Get connected users
|
33
30
|
# @return [Bitmex::Mash] an array with browser users in the first position and API users (bots) in the second position.
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
# @yield [Hash] the stats
|
32
|
+
def stats(&ablock)
|
33
|
+
if block_given?
|
34
|
+
websocket.listen connected: nil, &ablock
|
35
|
+
else
|
36
|
+
rest.get chat_path(:connected)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
40
|
# Send a chat message
|
41
41
|
# @param message [String] the message to send
|
42
42
|
# @param options [Hash] filter options
|
43
|
-
# @option options [Integer] :
|
44
|
-
def send(message, options = {
|
45
|
-
params = { message: message, channelID: options[:
|
46
|
-
|
47
|
-
response_handler response
|
48
|
-
end
|
43
|
+
# @option options [Integer] :channelID (1) channel to post to
|
44
|
+
def send(message, options = { channelID: 1 })
|
45
|
+
params = { message: message, channelID: options[:channelID] }
|
46
|
+
rest.post chat_path, params: params
|
49
47
|
end
|
50
48
|
|
51
49
|
private
|
data/lib/bitmex/client.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'faye/websocket'
|
3
|
-
require 'eventmachine'
|
4
|
-
require 'logger'
|
5
|
-
|
6
1
|
module Bitmex
|
2
|
+
# Main client interface for Bitmex API.
|
7
3
|
class Client
|
8
|
-
include HTTParty
|
9
|
-
# logger ::Logger.new(STDOUT), :debug, :curl
|
10
|
-
|
11
|
-
AUTHORIZATIONS = %w(apikey execution position globalnotification order leaderboard quote user userevent)
|
12
|
-
|
13
4
|
attr_reader :host, :api_key, :api_secret
|
14
5
|
|
15
6
|
# Create new client instance
|
@@ -24,45 +15,53 @@ module Bitmex
|
|
24
15
|
|
25
16
|
# Get site announcements
|
26
17
|
# @return [Array] the public announcements
|
27
|
-
def announcements
|
28
|
-
|
29
|
-
|
18
|
+
def announcements(&ablock)
|
19
|
+
if block_given?
|
20
|
+
websocket.listen announcement: nil, &ablock
|
21
|
+
else
|
22
|
+
rest.get :announcement
|
30
23
|
end
|
31
24
|
end
|
32
25
|
|
33
26
|
# Persistent API Keys for Developers
|
34
27
|
# @return [Bitmex::Apikey] the apikey instance
|
35
28
|
def apikey(api_key = nil)
|
36
|
-
Bitmex::Apikey.new
|
29
|
+
Bitmex::Apikey.new rest, api_key
|
37
30
|
end
|
38
31
|
|
39
32
|
# Trollbox Data
|
40
33
|
# @return [Bitmex::Chat] the chat instance
|
41
34
|
def chat
|
42
|
-
Bitmex::Chat.new
|
35
|
+
Bitmex::Chat.new rest, websocket
|
43
36
|
end
|
44
37
|
|
45
38
|
# Tradeable Contracts, Indices, and History
|
46
39
|
# @return [Bitmex::Instrument] the instrument model
|
47
40
|
def instrument
|
48
|
-
Bitmex::Instrument.new
|
41
|
+
Bitmex::Instrument.new rest, websocket
|
49
42
|
end
|
50
43
|
|
51
44
|
# Get funding history
|
52
45
|
# @!macro bitmex.filters
|
53
46
|
# @return [Array] the history
|
54
|
-
|
55
|
-
|
56
|
-
|
47
|
+
# @yield [Hash] the funding data
|
48
|
+
def funding(filters = {}, &ablock)
|
49
|
+
if block_given?
|
50
|
+
websocket.listen funding: nil, &ablock
|
51
|
+
else
|
52
|
+
rest.get :funding, params: filters
|
57
53
|
end
|
58
54
|
end
|
59
55
|
|
60
56
|
# Get insurance fund history
|
61
57
|
# @!macro bitmex.filters
|
62
58
|
# @return [Array] the history
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
# @yield [Hash] the insurance data
|
60
|
+
def insurance(filters = {}, &ablock)
|
61
|
+
if block_given?
|
62
|
+
websocket.listen insurance: nil, &ablock
|
63
|
+
else
|
64
|
+
rest.get :insurance, params: filters
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
@@ -70,17 +69,24 @@ module Bitmex
|
|
70
69
|
# @param ranking [notional ROE] the ranking type
|
71
70
|
# @return [Array] current leaders
|
72
71
|
def leaderboard(ranking = 'notional')
|
73
|
-
get
|
74
|
-
response_handler response
|
75
|
-
end
|
72
|
+
rest.get :leaderboard, params: { method: ranking }
|
76
73
|
end
|
77
74
|
|
78
75
|
# Get liquidation orders
|
76
|
+
# @example Get liquidations orders
|
77
|
+
# liquidations = client.liquidations symbol: 'XBTUSD'
|
78
|
+
# @example Listen for liquidation orders
|
79
|
+
# client.liquidations symbol: 'XBTUSD' do |liquidation|
|
80
|
+
# puts liquidation.inspect
|
81
|
+
# end
|
79
82
|
# @!macro bitmex.filters
|
80
83
|
# @return [Array] the liquidations
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
# @yield [Hash] the liquidation data
|
85
|
+
def liquidations(filters = {}, &ablock)
|
86
|
+
if block_given?
|
87
|
+
websocket.listen liquidation: filters[:symbol], &ablock
|
88
|
+
else
|
89
|
+
rest.get :liquidation, params: filters
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
@@ -88,7 +94,7 @@ module Bitmex
|
|
88
94
|
# @return [Bitmex::Order] the order model
|
89
95
|
def orders
|
90
96
|
# TODO: use class method
|
91
|
-
Bitmex::Order.new
|
97
|
+
Bitmex::Order.new rest, websocket
|
92
98
|
end
|
93
99
|
|
94
100
|
# Get an order by id
|
@@ -98,17 +104,27 @@ module Bitmex
|
|
98
104
|
def order(orderID: nil, clOrdID: nil)
|
99
105
|
raise ArgumentError, 'either orderID or clOrdID is required' if orderID.nil? && clOrdID.nil?
|
100
106
|
|
101
|
-
Bitmex::Order.new
|
107
|
+
Bitmex::Order.new rest, websocket, orderID, clOrdID
|
102
108
|
end
|
103
109
|
|
104
|
-
# Get current orderbook in vertical format
|
110
|
+
# Get current Level 2 orderbook in vertical format
|
111
|
+
# @example Get the first level
|
112
|
+
# orderbook = client.orderbook 'XBTUSD', depth: 1
|
113
|
+
# @example Listen to orderbook changes
|
114
|
+
# client.orderbook 'XBTUSD' do |orderbook|
|
115
|
+
# puts orderbook.inspect
|
116
|
+
# end
|
105
117
|
# @param symbol [String] instrument symbol, send a series (e.g. XBT) to get data for the nearest contract in that series
|
106
118
|
# @param depth [Integer] orderbook depth per side. send 0 for full depth.
|
107
119
|
# @return [Array] the orderbook
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
120
|
+
# @yield [Hash] the orderbook data
|
121
|
+
def orderbook(symbol, depth: 25, &ablock)
|
122
|
+
raise ArgumentError, 'symbol is required' unless symbol
|
123
|
+
|
124
|
+
if block_given?
|
125
|
+
websocket.listen orderBookL2: symbol, &ablock
|
126
|
+
else
|
127
|
+
rest.get 'orderbook/L2', params: { symbol: symbol, depth: depth }
|
112
128
|
end
|
113
129
|
end
|
114
130
|
|
@@ -116,209 +132,64 @@ module Bitmex
|
|
116
132
|
# @return [Array] the list of positions
|
117
133
|
def positions
|
118
134
|
# TODO: use class method
|
119
|
-
Bitmex::Position.new
|
135
|
+
Bitmex::Position.new rest, websocket
|
120
136
|
end
|
121
137
|
|
122
138
|
# Get an open position
|
123
139
|
# @param symbol [String] symbol of position
|
124
140
|
# @return [Bitmex::Position] open position
|
125
141
|
def position(symbol)
|
126
|
-
Bitmex::Position.new
|
142
|
+
Bitmex::Position.new rest, websocket, symbol
|
127
143
|
end
|
128
144
|
|
129
145
|
# Best Bid/Offer Snapshots & Historical Bins
|
130
146
|
# @return [Bitmex::Quote] the quote model
|
131
147
|
def quotes
|
132
148
|
# TODO: use class method
|
133
|
-
Bitmex::Quote.new
|
149
|
+
Bitmex::Quote.new rest, websocket
|
134
150
|
end
|
135
151
|
|
136
152
|
# Get model schemata for data objects returned by this AP
|
137
153
|
# @return [Hash] the schema
|
138
154
|
def schema
|
139
|
-
get
|
140
|
-
response_handler response
|
141
|
-
end
|
155
|
+
rest.get :schema
|
142
156
|
end
|
143
157
|
|
144
158
|
# Get settlement history
|
145
159
|
# @return [Array] the settlement history
|
146
|
-
|
147
|
-
|
148
|
-
|
160
|
+
# @yield [Hash] the settlement data
|
161
|
+
def settlements(&ablock)
|
162
|
+
if block_given?
|
163
|
+
websocket.listen settlement: nil, &ablock
|
164
|
+
else
|
165
|
+
rest.get :settlement
|
149
166
|
end
|
150
167
|
end
|
151
168
|
|
152
169
|
# Exchange statistics
|
153
170
|
# @return [Bitmex::Stats] the stats model
|
154
171
|
def stats
|
155
|
-
Bitmex::Stats.new
|
172
|
+
Bitmex::Stats.new rest
|
156
173
|
end
|
157
174
|
|
158
175
|
# Individual and bucketed trades
|
159
176
|
# @return [Bitmex::Trade] the trade model
|
160
177
|
def trades
|
161
|
-
Bitmex::Trade.new
|
178
|
+
Bitmex::Trade.new rest, websocket
|
162
179
|
end
|
163
180
|
|
164
181
|
# Account operations
|
165
182
|
# @return [Bitmex::User] the user model
|
166
183
|
def user
|
167
|
-
Bitmex::User.new
|
168
|
-
end
|
169
|
-
|
170
|
-
# Listen to generic topics
|
171
|
-
# @param topics [Hash] topics to listen to e.g. { trade: "XBTUSD" }
|
172
|
-
# @yield [data] data pushed via websocket
|
173
|
-
def listen(topics, &ablock)
|
174
|
-
EM.run do
|
175
|
-
topics.each do |topic, symbol|
|
176
|
-
websocket.subscribe topic, symbol, &ablock
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
#
|
182
|
-
# Stop websocket listener
|
183
|
-
#
|
184
|
-
def stop
|
185
|
-
EM.stop_event_loop
|
184
|
+
Bitmex::User.new rest, websocket
|
186
185
|
end
|
187
186
|
|
188
187
|
def websocket
|
189
|
-
@websocket ||= Websocket.new
|
190
|
-
end
|
191
|
-
|
192
|
-
# TODO: move these methods into rest client
|
193
|
-
def get(path, params: {}, auth: false, &ablock)
|
194
|
-
options = {}
|
195
|
-
options[:query] = params unless params.empty?
|
196
|
-
options[:headers] = headers 'GET', path, '' if auth
|
197
|
-
|
198
|
-
response = self.class.get "#{domain_url}#{path}", options
|
199
|
-
yield response
|
200
|
-
end
|
201
|
-
|
202
|
-
def put(path, params: {}, auth: true, json: true)
|
203
|
-
body = json ? params.to_json.to_s : URI.encode_www_form(params)
|
204
|
-
|
205
|
-
options = {}
|
206
|
-
options[:body] = body
|
207
|
-
options[:headers] = headers 'PUT', path, body, json: json if auth
|
208
|
-
|
209
|
-
response = self.class.put "#{domain_url}#{path}", options
|
210
|
-
yield response
|
211
|
-
end
|
212
|
-
|
213
|
-
def post(path, params: {}, auth: true, json: true)
|
214
|
-
body = json ? params.to_json.to_s : URI.encode_www_form(params)
|
215
|
-
|
216
|
-
options = {}
|
217
|
-
options[:body] = body
|
218
|
-
options[:headers] = headers 'POST', path, body, json: json if auth
|
219
|
-
|
220
|
-
response = self.class.post "#{domain_url}#{path}", options
|
221
|
-
yield response
|
222
|
-
end
|
223
|
-
|
224
|
-
def delete(path, params: {}, auth: true, json: true)
|
225
|
-
body = json ? params.to_json.to_s : URI.encode_www_form(params)
|
226
|
-
|
227
|
-
options = {}
|
228
|
-
options[:body] = body
|
229
|
-
options[:headers] = headers 'DELETE', path, body, json: json if auth
|
230
|
-
|
231
|
-
response = self.class.delete "#{domain_url}#{path}", options
|
232
|
-
yield response
|
233
|
-
end
|
234
|
-
|
235
|
-
def base_path(resource, action = '')
|
236
|
-
"/api/v1/#{resource}/#{action}"
|
237
|
-
end
|
238
|
-
|
239
|
-
def response_handler(response)
|
240
|
-
fail response.body unless response.success?
|
241
|
-
|
242
|
-
if response.parsed_response.is_a? Array
|
243
|
-
response.to_a.map { |s| Bitmex::Mash.new s }
|
244
|
-
else
|
245
|
-
Bitmex::Mash.new response
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
private
|
250
|
-
|
251
|
-
def method_missing(m, *args, &ablock)
|
252
|
-
name = m.to_s.gsub '_', ''
|
253
|
-
params = args.first || {}
|
254
|
-
type = params&.delete :type
|
255
|
-
types = self.class.const_get "#{name.upcase}_ARGS"
|
256
|
-
check! type, types
|
257
|
-
|
258
|
-
params[:auth] = auth_required? name
|
259
|
-
|
260
|
-
execute name, type, params do |response|
|
261
|
-
# p response.body
|
262
|
-
if response.parsed_response.is_a? Array
|
263
|
-
response.to_a.map do |s|
|
264
|
-
Bitmex::Mash.new s
|
265
|
-
end
|
266
|
-
else
|
267
|
-
Bitmex::Mash.new response
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
def auth_required?(action)
|
273
|
-
AUTHORIZATIONS.include? action
|
274
|
-
end
|
275
|
-
|
276
|
-
def execute(endpoint, type, params, &ablock)
|
277
|
-
url = "#{rest_url}/#{endpoint}/#{type}"
|
278
|
-
path = "/api/v1/#{endpoint}/#{type}"
|
279
|
-
auth = params&.delete(:auth)
|
280
|
-
params = nil if params&.empty?
|
281
|
-
|
282
|
-
options = { query: params }
|
283
|
-
options[:headers] = headers 'GET', path, '' if auth
|
284
|
-
|
285
|
-
response = self.class.get url, options
|
286
|
-
fail response.body unless response.success?
|
287
|
-
yield response
|
288
|
-
end
|
289
|
-
|
290
|
-
def headers(verb, path, body, json: true)
|
291
|
-
raise 'api_key and api_secret are required' unless api_key || api_secret
|
292
|
-
|
293
|
-
expires = Time.now.utc.to_i + 60
|
294
|
-
headers = {
|
295
|
-
'api-expires' => expires.to_s,
|
296
|
-
'api-key' => api_key,
|
297
|
-
'api-signature' => Bitmex.signature(api_secret, verb, path, expires, body)
|
298
|
-
}
|
299
|
-
if json
|
300
|
-
headers['Content-Type'] = 'application/json'
|
301
|
-
else
|
302
|
-
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
303
|
-
end
|
304
|
-
headers
|
305
|
-
end
|
306
|
-
|
307
|
-
def check!(type, types)
|
308
|
-
return true if type.nil? or type == ''
|
309
|
-
raise ArgumentError, "invalid argument #{type}, only #{types} are supported" if !types.include?(type.to_s)
|
310
|
-
end
|
311
|
-
|
312
|
-
def rest_url
|
313
|
-
"https://#{host}/api/v1"
|
314
|
-
end
|
315
|
-
|
316
|
-
def domain_url
|
317
|
-
"https://#{host}"
|
188
|
+
@websocket ||= Websocket.new host, api_key: api_key, api_secret: api_secret
|
318
189
|
end
|
319
190
|
|
320
|
-
def
|
321
|
-
|
191
|
+
def rest
|
192
|
+
@rest ||= Rest.new host, api_key: api_key, api_secret: api_secret
|
322
193
|
end
|
323
194
|
end
|
324
195
|
end
|