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 +7 -0
- data/lib/binance/sdk/account.rb +58 -0
- data/lib/binance/sdk/asset.rb +24 -0
- data/lib/binance/sdk/configuration.rb +66 -0
- data/lib/binance/sdk/endpoints.rb +43 -0
- data/lib/binance/sdk/error.rb +138 -0
- data/lib/binance/sdk/exchange.rb +39 -0
- data/lib/binance/sdk/klines.rb +45 -0
- data/lib/binance/sdk/orders.rb +111 -0
- data/lib/binance/sdk/request.rb +81 -0
- data/lib/binance/sdk/server.rb +15 -0
- data/lib/binance/sdk/ticker.rb +19 -0
- data/lib/binance/sdk/version.rb +7 -0
- data/lib/binance/sdk/websocket.rb +104 -0
- data/lib/binance-sdk.rb +24 -0
- metadata +173 -0
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,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
|
data/lib/binance-sdk.rb
ADDED
@@ -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: []
|