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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -5
  3. data/lib/honeymaker/client.rb +14 -0
  4. data/lib/honeymaker/clients/binance.rb +264 -2
  5. data/lib/honeymaker/clients/binance_us.rb +33 -0
  6. data/lib/honeymaker/clients/bingx.rb +100 -4
  7. data/lib/honeymaker/clients/bitget.rb +163 -2
  8. data/lib/honeymaker/clients/bitmart.rb +108 -2
  9. data/lib/honeymaker/clients/bitrue.rb +90 -2
  10. data/lib/honeymaker/clients/bitvavo.rb +80 -4
  11. data/lib/honeymaker/clients/bybit.rb +120 -2
  12. data/lib/honeymaker/clients/coinbase.rb +108 -2
  13. data/lib/honeymaker/clients/gemini.rb +85 -4
  14. data/lib/honeymaker/clients/hyperliquid.rb +69 -1
  15. data/lib/honeymaker/clients/kraken.rb +112 -2
  16. data/lib/honeymaker/clients/kraken_futures.rb +78 -0
  17. data/lib/honeymaker/clients/kucoin.rb +120 -2
  18. data/lib/honeymaker/clients/mexc.rb +85 -2
  19. data/lib/honeymaker/version.rb +1 -1
  20. data/lib/honeymaker.rb +3 -1
  21. data/test/honeymaker/clients/binance_client_test.rb +9 -2
  22. data/test/honeymaker/clients/bitget_client_test.rb +9 -3
  23. data/test/honeymaker/clients/bitmart_client_test.rb +7 -2
  24. data/test/honeymaker/clients/bitvavo_client_test.rb +2 -2
  25. data/test/honeymaker/clients/bybit_client_test.rb +7 -3
  26. data/test/honeymaker/clients/coinbase_client_test.rb +10 -3
  27. data/test/honeymaker/clients/honeymaker_client_registry_test.rb +1 -1
  28. data/test/honeymaker/clients/kraken_client_test.rb +2 -1
  29. data/test/honeymaker/clients/kraken_futures_client_test.rb +54 -0
  30. data/test/honeymaker/clients/kucoin_client_test.rb +8 -2
  31. data/test/honeymaker/clients/mexc_client_test.rb +6 -1
  32. 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")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Honeymaker
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
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["orderId"]
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, { "orderId" => 123, "status" => "FILLED" })
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, { "data" => [{ "orderId" => "123" }] })
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, { "data" => { "order_id" => 123 } })
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 test_get_balance
26
+ def test_get_raw_balance
27
27
  stub_connection(:get, [{ "symbol" => "BTC", "available" => "0.5" }])
28
- result = @client.get_balance
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["result"]["orderId"]
36
+ assert_equal "123", result.data[:order_id]
37
37
  end
38
38
 
39
39
  def test_get_order
40
- stub_connection(:get, { "result" => { "list" => [{ "orderId" => "123" }] } })
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" => { "order_id" => "abc", "status" => "FILLED" } })
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["order"]["order_id"]
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
@@ -18,7 +18,7 @@ class HoneymakerClientRegistryTest < Minitest::Test
18
18
  end
19
19
 
20
20
  def test_all_clients_registered
21
- assert_equal 14, Honeymaker::CLIENTS.size
21
+ assert_equal 15, Honeymaker::CLIENTS.size
22
22
  end
23
23
 
24
24
  def test_client_passes_credentials
@@ -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 ["ORDER-123"], result.data["result"]["txid"]
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, { "data" => { "id" => "123", "isActive" => false } })
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, { "orderId" => "123", "status" => "FILLED" })
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