binance-sdk 1.0.4

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: d6b6fa1ee177c935d5342d2767c82c0c80d3c1befdebf4aaf4bf947fb5f01fbb
4
+ data.tar.gz: 3ea7bcadb01ef35b7d6c74b0849644efc4b5ea77f837a28150cdbbb3bbf0bbb4
5
+ SHA512:
6
+ metadata.gz: 9194ed2de6be9c11529b762588d940d3ba88774c0ff05661b6332cdab111f831cd73c8835717baaeda7165f1bdb5868019a95ab6f8fb9d44e0658d98f2a3aaf9
7
+ data.tar.gz: 62ed6aed957dad77dfd65f734221c79c6fc80713a0142657ffbbad4e2da296e24e1a25ae3f36992c3f3c96b938083355c395d7bf3f5e5b7417a1d974a0e7a068
@@ -0,0 +1,58 @@
1
+ module Binance
2
+ module SDK
3
+ class Account
4
+ class << self
5
+ def info!(recvWindow: 5000)
6
+ timestamp = Configuration.timestamp
7
+
8
+ params = { recvWindow: recvWindow, timestamp: timestamp }
9
+
10
+ response = Request.send!(
11
+ api_key_type: :read_info, path: "/sapi/v1/account/status",
12
+ params: params.delete_if { |key, value| value.nil? },
13
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
14
+ )
15
+
16
+ puts response
17
+ end
18
+
19
+ def balance!(recvWindow: 5000, currency: 'USDT')
20
+ timestamp = Configuration.timestamp
21
+
22
+ params = { recvWindow: recvWindow, timestamp: timestamp }
23
+
24
+ balance = Request.send!(
25
+ api_key_type: :read_info, path: Endpoints.fetch(:account_balance),
26
+ params: params.delete_if { |key, value| value.nil? },
27
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
28
+ )
29
+
30
+ if futures?
31
+ balance.find { |i| i[:asset] == currency }[:balance].to_f
32
+ else
33
+ balance[:balances].find { |i| i[:asset] == currency }[:free]
34
+ end
35
+ end
36
+
37
+ def positions
38
+ params = {
39
+ recvWindow: 60000,
40
+ timestamp: Configuration.timestamp
41
+ }
42
+
43
+ Request.send!(
44
+ api_key_type: :read_info, path: Endpoints.fetch(:account_position_risk),
45
+ params: params.delete_if { |key, value| value.nil? },
46
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
47
+ )
48
+ end
49
+
50
+ private
51
+
52
+ def futures?
53
+ Configuration.futures?
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,24 @@
1
+ module Binance
2
+ module SDK
3
+ class Asset
4
+ class << self
5
+ def change_margin_type(symbol:, type: 'ISOLATED')
6
+ params = {
7
+ recvWindow: 60000,
8
+ timestamp: Configuration.timestamp.to_i - 1000,
9
+ symbol: symbol,
10
+ marginType: type
11
+ }
12
+
13
+ Request.send!(
14
+ method: :post,
15
+ api_key_type: :read_info, path: Endpoints.fetch(:margin_type),
16
+ params: params.delete_if { |_, value| value.nil? },
17
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
18
+ )
19
+ rescue Binance::SDK::Error::NoNeedToChangeMarginType
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ module Binance
2
+ module SDK
3
+ class Configuration
4
+ class << self
5
+ attr_writer :api_key, :locale, :read_info_api_key, :secret_key, :trading_api_key, :withdrawals_api_key, :futures, :testnet
6
+
7
+ def api_key(type: nil)
8
+ raise Error.new(message: "Invalid api_key type: #{type}.") unless type.nil? || API_KEY_TYPES.include?(type)
9
+
10
+ instance_api_key(type: type)
11
+ end
12
+
13
+ def secret_key
14
+ instance_variable_get("@secret_key") || ENV["BINANCE_SECRET_KEY"]
15
+ end
16
+
17
+ def futures?
18
+ (instance_variable_get("@futures") || ENV["BINANCE_FUTURES"]) == true
19
+ end
20
+
21
+ def spot?
22
+ !futures?
23
+ end
24
+
25
+ def testnet?
26
+ (instance_variable_get("@testnet") || ENV["BINANCE_TESTNET"]) == true
27
+ end
28
+
29
+ def signed_request_signature(payload:, api_secret_key: nil)
30
+ raise Error.new(message: "Environment variable 'BINANCE_SECRET_KEY' is required for signed requests.") unless api_secret_key || secret_key
31
+
32
+ digest = OpenSSL::Digest::SHA256.new
33
+ OpenSSL::HMAC.hexdigest(digest, api_secret_key || secret_key, payload)
34
+ end
35
+
36
+ def tld
37
+ tld = ENV["BINANCE_TLD"]&.downcase&.to_sym || :com
38
+ validate_tld!(tld)
39
+
40
+ tld
41
+ end
42
+
43
+ def timestamp
44
+ Time.now.strftime("%s%3N")
45
+ end
46
+
47
+ def validate_tld!(tld)
48
+ error_message = "Invalid tld (top-level-domain): #{tld}. Use one of: #{ALLOWED_TLDS.join(", ")}."
49
+
50
+ raise Error.new(message: error_message) unless ALLOWED_TLDS.include?(tld&.to_sym)
51
+ end
52
+
53
+ private
54
+
55
+ API_KEY_TYPES = [:none, :read_info, :trading, :withdrawals].freeze
56
+ ALLOWED_TLDS = [:com, :us].freeze
57
+
58
+ def instance_api_key(type: nil)
59
+ var = "#{type.to_s.downcase}_api_key".sub(/^\_/, "")
60
+
61
+ instance_variable_get("@#{var}")
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ module Binance
2
+ module SDK
3
+ class Endpoints
4
+ class << self
5
+ def fetch(identifier)
6
+ Configuration.futures? ? future_endpoints[identifier] : spot_endpoints[identifier]
7
+ end
8
+
9
+ private
10
+
11
+ def future_endpoints
12
+ {
13
+ server_time: '/fapi/v1/time',
14
+ account_balance: '/fapi/v2/balance',
15
+ account_position_risk: '/fapi/v2/positionRisk',
16
+ exchange_info: '/fapi/v1/exchangeInfo',
17
+ klines: '/fapi/v1/klines',
18
+ put_order: '/fapi/v1/order',
19
+ cancel_orders: '/fapi/v1/allOpenOrders',
20
+ ticker_price: '/fapi/v1/ticker/price',
21
+ change_leverage: '/fapi/v1/leverage',
22
+ long_short_ratio: '/futures/data/globalLongShortAccountRatio',
23
+ taker_buy_sell_volume: '/futures/data/takerlongshortRatio',
24
+ margin_type: '/fapi/v1/marginType'
25
+ }
26
+ end
27
+
28
+ def spot_endpoints
29
+ {
30
+ server_time: '/sapi/v1/time',
31
+ account_balance: '/sapi/v2/balance',
32
+ account_position_risk: '/sapi/v2/positionRisk',
33
+ exchange_info: '/sapi/v1/exchangeInfo',
34
+ klines: '/sapi/v1/klines',
35
+ put_order: '/sapi/v1/order',
36
+ cancel_orders: '/sapi/v1/allOpenOrders',
37
+ ticker_price: '/sapi/v1/ticker/price',
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,138 @@
1
+ module Binance
2
+ module SDK
3
+ class Error < StandardError
4
+ attr_reader :code, :msg, :symbol
5
+
6
+ class << self
7
+ def is_error_response?(response:)
8
+ response.code >= 400
9
+ end
10
+
11
+ def localized(message)
12
+ code = message#.to_s.match(/\d+/).to_s.to_i
13
+ case code
14
+ when 1000 then Unknown
15
+ when 1001 then Disconnected
16
+ when 1002 then Unauthorized
17
+ when 1003 then TooManyRequests
18
+ when 1006 then UnexpectedResponse
19
+ when 1007 then Timeout
20
+ when 1013 then InvalidQuantity
21
+ when 1014 then UnknownOrderComposition
22
+ when 1015 then TooManyOrders
23
+ when 1016 then ServiceShuttingDown
24
+ when 1020 then UnsupportedOperation
25
+ when 1021 then InvalidTimestamp
26
+ when 1022 then InvalidSignature
27
+ when 1100 then IllegalChars
28
+ when 1101 then TooManyParameters
29
+ when 1102 then MandatoryParamEmptyOrMalformed
30
+ when 1103 then UnknownParam
31
+ when 1104 then UnreadParameters
32
+ when 1105 then ParamEmpty
33
+ when 1106 then ParamNotRequired
34
+ when 1111 then BadPrecision
35
+ when 1112 then NoDepth
36
+ when 1114 then TIFNotRequired
37
+ when 1115 then InvalidTIF
38
+ when 1116 then InvalidOrderType
39
+ when 1117 then InvalidSide
40
+ when 1118 then EmptyNewCLOrderId
41
+ when 1119 then EmptyOrgCLOrderId
42
+ when 1120 then BadInterval
43
+ when 1121 then BadSymbol
44
+ when 1125 then InvalidListenKey
45
+ when 1127 then IntervalTooLarge
46
+ when 1128 then OptionalParamsBadCombo
47
+ when 1130 then InvalidParameter
48
+ when 2007 then InvalidCallbackRate
49
+ when 2010 then NewOrderRejected
50
+ when 2011 then CancelOrderRejected
51
+ when 2013 then NoSuchOrder
52
+ when 2014 then BadAPIKeyFormat
53
+ when 2015 then RejectedAPIKey
54
+ when 2016 then NoTradingWindow
55
+ when -1021 then TimestampOutsideRecvWindow
56
+ when -4131 then CounterPartyBestPrice
57
+ when -4005 then QuantityGreaterThanMaxQuantity
58
+ when -1117 then InvalidSide
59
+ when -4046 then NoNeedToChangeMarginType
60
+ when -4061 then IncorrectOrderPositionSide
61
+ when -1120 then InvalidInterval
62
+ else Binance::SDK::Error
63
+ end
64
+ end
65
+ end
66
+
67
+ def initialize(code: nil, json: {}, message: nil, symbol: nil)
68
+ @code = code || json[:code]
69
+ @msg = message || json[:message]
70
+ @symbol = message || json[:symbol]
71
+ end
72
+
73
+ def inspect
74
+ message = ""
75
+
76
+ message += "(#{code}) " unless code.nil?
77
+ message += "@#{symbol} " unless symbol.nil?
78
+ message += "#{msg}" unless msg.nil?
79
+ end
80
+
81
+ def message
82
+ inspect
83
+ end
84
+
85
+ def to_s
86
+ inspect
87
+ end
88
+
89
+ class Unknown < Error; end
90
+ class Disconnected < Error; end
91
+ class Unauthorized < Error; end
92
+ class TooManyRequests < Error; end
93
+ class UnexpectedResponse < Error; end
94
+ class Timeout < Error; end
95
+ class InvalidQuantity < Error; end
96
+ class UnknownOrderComposition < Error; end
97
+ class TooManyOrders < Error; end
98
+ class ServiceShuttingDown < Error; end
99
+ class UnsupportedOperation < Error; end
100
+ class InvalidTimestamp < Error; end
101
+ class InvalidSignature < Error; end
102
+ class IllegalChars < Error; end
103
+ class TooManyParameters < Error; end
104
+ class MandatoryParamEmptyOrMalformed < Error; end
105
+ class UnknownParam < Error; end
106
+ class UnreadParameters < Error; end
107
+ class ParamEmpty < Error; end
108
+ class ParamNotRequired < Error; end
109
+ class BadPrecision < Error; end
110
+ class NoDepth < Error; end
111
+ class TIFNotRequired < Error; end
112
+ class InvalidTIF < Error; end
113
+ class InvalidOrderType < Error; end
114
+ class InvalidSide < Error; end
115
+ class EmptyNewCLOrderId < Error; end
116
+ class EmptyOrgCLOrderId < Error; end
117
+ class BadInterval < Error; end
118
+ class BadSymbol < Error; end
119
+ class InvalidListenKey < Error; end
120
+ class IntervalTooLarge < Error; end
121
+ class OptionalParamsBadCombo < Error; end
122
+ class InvalidParameter < Error; end
123
+ class NewOrderRejected < Error; end
124
+ class CancelOrderRejected < Error; end
125
+ class NoSuchOrder < Error; end
126
+ class BadAPIKeyFormat < Error; end
127
+ class RejectedAPIKey < Error; end
128
+ class NoTradingWindow < Error; end
129
+ class CounterPartyBestPrice < Error; end
130
+ class InvalidCallbackRate < Error; end
131
+ class QuantityGreaterThanMaxQuantity < Error; end
132
+ class TimestampOutsideRecvWindow < Error; end
133
+ class NoNeedToChangeMarginType < Error; end
134
+ class IncorrectOrderPositionSide < Error; end
135
+ class InvalidInterval < Error; end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,39 @@
1
+ module Binance
2
+ module SDK
3
+ class Exchange
4
+ class << self
5
+ def info!
6
+ timestamp = Configuration.timestamp
7
+
8
+ params = { recvWindow: 5000, timestamp: timestamp }
9
+
10
+ Request.send!(
11
+ api_key_type: :read_info, path: Endpoints.fetch(:exchange_info),
12
+ params: params.delete_if { |key, value| value.nil? },
13
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
14
+ )
15
+ end
16
+
17
+ def long_short_ratio(symbol:, period:, limit: 30)
18
+ params = { recvWindow: 5000, timestamp: Configuration.timestamp, symbol: symbol, period: period, limit: limit }
19
+
20
+ Request.send!(
21
+ api_key_type: :read_info, path: Endpoints.fetch(:long_short_ratio),
22
+ params: params.delete_if { |_, value| value.nil? },
23
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
24
+ )
25
+ end
26
+
27
+ def taker_buy_sell_volume(symbol:, period:, limit: 30)
28
+ params = { recvWindow: 5000, timestamp: Configuration.timestamp, symbol: symbol, period: period, limit: limit }
29
+
30
+ Request.send!(
31
+ api_key_type: :read_info, path: Endpoints.fetch(:taker_buy_sell_volume),
32
+ params: params.delete_if { |_, value| value.nil? },
33
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ module Binance
2
+ module SDK
3
+ class Klines
4
+ class << self
5
+ # [
6
+ # [
7
+ # 1499040000000, // Open time
8
+ # "0.01634790", // Open
9
+ # "0.80000000", // High
10
+ # "0.01575800", // Low
11
+ # "0.01577100", // Close
12
+ # "148976.11427815", // Volume
13
+ # 1499644799999, // Close time
14
+ # "2434.19055334", // Quote asset volume
15
+ # 308, // Number of trades
16
+ # "1756.87402397", // Taker buy base asset volume
17
+ # "28.46694368", // Taker buy quote asset volume
18
+ # "17928899.62484339" // Ignore.
19
+ # ]
20
+ # ]
21
+ def data(symbol, interval = '5m', start_time = nil, end_time = nil, limit = nil, recvWindow: 5000, force_float: false)
22
+ timestamp = Configuration.timestamp
23
+
24
+ params = {
25
+ recvWindow: recvWindow,
26
+ timestamp: timestamp,
27
+ symbol: symbol,
28
+ interval: interval,
29
+ startTime: start_time,
30
+ endTime: end_time,
31
+ limit: limit
32
+ }
33
+
34
+ response = Request.send!(
35
+ api_key_type: :read_info, path: Endpoints.fetch(:klines),
36
+ params: params.delete_if { |key, value| value.nil? },
37
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
38
+ )
39
+
40
+ force_float ? response.map! { |i| i.map(&:to_f) } : response
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,111 @@
1
+ module Binance
2
+ module SDK
3
+ class Orders
4
+ class << self
5
+ def put_order!(symbol:, type: 'TRAILING_STOP_MARKET', side: 'BUY', callback_rate: 4, quantity: 1.0, working_type: nil, position_side: 'BOTH')
6
+ params = {
7
+ recvWindow: 60000,
8
+ timestamp: Configuration.timestamp.to_i - 1000,
9
+ symbol: symbol,
10
+ side: side,
11
+ type: type,
12
+ quantity: quantity,
13
+ callbackRate: callback_rate,
14
+ workingType: working_type,
15
+ positionSide: position_side,
16
+ }
17
+
18
+ Request.send!(
19
+ method: :post,
20
+ api_key_type: :read_info, path: Endpoints.fetch(:put_order),
21
+ params: params.delete_if { |_, value| value.nil? },
22
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
23
+ )
24
+ end
25
+
26
+ def close_position!(symbol:, side:, quantity:)
27
+ params = {
28
+ recvWindow: 60000,
29
+ timestamp: Configuration.timestamp.to_i - 1000,
30
+ symbol: symbol,
31
+ side: side,
32
+ type: 'MARKET',
33
+ quantity: quantity,
34
+ positionSide: side,
35
+ }
36
+
37
+ Request.send!(
38
+ method: :post,
39
+ api_key_type: :read_info, path: Endpoints.fetch(:put_order),
40
+ params: params.delete_if { |_, value| value.nil? },
41
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
42
+ )
43
+ end
44
+
45
+ def stop_loss(symbol:, side:, stop_price:)
46
+ params = {
47
+ recvWindow: 60000,
48
+ timestamp: Configuration.timestamp.to_i - 1000,
49
+ symbol: symbol,
50
+ type: 'STOP',
51
+ side: side,
52
+ stopPrice: stop_price
53
+ }
54
+
55
+ Request.send!(
56
+ method: :post,
57
+ api_key_type: :read_info, path: Endpoints.fetch(:put_order),
58
+ params: params.delete_if { |_, value| value.nil? },
59
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
60
+ )
61
+ end
62
+
63
+ def cancel_order!(symbol:)
64
+ params = {
65
+ recvWindow: 60000,
66
+ timestamp: Configuration.timestamp.to_i - 1000,
67
+ symbol: symbol,
68
+ type: 'MARKET'
69
+ }
70
+
71
+ Request.send!(
72
+ method: :delete,
73
+ api_key_type: :read_info, path: Endpoints.fetch(:put_order),
74
+ params: params.delete_if { |_, value| value.nil? },
75
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
76
+ )
77
+ end
78
+
79
+ def cancel_orders!(symbol:)
80
+ params = {
81
+ recvWindow: 60000,
82
+ timestamp: Configuration.timestamp.to_i - 1000,
83
+ symbol: symbol
84
+ }
85
+
86
+ Request.send!(
87
+ method: :delete,
88
+ api_key_type: :read_info, path: Endpoints.fetch(:cancel_orders),
89
+ params: params.delete_if { |_, value| value.nil? },
90
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
91
+ )
92
+ end
93
+
94
+ def change_leverage(symbol:, leverage:)
95
+ params = {
96
+ symbol: symbol,
97
+ leverage: leverage.to_i,
98
+ timestamp: Configuration.timestamp
99
+ }
100
+
101
+ Request.send!(
102
+ method: :post,
103
+ api_key_type: :read_info, path: Endpoints.fetch(:change_leverage),
104
+ params: params,
105
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,81 @@
1
+ module Binance
2
+ module SDK
3
+ class Request
4
+ include HTTParty
5
+
6
+ class << self
7
+ SECURITY_TYPES = [:none, :trade, :user_data, :user_stream, :market_data, :margin].freeze
8
+
9
+ def send!(api_key_type: :none, headers: {}, method: :get, path: "/", params: {}, security_type: :none, tld: Configuration.tld, api_key: nil, api_secret_key: nil, testnet: false, futures: false)
10
+ Configuration.validate_tld!(tld)
11
+
12
+ if Configuration.testnet?
13
+ self.base_uri 'https://testnet.binancefuture.com'
14
+ else
15
+ if Configuration.futures?
16
+ self.base_uri 'https://fapi.binance.com'
17
+ else
18
+ self.base_uri 'https://api.binance.com'
19
+ end
20
+ end
21
+
22
+ raise Error.new(message: "Invalid security type #{security_type}") unless SECURITY_TYPES.include?(security_type)
23
+ all_headers = default_headers(api_key_type: api_key_type, security_type: security_type, api_key: api_key)
24
+ params.delete_if { |k, v| v.nil? }
25
+
26
+ if %w(trade user_data).include?(security_type&.to_s)
27
+ signature = signed_request_signature(params: params, api_secret_key: api_secret_key)
28
+ params.merge!(signature: signature)
29
+ end
30
+
31
+ case method
32
+ when :get
33
+ response = get(path, headers: all_headers, query: params)
34
+ when :post
35
+ response = post(path, query: params, headers: all_headers)
36
+ when :put
37
+ response = put(path, query: params, headers: all_headers)
38
+ when :delete
39
+ response = delete(path, query: params, headers: all_headers)
40
+ else
41
+ raise Error.new(message: "invalid http method used: #{method}")
42
+ end
43
+
44
+ process!(response: response || "{}")
45
+ end
46
+
47
+ private
48
+
49
+ def default_headers(api_key_type:, security_type:, api_key: nil)
50
+ headers = {}
51
+ headers["Content-Type"] = "application/json; charset=utf-8"
52
+ headers["X-MBX-APIKEY"] = (api_key || Configuration.api_key(type: api_key_type)) unless security_type == :none
53
+ headers
54
+ end
55
+
56
+ def process!(response:)
57
+ json = begin
58
+ JSON.parse(response.body, symbolize_names: true)
59
+ rescue JSON::ParserError => error
60
+ # binance 500 errors are html format
61
+
62
+ raise Error.new(message: "Error: #{error}")
63
+ end
64
+ raise Error.localized(json[:code]).new(json: json) if Error.is_error_response?(response: response)
65
+ json
66
+ end
67
+
68
+ def signed_request_signature(params:, api_secret_key: nil)
69
+ payload = params.map { |key, value| "#{key}=#{value}" }.join("&")
70
+ Configuration.signed_request_signature(payload: payload, api_secret_key: api_secret_key)
71
+ end
72
+
73
+ def base_url
74
+ return 'https://testnet.binancefuture.com' if testnet?
75
+
76
+ 'https://api.binance.com'
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,15 @@
1
+ module Binance
2
+ module SDK
3
+ class Server
4
+ class << self
5
+ def time
6
+ Request.send!(
7
+ api_key_type: :read_info, path: Endpoints.fetch(:server_time),
8
+ params: {},
9
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Binance
2
+ module SDK
3
+ class Ticker
4
+ class << self
5
+ def price(symbol)
6
+ timestamp = Configuration.timestamp
7
+
8
+ params = { recvWindow: 5000, timestamp: timestamp, symbol: symbol }
9
+
10
+ Request.send!(
11
+ api_key_type: :read_info, path: Endpoints.fetch(:ticker_price),
12
+ params: params.delete_if { |_, value| value.nil? },
13
+ security_type: :user_data, api_key: Configuration.api_key, api_secret_key: Configuration.secret_key
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Binance
4
+ module SDK
5
+ VERSION = "1.0.4"
6
+ end
7
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Binance
4
+ class WebSocket < Faye::WebSocket::Client
5
+ class Error < StandardError; end
6
+
7
+ def initialize(on_open: nil, on_close: nil)
8
+ websocket_api_url = if SDK::Configuration.futures?
9
+ 'wss://fstream.binance.com/stream'
10
+ else
11
+ 'wss://stream.binance.com:9443/stream'
12
+ end
13
+ super websocket_api_url, nil, ping: 180
14
+
15
+ @request_id_inc = 0
16
+ @user_stream_handlers = {}
17
+
18
+ on :open do |event|
19
+ on_open&.call(event)
20
+ end
21
+
22
+ on :message do |event|
23
+ process_data(event.data)
24
+ end
25
+
26
+ on :close do |event|
27
+ on_close&.call(event)
28
+ end
29
+ end
30
+
31
+ # stream name: <symbol>@kline_<interval>
32
+ #
33
+ # {
34
+ # "e": "kline", // Event type
35
+ # "E": 123456789, // Event time
36
+ # "s": "BTCUSDT", // Symbol
37
+ # "k": {
38
+ # "t": 123400000, // Kline start time
39
+ # "T": 123460000, // Kline close time
40
+ # "s": "BTCUSDT", // Symbol
41
+ # "i": "1m", // Interval
42
+ # "f": 100, // First trade ID
43
+ # "L": 200, // Last trade ID
44
+ # "o": "0.0010", // Open price
45
+ # "c": "0.0020", // Close price
46
+ # "h": "0.0025", // High price
47
+ # "l": "0.0015", // Low price
48
+ # "v": "1000", // Base asset volume
49
+ # "n": 100, // Number of trades
50
+ # "x": false, // Is this kline closed?
51
+ # "q": "1.0000", // Quote asset volume
52
+ # "V": "500", // Taker buy base asset volume
53
+ # "Q": "0.500", // Taker buy quote asset volume
54
+ # "B": "123456" // Ignore
55
+ # }
56
+ # }
57
+
58
+ def candlesticks!(symbols, interval, &on_receive)
59
+ symbols_fmt = symbols.is_a?(String) ? [symbols] : symbols
60
+ @candlesticks_handler = on_receive
61
+ subscribe(symbols_fmt.map { |s| "#{s.downcase}@kline_#{interval}" })
62
+ end
63
+
64
+ def user_data_stream!(listen_key, &on_receive)
65
+ @user_stream_handlers[listen_key] = on_receive
66
+ subscribe([listen_key])
67
+ end
68
+
69
+ private
70
+
71
+ def process_data(data)
72
+ json = JSON.parse(data, symbolize_names: true)
73
+ if json[:error]
74
+ raise Error.new("(#{json[:code]}) #{json[:msg]}")
75
+ elsif json.key?(:result)
76
+ # Binance stream connected successfully
77
+ else
78
+ case json[:data][:e]&.to_sym
79
+ when :kline
80
+ @candlesticks_handler&.call(json[:stream], json[:data])
81
+ when :outboundAccountPosition
82
+ when :balanceUpdate
83
+ when :executionReport
84
+ listen_key = json[:stream]
85
+ @user_stream_handlers[listen_key]&.call(listen_key, json[:data])
86
+ end
87
+ end
88
+ end
89
+
90
+ def request_id
91
+ @request_id_inc += 1
92
+ end
93
+
94
+ def subscribe(streams)
95
+ send(
96
+ {
97
+ method: 'SUBSCRIBE',
98
+ params: streams,
99
+ id: request_id
100
+ }.to_json
101
+ )
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require 'pry'
3
+ require 'active_support/core_ext/string'
4
+ require 'awrence'
5
+ require 'httparty'
6
+ require 'faye/websocket'
7
+ require 'dry/inflector'
8
+
9
+ Dry::Inflector.new do |inflections|
10
+ inflections.acronym 'SDK'
11
+ end
12
+
13
+ require_relative 'binance/sdk/version'
14
+ require_relative 'binance/sdk/error'
15
+ require_relative 'binance/sdk/configuration'
16
+ require_relative 'binance/sdk/request'
17
+ require_relative 'binance/sdk/endpoints'
18
+ require_relative 'binance/sdk/account'
19
+ require_relative 'binance/sdk/klines'
20
+ require_relative 'binance/sdk/ticker'
21
+ require_relative 'binance/sdk/exchange'
22
+ require_relative 'binance/sdk/orders'
23
+ require_relative 'binance/sdk/websocket'
24
+ require_relative 'binance/sdk/asset'
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binance-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Andrei Merfu
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-01-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: awrence
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: httparty
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: faye-websocket
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: eventmachine
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: dry-inflector
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activesupport
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 6.1.4
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 6.1.4
125
+ description: Ruby SDK for Binance API
126
+ email:
127
+ - andrei@amgital.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - lib/binance-sdk.rb
133
+ - lib/binance/sdk/account.rb
134
+ - lib/binance/sdk/asset.rb
135
+ - lib/binance/sdk/configuration.rb
136
+ - lib/binance/sdk/endpoints.rb
137
+ - lib/binance/sdk/error.rb
138
+ - lib/binance/sdk/exchange.rb
139
+ - lib/binance/sdk/klines.rb
140
+ - lib/binance/sdk/orders.rb
141
+ - lib/binance/sdk/request.rb
142
+ - lib/binance/sdk/server.rb
143
+ - lib/binance/sdk/ticker.rb
144
+ - lib/binance/sdk/version.rb
145
+ - lib/binance/sdk/websocket.rb
146
+ homepage: https://github.com/andreimerfu/binance-sdk
147
+ licenses:
148
+ - MIT
149
+ metadata:
150
+ allowed_push_host: https://rubygems.org
151
+ homepage_uri: https://github.com/andreimerfu/binance-sdk
152
+ source_code_uri: https://github.com/andreimerfu/binance-sdk
153
+ changelog_uri: https://github.com/andreimerfu/binance-sdk/changelog.md
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 3.1.0
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubygems_version: 3.3.3
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: binance-sdk-1.0.4
173
+ test_files: []