deribit-api 0.1.5 → 2.0.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.
@@ -1,32 +1,33 @@
1
1
  module Deribit
2
2
  # HTTP API
3
+ # @author Iulian Costan (deribit-api@iuliancostan.com)
3
4
  # @see https://docs.deribit.com/api-http.html
4
5
  class Http
5
6
  def initialize(host, key: nil, secret: nil, debug: false)
6
- url = 'https://' + host
7
- @connection = Faraday::Connection.new(url: url) do |f|
7
+ @connection = Faraday::Connection.new(url: http_url(host)) do |f|
8
8
  f.request :json
9
+ f.use Deribit::Authentication, key, secret
9
10
  f.response :mashify
10
11
  f.response :json
11
- f.use Deribit::Authentication, key, secret
12
+ f.use Faraday::Response::RaiseError
12
13
  f.response :detailed_logger if debug
13
14
  f.adapter Faraday.default_adapter
14
15
  end
15
16
  end
16
17
 
17
- def get(action, params: {}, raw_body: false, auth: false)
18
- response = @connection.get path(action, auth), params
18
+ def get(uri, params = {})
19
+ response = @connection.get path(uri), params
19
20
 
20
21
  # TODO: move to middleware
21
- raise response.message unless response.success?
22
- raise response.body.message unless response.body.success
22
+ # raise response.error unless response.error
23
+ # raise response.body.message unless response.body.success
23
24
 
24
25
  body = response.body
25
- raw_body ? body : body.result
26
+ body.result
26
27
  end
27
28
 
28
- def post(action, params)
29
- response = @connection.post path(action, true), params
29
+ def post(uri, params)
30
+ response = @connection.post path(uri), params
30
31
 
31
32
  # TODO: move to middleware
32
33
  raise response.message unless response.success?
@@ -37,9 +38,15 @@ module Deribit
37
38
 
38
39
  private
39
40
 
40
- def path(action, auth = false)
41
- access = auth ? 'private' : 'public'
42
- "/api/v1/#{access}/#{action}"
41
+ def path(uri)
42
+ path = '/api/v2'
43
+ path += '/' unless uri.start_with? '/'
44
+ path += uri
45
+ path
46
+ end
47
+
48
+ def http_url(host)
49
+ 'https://' + host
43
50
  end
44
51
  end
45
52
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Naming
4
+ def self.trades_channel(options)
5
+ private = options.delete :private
6
+ trades = private ? 'user.trades' : 'trades'
7
+ channel trades, options
8
+ end
9
+
10
+ def self.trades_uri(options)
11
+ private = options.delete :private
12
+ uri = private ? 'private/get_user_trades' : 'public/get_last_trades'
13
+ uri += options[:instrument_name] ? '_by_instrument' : '_by_currency'
14
+ uri += options[:end_timestamp] ? '_and_time' : ''
15
+ uri
16
+ end
17
+
18
+ def self.book_channel(options)
19
+ 'book' + for_instrument(options) + with_group_and_depth(options) + with_interval(options)
20
+ end
21
+
22
+ def self.channel(prefix, options)
23
+ prefix + channel_suffix(options)
24
+ end
25
+
26
+ def self.channel_for_instrument(prefix, options)
27
+ prefix + for_instrument(options)
28
+ end
29
+
30
+ def self.instrument_with_interval(prefix, options)
31
+ prefix + for_instrument(options) + with_interval(options)
32
+ end
33
+
34
+ def self.cancel_uri(options)
35
+ "/private/cancel_all" + by_instrument(options) + by_currency(options)
36
+ end
37
+
38
+ private
39
+
40
+ def self.by_instrument(options)
41
+ options[:instrument_name] ? '_by_instrument' : ''
42
+ end
43
+
44
+ def self.by_currency(options)
45
+ options[:currency] ? '_by_currency' : ''
46
+ end
47
+
48
+ def self.for_instrument(options)
49
+ raise 'instrument_name param is required' unless options[:instrument_name]
50
+
51
+ ".#{options[:instrument_name]}"
52
+ end
53
+
54
+ def self.with_interval(options)
55
+ interval = options[:interval] || '100ms'
56
+ ".#{interval}"
57
+ end
58
+
59
+ def self.with_group_and_depth(options)
60
+ if options[:group] || options[:depth]
61
+ group = options[:group] || '5'
62
+ depth = options[:depth] || '10'
63
+ ".#{group}.#{depth}"
64
+ else
65
+ ''
66
+ end
67
+ end
68
+
69
+ def self.channel_suffix(options)
70
+ currency = options[:currency]
71
+ instrument_name = options[:instrument_name]
72
+ kind = options[:kind] || 'any'
73
+ interval = options[:interval] || 'raw'
74
+ if instrument_name
75
+ ".#{instrument_name}.#{interval}"
76
+ elsif currency
77
+ ".#{kind}.#{currency}.#{interval}"
78
+ else
79
+ raise 'invalid args'
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module Deribit
2
- VERSION = '0.1.5'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Deribit
2
4
  # Websocket API
3
- # @see https://docs.deribit.com/api-websocket.html
5
+ # @author Iulian Costan (deribit-api@iuliancostan.com)
6
+ # @see https://docs.deribit.com/#subscriptions
4
7
  class Websocket
5
- attr_reader :host, :key, :secret
8
+ attr_reader :host, :key, :secret, :access_token, :callbacks
6
9
 
7
10
  # Create new websocket instance
8
11
  # @param host [String] the underlying host to connect to
@@ -14,6 +17,7 @@ module Deribit
14
17
  @key = key
15
18
  @secret = secret
16
19
  @callbacks = {}
20
+ @ws = nil
17
21
  end
18
22
 
19
23
  # Subscribe to a specific topic and optionally filter by symbol
@@ -21,85 +25,100 @@ module Deribit
21
25
  # @param params [Hash] the arguments for subscription
22
26
  # @yield [Array] data payload
23
27
  def subscribe(topic, params: {}, &callback)
24
- raise 'callback block is required' unless block_given?
25
-
26
- EM.run do
27
- connect
28
+ raise 'block is required' unless block_given?
28
29
 
29
- # request id
30
- id = Time.now.to_i
31
- @callbacks[id] = callback
30
+ # connect on demand
31
+ @ws = connect unless connected?
32
+ raise 'websocket is closed' unless @ws.open?
32
33
 
33
- # request action
34
- auth = params.delete :auth
35
- uri = auth ? 'private' : 'public'
36
- action = "/api/v1/#{uri}/#{topic}"
34
+ # save callback handler
35
+ @callbacks[topic.to_s] = callback
37
36
 
38
- payload = { id: id, action: action, arguments: params }
39
- payload[:sig] = signature id, action, params if auth
37
+ # authorize if needed
38
+ authorize if authorization_required?(topic)
40
39
 
41
- @faye.send payload.to_json.to_s
42
- end
40
+ # subscription request
41
+ payload = {
42
+ jsonrpc: '2.0',
43
+ method: 'public/subscribe',
44
+ id: rand(9999),
45
+ params: { channels: [topic] }
46
+ }
47
+ @ws.send payload.to_json.to_s
43
48
  end
44
49
 
45
- # Stop websocket listener
46
- def stop
47
- EM.stop_event_loop
50
+ def authorized?
51
+ !access_token.nil?
48
52
  end
49
53
 
50
54
  private
51
55
 
52
- def signature(nonce, action, params)
53
- payload = {
54
- _: nonce,
55
- _ackey: @key,
56
- _acsec: @secret,
57
- _action: action
58
- }
59
- payload.merge! params
60
- # query = env['url'].query
56
+ def connected?
57
+ !@ws.nil?
58
+ end
61
59
 
62
- Deribit.signature @key, nonce, payload, nil
60
+ def authorization_required?(topic)
61
+ topic.include? 'user'
63
62
  end
64
63
 
65
- def websocket_url
66
- "wss://#{host}/ws/api/v1/"
64
+ def authorize
65
+ timestamp = Time.now.utc.to_i * 1000
66
+ nonce = rand(999_999).to_s
67
+ signature = Deribit.signature timestamp, nonce, '', @secret
68
+ payload = {
69
+ jsonrpc: '2.0',
70
+ method: 'public/auth',
71
+ id: 'auth',
72
+ params: {
73
+ grant_type: :client_signature,
74
+ client_id: @key,
75
+ timestamp: timestamp,
76
+ nonce: nonce,
77
+ data: '',
78
+ signature: signature
79
+ }
80
+ }
81
+ @callbacks['auth'] = lambda do |result|
82
+ @access_token = result['access_token']
83
+ end
84
+ @ws.send payload.to_json.to_s
67
85
  end
68
86
 
69
- def headers
70
- {}
87
+ def websocket_url
88
+ "wss://#{host}/ws/api/v2"
71
89
  end
72
90
 
73
91
  def connect
74
- @result = nil
75
- @faye = Faye::WebSocket::Client.new websocket_url, [], headers: headers
76
- @faye.on :open do |_event|
77
- # puts [:open, event.data]
92
+ websocket = self
93
+ ws = WebSocket::Client::Simple.connect websocket_url
94
+ ws.on :open do |event|
95
+ # puts [:open, event]
78
96
  end
79
- @faye.on :error do |event|
80
- raise event.message
97
+ ws.on :error do |event|
98
+ # puts [:error, event.inspect]
81
99
  end
82
- @faye.on :close do |_event|
100
+ ws.on :close do |event|
83
101
  # puts [:close, event.reason]
84
- @faye = nil
102
+ ws = nil
85
103
  end
86
- @faye.on :message do |event|
104
+ ws.on :message do |event|
87
105
  json = JSON.parse event.data
88
- id = json['id']
89
- data = json['result'] || json['message']
90
-
91
- callback = @callbacks[id]
92
- # TODO: rewrite this part
93
- if callback && data
94
- data = [data] unless data.is_a? Array
95
- data.each do |payload|
96
- payload = Hashie::Mash.new payload if payload.is_a? Hash
97
- @result = callback.yield payload, @result
106
+ if json['method'] == 'subscription'
107
+ id = json['params']['channel']
108
+ data = json['params']['data']
109
+ callback = websocket.callbacks[id]
110
+ data = [data] if data.is_a? Hash
111
+ data.each do |datum|
112
+ @result = callback.yield Hashie::Mash.new(datum)
98
113
  end
99
114
  else
100
- puts "==> #{event.data}"
115
+ id = json['id']
116
+ callback = websocket.callbacks[id]
117
+ callback&.yield json['result']
101
118
  end
102
119
  end
120
+ until ws.open? do sleep 1 end # wait for opening
121
+ ws
103
122
  end
104
123
  end
105
124
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deribit-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iulian Costan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-09 00:00:00.000000000 Z
11
+ date: 2020-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: hashie
42
+ name: faraday-detailed_logger
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: faye-websocket
56
+ name: websocket-client-simple
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: faraday-detailed_logger
70
+ name: hashie
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -206,9 +206,51 @@ dependencies:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
208
  version: '0'
209
- description: Ruby library for Deribit API
209
+ - !ruby/object:Gem::Dependency
210
+ name: rubocop
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: solargraph
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: irb
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ description: Idiomatic Ruby library for Deribit API 2.0
210
252
  email:
211
- - iulian.costan@gmail.com
253
+ - deribit-api@iuliancostan.com
212
254
  executables: []
213
255
  extensions: []
214
256
  extra_rdoc_files: []
@@ -223,14 +265,19 @@ files:
223
265
  - README.md
224
266
  - Rakefile
225
267
  - TODOs.org
268
+ - bin/auth.sh
226
269
  - bin/console
227
270
  - bin/setup
271
+ - bin/trades.py
272
+ - bin/trades.rb
273
+ - bin/whales-watching.rb
228
274
  - deribit.gemspec
229
275
  - lib/deribit-api.rb
230
276
  - lib/deribit.rb
231
277
  - lib/deribit/authentication.rb
232
278
  - lib/deribit/client.rb
233
279
  - lib/deribit/http.rb
280
+ - lib/deribit/naming.rb
234
281
  - lib/deribit/version.rb
235
282
  - lib/deribit/websocket.rb
236
283
  homepage: https://github.com/icostan/deribit-api-ruby
@@ -258,5 +305,5 @@ requirements: []
258
305
  rubygems_version: 3.1.2
259
306
  signing_key:
260
307
  specification_version: 4
261
- summary: Ruby library for Deribit API
308
+ summary: Idiomatic Ruby library for Deribit API 2.0
262
309
  test_files: []