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.
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