deribit-api 0.1.3 → 2.0.2

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
- # HTTP API
2
+ # HTTP API adapter
3
+ # @author Iulian Costan (deribit-api@iuliancostan.com)
3
4
  # @see https://docs.deribit.com/api-http.html
4
5
  class Http
5
- def initialize(host, key: nil, secret: nil, debug: false)
6
- url = 'https://' + host
7
- @connection = Faraday::Connection.new(url: url) do |f|
6
+ def initialize(host, key: nil, secret: nil, debug: false, raise_error: true)
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.response :logger if debug
12
+ f.use Faraday::Response::RaiseError if raise_error
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,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deribit
4
+ # @author Iulian Costan (deribit-api@iuliancostan.com)
5
+ module Naming
6
+ def trades_channel(options)
7
+ private = options.delete :private
8
+ trades = private ? 'user.trades' : 'trades'
9
+ channel trades, options
10
+ end
11
+
12
+ def trades_uri(options)
13
+ private = options.delete :private
14
+ uri = private ? 'private/get_user_trades' : 'public/get_last_trades'
15
+ uri += by_instrument(options) + by_currency(options)
16
+ uri += options[:end_timestamp] ? '_and_time' : ''
17
+ uri
18
+ end
19
+
20
+ def book_channel(options)
21
+ 'book' + for_instrument(options) + with_group_and_depth(options) + with_interval(options)
22
+ end
23
+
24
+ def channel(prefix, options)
25
+ channel = prefix
26
+ channel += for_instrument(options) if options[:instrument_name]
27
+ channel += for_currency(options) if options[:currency]
28
+ channel += with_interval(options)
29
+ channel
30
+ end
31
+
32
+ def channel_for_instrument(prefix, options)
33
+ prefix + for_instrument(options)
34
+ end
35
+
36
+ def instrument_with_interval(prefix, options)
37
+ prefix + for_instrument(options) + with_interval(options)
38
+ end
39
+
40
+ def cancel_uri(options)
41
+ '/private/cancel_all' + by_instrument(options) + by_currency(options)
42
+ end
43
+
44
+ def orders_uri(options)
45
+ '/private/get_open_orders' + by_instrument(options) + by_currency(options)
46
+ end
47
+
48
+ def by_instrument(options)
49
+ options[:instrument_name] ? '_by_instrument' : ''
50
+ end
51
+
52
+ def by_currency(options)
53
+ options[:currency] ? '_by_currency' : ''
54
+ end
55
+
56
+ def for_instrument(options)
57
+ raise 'instrument_name param is required' unless options[:instrument_name]
58
+
59
+ ".#{options[:instrument_name]}"
60
+ end
61
+
62
+ def for_currency(options)
63
+ raise 'currency param is required' unless options[:currency]
64
+
65
+ kind = options[:kind] || 'any'
66
+ ".#{kind}.#{options[:currency]}"
67
+ end
68
+
69
+ def with_interval(options)
70
+ interval = options[:interval] || '100ms'
71
+ ".#{interval}"
72
+ end
73
+
74
+ def with_group_and_depth(options)
75
+ if options[:group] || options[:depth]
76
+ group = options[:group] || '5'
77
+ depth = options[:depth] || '10'
78
+ ".#{group}.#{depth}"
79
+ else
80
+ ''
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,3 +1,3 @@
1
1
  module Deribit
2
- VERSION = '0.1.3'
2
+ VERSION = '2.0.2'
3
3
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Deribit
2
- # Websocket API
3
- # @see https://docs.deribit.com/api-websocket.html
4
+ # Websocket API adapter
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,104 @@ 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
+ @callbacks['auth'] = lambda do |result|
66
+ @access_token = result['access_token']
67
+ end
68
+
69
+ @ws.send authorize_payload.to_json.to_s
67
70
  end
68
71
 
69
- def headers
70
- {}
72
+ def authorize_payload
73
+ timestamp = Time.now.utc.to_i * 1000
74
+ nonce = rand(999_999).to_s
75
+ signature = Deribit.signature timestamp, nonce, '', @secret
76
+ {
77
+ jsonrpc: '2.0',
78
+ method: 'public/auth',
79
+ id: 'auth',
80
+ params: {
81
+ grant_type: :client_signature,
82
+ client_id: @key,
83
+ timestamp: timestamp,
84
+ nonce: nonce,
85
+ data: '',
86
+ signature: signature
87
+ }
88
+ }
89
+ end
90
+
91
+ def websocket_url
92
+ "wss://#{host}/ws/api/v2"
71
93
  end
72
94
 
73
95
  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]
96
+ websocket = self
97
+ ws = WebSocket::Client::Simple.connect websocket_url
98
+ ws.on :open do |event|
99
+ # puts [:open, event]
78
100
  end
79
- @faye.on :error do |event|
80
- raise event.message
101
+ ws.on :error do |event|
102
+ # puts [:error, event.inspect]
81
103
  end
82
- @faye.on :close do |_event|
104
+ ws.on :close do |event|
83
105
  # puts [:close, event.reason]
84
- @faye = nil
106
+ ws = nil
85
107
  end
86
- @faye.on :message do |event|
108
+ ws.on :message do |event|
87
109
  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
110
+ if json['method'] == 'subscription'
111
+ id = json['params']['channel']
112
+ data = json['params']['data']
113
+ callback = websocket.callbacks[id]
114
+ data = [data] if data.is_a? Hash
115
+ data.each do |datum|
116
+ @result = callback.yield Hashie::Mash.new(datum)
98
117
  end
99
118
  else
100
- puts "==> #{event.data}"
119
+ id = json['id']
120
+ callback = websocket.callbacks[id]
121
+ callback&.yield json['result']
101
122
  end
102
123
  end
124
+ until ws.open? do sleep 1 end # wait for opening
125
+ ws
103
126
  end
104
127
  end
105
128
  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.3
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iulian Costan
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-07-02 00:00:00.000000000 Z
11
+ date: 2020-06-10 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,21 @@ 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
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: hashie
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -192,9 +206,51 @@ dependencies:
192
206
  - - ">="
193
207
  - !ruby/object:Gem::Version
194
208
  version: '0'
195
- 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
196
252
  email:
197
- - iulian.costan@gmail.com
253
+ - deribit-api@iuliancostan.com
198
254
  executables: []
199
255
  extensions: []
200
256
  extra_rdoc_files: []
@@ -210,14 +266,19 @@ files:
210
266
  - README.md
211
267
  - Rakefile
212
268
  - TODOs.org
269
+ - bin/auth.sh
213
270
  - bin/console
214
271
  - bin/setup
272
+ - bin/trades.py
273
+ - bin/trades.rb
274
+ - bin/whales-watching.rb
215
275
  - deribit.gemspec
216
276
  - lib/deribit-api.rb
217
277
  - lib/deribit.rb
218
278
  - lib/deribit/authentication.rb
219
279
  - lib/deribit/client.rb
220
280
  - lib/deribit/http.rb
281
+ - lib/deribit/naming.rb
221
282
  - lib/deribit/version.rb
222
283
  - lib/deribit/websocket.rb
223
284
  homepage: https://github.com/icostan/deribit-api-ruby
@@ -227,7 +288,7 @@ metadata:
227
288
  homepage_uri: https://github.com/icostan/deribit-api-ruby
228
289
  source_code_uri: https://github.com/icostan/deribit-api-ruby.git
229
290
  changelog_uri: https://github.com/icostan/deribit-api-ruby/blob/master/CHANGELOG.md
230
- post_install_message:
291
+ post_install_message:
231
292
  rdoc_options: []
232
293
  require_paths:
233
294
  - lib
@@ -242,8 +303,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
303
  - !ruby/object:Gem::Version
243
304
  version: '0'
244
305
  requirements: []
245
- rubygems_version: 3.0.4
246
- signing_key:
306
+ rubygems_version: 3.1.2
307
+ signing_key:
247
308
  specification_version: 4
248
- summary: Ruby library for Deribit API
309
+ summary: Idiomatic Ruby library for Deribit API 2.0
249
310
  test_files: []