marcel-binance 1.3.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 681a6d991592ad3bdb5d8c2e16c600c83d724ee1ec17946a0063a3cd1563f293
4
+ data.tar.gz: 0d50747b17a57e7cb6bf3210febef89337d60416f29a3be83d174b14aad66527
5
+ SHA512:
6
+ metadata.gz: f00c3b43942185d5ee9ba7c0e1354644b7b62df1e7894128afce666116e9caf90a0f0a4634a0c0622f8455c8b216481ecf8acf54fa8f905d053baa5a0e1b26f7
7
+ data.tar.gz: dfb7da75487d4fec9a83258c84e7d72d4ea04d374bcc347f32581cf8d2e9efd4544c7b76bab3798714a707fc69f64f91abfa6b995ce0cec3257e290bce9154ef
@@ -0,0 +1,106 @@
1
+ require 'faraday_middleware'
2
+
3
+ module Binance
4
+ module Client
5
+ class REST
6
+ BASE_URL = 'https://api.binance.com'.freeze
7
+
8
+ def public_client(adapter)
9
+ Faraday.new(url: "#{BASE_URL}/api") do |conn|
10
+ conn.request :json
11
+ conn.response :json, content_type: /\bjson$/
12
+ conn.adapter adapter
13
+ end
14
+ end
15
+
16
+ def verified_client(api_key, adapter)
17
+ Faraday.new(url: "#{BASE_URL}/api") do |conn|
18
+ conn.response :json, content_type: /\bjson$/
19
+ conn.headers['X-MBX-APIKEY'] = api_key
20
+ conn.adapter adapter
21
+ end
22
+ end
23
+
24
+ def signed_client(api_key, secret_key, adapter)
25
+ Faraday.new(url: "#{BASE_URL}/api") do |conn|
26
+ conn.request :json
27
+ conn.response :json, content_type: /\bjson$/
28
+ conn.headers['X-MBX-APIKEY'] = api_key
29
+ conn.use TimestampRequestMiddleware
30
+ conn.use SignRequestMiddleware, secret_key
31
+ conn.adapter adapter
32
+ end
33
+ end
34
+
35
+ def public_withdraw_client(adapter)
36
+ Faraday.new(url: "#{BASE_URL}/wapi") do |conn|
37
+ conn.request :json
38
+ conn.response :json, content_type: /\bjson$/
39
+ conn.adapter adapter
40
+ end
41
+ end
42
+
43
+ def withdraw_client(api_key, secret_key, adapter)
44
+ Faraday.new(url: "#{BASE_URL}/wapi") do |conn|
45
+ conn.request :url_encoded
46
+ conn.response :json, content_type: /\bjson$/
47
+ conn.headers['X-MBX-APIKEY'] = api_key
48
+ conn.use TimestampRequestMiddleware
49
+ conn.use SignRequestMiddleware, secret_key
50
+ conn.adapter adapter
51
+ end
52
+ end
53
+ end
54
+
55
+ class REST_FUTURE
56
+ BASE_URL = 'https://fapi.binance.com'.freeze
57
+ TEST_BASE_URL = 'https://testnet.binancefuture.com'.freeze
58
+ def public_client(adapter, test_api)
59
+ Faraday.new(url: "#{(test_api)? TEST_BASE_URL : BASE_URL}/fapi") do |conn|
60
+ conn.request :json
61
+ conn.response :json, content_type: /\bjson$/
62
+ conn.adapter adapter
63
+ end
64
+ end
65
+
66
+ def verified_client(api_key, adapter, test_api)
67
+ Faraday.new(url: "#{(test_api)? TEST_BASE_URL : BASE_URL}/fapi") do |conn|
68
+ conn.response :json, content_type: /\bjson$/
69
+ conn.headers['X-MBX-APIKEY'] = api_key
70
+ conn.adapter adapter
71
+ end
72
+ end
73
+
74
+ def signed_client(api_key, secret_key, adapter, test_api)
75
+ Faraday.new(url: "#{(test_api)? TEST_BASE_URL : BASE_URL}/fapi") do |conn|
76
+ conn.request :json
77
+ conn.response :json, content_type: /\bjson$/
78
+ conn.headers['X-MBX-APIKEY'] = api_key
79
+ conn.use TimestampRequestMiddleware
80
+ conn.use SignRequestMiddleware, secret_key
81
+ conn.adapter adapter
82
+ end
83
+ end
84
+
85
+ def public_withdraw_client(adapter, test_api)
86
+ Faraday.new(url: "#{(test_api)? TEST_BASE_URL : BASE_URL}/wapi") do |conn|
87
+ conn.request :json
88
+ conn.response :json, content_type: /\bjson$/
89
+ conn.adapter adapter
90
+ end
91
+ end
92
+
93
+ def withdraw_client(api_key, secret_key, adapter, test_api)
94
+ Faraday.new(url: "#{(test_api)? TEST_BASE_URL : BASE_URL}/wapi") do |conn|
95
+ conn.request :url_encoded
96
+ conn.response :json, content_type: /\bjson$/
97
+ conn.headers['X-MBX-APIKEY'] = api_key
98
+ conn.use TimestampRequestMiddleware
99
+ conn.use SignRequestMiddleware, secret_key
100
+ conn.adapter adapter
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,87 @@
1
+ module Binance
2
+ module Client
3
+ class REST
4
+ ENDPOINTS = {
5
+ # Public API Endpoints
6
+ ping: 'v1/ping',
7
+ time: 'v1/time',
8
+ exchange_info: 'v1/exchangeInfo',
9
+ depth: 'v1/depth',
10
+ trades: 'v1/trades',
11
+ historical_trades: 'v1/historicalTrades',
12
+ agg_trades: 'v1/aggTrades',
13
+ klines: 'v1/klines',
14
+ twenty_four_hour: 'v1/ticker/24hr',
15
+ price: 'v3/ticker/price',
16
+ book_ticker: 'v3/ticker/bookTicker',
17
+
18
+ # Account API Endpoints
19
+ order: 'v3/order',
20
+ test_order: 'v3/order/test',
21
+ open_orders: 'v3/openOrders',
22
+ all_orders: 'v3/allOrders',
23
+ account: 'v3/account',
24
+ my_trades: 'v3/myTrades',
25
+ user_data_stream: 'v1/userDataStream',
26
+
27
+ # Withdraw API Endpoints
28
+ balance: 'v1/balance',
29
+ withdraw: 'v3/withdraw.html',
30
+ deposit_history: 'v3/depositHistory.html',
31
+ withdraw_history: 'v3/withdrawHistory.html',
32
+ deposit_address: 'v3/depositAddress.html',
33
+ account_status: 'v3/accountStatus.html',
34
+ system_status: 'v3/systemStatus.html',
35
+ withdraw_fee: 'v3/withdrawFee.html',
36
+ dust_log: 'v3/userAssetDribbletLog.html'
37
+ }.freeze
38
+ end
39
+
40
+ class REST_FUTURE
41
+ ENDPOINTS = {
42
+ # Public API Endpoints
43
+ ping: 'v1/ping',
44
+ time: 'v1/time',
45
+ exchange_info: 'v1/exchangeInfo',
46
+ depth: 'v1/depth',
47
+ trades: 'v1/trades',
48
+ historical_trades: 'v1/historicalTrades',
49
+ agg_trades: 'v1/aggTrades',
50
+ klines: 'v1/klines',
51
+ twenty_four_hour: 'v1/ticker/24hr',
52
+ price: 'v1/ticker/price',
53
+ book_ticker: 'v1/ticker/bookTicker',
54
+
55
+ # Account API Endpoints
56
+ order: 'v1/order',
57
+ open_orders: 'v1/openOrders',
58
+ all_orders: 'v1/allOrders',
59
+ account: 'v1/account',
60
+ user_trades: 'v1/userTrades',
61
+ test_order: 'v1/order/test',
62
+ user_data_stream: 'v1/userDataStream',
63
+ withdraw: 'v1/withdraw.html',
64
+
65
+ # Withdraw API Endpoints
66
+ balance: 'v1/balance',
67
+ funding_rate: 'v1/fundingRate',
68
+ income: 'v1/income',
69
+ leverage: 'v1/leverage',
70
+ listenKey: 'v1/listenKey',
71
+ position_risk: 'v1/positionRisk',
72
+ premium_index: 'v1/premiumIndex',
73
+
74
+ #not found in Future-API yet
75
+ my_trades: 'v1/myTrades',
76
+ deposit_history: 'v1/depositHistory.html',
77
+ withdraw_history: 'v1/withdrawHistory.html',
78
+ deposit_address: 'v1/depositAddress.html',
79
+ account_status: 'v1/accountStatus.html',
80
+ system_status: 'v1/systemStatus.html',
81
+ withdraw_fee: 'v1/withdrawFee.html',
82
+ dust_log: 'v1/userAssetDribbletLog.html'
83
+ }.freeze
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,245 @@
1
+ module Binance
2
+ module Client
3
+
4
+ class REST
5
+ METHODS = [
6
+ # Public API Methods
7
+ # #ping
8
+ { name: :ping, client: :public,
9
+ action: :get, endpoint: :ping },
10
+ # #time
11
+ { name: :time, client: :public,
12
+ action: :get, endpoint: :time },
13
+ # #exchange_info
14
+ { name: :exchange_info, client: :public,
15
+ action: :get, endpoint: :exchange_info },
16
+ # #depth
17
+ { name: :depth, client: :public,
18
+ action: :get, endpoint: :depth },
19
+ # #trades
20
+ { name: :trades, client: :public,
21
+ action: :get, endpoint: :trades },
22
+ # #historical_trades
23
+ { name: :historical_trades, client: :verified,
24
+ action: :get, endpoint: :historical_trades },
25
+ # #agg_trades
26
+ { name: :agg_trades, client: :public,
27
+ action: :get, endpoint: :agg_trades },
28
+ # #klines
29
+ { name: :klines, client: :public,
30
+ action: :get, endpoint: :klines },
31
+ # #twenty_four_hour
32
+ { name: :twenty_four_hour, client: :public,
33
+ action: :get, endpoint: :twenty_four_hour },
34
+ # #price
35
+ { name: :price, client: :public,
36
+ action: :get, endpoint: :price },
37
+ # #all_prices
38
+ { name: :all_prices, client: :public,
39
+ action: :get, endpoint: :price },
40
+ # #book_ticker
41
+ { name: :book_ticker, client: :public,
42
+ action: :get, endpoint: :book_ticker },
43
+
44
+ # Account API Methods
45
+ # #create_order!
46
+ { name: :create_order!, client: :signed,
47
+ action: :post, endpoint: :order },
48
+ # #create_test_order
49
+ { name: :create_test_order, client: :signed,
50
+ action: :post, endpoint: :test_order },
51
+ # #query_order
52
+ { name: :query_order, client: :signed,
53
+ action: :get, endpoint: :order },
54
+ # #cancel_order!
55
+ { name: :cancel_order!, client: :signed,
56
+ action: :delete, endpoint: :order },
57
+ # #open_orders
58
+ { name: :open_orders, client: :signed,
59
+ action: :get, endpoint: :open_orders },
60
+ # #all_orders
61
+ { name: :all_orders, client: :signed,
62
+ action: :get, endpoint: :all_orders },
63
+ # #account_info
64
+ { name: :account_info, client: :signed,
65
+ action: :get, endpoint: :account },
66
+ # #my_trades
67
+ { name: :my_trades, client: :signed,
68
+ action: :get, endpoint: :my_trades },
69
+ # #listen_key
70
+ { name: :listen_key, client: :verified,
71
+ action: :post, endpoint: :user_data_stream },
72
+ # #keep_alive_stream!
73
+ { name: :keep_alive_stream!, client: :verified,
74
+ action: :put, endpoint: :user_data_stream },
75
+ # #close_stream!
76
+ { name: :close_stream!, client: :verified,
77
+ action: :delete, endpoint: :user_data_stream },
78
+
79
+ # #withdraw!
80
+ { name: :withdraw!, client: :withdraw,
81
+ action: :post, endpoint: :withdraw },
82
+ # #deposit_history
83
+ { name: :deposit_history, client: :withdraw,
84
+ action: :get, endpoint: :deposit_history },
85
+ # #withdraw_history
86
+ { name: :withdraw_history, client: :withdraw,
87
+ action: :get, endpoint: :withdraw_history },
88
+ # #deposit_address
89
+ { name: :deposit_address, client: :withdraw,
90
+ action: :get, endpoint: :deposit_address },
91
+ # #account_status
92
+ { name: :account_status, client: :withdraw,
93
+ action: :get, endpoint: :account_status },
94
+ # #system_status
95
+ { name: :system_status, client: :public_withdraw,
96
+ action: :get, endpoint: :system_status },
97
+ # #withdraw_fee
98
+ { name: :withdraw_fee, client: :withdraw,
99
+ action: :get, endpoint: :withdraw_fee },
100
+ # dust_log
101
+ { name: :dust_log, client: :withdraw,
102
+ action: :get, endpoint: :dust_log }
103
+ ].freeze
104
+ end
105
+
106
+ class REST_FUTURE
107
+ METHODS = [
108
+ # Public API Methods
109
+ # #ping
110
+ { name: :ping, client: :public,
111
+ action: :get, endpoint: :ping },
112
+ # #time
113
+ { name: :time, client: :public,
114
+ action: :get, endpoint: :time },
115
+ # #exchange_info
116
+ { name: :exchange_info, client: :public,
117
+ action: :get, endpoint: :exchange_info },
118
+ # #depth
119
+ { name: :depth, client: :public,
120
+ action: :get, endpoint: :depth },
121
+ # #trades
122
+ { name: :trades, client: :public,
123
+ action: :get, endpoint: :trades },
124
+ # #historical_trades
125
+ { name: :historical_trades, client: :verified,
126
+ action: :get, endpoint: :historical_trades },
127
+ # #agg_trades
128
+ { name: :agg_trades, client: :public,
129
+ action: :get, endpoint: :agg_trades },
130
+ # #klines
131
+ { name: :klines, client: :public,
132
+ action: :get, endpoint: :klines },
133
+ # #twenty_four_hour
134
+ { name: :twenty_four_hour, client: :public,
135
+ action: :get, endpoint: :twenty_four_hour },
136
+ # #price
137
+ { name: :price, client: :public,
138
+ action: :get, endpoint: :price },
139
+ # #all_prices
140
+ { name: :all_prices, client: :public,
141
+ action: :get, endpoint: :price },
142
+ # #book_ticker
143
+ { name: :book_ticker, client: :public,
144
+ action: :get, endpoint: :book_ticker },
145
+
146
+ # Account API Methods
147
+ # #create_order!
148
+ { name: :create_order!, client: :signed,
149
+ action: :post, endpoint: :order },
150
+ # #create_test_order
151
+ { name: :create_test_order, client: :signed,
152
+ action: :post, endpoint: :test_order },
153
+ # #query_order
154
+ { name: :query_order, client: :signed,
155
+ action: :get, endpoint: :order },
156
+ # #cancel_order!
157
+ { name: :cancel_order!, client: :signed,
158
+ action: :delete, endpoint: :order },
159
+ # #cancel_all_open_orders
160
+ { name: :cancel_all, client: :signed,
161
+ action: :delete, endpoint: :all_open_orders },
162
+ # #cancel_batch_orders
163
+ { name: :cancel_orders, client: :signed,
164
+ action: :delete, endpoint: :batch_orders },
165
+ # #open_orders
166
+ { name: :open_orders, client: :signed,
167
+ action: :get, endpoint: :open_orders },
168
+ # #all_orders
169
+ { name: :all_orders, client: :signed,
170
+ action: :get, endpoint: :all_orders },
171
+ # #account_info
172
+ { name: :account_info, client: :signed,
173
+ action: :get, endpoint: :account },
174
+ # #user_trades
175
+ { name: :user_trades, client: :signed,
176
+ action: :get, endpoint: :user_trades },
177
+ # #listen_key
178
+ { name: :listen_key, client: :verified,
179
+ action: :post, endpoint: :user_data_stream },
180
+ # #keep_alive_stream!
181
+ { name: :keep_alive_stream!, client: :verified,
182
+ action: :put, endpoint: :user_data_stream },
183
+ # #close_stream!
184
+ { name: :close_stream!, client: :verified,
185
+ action: :delete, endpoint: :user_data_stream },
186
+
187
+ # Withdraw API Endpoints
188
+ # # funding_rate
189
+ { name: :funding_rate, client: :signed,
190
+ action: :get, endpoint: :funding_rate },
191
+ # # income
192
+ { name: :income, client: :signed,
193
+ action: :get, endpoint: :income },
194
+ # # leverage
195
+ { name: :leverage, client: :signed,
196
+ action: :post, endpoint: :leverage },
197
+ # listenKey
198
+ { name: :listenKey, client: :signed,
199
+ action: :post, endpoint: :listenKey },
200
+ # # listenKey
201
+ { name: :getlistenKey, client: :signed,
202
+ action: :get, endpoint: :listenKey },
203
+ # # position_risk
204
+ { name: :position_risk, client: :signed,
205
+ action: :get, endpoint: :position_risk },
206
+ # # premium_index
207
+ { name: :premium_index, client: :signed,
208
+ action: :get, endpoint: :premium_index },
209
+ # #withdraw!
210
+ { name: :withdraw!, client: :withdraw,
211
+ action: :post, endpoint: :withdraw },
212
+ # #balance
213
+ { name: :balance, client: :signed,
214
+ action: :get, endpoint: :balance },
215
+
216
+ # NOT FOUND IN FUTURE API (yet)
217
+ # #my_trades
218
+ { name: :my_trades, client: :signed,
219
+ action: :get, endpoint: :my_trades },
220
+ # #deposit_history
221
+ { name: :deposit_history, client: :withdraw,
222
+ action: :get, endpoint: :deposit_history },
223
+ # #withdraw_history
224
+ { name: :withdraw_history, client: :withdraw,
225
+ action: :get, endpoint: :withdraw_history },
226
+ # #deposit_address
227
+ { name: :deposit_address, client: :withdraw,
228
+ action: :get, endpoint: :deposit_address },
229
+ # #account_status
230
+ { name: :account_status, client: :withdraw,
231
+ action: :get, endpoint: :account_status },
232
+ # #system_status
233
+ { name: :system_status, client: :public_withdraw,
234
+ action: :get, endpoint: :system_status },
235
+ # #withdraw_fee
236
+ { name: :withdraw_fee, client: :withdraw,
237
+ action: :get, endpoint: :withdraw_fee },
238
+ # dust_log
239
+ { name: :dust_log, client: :withdraw,
240
+ action: :get, endpoint: :dust_log }
241
+ ].freeze
242
+
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,31 @@
1
+ module Binance
2
+ module Client
3
+ class REST
4
+ # Sign the query string using HMAC(sha-256) and appends to query string
5
+ SignRequestMiddleware = Struct.new(:app, :secret_key) do
6
+ def call(env)
7
+ hash = OpenSSL::HMAC.hexdigest(
8
+ OpenSSL::Digest.new('sha256'), secret_key, env.url.query
9
+ )
10
+ env.url.query = REST.add_query_param(env.url.query, 'signature', hash)
11
+
12
+ app.call env
13
+ end
14
+ end
15
+ end
16
+
17
+ class REST_FUTURE
18
+ # Sign the query string using HMAC(sha-256) and appends to query string
19
+ SignRequestMiddleware = Struct.new(:app, :secret_key) do
20
+ def call(env)
21
+ hash = OpenSSL::HMAC.hexdigest(
22
+ OpenSSL::Digest.new('sha256'), secret_key, env.url.query
23
+ )
24
+ env.url.query = REST_FUTURE.add_query_param(env.url.query, 'signature', hash)
25
+
26
+ app.call env
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ require 'date'
2
+
3
+ module Binance
4
+ module Client
5
+ class REST
6
+ # Generate a timestamp in milliseconds and append to query string
7
+ TimestampRequestMiddleware = Struct.new(:app) do
8
+ def call(env)
9
+ date = DateTime.now
10
+ date = Time.at(date.to_time.to_f-$diff).to_datetime if !$diff.nil?
11
+ env.url.query = REST.add_query_param(
12
+ env.url.query, 'timestamp', date.strftime('%Q')
13
+ )
14
+ app.call env
15
+ end
16
+ end
17
+ end
18
+
19
+ class REST_FUTURE
20
+ # Generate a timestamp in milliseconds and append to query string
21
+ TimestampRequestMiddleware = Struct.new(:app) do
22
+ def call(env)
23
+ date = DateTime.now
24
+ date = Time.at(date.to_time.to_f-$diff).to_datetime if !$diff.nil?
25
+ env.url.query = REST_FUTURE.add_query_param(
26
+ env.url.query, 'timestamp', date.strftime('%Q')
27
+ )
28
+ app.call env
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,77 @@
1
+ require 'faraday'
2
+
3
+ require_relative 'rest/sign_request_middleware'
4
+ require_relative 'rest/timestamp_request_middleware'
5
+ require_relative 'rest/clients'
6
+ require_relative 'rest/endpoints'
7
+ require_relative 'rest/methods'
8
+
9
+ module Binance
10
+ module Client
11
+ class REST
12
+ def initialize(api_key: '', secret_key: '',
13
+ adapter: Faraday.default_adapter)
14
+ @clients = {}
15
+ @clients[:public] = public_client adapter
16
+ @clients[:verified] = verified_client api_key, adapter
17
+ @clients[:signed] = signed_client api_key, secret_key, adapter
18
+ @clients[:withdraw] = withdraw_client api_key, secret_key, adapter
19
+ @clients[:public_withdraw] = public_withdraw_client adapter
20
+ end
21
+
22
+ METHODS.each do |method|
23
+ define_method(method[:name]) do |options = {}|
24
+ response = @clients[method[:client]].send(method[:action]) do |req|
25
+ req.url ENDPOINTS[method[:endpoint]]
26
+ req.params.merge! options.map { |k, v| [camelize(k.to_s), v] }.to_h
27
+ end
28
+ response.body
29
+ end
30
+ end
31
+
32
+ def self.add_query_param(query, key, value)
33
+ query = query.to_s
34
+ query << '&' unless query.empty?
35
+ query << "#{Faraday::Utils.escape key}=#{Faraday::Utils.escape value}"
36
+ end
37
+
38
+ def camelize(str)
39
+ str.split('_')
40
+ .map.with_index { |word, i| i.zero? ? word : word.capitalize }.join
41
+ end
42
+ end
43
+
44
+ class REST_FUTURE
45
+ def initialize(api_key: '', secret_key: '',
46
+ adapter: Faraday.default_adapter, test_api: false)
47
+ @clients = {}
48
+ @clients[:public] = public_client adapter, test_api
49
+ @clients[:verified] = verified_client api_key, adapter, test_api
50
+ @clients[:signed] = signed_client api_key, secret_key, adapter, test_api
51
+ @clients[:withdraw] = withdraw_client api_key, secret_key, adapter, test_api
52
+ @clients[:public_withdraw] = public_withdraw_client adapter, test_api
53
+ end
54
+
55
+ METHODS.each do |method|
56
+ define_method(method[:name]) do |options = {}|
57
+ response = @clients[method[:client]].send(method[:action]) do |req|
58
+ req.url ENDPOINTS[method[:endpoint]]
59
+ req.params.merge! options.map { |k, v| [camelize(k.to_s), v] }.to_h
60
+ end
61
+ response.body
62
+ end
63
+ end
64
+
65
+ def self.add_query_param(query, key, value)
66
+ query = query.to_s
67
+ query << '&' unless query.empty?
68
+ query << "#{Faraday::Utils.escape key}=#{Faraday::Utils.escape value}"
69
+ end
70
+
71
+ def camelize(str)
72
+ str.split('_')
73
+ .map.with_index { |word, i| i.zero? ? word : word.capitalize }.join
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,430 @@
1
+ require 'faye/websocket'
2
+
3
+ module Binance
4
+ module Client
5
+ # Public: Client with methods mirroring the Binance WebSocket API
6
+ class WebSocket
7
+ # Public: String base url for WebSocket client to use
8
+ BASE_URL = 'wss://stream.binance.com:9443'.freeze
9
+
10
+ # Public: Create a single WebSocket stream
11
+ #
12
+ # :stream - The Hash used to define the stream
13
+ # :symbol - The String symbol to listen to
14
+ # :type - The String type of stream to listen to
15
+ # :level - The String level to use for the depth stream (optional)
16
+ # :interval - The String interval to use for the kline stream (optional)
17
+ #
18
+ # :methods - The Hash which contains the event handler methods to pass to
19
+ # the WebSocket client
20
+ # :open - The Proc called when a stream is opened (optional)
21
+ # :message - The Proc called when a stream receives a message
22
+ # :error - The Proc called when a stream receives an error (optional)
23
+ # :close - The Proc called when a stream is closed (optional)
24
+ def single(stream:, methods:)
25
+ create_stream("#{BASE_URL}/ws/#{stream_url(stream)}",
26
+ methods: methods)
27
+ end
28
+
29
+ # Public: Create multiple WebSocket streams
30
+ #
31
+ # :streams - The Array of Hashes used to define the stream. Each Hash can
32
+ # have the following keys:
33
+ # :symbol - The String symbol the stream will listen to
34
+ # :type - The String type of stream to listen to
35
+ # :level - The String level to use for the depth stream (optional)
36
+ # :interval - The String interval to use for the kline stream (optional)
37
+ #
38
+ # :methods - The Hash which contains the event handler methods to pass to
39
+ # the WebSocket client
40
+ # :open - The Proc called when a stream is opened (optional)
41
+ # :message - The Proc called when a stream receives a message
42
+ # :error - The Proc called when a stream receives an error (optional)
43
+ # :close - The Proc called when a stream is closed (optional)
44
+ def multi(streams:, methods:)
45
+ names = streams.map { |stream| stream_url(stream) }
46
+ create_stream("#{BASE_URL}/stream?streams=#{names.join('/')}",
47
+ methods: methods)
48
+ end
49
+
50
+ # Public: Create an Aggregate Trade stream
51
+ #
52
+ # :symbol - The String symbol the stream will listen to
53
+ #
54
+ # :methods - The Hash which contains the event handler methods to pass to
55
+ # the WebSocket client
56
+ # :open - The Proc called when a stream is opened (optional)
57
+ # :message - The Proc called when a stream receives a message
58
+ # :error - The Proc called when a stream receives an error (optional)
59
+ # :close - The Proc called when a stream is closed (optional)
60
+ def agg_trade(symbol:, methods:)
61
+ single stream: { symbol: symbol, type: 'aggTrade' }, methods: methods
62
+ end
63
+
64
+ # Public: Create a Trade stream
65
+ #
66
+ # :symbol - The String symbol the stream will listen to
67
+ #
68
+ # :methods - The Hash which contains the event handler methods to pass to
69
+ # the WebSocket client
70
+ # :open - The Proc called when a stream is opened (optional)
71
+ # :message - The Proc called when a stream receives a message
72
+ # :error - The Proc called when a stream receives an error (optional)
73
+ # :close - The Proc called when a stream is closed (optional)
74
+ def trade(symbol:, methods:)
75
+ single stream: { symbol: symbol, type: 'trade' }, methods: methods
76
+ end
77
+
78
+ # Public: Create an Kline stream
79
+ #
80
+ # :symbol - The String symbol the stream will listen to
81
+ #
82
+ # :interval - The String interval the stream will update with. The
83
+ # intervals that may be used can be found in the Binance API
84
+ # docs.
85
+ #
86
+ # :methods - The Hash which contains the event handler methods to pass to
87
+ # the WebSocket client
88
+ # :open - The Proc called when a stream is opened (optional)
89
+ # :message - The Proc called when a stream receives a message
90
+ # :error - The Proc called when a stream receives an error (optional)
91
+ # :close - The Proc called when a stream is closed (optional)
92
+ def kline(symbol:, interval:, methods:)
93
+ single stream: { symbol: symbol, type: 'kline', interval: interval },
94
+ methods: methods
95
+ end
96
+
97
+ # Public: Create a Ticker stream
98
+ #
99
+ # :symbol - The String symbol the stream will listen to
100
+ #
101
+ # :methods - The Hash which contains the event handler methods to pass to
102
+ # the WebSocket client
103
+ # :open - The Proc called when a stream is opened (optional)
104
+ # :message - The Proc called when a stream receives a message
105
+ # :error - The Proc called when a stream receives an error (optional)
106
+ # :close - The Proc called when a stream is closed (optional)
107
+ def ticker(symbol:, methods:)
108
+ single stream: { symbol: symbol, type: 'ticker' }, methods: methods
109
+ end
110
+
111
+ # Public: Create a Ticker stream for all symbols
112
+ #
113
+ # :methods - The Hash which contains the event handler methods to pass to
114
+ # the WebSocket client
115
+ # :open - The Proc called when a stream is opened (optional)
116
+ # :message - The Proc called when a stream receives a message
117
+ # :error - The Proc called when a stream receives an error (optional)
118
+ # :close - The Proc called when a stream is closed (optional)
119
+ def all_market_ticker(methods:)
120
+ single stream: { symbol: '!ticker', type: 'arr' }, methods: methods
121
+ end
122
+
123
+ # Public: Create an Partial Book Depth stream
124
+ #
125
+ # :symbol - The String symbol the stream will listen to
126
+ #
127
+ # :level - The String interval the stream will update with. The intervals
128
+ # that may be used can be found in the Binance API docs.
129
+ #
130
+ # :methods - The Hash which contains the event handler methods to pass to
131
+ # the WebSocket client
132
+ # :open - The Proc called when a stream is opened (optional)
133
+ # :message - The Proc called when a stream receives a message
134
+ # :error - The Proc called when a stream receives an error (optional)
135
+ # :close - The Proc called when a stream is closed (optional)
136
+ def partial_book_depth(symbol:, level:, methods:)
137
+ single stream: { symbol: symbol, type: 'depth', level: level },
138
+ methods: methods
139
+ end
140
+
141
+ # Public: Create a Diff Depth stream
142
+ #
143
+ # :symbol - The String symbol the stream will listen to
144
+ #
145
+ # :methods - The Hash which contains the event handler methods to pass to
146
+ # the WebSocket client
147
+ # :open - The Proc called when a stream is opened (optional)
148
+ # :message - The Proc called when a stream receives a message
149
+ # :error - The Proc called when a stream receives an error (optional)
150
+ # :close - The Proc called when a stream is closed (optional)
151
+ def diff_depth(symbol:, methods:)
152
+ single stream: { symbol: symbol, type: 'depth' }, methods: methods
153
+ end
154
+
155
+ # Public: Create a User Data stream
156
+ #
157
+ # listen_key - The String key the stream will listen to, attained by
158
+ # interacting with the REST API userDataStream endpoint
159
+ #
160
+ # :methods - The Hash which contains the event handler methods to pass to
161
+ # the WebSocket client
162
+ # :open - The Proc called when a stream is opened (optional)
163
+ # :message - The Proc called when a stream receives a message
164
+ # :error - The Proc called when a stream receives an error (optional)
165
+ # :close - The Proc called when a stream is closed (optional)
166
+ def user_data(listen_key:, methods:)
167
+ create_stream "#{BASE_URL}/ws/#{listen_key}", methods: methods
168
+ end
169
+
170
+ private
171
+
172
+ # Internal: Create a valid URL for a WebSocket to use
173
+ #
174
+ # :symbol - The String symbol to listen to
175
+ # :type - The String type the stream will listen to
176
+ # :level - The String level to use for the depth stream (optional)
177
+ # :interval - The String interval to use for the kline stream (optional)
178
+ def stream_url(symbol:, type:, level: '', interval: '')
179
+ "#{symbol.downcase}@#{type}".tap do |url|
180
+ url << level
181
+ url << "_#{interval}" unless interval.empty?
182
+ end
183
+ end
184
+
185
+ # Internal: Initialize and return a Faye::WebSocket::Client
186
+ #
187
+ # url - The String url that the WebSocket should try to connect to
188
+ #
189
+ # :methods - The Hash which contains the event handler methods to pass to
190
+ # the WebSocket client
191
+ # :open - The Proc called when a stream is opened (optional)
192
+ # :message - The Proc called when a stream receives a message
193
+ # :error - The Proc called when a stream receives an error (optional)
194
+ # :close - The Proc called when a stream is closed (optional)
195
+ def create_stream(url, methods:)
196
+ Faye::WebSocket::Client.new(url)
197
+ .tap { |ws| attach_methods(ws, methods) }
198
+ end
199
+
200
+ # Internal: Iterate through methods passed and add them to the WebSocket
201
+ #
202
+ # websocket - The Faye::WebSocket::Client to apply methods to
203
+ #
204
+ # methods - The Hash which contains the event handler methods to pass to
205
+ # the WebSocket client
206
+ # :open - The Proc called when a stream is opened (optional)
207
+ # :message - The Proc called when a stream receives a message
208
+ # :error - The Proc called when a stream receives an error (optional)
209
+ # :close - The Proc called when a stream is closed (optional)
210
+ def attach_methods(websocket, methods)
211
+ methods.each_pair do |key, method|
212
+ websocket.on(key) { |event| method.call(event) }
213
+ end
214
+ end
215
+ end
216
+
217
+ class WebSocketFuture
218
+ # Public: String base url for WebSocket client to use
219
+ BASE_URL = 'wss://fstream.binance.com'.freeze
220
+
221
+ # Public: Create a single WebSocket stream
222
+ #
223
+ # :stream - The Hash used to define the stream
224
+ # :symbol - The String symbol to listen to
225
+ # :type - The String type of stream to listen to
226
+ # :level - The String level to use for the depth stream (optional)
227
+ # :interval - The String interval to use for the kline stream (optional)
228
+ #
229
+ # :methods - The Hash which contains the event handler methods to pass to
230
+ # the WebSocket client
231
+ # :open - The Proc called when a stream is opened (optional)
232
+ # :message - The Proc called when a stream receives a message
233
+ # :error - The Proc called when a stream receives an error (optional)
234
+ # :close - The Proc called when a stream is closed (optional)
235
+ def single(stream:, methods:)
236
+ create_stream("#{BASE_URL}/ws/#{stream_url(stream)}",
237
+ methods: methods)
238
+ end
239
+
240
+ # Public: Create multiple WebSocket streams
241
+ #
242
+ # :streams - The Array of Hashes used to define the stream. Each Hash can
243
+ # have the following keys:
244
+ # :symbol - The String symbol the stream will listen to
245
+ # :type - The String type of stream to listen to
246
+ # :level - The String level to use for the depth stream (optional)
247
+ # :interval - The String interval to use for the kline stream (optional)
248
+ #
249
+ # :methods - The Hash which contains the event handler methods to pass to
250
+ # the WebSocket client
251
+ # :open - The Proc called when a stream is opened (optional)
252
+ # :message - The Proc called when a stream receives a message
253
+ # :error - The Proc called when a stream receives an error (optional)
254
+ # :close - The Proc called when a stream is closed (optional)
255
+ def multi(streams:, methods:)
256
+ names = streams.map { |stream| stream_url(stream) }
257
+ create_stream("#{BASE_URL}/stream?streams=#{names.join('/')}",
258
+ methods: methods)
259
+ end
260
+
261
+ # Public: Create an Aggregate Trade stream
262
+ #
263
+ # :symbol - The String symbol the stream will listen to
264
+ #
265
+ # :methods - The Hash which contains the event handler methods to pass to
266
+ # the WebSocket client
267
+ # :open - The Proc called when a stream is opened (optional)
268
+ # :message - The Proc called when a stream receives a message
269
+ # :error - The Proc called when a stream receives an error (optional)
270
+ # :close - The Proc called when a stream is closed (optional)
271
+ def agg_trade(symbol:, methods:)
272
+ single stream: { symbol: symbol, type: 'aggTrade' }, methods: methods
273
+ end
274
+
275
+ # Public: Create a Trade stream
276
+ #
277
+ # :symbol - The String symbol the stream will listen to
278
+ #
279
+ # :methods - The Hash which contains the event handler methods to pass to
280
+ # the WebSocket client
281
+ # :open - The Proc called when a stream is opened (optional)
282
+ # :message - The Proc called when a stream receives a message
283
+ # :error - The Proc called when a stream receives an error (optional)
284
+ # :close - The Proc called when a stream is closed (optional)
285
+ def trade(symbol:, methods:)
286
+ single stream: { symbol: symbol, type: 'trade' }, methods: methods
287
+ end
288
+
289
+ # Public: Create an Kline stream
290
+ #
291
+ # :symbol - The String symbol the stream will listen to
292
+ #
293
+ # :interval - The String interval the stream will update with. The
294
+ # intervals that may be used can be found in the Binance API
295
+ # docs.
296
+ #
297
+ # :methods - The Hash which contains the event handler methods to pass to
298
+ # the WebSocket client
299
+ # :open - The Proc called when a stream is opened (optional)
300
+ # :message - The Proc called when a stream receives a message
301
+ # :error - The Proc called when a stream receives an error (optional)
302
+ # :close - The Proc called when a stream is closed (optional)
303
+ def kline(symbol:, interval:, methods:)
304
+ single stream: { symbol: symbol, type: 'kline', interval: interval },
305
+ methods: methods
306
+ end
307
+
308
+ # Public: Create a Ticker stream
309
+ #
310
+ # :symbol - The String symbol the stream will listen to
311
+ #
312
+ # :methods - The Hash which contains the event handler methods to pass to
313
+ # the WebSocket client
314
+ # :open - The Proc called when a stream is opened (optional)
315
+ # :message - The Proc called when a stream receives a message
316
+ # :error - The Proc called when a stream receives an error (optional)
317
+ # :close - The Proc called when a stream is closed (optional)
318
+ def ticker(symbol:, methods:)
319
+ single stream: { symbol: symbol, type: 'ticker' }, methods: methods
320
+ end
321
+
322
+ # Public: Create a Ticker stream for all symbols
323
+ #
324
+ # :methods - The Hash which contains the event handler methods to pass to
325
+ # the WebSocket client
326
+ # :open - The Proc called when a stream is opened (optional)
327
+ # :message - The Proc called when a stream receives a message
328
+ # :error - The Proc called when a stream receives an error (optional)
329
+ # :close - The Proc called when a stream is closed (optional)
330
+ def all_market_ticker(methods:)
331
+ single stream: { symbol: '!ticker', type: 'arr' }, methods: methods
332
+ end
333
+
334
+ # Public: Create an Partial Book Depth stream
335
+ #
336
+ # :symbol - The String symbol the stream will listen to
337
+ #
338
+ # :level - The String interval the stream will update with. The intervals
339
+ # that may be used can be found in the Binance API docs.
340
+ #
341
+ # :methods - The Hash which contains the event handler methods to pass to
342
+ # the WebSocket client
343
+ # :open - The Proc called when a stream is opened (optional)
344
+ # :message - The Proc called when a stream receives a message
345
+ # :error - The Proc called when a stream receives an error (optional)
346
+ # :close - The Proc called when a stream is closed (optional)
347
+ def partial_book_depth(symbol:, level:, methods:)
348
+ single stream: { symbol: symbol, type: 'depth', level: level },
349
+ methods: methods
350
+ end
351
+
352
+ # Public: Create a Diff Depth stream
353
+ #
354
+ # :symbol - The String symbol the stream will listen to
355
+ #
356
+ # :methods - The Hash which contains the event handler methods to pass to
357
+ # the WebSocket client
358
+ # :open - The Proc called when a stream is opened (optional)
359
+ # :message - The Proc called when a stream receives a message
360
+ # :error - The Proc called when a stream receives an error (optional)
361
+ # :close - The Proc called when a stream is closed (optional)
362
+ def diff_depth(symbol:, methods:)
363
+ single stream: { symbol: symbol, type: 'depth' }, methods: methods
364
+ end
365
+
366
+ # Public: Create a User Data stream
367
+ #
368
+ # listen_key - The String key the stream will listen to, attained by
369
+ # interacting with the REST API userDataStream endpoint
370
+ #
371
+ # :methods - The Hash which contains the event handler methods to pass to
372
+ # the WebSocket client
373
+ # :open - The Proc called when a stream is opened (optional)
374
+ # :message - The Proc called when a stream receives a message
375
+ # :error - The Proc called when a stream receives an error (optional)
376
+ # :close - The Proc called when a stream is closed (optional)
377
+ def user_data(listen_key:, methods:)
378
+ create_stream "#{BASE_URL}/ws/#{listen_key}", methods: methods
379
+ end
380
+
381
+ private
382
+
383
+ # Internal: Create a valid URL for a WebSocket to use
384
+ #
385
+ # :symbol - The String symbol to listen to
386
+ # :type - The String type the stream will listen to
387
+ # :level - The String level to use for the depth stream (optional)
388
+ # :interval - The String interval to use for the kline stream (optional)
389
+ def stream_url(symbol:, type:, level: '', interval: '')
390
+ "#{symbol.downcase}@#{type}".tap do |url|
391
+ url << level
392
+ url << "_#{interval}" unless interval.empty?
393
+ end
394
+ end
395
+
396
+ # Internal: Initialize and return a Faye::WebSocket::Client
397
+ #
398
+ # url - The String url that the WebSocket should try to connect to
399
+ #
400
+ # :methods - The Hash which contains the event handler methods to pass to
401
+ # the WebSocket client
402
+ # :open - The Proc called when a stream is opened (optional)
403
+ # :message - The Proc called when a stream receives a message
404
+ # :error - The Proc called when a stream receives an error (optional)
405
+ # :close - The Proc called when a stream is closed (optional)
406
+ def create_stream(url, methods:)
407
+ Faye::WebSocket::Client.new(url)
408
+ .tap { |ws| attach_methods(ws, methods) }
409
+ end
410
+
411
+ # Internal: Iterate through methods passed and add them to the WebSocket
412
+ #
413
+ # websocket - The Faye::WebSocket::Client to apply methods to
414
+ #
415
+ # methods - The Hash which contains the event handler methods to pass to
416
+ # the WebSocket client
417
+ # :open - The Proc called when a stream is opened (optional)
418
+ # :message - The Proc called when a stream receives a message
419
+ # :error - The Proc called when a stream receives an error (optional)
420
+ # :close - The Proc called when a stream is closed (optional)
421
+ def attach_methods(websocket, methods)
422
+ methods.each_pair do |key, method|
423
+ websocket.on(key) { |event| method.call(event) }
424
+ end
425
+ end
426
+
427
+
428
+ end
429
+ end
430
+ end
@@ -0,0 +1,3 @@
1
+ module Binance
2
+ VERSION = '1.3.0'.freeze
3
+ end
data/lib/binance.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'binance/version'
2
+ require 'binance/client/rest'
3
+ require 'binance/client/websocket'
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: marcel-binance
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Marcel Bonnet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.12'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday_middleware
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.12'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faye-websocket
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.10'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.10'
97
+ description:
98
+ email:
99
+ - marcel.social@protonmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - lib/binance.rb
105
+ - lib/binance/client/rest.rb
106
+ - lib/binance/client/rest/clients.rb
107
+ - lib/binance/client/rest/endpoints.rb
108
+ - lib/binance/client/rest/methods.rb
109
+ - lib/binance/client/rest/sign_request_middleware.rb
110
+ - lib/binance/client/rest/timestamp_request_middleware.rb
111
+ - lib/binance/client/websocket.rb
112
+ - lib/binance/version.rb
113
+ homepage: https://github.com/marcelbonnet/binance
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubygems_version: 3.1.2
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: API Wrapper for the Binance cryptocurrency exchange.
136
+ test_files: []