honeymaker 0.4.0 → 0.5.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 +4 -4
- data/README.md +100 -5
- data/lib/honeymaker/client.rb +14 -0
- data/lib/honeymaker/clients/binance.rb +264 -2
- data/lib/honeymaker/clients/binance_us.rb +33 -0
- data/lib/honeymaker/clients/bingx.rb +100 -4
- data/lib/honeymaker/clients/bitget.rb +163 -2
- data/lib/honeymaker/clients/bitmart.rb +108 -2
- data/lib/honeymaker/clients/bitrue.rb +90 -2
- data/lib/honeymaker/clients/bitvavo.rb +80 -4
- data/lib/honeymaker/clients/bybit.rb +120 -2
- data/lib/honeymaker/clients/coinbase.rb +108 -2
- data/lib/honeymaker/clients/gemini.rb +85 -4
- data/lib/honeymaker/clients/hyperliquid.rb +69 -1
- data/lib/honeymaker/clients/kraken.rb +112 -2
- data/lib/honeymaker/clients/kraken_futures.rb +78 -0
- data/lib/honeymaker/clients/kucoin.rb +120 -2
- data/lib/honeymaker/clients/mexc.rb +85 -2
- data/lib/honeymaker/version.rb +1 -1
- data/lib/honeymaker.rb +3 -1
- data/test/honeymaker/clients/binance_client_test.rb +9 -2
- data/test/honeymaker/clients/bitget_client_test.rb +9 -3
- data/test/honeymaker/clients/bitmart_client_test.rb +7 -2
- data/test/honeymaker/clients/bitvavo_client_test.rb +2 -2
- data/test/honeymaker/clients/bybit_client_test.rb +7 -3
- data/test/honeymaker/clients/coinbase_client_test.rb +10 -3
- data/test/honeymaker/clients/honeymaker_client_registry_test.rb +1 -1
- data/test/honeymaker/clients/kraken_client_test.rb +2 -1
- data/test/honeymaker/clients/kraken_futures_client_test.rb +54 -0
- data/test/honeymaker/clients/kucoin_client_test.rb +8 -2
- data/test/honeymaker/clients/mexc_client_test.rb +6 -1
- metadata +17 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module Honeymaker
|
|
6
|
+
module Clients
|
|
7
|
+
class KrakenFutures < Client
|
|
8
|
+
URL = "https://futures.kraken.com"
|
|
9
|
+
|
|
10
|
+
RATE_LIMITS = { default: 500, orders: 500 }.freeze
|
|
11
|
+
|
|
12
|
+
def get_accounts
|
|
13
|
+
get_private("/derivatives/api/v3/accounts")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get_fills(last_fill_time: nil)
|
|
17
|
+
get_private("/derivatives/api/v3/fills", { lastFillTime: last_fill_time })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get_open_positions
|
|
21
|
+
get_private("/derivatives/api/v3/openpositions")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def historical_funding_rates(symbol:)
|
|
25
|
+
get_public("/derivatives/api/v3/historicalfundingrates", { symbol: symbol })
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def validate_trading_credentials
|
|
31
|
+
result = get_accounts
|
|
32
|
+
return Result::Failure.new("Invalid trading credentials") if result.failure?
|
|
33
|
+
Result::Success.new(true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate_read_credentials
|
|
37
|
+
validate_trading_credentials
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def get_public(path, params = {})
|
|
41
|
+
with_rescue do
|
|
42
|
+
response = connection.get do |req|
|
|
43
|
+
req.url path
|
|
44
|
+
req.params = params.compact
|
|
45
|
+
end
|
|
46
|
+
response.body
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_private(path, params = {})
|
|
51
|
+
with_rescue do
|
|
52
|
+
params = params.compact
|
|
53
|
+
query_string = params.empty? ? "" : Faraday::Utils.build_query(params)
|
|
54
|
+
nonce = timestamp_ms.to_s
|
|
55
|
+
|
|
56
|
+
post_data = query_string
|
|
57
|
+
hash_input = "#{post_data}#{nonce}#{path}"
|
|
58
|
+
sha256_hash = Digest::SHA256.digest(hash_input)
|
|
59
|
+
decoded_secret = Base64.decode64(@api_secret)
|
|
60
|
+
hmac = OpenSSL::HMAC.digest("sha512", decoded_secret, sha256_hash)
|
|
61
|
+
authent = Base64.strict_encode64(hmac)
|
|
62
|
+
|
|
63
|
+
response = connection.get do |req|
|
|
64
|
+
req.url path
|
|
65
|
+
req.headers = {
|
|
66
|
+
"APIKey": @api_key,
|
|
67
|
+
"Authent": authent,
|
|
68
|
+
"Nonce": nonce,
|
|
69
|
+
Accept: "application/json"
|
|
70
|
+
}
|
|
71
|
+
req.params = params
|
|
72
|
+
end
|
|
73
|
+
response.body
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -4,6 +4,7 @@ module Honeymaker
|
|
|
4
4
|
module Clients
|
|
5
5
|
class Kucoin < Client
|
|
6
6
|
URL = "https://api.kucoin.com"
|
|
7
|
+
RATE_LIMITS = { default: 100, orders: 200 }.freeze
|
|
7
8
|
|
|
8
9
|
attr_reader :passphrase
|
|
9
10
|
|
|
@@ -34,17 +35,47 @@ module Honeymaker
|
|
|
34
35
|
get_signed("/api/v1/accounts", { currency: currency, type: type })
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
def get_balances(type: "trade")
|
|
39
|
+
result = get_accounts(type: type)
|
|
40
|
+
return result if result.failure?
|
|
41
|
+
|
|
42
|
+
return Result::Failure.new("KuCoin API error") unless result.data["code"] == "200000"
|
|
43
|
+
|
|
44
|
+
balances = {}
|
|
45
|
+
(result.data["data"] || []).each do |account|
|
|
46
|
+
symbol = account["currency"]
|
|
47
|
+
free = BigDecimal((account["available"] || "0").to_s)
|
|
48
|
+
locked = BigDecimal((account["holds"] || "0").to_s)
|
|
49
|
+
next if free.zero? && locked.zero?
|
|
50
|
+
balances[symbol] = { free: free, locked: locked }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Result::Success.new(balances)
|
|
54
|
+
end
|
|
55
|
+
|
|
37
56
|
def place_order(client_oid:, side:, symbol:, type:, size: nil, funds: nil, price: nil,
|
|
38
57
|
time_in_force: nil, stp: nil)
|
|
39
|
-
post_signed("/api/v1/orders", {
|
|
58
|
+
result = post_signed("/api/v1/orders", {
|
|
40
59
|
clientOid: client_oid, side: side, symbol: symbol, type: type,
|
|
41
60
|
size: size, funds: funds, price: price,
|
|
42
61
|
timeInForce: time_in_force, stp: stp
|
|
43
62
|
})
|
|
63
|
+
return result if result.failure?
|
|
64
|
+
return Result::Failure.new("KuCoin API error") unless result.data["code"] == "200000"
|
|
65
|
+
|
|
66
|
+
order_id = result.data.dig("data", "orderId")
|
|
67
|
+
Result::Success.new({ order_id: order_id, raw: result.data })
|
|
44
68
|
end
|
|
45
69
|
|
|
46
70
|
def get_order(order_id:)
|
|
47
|
-
get_signed("/api/v1/orders/#{order_id}")
|
|
71
|
+
result = get_signed("/api/v1/orders/#{order_id}")
|
|
72
|
+
return result if result.failure?
|
|
73
|
+
return Result::Failure.new("KuCoin API error") unless result.data["code"] == "200000"
|
|
74
|
+
|
|
75
|
+
raw = result.data["data"]
|
|
76
|
+
return Result::Failure.new("Order not found") unless raw
|
|
77
|
+
|
|
78
|
+
Result::Success.new(normalize_order(order_id, raw))
|
|
48
79
|
end
|
|
49
80
|
|
|
50
81
|
def cancel_order(order_id:)
|
|
@@ -97,8 +128,91 @@ module Honeymaker
|
|
|
97
128
|
})
|
|
98
129
|
end
|
|
99
130
|
|
|
131
|
+
# --- Margin ---
|
|
132
|
+
|
|
133
|
+
def margin_borrow_history(currency: nil, is_isolated: nil, symbol: nil, order_no: nil, start_time: nil, end_time: nil, current_page: nil, page_size: nil)
|
|
134
|
+
get_signed("/api/v3/margin/borrow", {
|
|
135
|
+
currency: currency, isIsolated: is_isolated, symbol: symbol, orderNo: order_no,
|
|
136
|
+
startTime: start_time, endTime: end_time, currentPage: current_page, pageSize: page_size
|
|
137
|
+
})
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def margin_repay_history(currency: nil, is_isolated: nil, symbol: nil, order_no: nil, start_time: nil, end_time: nil, current_page: nil, page_size: nil)
|
|
141
|
+
get_signed("/api/v3/margin/repay", {
|
|
142
|
+
currency: currency, isIsolated: is_isolated, symbol: symbol, orderNo: order_no,
|
|
143
|
+
startTime: start_time, endTime: end_time, currentPage: current_page, pageSize: page_size
|
|
144
|
+
})
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def margin_interest_history(currency: nil, is_isolated: nil, symbol: nil, start_time: nil, end_time: nil, current_page: nil, page_size: nil)
|
|
148
|
+
get_signed("/api/v3/margin/interest", {
|
|
149
|
+
currency: currency, isIsolated: is_isolated, symbol: symbol,
|
|
150
|
+
startTime: start_time, endTime: end_time, currentPage: current_page, pageSize: page_size
|
|
151
|
+
})
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# --- Futures ---
|
|
155
|
+
|
|
156
|
+
def futures_funding_history(symbol:, start_at: nil, end_at: nil, reverse: nil, offset: nil, forward: nil, max_count: nil)
|
|
157
|
+
with_rescue do
|
|
158
|
+
params = {
|
|
159
|
+
symbol: symbol, startAt: start_at, endAt: end_at,
|
|
160
|
+
reverse: reverse, offset: offset, forward: forward, maxCount: max_count
|
|
161
|
+
}.compact
|
|
162
|
+
query_string = params.empty? ? "" : "?#{Faraday::Utils.build_query(params)}"
|
|
163
|
+
path = "/api/v1/funding-history"
|
|
164
|
+
ts = timestamp_ms.to_s
|
|
165
|
+
pre_sign = "#{ts}GET#{path}#{query_string}"
|
|
166
|
+
|
|
167
|
+
response = futures_connection.get do |req|
|
|
168
|
+
req.url path
|
|
169
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
170
|
+
req.params = params
|
|
171
|
+
end
|
|
172
|
+
response.body
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
100
176
|
private
|
|
101
177
|
|
|
178
|
+
def normalize_order(order_id, raw)
|
|
179
|
+
order_type = parse_order_type(raw["type"])
|
|
180
|
+
side = raw["side"]&.downcase&.to_sym
|
|
181
|
+
status = parse_order_status(raw)
|
|
182
|
+
|
|
183
|
+
deal_funds = BigDecimal((raw["dealFunds"] || "0").to_s)
|
|
184
|
+
deal_size = BigDecimal((raw["dealSize"] || "0").to_s)
|
|
185
|
+
price = deal_funds.positive? && deal_size.positive? ? deal_funds / deal_size : BigDecimal((raw["price"] || "0").to_s)
|
|
186
|
+
price = nil if price.zero?
|
|
187
|
+
|
|
188
|
+
amount = raw["size"] ? BigDecimal(raw["size"].to_s) : nil
|
|
189
|
+
quote_amount = raw["funds"] ? BigDecimal(raw["funds"].to_s) : nil
|
|
190
|
+
|
|
191
|
+
{
|
|
192
|
+
order_id: order_id, status: status, side: side, order_type: order_type,
|
|
193
|
+
price: price, amount: amount, quote_amount: quote_amount,
|
|
194
|
+
amount_exec: deal_size, quote_amount_exec: deal_funds, raw: raw
|
|
195
|
+
}
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def parse_order_type(type)
|
|
199
|
+
case type&.downcase
|
|
200
|
+
when "market" then :market
|
|
201
|
+
when "limit" then :limit
|
|
202
|
+
else :unknown
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def parse_order_status(raw)
|
|
207
|
+
if raw["cancelExist"]
|
|
208
|
+
:cancelled
|
|
209
|
+
elsif raw["isActive"]
|
|
210
|
+
:open
|
|
211
|
+
else
|
|
212
|
+
BigDecimal((raw["dealSize"] || "0").to_s).positive? ? :closed : :unknown
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
102
216
|
def validate_trading_credentials
|
|
103
217
|
result = get_accounts(type: "trade")
|
|
104
218
|
return Result::Failure.new("Invalid trading credentials") if result.failure?
|
|
@@ -183,6 +297,10 @@ module Honeymaker
|
|
|
183
297
|
"Content-Type": "application/json"
|
|
184
298
|
}
|
|
185
299
|
end
|
|
300
|
+
|
|
301
|
+
def futures_connection
|
|
302
|
+
@futures_connection ||= build_client_connection("https://api-futures.kucoin.com")
|
|
303
|
+
end
|
|
186
304
|
end
|
|
187
305
|
end
|
|
188
306
|
end
|
|
@@ -4,6 +4,7 @@ module Honeymaker
|
|
|
4
4
|
module Clients
|
|
5
5
|
class Mexc < Client
|
|
6
6
|
URL = "https://api.mexc.com"
|
|
7
|
+
RATE_LIMITS = { default: 100, orders: 200 }.freeze
|
|
7
8
|
|
|
8
9
|
def get_all_coins_information
|
|
9
10
|
get_public("/api/v3/capital/config/getall")
|
|
@@ -32,21 +33,45 @@ module Honeymaker
|
|
|
32
33
|
get_signed("/api/v3/account", { recvWindow: recv_window })
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
def get_balances
|
|
37
|
+
result = account_information
|
|
38
|
+
return result if result.failure?
|
|
39
|
+
|
|
40
|
+
balances = {}
|
|
41
|
+
Array(result.data["balances"]).each do |balance|
|
|
42
|
+
symbol = balance["asset"]
|
|
43
|
+
free = BigDecimal(balance["free"].to_s)
|
|
44
|
+
locked = BigDecimal(balance["locked"].to_s)
|
|
45
|
+
next if free.zero? && locked.zero?
|
|
46
|
+
balances[symbol] = { free: free, locked: locked }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Result::Success.new(balances)
|
|
50
|
+
end
|
|
51
|
+
|
|
35
52
|
def query_order(symbol:, order_id: nil, orig_client_order_id: nil, recv_window: 5000)
|
|
36
|
-
get_signed("/api/v3/order", {
|
|
53
|
+
result = get_signed("/api/v3/order", {
|
|
37
54
|
symbol: symbol, orderId: order_id,
|
|
38
55
|
origClientOrderId: orig_client_order_id, recvWindow: recv_window
|
|
39
56
|
})
|
|
57
|
+
return result if result.failure?
|
|
58
|
+
|
|
59
|
+
raw = result.data
|
|
60
|
+
Result::Success.new(normalize_order("#{symbol}-#{raw['orderId']}", raw))
|
|
40
61
|
end
|
|
41
62
|
|
|
42
63
|
def new_order(symbol:, side:, type:, time_in_force: nil, quantity: nil, quote_order_qty: nil,
|
|
43
64
|
price: nil, new_client_order_id: nil, recv_window: 5000)
|
|
44
|
-
post_signed("/api/v3/order", {
|
|
65
|
+
result = post_signed("/api/v3/order", {
|
|
45
66
|
symbol: symbol, side: side, type: type,
|
|
46
67
|
timeInForce: time_in_force, quantity: quantity,
|
|
47
68
|
quoteOrderQty: quote_order_qty, price: price,
|
|
48
69
|
newClientOrderId: new_client_order_id, recvWindow: recv_window
|
|
49
70
|
})
|
|
71
|
+
return result if result.failure?
|
|
72
|
+
|
|
73
|
+
raw = result.data
|
|
74
|
+
Result::Success.new({ order_id: "#{symbol}-#{raw['orderId']}", raw: raw })
|
|
50
75
|
end
|
|
51
76
|
|
|
52
77
|
def cancel_order(symbol:, order_id: nil, orig_client_order_id: nil,
|
|
@@ -90,8 +115,66 @@ module Honeymaker
|
|
|
90
115
|
})
|
|
91
116
|
end
|
|
92
117
|
|
|
118
|
+
# --- Other ---
|
|
119
|
+
|
|
120
|
+
def dust_conversion_history(recv_window: 5000)
|
|
121
|
+
get_signed("/api/v3/capital/convert", { recvWindow: recv_window })
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def universal_transfer_history(type:, start_time: nil, end_time: nil, page: nil, size: nil, recv_window: 5000)
|
|
125
|
+
get_signed("/api/v3/capital/transfer", {
|
|
126
|
+
type: type, startTime: start_time, endTime: end_time,
|
|
127
|
+
page: page, size: size, recvWindow: recv_window
|
|
128
|
+
})
|
|
129
|
+
end
|
|
130
|
+
|
|
93
131
|
private
|
|
94
132
|
|
|
133
|
+
def normalize_order(order_id, raw)
|
|
134
|
+
order_type = parse_order_type(raw["type"])
|
|
135
|
+
side = raw["side"]&.downcase&.to_sym
|
|
136
|
+
status = parse_order_status(raw["status"])
|
|
137
|
+
|
|
138
|
+
amount = BigDecimal(raw["origQty"].to_s)
|
|
139
|
+
amount = nil if amount.zero?
|
|
140
|
+
quote_amount = raw["origQuoteOrderQty"] ? BigDecimal(raw["origQuoteOrderQty"].to_s) : nil
|
|
141
|
+
quote_amount = nil if quote_amount&.zero?
|
|
142
|
+
|
|
143
|
+
amount_exec = BigDecimal(raw["executedQty"].to_s)
|
|
144
|
+
quote_amount_exec = BigDecimal(raw["cummulativeQuoteQty"].to_s)
|
|
145
|
+
quote_amount_exec = nil if quote_amount_exec.negative?
|
|
146
|
+
|
|
147
|
+
price = BigDecimal(raw["price"].to_s)
|
|
148
|
+
if price.zero? && quote_amount_exec&.positive? && amount_exec.positive?
|
|
149
|
+
price = quote_amount_exec / amount_exec
|
|
150
|
+
end
|
|
151
|
+
price = nil if price.zero?
|
|
152
|
+
|
|
153
|
+
{
|
|
154
|
+
order_id: order_id, status: status, side: side, order_type: order_type,
|
|
155
|
+
price: price, amount: amount, quote_amount: quote_amount,
|
|
156
|
+
amount_exec: amount_exec, quote_amount_exec: quote_amount_exec, raw: raw
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def parse_order_type(type)
|
|
161
|
+
case type
|
|
162
|
+
when "MARKET" then :market
|
|
163
|
+
when "LIMIT" then :limit
|
|
164
|
+
else :unknown
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def parse_order_status(status)
|
|
169
|
+
case status
|
|
170
|
+
when "NEW", "PARTIALLY_FILLED" then :open
|
|
171
|
+
when "FILLED" then :closed
|
|
172
|
+
when "CANCELED", "EXPIRED" then :cancelled
|
|
173
|
+
when "REJECTED" then :failed
|
|
174
|
+
else :unknown
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
95
178
|
def validate_trading_credentials
|
|
96
179
|
result = account_information
|
|
97
180
|
result.success? ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
data/lib/honeymaker/version.rb
CHANGED
data/lib/honeymaker.rb
CHANGED
|
@@ -23,6 +23,7 @@ require_relative "honeymaker/clients/bingx"
|
|
|
23
23
|
require_relative "honeymaker/clients/bitrue"
|
|
24
24
|
require_relative "honeymaker/clients/bitmart"
|
|
25
25
|
require_relative "honeymaker/clients/hyperliquid"
|
|
26
|
+
require_relative "honeymaker/clients/kraken_futures"
|
|
26
27
|
require_relative "honeymaker/exchanges/binance"
|
|
27
28
|
require_relative "honeymaker/exchanges/binance_us"
|
|
28
29
|
require_relative "honeymaker/exchanges/kraken"
|
|
@@ -72,7 +73,8 @@ module Honeymaker
|
|
|
72
73
|
"bingx" => Clients::BingX,
|
|
73
74
|
"bitrue" => Clients::Bitrue,
|
|
74
75
|
"bitmart" => Clients::BitMart,
|
|
75
|
-
"hyperliquid" => Clients::Hyperliquid
|
|
76
|
+
"hyperliquid" => Clients::Hyperliquid,
|
|
77
|
+
"kraken_futures" => Clients::KrakenFutures
|
|
76
78
|
}.freeze
|
|
77
79
|
|
|
78
80
|
def self.exchange(name)
|
|
@@ -22,13 +22,20 @@ class Honeymaker::Clients::BinanceTest < Minitest::Test
|
|
|
22
22
|
stub_connection(:post, { "orderId" => 123, "status" => "FILLED" })
|
|
23
23
|
result = @client.new_order(symbol: "BTCUSDT", side: "BUY", type: "MARKET", quantity: "0.001")
|
|
24
24
|
assert result.success?
|
|
25
|
-
assert_equal 123, result.data[
|
|
25
|
+
assert_equal "BTCUSDT-123", result.data[:order_id]
|
|
26
|
+
assert_equal 123, result.data[:raw]["orderId"]
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def test_query_order
|
|
29
|
-
stub_connection(:get, {
|
|
30
|
+
stub_connection(:get, {
|
|
31
|
+
"orderId" => 123, "symbol" => "BTCUSDT", "status" => "FILLED", "type" => "MARKET",
|
|
32
|
+
"side" => "BUY", "origQty" => "0.001", "origQuoteOrderQty" => "0",
|
|
33
|
+
"executedQty" => "0.001", "cummulativeQuoteQty" => "50", "price" => "50000"
|
|
34
|
+
})
|
|
30
35
|
result = @client.query_order(symbol: "BTCUSDT", order_id: 123)
|
|
31
36
|
assert result.success?
|
|
37
|
+
assert_equal :closed, result.data[:status]
|
|
38
|
+
assert_equal :buy, result.data[:side]
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
def test_account_information
|
|
@@ -36,21 +36,27 @@ class Honeymaker::Clients::BitgetTest < Minitest::Test
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def test_place_order
|
|
39
|
-
stub_connection(:post, { "data" => { "orderId" => "123" } })
|
|
39
|
+
stub_connection(:post, { "code" => "00000", "data" => { "orderId" => "123" } })
|
|
40
40
|
result = @client.place_order(symbol: "BTCUSDT", side: "buy", order_type: "market", size: "0.001")
|
|
41
41
|
assert result.success?
|
|
42
|
+
assert_equal "123", result.data[:order_id]
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def test_place_order_with_quote_size
|
|
45
|
-
stub_connection(:post, { "data" => { "orderId" => "456" } })
|
|
46
|
+
stub_connection(:post, { "code" => "00000", "data" => { "orderId" => "456" } })
|
|
46
47
|
result = @client.place_order(symbol: "BTCUSDT", side: "buy", order_type: "market", quote_size: "100")
|
|
47
48
|
assert result.success?
|
|
49
|
+
assert_equal "456", result.data[:order_id]
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def test_get_order
|
|
51
|
-
stub_connection(:get, { "
|
|
53
|
+
stub_connection(:get, { "code" => "00000", "data" => [{
|
|
54
|
+
"orderId" => "123", "orderType" => "market", "side" => "buy", "status" => "full_fill",
|
|
55
|
+
"priceAvg" => "50000", "size" => "0.001", "baseVolume" => "0.001", "quoteVolume" => "50"
|
|
56
|
+
}] })
|
|
52
57
|
result = @client.get_order(order_id: "123")
|
|
53
58
|
assert result.success?
|
|
59
|
+
assert_equal :closed, result.data[:status]
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def test_cancel_order
|
|
@@ -36,15 +36,20 @@ class Honeymaker::Clients::BitMartTest < Minitest::Test
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def test_submit_order
|
|
39
|
-
stub_connection(:post, { "data" => { "order_id" => 123 } })
|
|
39
|
+
stub_connection(:post, { "code" => 1000, "data" => { "order_id" => 123 } })
|
|
40
40
|
result = @client.submit_order(symbol: "BTC_USDT", side: "buy", type: "market", notional: "100")
|
|
41
41
|
assert result.success?
|
|
42
|
+
assert_equal "123", result.data[:order_id]
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def test_get_order
|
|
45
|
-
stub_connection(:post, { "
|
|
46
|
+
stub_connection(:post, { "code" => 1000, "data" => {
|
|
47
|
+
"symbol" => "BTC_USDT", "type" => "market", "side" => "buy", "status" => "filled",
|
|
48
|
+
"size" => "0.001", "filled_size" => "0.001", "filled_notional" => "50", "price" => "50000"
|
|
49
|
+
} })
|
|
46
50
|
result = @client.get_order(order_id: "123")
|
|
47
51
|
assert result.success?
|
|
52
|
+
assert_equal :closed, result.data[:status]
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def test_cancel_order
|
|
@@ -23,9 +23,9 @@ class Honeymaker::Clients::BitvavoTest < Minitest::Test
|
|
|
23
23
|
assert result.success?
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def
|
|
26
|
+
def test_get_raw_balance
|
|
27
27
|
stub_connection(:get, [{ "symbol" => "BTC", "available" => "0.5" }])
|
|
28
|
-
result = @client.
|
|
28
|
+
result = @client.get_raw_balance
|
|
29
29
|
assert result.success?
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -30,16 +30,20 @@ class Honeymaker::Clients::BybitTest < Minitest::Test
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def test_create_order
|
|
33
|
-
stub_connection(:post, { "result" => { "orderId" => "123" } })
|
|
33
|
+
stub_connection(:post, { "retCode" => 0, "retMsg" => "OK", "result" => { "orderId" => "123" } })
|
|
34
34
|
result = @client.create_order(category: "spot", symbol: "BTCUSDT", side: "Buy", order_type: "Market", qty: "0.001")
|
|
35
35
|
assert result.success?
|
|
36
|
-
assert_equal "123", result.data[
|
|
36
|
+
assert_equal "123", result.data[:order_id]
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def test_get_order
|
|
40
|
-
stub_connection(:get, { "
|
|
40
|
+
stub_connection(:get, { "retCode" => 0, "retMsg" => "OK", "result" => { "list" => [{
|
|
41
|
+
"orderId" => "123", "orderType" => "Market", "side" => "Buy", "orderStatus" => "Filled",
|
|
42
|
+
"avgPrice" => "50000", "qty" => "0.001", "cumExecQty" => "0.001", "cumExecValue" => "50"
|
|
43
|
+
}] } })
|
|
41
44
|
result = @client.get_order(category: "spot", order_id: "123")
|
|
42
45
|
assert result.success?
|
|
46
|
+
assert_equal :closed, result.data[:status]
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
def test_cancel_order
|
|
@@ -17,19 +17,26 @@ class Honeymaker::Clients::CoinbaseTest < Minitest::Test
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def test_get_order
|
|
20
|
-
stub_connection(:get, { "order" => {
|
|
20
|
+
stub_connection(:get, { "order" => {
|
|
21
|
+
"order_id" => "abc", "status" => "FILLED", "order_type" => "MARKET", "side" => "BUY",
|
|
22
|
+
"average_filled_price" => "50000", "filled_size" => "0.001",
|
|
23
|
+
"total_value_after_fees" => "50", "outstanding_hold_amount" => "0",
|
|
24
|
+
"order_configuration" => { "market_market_ioc" => { "quote_size" => "50" } }
|
|
25
|
+
} })
|
|
21
26
|
result = @client.get_order(order_id: "abc")
|
|
22
27
|
assert result.success?
|
|
23
|
-
assert_equal "abc", result.data[
|
|
28
|
+
assert_equal "abc", result.data[:order_id]
|
|
29
|
+
assert_equal :closed, result.data[:status]
|
|
24
30
|
end
|
|
25
31
|
|
|
26
32
|
def test_create_order
|
|
27
|
-
stub_connection(:post, { "success" => true, "order_id" => "xyz" })
|
|
33
|
+
stub_connection(:post, { "success" => true, "success_response" => { "order_id" => "xyz" } })
|
|
28
34
|
result = @client.create_order(
|
|
29
35
|
client_order_id: "c1", product_id: "BTC-USD",
|
|
30
36
|
side: "BUY", order_configuration: { market_market_ioc: { quote_size: "100" } }
|
|
31
37
|
)
|
|
32
38
|
assert result.success?
|
|
39
|
+
assert_equal "xyz", result.data[:order_id]
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
def test_cancel_orders
|
|
@@ -31,7 +31,8 @@ class Honeymaker::Clients::KrakenTest < Minitest::Test
|
|
|
31
31
|
stub_connection(:post, { "error" => [], "result" => { "txid" => ["ORDER-123"] } })
|
|
32
32
|
result = @client.add_order(ordertype: "market", type: "buy", volume: "0.001", pair: "XBTUSDT")
|
|
33
33
|
assert result.success?
|
|
34
|
-
assert_equal
|
|
34
|
+
assert_equal "ORDER-123", result.data[:order_id]
|
|
35
|
+
assert_equal ["ORDER-123"], result.data[:raw]["result"]["txid"]
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def test_cancel_order
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::KrakenFuturesTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@client = Honeymaker::Clients::KrakenFutures.new(
|
|
8
|
+
api_key: "test_key",
|
|
9
|
+
api_secret: Base64.strict_encode64("test_secret_key_1234567890123456")
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_url
|
|
14
|
+
assert_equal "https://futures.kraken.com", Honeymaker::Clients::KrakenFutures::URL
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_get_accounts
|
|
18
|
+
stub_connection(:get, { "result" => "success", "accounts" => {} })
|
|
19
|
+
result = @client.get_accounts
|
|
20
|
+
assert result.success?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_get_fills
|
|
24
|
+
stub_connection(:get, { "result" => "success", "fills" => [] })
|
|
25
|
+
result = @client.get_fills
|
|
26
|
+
assert result.success?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_get_open_positions
|
|
30
|
+
stub_connection(:get, { "result" => "success", "openPositions" => [] })
|
|
31
|
+
result = @client.get_open_positions
|
|
32
|
+
assert result.success?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_historical_funding_rates
|
|
36
|
+
stub_connection(:get, { "result" => "success", "rates" => [] })
|
|
37
|
+
result = @client.historical_funding_rates(symbol: "PF_XBTUSD")
|
|
38
|
+
assert result.success?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_client_registered
|
|
42
|
+
client = Honeymaker.client("kraken_futures", api_key: "k", api_secret: Base64.strict_encode64("s" * 32))
|
|
43
|
+
assert_instance_of Honeymaker::Clients::KrakenFutures, client
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def stub_connection(method, body)
|
|
49
|
+
response = stub(body: body)
|
|
50
|
+
connection = stub
|
|
51
|
+
connection.stubs(method).returns(response)
|
|
52
|
+
@client.instance_variable_set(:@connection, connection)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -36,15 +36,21 @@ class Honeymaker::Clients::KucoinTest < Minitest::Test
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def test_place_order
|
|
39
|
-
stub_connection(:post, { "data" => { "orderId" => "123" } })
|
|
39
|
+
stub_connection(:post, { "code" => "200000", "data" => { "orderId" => "123" } })
|
|
40
40
|
result = @client.place_order(client_oid: "c1", side: "buy", symbol: "BTC-USDT", type: "market", funds: "100")
|
|
41
41
|
assert result.success?
|
|
42
|
+
assert_equal "123", result.data[:order_id]
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def test_get_order
|
|
45
|
-
stub_connection(:get, { "
|
|
46
|
+
stub_connection(:get, { "code" => "200000", "data" => {
|
|
47
|
+
"id" => "123", "symbol" => "BTC-USDT", "type" => "market", "side" => "buy",
|
|
48
|
+
"isActive" => false, "cancelExist" => false, "dealSize" => "0.001",
|
|
49
|
+
"dealFunds" => "50", "size" => "0.001", "price" => "50000"
|
|
50
|
+
} })
|
|
46
51
|
result = @client.get_order(order_id: "123")
|
|
47
52
|
assert result.success?
|
|
53
|
+
assert_equal :closed, result.data[:status]
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
def test_cancel_order
|
|
@@ -36,9 +36,14 @@ class Honeymaker::Clients::MexcTest < Minitest::Test
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def test_query_order
|
|
39
|
-
stub_connection(:get, {
|
|
39
|
+
stub_connection(:get, {
|
|
40
|
+
"orderId" => "123", "symbol" => "BTCUSDT", "status" => "FILLED", "type" => "MARKET",
|
|
41
|
+
"side" => "BUY", "origQty" => "0.001", "executedQty" => "0.001",
|
|
42
|
+
"cummulativeQuoteQty" => "50", "price" => "50000"
|
|
43
|
+
})
|
|
40
44
|
result = @client.query_order(symbol: "BTCUSDT", order_id: "123")
|
|
41
45
|
assert result.success?
|
|
46
|
+
assert_equal :closed, result.data[:status]
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def test_cancel_order
|