bitmex-api 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- ** TODO auth for private subscription topics
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
- ** issue: {"error":{"message":"Signature not valid.","name":"HTTPError"}} in user#executions
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>
@@ -6,6 +6,6 @@ require 'bitmex'
6
6
  puts 'Listening to chat message from English channel...'
7
7
 
8
8
  client = Bitmex::Client.new
9
- client.listen chat: 1 do |message|
9
+ client.websocket.listen chat: 1 do |message|
10
10
  puts "#{message.user}: #{message.message}"
11
11
  end
@@ -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 "bitmex/version"
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 = %q{Ruby library for BitMEX API}
13
- spec.description = %q{Ruby library for BitMEX API}
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["allowed_push_host"] = 'https://rubygems.org'
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
19
 
21
- spec.metadata["homepage_uri"] = spec.homepage
22
- spec.metadata["source_code_uri"] = 'https://github.com/icostan/bitmex-api-ruby.git'
23
- spec.metadata["changelog_uri"] = 'https://github.com/icostan/bitmex-api-ruby/blob/master/CHANGELOG.md'
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 "RubyGems 2.0 or newer is required to protect against " \
26
- "public gem pushes."
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 = Dir.chdir(File.expand_path('..', __FILE__)) do
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 = "exe"
33
+ spec.bindir = 'exe'
35
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
- spec.require_paths = ["lib"]
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 'simplecov'
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
@@ -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
@@ -5,17 +5,17 @@ module Bitmex
5
5
  attr_reader :api_key
6
6
 
7
7
  # Create new Apikey
8
- # @param client [Bitmex::Client] the rest client
8
+ # @param rest [Bitmex::Rest] the rest client
9
9
  # @param api_key [String] public apikey
10
- def initialize(client, api_key = nil)
11
- super client
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
- client.get apikey_path, auth: true do |response|
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
- client.post apikey_path(:enable) do |response|
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
- client.post apikey_path(:disable) do |response|
34
+ rest.post apikey_path(:disable) do |response|
35
35
  response_handler response
36
36
  end
37
37
  end
@@ -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 :client
5
+ attr_reader :rest, :websocket
6
6
 
7
- # @param client [Bitmex::Client] the client object
8
- def initialize(client)
9
- @client = client
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
- client.response_handler response
21
+ rest.response_handler response
16
22
  end
17
23
 
18
- def requires!(arg, args)
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
- client.base_path resource, action
29
+ rest.base_path resource, action
24
30
  end
25
31
  end
26
32
  end
@@ -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 channel_id: 1, count: 10, reverse: true
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
- def messages(options = { count: 100, reverse: true })
15
- params = {
16
- count: options[:count], start: options[:start],
17
- reverse: options[:reverse], channelID: options[:channel_id]
18
- }
19
- client.get chat_path, params: params do |response|
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
- client.get chat_path(:channels) do |response|
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
- def stats
35
- client.get chat_path(:connected) do |response|
36
- response_handler response
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] :channel_id (1) channel to post to
44
- def send(message, options = { channel_id: 1 })
45
- params = { message: message, channelID: options[:channel_id] }
46
- client.post chat_path, params: params do |response|
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
@@ -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
- get base_path(:announcement) do |response|
29
- response_handler response
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 self, api_key
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 self
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 self
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
- def funding(filters = {})
55
- get base_path(:funding), params: filters do |response|
56
- response_handler response
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
- def insurance(filters = {})
64
- get base_path(:insurance), params: filters do |response|
65
- response_handler response
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 base_path(:leaderboard), params: { method: ranking } do |response|
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
- def liquidations(filters = {})
82
- get base_path(:liquidation), params: filters do |response|
83
- response_handler response
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(self)
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(self, orderID, clOrdID)
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
- def orderbook(symbol, depth: 25)
109
- params = { symbol: symbol, depth: depth }
110
- get base_path('orderbook/L2'), params: params do |response|
111
- response_handler response
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(self).all
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(self, symbol)
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 self
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 base_path(:schema) do |response|
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
- def settlement
147
- get base_path(:settlement) do |response|
148
- response_handler response
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 self
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 self
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 self
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 realtime_url
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 realtime_url
321
- "wss://#{host}/realtime"
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