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.
@@ -5,43 +5,38 @@ module Bitmex
5
5
  # Get all instruments
6
6
  # @!macro bitmex.filters
7
7
  # @return [Array] all instruments
8
- def all(filters = {})
9
- client.get instrument_path, params: filters do |response|
10
- response_handler response
8
+ # @yield [Hash] the instrument
9
+ def all(filters = {}, &ablock)
10
+ if block_given?
11
+ websocket.listen instrument: filters[:symbol], &ablock
12
+ else
13
+ rest.get instrument_path, params: filters
11
14
  end
12
15
  end
13
16
 
14
17
  # Get all active instruments and instruments that have expired in <24hrs.
15
18
  # @return [Array] active instruments
16
19
  def active
17
- client.get instrument_path('active') do |response|
18
- response_handler response
19
- end
20
+ rest.get instrument_path('active')
20
21
  end
21
22
 
22
23
  # Return all active contract series and interval pairs
23
24
  # @return [Bitmex::Mash] active intervals and symbols
24
25
  def intervals
25
- client.get instrument_path('activeIntervals') do |response|
26
- response_handler response
27
- end
26
+ rest.get instrument_path('activeIntervals')
28
27
  end
29
28
 
30
29
  # Show constituent parts of an index.
31
30
  # @!macro bitmex.filters
32
31
  # @return [Array] the parts of an index
33
32
  def composite_index(filters = { symbol: '.XBT' })
34
- client.get instrument_path('compositeIndex'), params: filters do |response|
35
- response_handler response
36
- end
33
+ rest.get instrument_path('compositeIndex'), params: filters
37
34
  end
38
35
 
39
36
  # Get all price indices
40
37
  # @return [Array] all indices
41
38
  def indices
42
- client.get instrument_path('indices') do |response|
43
- response_handler response
44
- end
39
+ rest.get instrument_path('indices')
45
40
  end
46
41
 
47
42
  private
@@ -4,8 +4,8 @@ module Bitmex
4
4
  class Order < Base
5
5
  attr_reader :orderID, :clOrdID
6
6
 
7
- def initialize(client, orderID = nil, clOrdID = nil)
8
- super client
7
+ def initialize(rest, websocket = nil, orderID = nil, clOrdID = nil)
8
+ super rest, websocket
9
9
  @orderID = orderID
10
10
  @clOrdID = clOrdID
11
11
  end
@@ -13,9 +13,12 @@ module Bitmex
13
13
  # Get your orders
14
14
  # @!macro bitmex.filters
15
15
  # @return [Array] the orders
16
- def all(filters = {})
17
- client.get order_path, params: filters, auth: true do |response|
18
- response_handler response
16
+ # @yield [Hash] the order
17
+ def all(filters = {}, &ablock)
18
+ if block_given?
19
+ websocket.listen order: filters[:symbol], &ablock
20
+ else
21
+ rest.get order_path, params: filters, auth: true
19
22
  end
20
23
  end
21
24
 
@@ -30,9 +33,7 @@ module Bitmex
30
33
  # @return [Bitmex::Mash] the updated order
31
34
  def update(attributes)
32
35
  params = attributes.merge orderID: orderID, origClOrdID: clOrdID
33
- client.put order_path, params: params do |response|
34
- response_handler response
35
- end
36
+ rest.put order_path, params: params
36
37
  end
37
38
 
38
39
  # Place new order
@@ -53,9 +54,7 @@ module Bitmex
53
54
  # @return [Bitmex::Mash] the created order
54
55
  def create(symbol, attributes)
55
56
  params = attributes.merge symbol: symbol
56
- client.post order_path, params: params do |response|
57
- response_handler response
58
- end
57
+ rest.post order_path, params: params
59
58
  end
60
59
 
61
60
  # Cancel an order
@@ -63,7 +62,7 @@ module Bitmex
63
62
  # @return [Bitmex::Mash] the canceled order
64
63
  def cancel(text = nil)
65
64
  params = { orderID: orderID, clOrdID: clOrdID, text: text }
66
- client.delete order_path, params: params do |response|
65
+ rest.delete order_path, params: params do |response|
67
66
  # a single order only
68
67
  response_handler(response).first
69
68
  end
@@ -72,7 +71,7 @@ module Bitmex
72
71
  private
73
72
 
74
73
  def order_path(action = '')
75
- client.base_path 'order', action
74
+ rest.base_path 'order', action
76
75
  end
77
76
  end
78
77
  end
@@ -5,18 +5,27 @@ module Bitmex
5
5
  attr_reader :symbol
6
6
 
7
7
  # A new instance of Position
8
- # @param client [Bitmex::Client] the HTTP client
8
+ # @param rest [Bitmex::Rest] the HTTP rest
9
9
  # @param symbol [String] the symbol of the underlying position
10
- def initialize(client, symbol = 'XBTUSD')
11
- super client
10
+ def initialize(rest, websocket, symbol = 'XBTUSD')
11
+ super rest, websocket
12
12
  @symbol = symbol
13
13
  end
14
14
 
15
15
  # Get your positions
16
+ # @example Get all positions
17
+ # positions = client.positions.all
18
+ # @example Listen for positions changes
19
+ # client.positions.all do |position|
20
+ # puts position.inspect
21
+ # end
16
22
  # @return [Array] the list of positions
17
- def all
18
- client.get position_path, auth: true do |response|
19
- response_handler response
23
+ # @yield [Hash] the position
24
+ def all(&ablock)
25
+ if block_given?
26
+ websocket.listen position: nil, &ablock
27
+ else
28
+ rest.get position_path, auth: true
20
29
  end
21
30
  end
22
31
 
@@ -26,7 +35,7 @@ module Bitmex
26
35
  def isolate(enabled: true)
27
36
  path = position_path(:isolate)
28
37
  params = { symbol: symbol, enabled: enabled }
29
- client.post path, params: params do |response|
38
+ rest.post path, params: params do |response|
30
39
  response_handler response
31
40
  end
32
41
  end
@@ -39,7 +48,7 @@ module Bitmex
39
48
 
40
49
  path = position_path(:leverage)
41
50
  params = { symbol: symbol, leverage: leverage }
42
- client.post path, params: params do |response|
51
+ rest.post path, params: params do |response|
43
52
  response_handler response
44
53
  end
45
54
  end
@@ -50,7 +59,7 @@ module Bitmex
50
59
  def risk_limit(risk_limit)
51
60
  path = position_path(:riskLimit)
52
61
  params = { symbol: symbol, riskLimit: risk_limit }
53
- client.post path, params: params do |response|
62
+ rest.post path, params: params do |response|
54
63
  response_handler response
55
64
  end
56
65
  end
@@ -63,7 +72,7 @@ module Bitmex
63
72
  def transfer_margin(amount)
64
73
  path = position_path(:transferMargin)
65
74
  params = { symbol: symbol, amount: amount }
66
- client.post path, params: params do |response|
75
+ rest.post path, params: params do |response|
67
76
  response_handler response
68
77
  end
69
78
  end
@@ -71,7 +80,7 @@ module Bitmex
71
80
  private
72
81
 
73
82
  def position_path(action = '')
74
- client.base_path :position, action
83
+ rest.base_path :position, action
75
84
  end
76
85
  end
77
86
  end
@@ -1,32 +1,41 @@
1
1
  module Bitmex
2
2
  # Best Bid/Offer Snapshots & Historical Bins
3
- # TODO: looks like all quotes related methods are forbidden
3
+ # Looks like all REST API methods return '403 Forbidden' but Web socket API works just fine.
4
4
  # @author Iulian Costan
5
5
  class Quote < Base
6
6
  # Get all quotes
7
7
  # @!macro bitmex.filters
8
8
  # @return [Array] the quotes
9
- def all(filters = {})
10
- client.get quotes_path, params: filters do |response|
11
- response_handler response
9
+ # @yield [Hash] the quote data
10
+ def all(filters = {}, &ablock)
11
+ if block_given?
12
+ websocket.listen quote: filters[:symbol], &ablock
13
+ else
14
+ rest.get quotes_path, params: filters
12
15
  end
13
16
  end
14
17
 
15
18
  # Get previous quotes in time buckets
16
- # @param binSize ['1m','5m','1h','1d'] the interval to bucket by
19
+ # @param bin_size ['1m','5m','1h','1d'] the interval to bucket by
17
20
  # @!macro bitmex.filters
18
21
  # @return [Array] the quotes by bucket
19
- def bucketed(binSize = '1h', filters = {})
20
- params = filters.merge binSize: binSize
21
- client.get quotes_path(:bucketed), params: params do |response|
22
- response_handler response
22
+ # @yield [Hash] the quote data
23
+ def bucketed(bin_size = '1h', filters = {}, &ablock)
24
+ check_binsize bin_size
25
+
26
+ if block_given?
27
+ topic = { "quoteBin#{bin_size}": filters[:symbol] }
28
+ websocket.listen topic, &ablock
29
+ else
30
+ params = filters.merge binSize: bin_size
31
+ rest.get quotes_path(:bucketed), params: params
23
32
  end
24
33
  end
25
34
 
26
35
  private
27
36
 
28
37
  def quotes_path(action = '')
29
- client.base_path :quote, action
38
+ rest.base_path :quote, action
30
39
  end
31
40
  end
32
41
  end
@@ -0,0 +1,103 @@
1
+ module Bitmex
2
+ # REST API support
3
+ # https://www.bitmex.com/api/explorer/
4
+ class Rest
5
+ include HTTParty
6
+ # logger ::Logger.new(STDOUT), :debug, :curl
7
+
8
+ attr_reader :host, :api_key, :api_secret
9
+
10
+ # Create new rest instance
11
+ # @param host [String] the underlying host to connect to
12
+ # @param api_key [String] the api key
13
+ # @param api_secret [String] the api secret
14
+ # @return [Bitmex::Rest] the REST implementation
15
+ def initialize(host, api_key: nil, api_secret: nil)
16
+ @host = host
17
+ @api_key = api_key
18
+ @api_secret = api_secret
19
+ end
20
+
21
+ # Execute GET request
22
+ # @param path [String] either absolute or relative URI path
23
+ # @param params [Hash] extra parameters to pass to GET request
24
+ # @param auth [Boolean] if the request needs authentication
25
+ # @return [Hash, Array] response wrapped in either array or hash
26
+ # @yield [HTTParty::Response] the underlying response
27
+ def get(path, params: {}, auth: false, &ablock)
28
+ path = base_path(path) unless path.to_s.start_with?('/')
29
+
30
+ options = {}
31
+ options[:query] = params unless params.empty?
32
+ options[:headers] = rest_headers 'GET', path, '' if auth
33
+
34
+ response = self.class.get "#{domain_url}#{path}", options
35
+ block_given? ? yield(response) : response_handler(response)
36
+ end
37
+
38
+ def put(path, params: {}, auth: true, json: true)
39
+ body = json ? params.to_json.to_s : URI.encode_www_form(params)
40
+
41
+ options = {}
42
+ options[:body] = body
43
+ options[:headers] = rest_headers 'PUT', path, body, json: json if auth
44
+
45
+ response = self.class.put "#{domain_url}#{path}", options
46
+ block_given? ? yield(response) : response_handler(response)
47
+ end
48
+
49
+ def post(path, params: {}, auth: true, json: true)
50
+ body = json ? params.to_json.to_s : URI.encode_www_form(params)
51
+
52
+ options = {}
53
+ options[:body] = body
54
+ options[:headers] = rest_headers 'POST', path, body, json: json if auth
55
+
56
+ response = self.class.post "#{domain_url}#{path}", options
57
+ block_given? ? yield(response) : response_handler(response)
58
+ end
59
+
60
+ def delete(path, params: {}, auth: true, json: true)
61
+ body = json ? params.to_json.to_s : URI.encode_www_form(params)
62
+
63
+ options = {}
64
+ options[:body] = body
65
+ options[:headers] = rest_headers 'DELETE', path, body, json: json if auth
66
+
67
+ response = self.class.delete "#{domain_url}#{path}", options
68
+ block_given? ? yield(response) : response_handler(response)
69
+ end
70
+
71
+ def base_path(resource, action = '')
72
+ "/api/v1/#{resource}/#{action}"
73
+ end
74
+
75
+ def rest_headers(verb, path, body, json: true)
76
+ headers = headers verb, path, body
77
+ if json
78
+ headers['Content-Type'] = 'application/json'
79
+ else
80
+ headers['Content-Type'] = 'application/x-www-form-urlencoded'
81
+ end
82
+ headers
83
+ end
84
+
85
+ def headers(verb, path, body)
86
+ Bitmex.headers api_key, api_secret, verb, path, body
87
+ end
88
+
89
+ def response_handler(response)
90
+ raise Bitmex::ForbiddenError, response.body unless response.success?
91
+
92
+ if response.parsed_response.is_a? Array
93
+ response.to_a.map { |s| Bitmex::Mash.new s }
94
+ else
95
+ Bitmex::Mash.new response
96
+ end
97
+ end
98
+
99
+ def domain_url
100
+ "https://#{host}"
101
+ end
102
+ end
103
+ end
@@ -5,7 +5,7 @@ module Bitmex
5
5
  # Get exchange-wide and per-series turnover and volume statistics
6
6
  # @return [Array] the statistics
7
7
  def current
8
- client.get stats_path do |response|
8
+ rest.get stats_path do |response|
9
9
  response_handler response
10
10
  end
11
11
  end
@@ -13,7 +13,7 @@ module Bitmex
13
13
  # Get historical exchange-wide and per-series turnover and volume statistics
14
14
  # @return [Array] the history in XBT
15
15
  def history
16
- client.get stats_path(:history) do |response|
16
+ rest.get stats_path(:history) do |response|
17
17
  response_handler response
18
18
  end
19
19
  end
@@ -21,7 +21,7 @@ module Bitmex
21
21
  # Get a summary of exchange statistics in USD
22
22
  # @return [Array] the history in USD
23
23
  def history_usd
24
- client.get stats_path(:historyUSD) do |response|
24
+ rest.get stats_path(:historyUSD) do |response|
25
25
  response_handler response
26
26
  end
27
27
  end
@@ -29,7 +29,7 @@ module Bitmex
29
29
  private
30
30
 
31
31
  def stats_path(action = '')
32
- base_path :stats, action
32
+ rest.base_path :stats, action
33
33
  end
34
34
  end
35
35
  end
@@ -5,37 +5,48 @@ module Bitmex
5
5
  # Get all trades
6
6
  # @example Get first 10 traders starting Jan 1st for XBTUSD
7
7
  # client.trades.all symbol: 'XBTUSD', startTime: '2019-01-01', count: 10
8
+ # @example Listen to all XBTUSD trades
9
+ # client.trades.all symbol: 'XBTUSD' do |trade|
10
+ # puts trade.inspect
11
+ # end
8
12
  # @!macro bitmex.filters
9
13
  # @return [Array] the trades
10
- # @yield [trade] the trade
11
- def all(filters = {}, &callback)
14
+ # @yield [Hash] the trade
15
+ def all(filters = {}, &ablock)
12
16
  if block_given?
13
- # TODO: investigate eventmachine + faye
14
- EM.run { client.websocket.subscribe :trade, filters[:symbol], &callback }
17
+ websocket.listen trade: filters[:symbol], &ablock
15
18
  else
16
- client.get trade_path, params: filters do |response|
17
- response_handler response
18
- end
19
+ rest.get trade_path, params: filters
19
20
  end
20
21
  end
21
22
 
22
23
  # Get previous trades in time buckets
23
24
  # @example Get last hour in 2018 and first hour in 2019 in reverse order
24
25
  # client.trades.bucketed '1h', symbol: 'XBTUSD', endTime: Date.new(2019, 1, 1), count: 2, reverse: true
25
- # @param binSize ['1m','5m','1h','1d'] the interval to bucket by
26
+ # @example Listen to bucketed trades
27
+ # client.trades.bucketed '1h', symbol: 'XBTUSD' do |bucket|
28
+ # puts bucket.inspect
29
+ # end
30
+ # @param bin_size ['1m','5m','1h','1d'] the interval to bucket by
26
31
  # @!macro bitmex.filters
27
32
  # @return [Array] the trades by bucket
28
- def bucketed(binSize = '1h', filters = {})
29
- params = filters.merge binSize: binSize
30
- client.get trade_path(:bucketed), params: params do |response|
31
- response_handler response
33
+ # @yield [trade] the bucketed trade
34
+ def bucketed(bin_size = '1h', filters = {}, &ablock)
35
+ check_binsize bin_size
36
+
37
+ if block_given?
38
+ topic = { "tradeBin#{bin_size}": filters[:symbol] }
39
+ websocket.listen topic, &ablock
40
+ else
41
+ params = filters.merge binSize: bin_size
42
+ rest.get trade_path(:bucketed), params: params
32
43
  end
33
44
  end
34
45
 
35
46
  private
36
47
 
37
48
  def trade_path(action = '')
38
- base_path :trade, action
49
+ rest.base_path :trade, action
39
50
  end
40
51
  end
41
52
  end