honeymaker 0.1.0 → 0.2.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/honeymaker-0.1.0.gem +0 -0
  3. data/lib/honeymaker/client.rb +74 -0
  4. data/lib/honeymaker/clients/binance.rb +240 -0
  5. data/lib/honeymaker/clients/binance_us.rb +9 -0
  6. data/lib/honeymaker/clients/bingx.rb +105 -0
  7. data/lib/honeymaker/clients/bitget.rb +128 -0
  8. data/lib/honeymaker/clients/bitmart.rb +117 -0
  9. data/lib/honeymaker/clients/bitrue.rb +114 -0
  10. data/lib/honeymaker/clients/bitvavo.rb +136 -0
  11. data/lib/honeymaker/clients/bybit.rb +136 -0
  12. data/lib/honeymaker/clients/coinbase.rb +184 -0
  13. data/lib/honeymaker/clients/gemini.rb +81 -0
  14. data/lib/honeymaker/clients/hyperliquid.rb +42 -0
  15. data/lib/honeymaker/clients/kraken.rb +132 -0
  16. data/lib/honeymaker/clients/kucoin.rb +147 -0
  17. data/lib/honeymaker/clients/mexc.rb +136 -0
  18. data/lib/honeymaker/exchanges/binance.rb +11 -17
  19. data/lib/honeymaker/exchanges/bitget.rb +2 -2
  20. data/lib/honeymaker/exchanges/bitrue.rb +6 -16
  21. data/lib/honeymaker/exchanges/bitvavo.rb +2 -2
  22. data/lib/honeymaker/exchanges/bybit.rb +2 -2
  23. data/lib/honeymaker/exchanges/coinbase.rb +5 -9
  24. data/lib/honeymaker/exchanges/gemini.rb +4 -6
  25. data/lib/honeymaker/exchanges/kraken.rb +5 -9
  26. data/lib/honeymaker/exchanges/kucoin.rb +2 -2
  27. data/lib/honeymaker/exchanges/mexc.rb +11 -17
  28. data/lib/honeymaker/utils.rb +9 -0
  29. data/lib/honeymaker/version.rb +1 -1
  30. data/lib/honeymaker.rb +38 -0
  31. data/test/fixtures/bingx_symbols.json +26 -0
  32. data/test/fixtures/bitget_symbols.json +28 -0
  33. data/test/fixtures/bitmart_symbols.json +26 -0
  34. data/test/fixtures/bitrue_exchange_info.json +34 -0
  35. data/test/fixtures/bitvavo_markets.json +16 -0
  36. data/test/fixtures/bybit_instruments.json +23 -0
  37. data/test/fixtures/coinbase_products.json +24 -0
  38. data/test/fixtures/gemini_symbol_detail.json +9 -0
  39. data/test/fixtures/gemini_symbols.json +1 -0
  40. data/test/fixtures/hyperliquid_spot_meta.json +12 -0
  41. data/test/fixtures/kucoin_symbols.json +17 -0
  42. data/test/fixtures/mexc_exchange_info.json +40 -0
  43. data/test/honeymaker/client_test.rb +53 -0
  44. data/test/honeymaker/clients/binance_client_test.rb +80 -0
  45. data/test/honeymaker/clients/binance_us_client_test.rb +25 -0
  46. data/test/honeymaker/clients/bingx_client_test.rb +64 -0
  47. data/test/honeymaker/clients/bitget_client_test.rb +85 -0
  48. data/test/honeymaker/clients/bitmart_client_test.rb +78 -0
  49. data/test/honeymaker/clients/bitrue_client_test.rb +63 -0
  50. data/test/honeymaker/clients/bitvavo_client_test.rb +87 -0
  51. data/test/honeymaker/clients/bybit_client_test.rb +84 -0
  52. data/test/honeymaker/clients/coinbase_client_test.rb +118 -0
  53. data/test/honeymaker/clients/gemini_client_test.rb +71 -0
  54. data/test/honeymaker/clients/honeymaker_client_registry_test.rb +44 -0
  55. data/test/honeymaker/clients/hyperliquid_client_test.rb +53 -0
  56. data/test/honeymaker/clients/kraken_client_test.rb +70 -0
  57. data/test/honeymaker/clients/kucoin_client_test.rb +88 -0
  58. data/test/honeymaker/clients/mexc_client_test.rb +75 -0
  59. data/test/honeymaker/exchanges/binance_us_test.rb +40 -0
  60. data/test/honeymaker/exchanges/bingx_test.rb +53 -0
  61. data/test/honeymaker/exchanges/bitget_test.rb +52 -0
  62. data/test/honeymaker/exchanges/bitmart_test.rb +52 -0
  63. data/test/honeymaker/exchanges/bitrue_test.rb +53 -0
  64. data/test/honeymaker/exchanges/bitvavo_test.rb +52 -0
  65. data/test/honeymaker/exchanges/bybit_test.rb +43 -0
  66. data/test/honeymaker/exchanges/coinbase_test.rb +52 -0
  67. data/test/honeymaker/exchanges/gemini_test.rb +48 -0
  68. data/test/honeymaker/exchanges/hyperliquid_test.rb +52 -0
  69. data/test/honeymaker/exchanges/kucoin_test.rb +43 -0
  70. data/test/honeymaker/exchanges/mexc_test.rb +64 -0
  71. data/test/honeymaker/utils_test.rb +38 -0
  72. data/test/test_helper.rb +1 -0
  73. metadata +74 -3
@@ -9,29 +9,23 @@ module Honeymaker
9
9
  with_rescue do
10
10
  response = connection.get("/api/v3/exchangeInfo")
11
11
 
12
- response.body["symbols"].map do |product|
13
- ticker = product["symbol"]
14
- status = product["status"]
15
-
16
- filters = product["filters"]
17
- price_filter = filters.find { |f| f["filterType"] == "PRICE_FILTER" }
18
- lot_size_filter = filters.find { |f| f["filterType"] == "LOT_SIZE" }
19
- notional_filter = filters.find { |f| %w[NOTIONAL MIN_NOTIONAL].include?(f["filterType"]) }
12
+ response.body["symbols"].filter_map do |product|
13
+ f = Utils.parse_filters(product["filters"])
20
14
 
21
15
  {
22
- ticker: ticker,
16
+ ticker: product["symbol"],
23
17
  base: product["baseAsset"],
24
18
  quote: product["quoteAsset"],
25
- minimum_base_size: lot_size_filter&.[]("minQty"),
26
- minimum_quote_size: notional_filter&.[]("minNotional"),
27
- maximum_base_size: lot_size_filter&.[]("maxQty"),
28
- maximum_quote_size: notional_filter&.[]("maxNotional"),
29
- base_decimals: lot_size_filter ? Utils.decimals(lot_size_filter["stepSize"]) : product["baseAssetPrecision"],
19
+ minimum_base_size: f[:lot_size]&.[]("minQty"),
20
+ minimum_quote_size: f[:notional]&.[]("minNotional"),
21
+ maximum_base_size: f[:lot_size]&.[]("maxQty"),
22
+ maximum_quote_size: f[:notional]&.[]("maxNotional"),
23
+ base_decimals: f[:lot_size] ? Utils.decimals(f[:lot_size]["stepSize"]) : product["baseAssetPrecision"],
30
24
  quote_decimals: product["quoteAssetPrecision"],
31
- price_decimals: price_filter ? Utils.decimals(price_filter["tickSize"]) : product["quotePrecision"],
32
- available: status == "TRADING"
25
+ price_decimals: f[:price] ? Utils.decimals(f[:price]["tickSize"]) : product["quotePrecision"],
26
+ available: product["status"] == "TRADING"
33
27
  }
34
- end.compact
28
+ end
35
29
  end
36
30
  end
37
31
 
@@ -3,9 +3,18 @@
3
3
  module Honeymaker
4
4
  module Utils
5
5
  def self.decimals(num)
6
+ return 0 if num.nil?
6
7
  str = num.to_s.sub(/\.?0+$/, "")
7
8
  return 0 unless str.include?(".")
8
9
  str.split(".").last.length
9
10
  end
11
+
12
+ def self.parse_filters(filters)
13
+ {
14
+ price: filters.find { |f| f["filterType"] == "PRICE_FILTER" },
15
+ lot_size: filters.find { |f| f["filterType"] == "LOT_SIZE" },
16
+ notional: filters.find { |f| %w[NOTIONAL MIN_NOTIONAL].include?(f["filterType"]) }
17
+ }
18
+ end
10
19
  end
11
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Honeymaker
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/honeymaker.rb CHANGED
@@ -8,6 +8,21 @@ require_relative "honeymaker/version"
8
8
  require_relative "honeymaker/result"
9
9
  require_relative "honeymaker/utils"
10
10
  require_relative "honeymaker/exchange"
11
+ require_relative "honeymaker/client"
12
+ require_relative "honeymaker/clients/binance"
13
+ require_relative "honeymaker/clients/binance_us"
14
+ require_relative "honeymaker/clients/kraken"
15
+ require_relative "honeymaker/clients/coinbase"
16
+ require_relative "honeymaker/clients/bybit"
17
+ require_relative "honeymaker/clients/mexc"
18
+ require_relative "honeymaker/clients/bitget"
19
+ require_relative "honeymaker/clients/kucoin"
20
+ require_relative "honeymaker/clients/bitvavo"
21
+ require_relative "honeymaker/clients/gemini"
22
+ require_relative "honeymaker/clients/bingx"
23
+ require_relative "honeymaker/clients/bitrue"
24
+ require_relative "honeymaker/clients/bitmart"
25
+ require_relative "honeymaker/clients/hyperliquid"
11
26
  require_relative "honeymaker/exchanges/binance"
12
27
  require_relative "honeymaker/exchanges/binance_us"
13
28
  require_relative "honeymaker/exchanges/kraken"
@@ -43,9 +58,32 @@ module Honeymaker
43
58
  "bitmart" => Exchanges::BitMart
44
59
  }.freeze
45
60
 
61
+ CLIENTS = {
62
+ "binance" => Clients::Binance,
63
+ "binance_us" => Clients::BinanceUs,
64
+ "kraken" => Clients::Kraken,
65
+ "coinbase" => Clients::Coinbase,
66
+ "bybit" => Clients::Bybit,
67
+ "mexc" => Clients::Mexc,
68
+ "bitget" => Clients::Bitget,
69
+ "kucoin" => Clients::Kucoin,
70
+ "bitvavo" => Clients::Bitvavo,
71
+ "gemini" => Clients::Gemini,
72
+ "bingx" => Clients::BingX,
73
+ "bitrue" => Clients::Bitrue,
74
+ "bitmart" => Clients::BitMart,
75
+ "hyperliquid" => Clients::Hyperliquid
76
+ }.freeze
77
+
46
78
  def self.exchange(name)
47
79
  klass = EXCHANGES[name.to_s]
48
80
  raise Error, "Unknown exchange: #{name}" unless klass
49
81
  klass.new
50
82
  end
83
+
84
+ def self.client(name, **kwargs)
85
+ klass = CLIENTS[name.to_s]
86
+ raise Error, "Unknown exchange: #{name}" unless klass
87
+ klass.new(**kwargs)
88
+ end
51
89
  end
@@ -0,0 +1,26 @@
1
+ {
2
+ "data": {
3
+ "symbols": [
4
+ {
5
+ "symbol": "BTC-USDT",
6
+ "minQty": "0.00001",
7
+ "maxQty": "500",
8
+ "minNotional": "1",
9
+ "maxNotional": "5000000",
10
+ "stepSize": "0.00001",
11
+ "tickSize": "0.01",
12
+ "status": 1
13
+ },
14
+ {
15
+ "symbol": "INVALID",
16
+ "minQty": 1,
17
+ "maxQty": 1000,
18
+ "minNotional": 1,
19
+ "maxNotional": 10000,
20
+ "stepSize": "1",
21
+ "tickSize": "0.01",
22
+ "status": 1
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "symbol": "BTCUSDT",
5
+ "baseCoin": "BTC",
6
+ "quoteCoin": "USDT",
7
+ "minTradeAmount": "0.00001",
8
+ "minTradeUSDT": "5",
9
+ "maxTradeAmount": "9000",
10
+ "quantityPrecision": "6",
11
+ "quotePrecision": "8",
12
+ "pricePrecision": "2",
13
+ "status": "online"
14
+ },
15
+ {
16
+ "symbol": "ETHUSDT",
17
+ "baseCoin": "ETH",
18
+ "quoteCoin": "USDT",
19
+ "minTradeAmount": "0.001",
20
+ "minTradeUSDT": "5",
21
+ "maxTradeAmount": "5000",
22
+ "quantityPrecision": "4",
23
+ "quotePrecision": "8",
24
+ "pricePrecision": "2",
25
+ "status": "offline"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "data": {
3
+ "symbols": [
4
+ {
5
+ "symbol": "BTC_USDT",
6
+ "base_currency": "BTC",
7
+ "quote_currency": "USDT",
8
+ "base_min_size": "0.00001",
9
+ "min_buy_amount": "5",
10
+ "quote_increment": "0.01",
11
+ "price_max_precision": 2,
12
+ "trade_status": "trading"
13
+ },
14
+ {
15
+ "symbol": "ETH_USDT",
16
+ "base_currency": "ETH",
17
+ "quote_currency": "USDT",
18
+ "base_min_size": "0.001",
19
+ "min_buy_amount": "5",
20
+ "quote_increment": "0.0001",
21
+ "price_max_precision": 4,
22
+ "trade_status": "pre-trade"
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "symbols": [
3
+ {
4
+ "symbol": "BTCUSDT",
5
+ "baseAsset": "btc",
6
+ "quoteAsset": "usdt",
7
+ "baseAssetPrecision": 8,
8
+ "quotePrecision": 2,
9
+ "status": "TRADING",
10
+ "filters": [
11
+ {
12
+ "filterType": "PRICE_FILTER",
13
+ "tickSize": "0.01"
14
+ },
15
+ {
16
+ "filterType": "LOT_SIZE",
17
+ "minQty": "0.00001",
18
+ "maxQty": "9000",
19
+ "stepSize": "0.00001",
20
+ "minVal": "5"
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "symbol": "ETHUSDT",
26
+ "baseAsset": "eth",
27
+ "quoteAsset": "usdt",
28
+ "baseAssetPrecision": 6,
29
+ "quotePrecision": 2,
30
+ "status": "TRADING",
31
+ "filters": []
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "market": "BTC-EUR",
4
+ "status": "trading",
5
+ "minOrderInBaseAsset": "0.00001",
6
+ "minOrderInQuoteAsset": "5",
7
+ "pricePrecision": 5
8
+ },
9
+ {
10
+ "market": "ETH-EUR",
11
+ "status": "halted",
12
+ "minOrderInBaseAsset": "0.001",
13
+ "minOrderInQuoteAsset": "5",
14
+ "pricePrecision": 4
15
+ }
16
+ ]
@@ -0,0 +1,23 @@
1
+ {
2
+ "result": {
3
+ "list": [
4
+ {
5
+ "symbol": "BTCUSDT",
6
+ "baseCoin": "BTC",
7
+ "quoteCoin": "USDT",
8
+ "status": "Trading",
9
+ "lotSizeFilter": {
10
+ "minOrderQty": "0.000048",
11
+ "maxOrderQty": "71.73956243",
12
+ "minOrderAmt": "1",
13
+ "maxOrderAmt": "4000000",
14
+ "basePrecision": "0.000001",
15
+ "quotePrecision": "0.00000001"
16
+ },
17
+ "priceFilter": {
18
+ "tickSize": "0.01"
19
+ }
20
+ }
21
+ ]
22
+ }
23
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "products": [
3
+ {
4
+ "product_id": "BTC-USD",
5
+ "base_increment": "0.00000001",
6
+ "quote_increment": "0.01",
7
+ "price_increment": "0.01",
8
+ "base_min_size": "0.00000001",
9
+ "quote_min_size": "1",
10
+ "base_max_size": "3400",
11
+ "quote_max_size": "250000000"
12
+ },
13
+ {
14
+ "product_id": "RENDER-USD",
15
+ "base_increment": "0.01",
16
+ "quote_increment": "0.01",
17
+ "price_increment": "0.01",
18
+ "base_min_size": "0.01",
19
+ "quote_min_size": "1",
20
+ "base_max_size": "10000",
21
+ "quote_max_size": "250000000"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "symbol": "BTCUSD",
3
+ "base_currency": "btc",
4
+ "quote_currency": "usd",
5
+ "tick_size": "0.00000001",
6
+ "quote_increment": "0.01",
7
+ "min_order_size": "0.00001",
8
+ "status": "open"
9
+ }
@@ -0,0 +1 @@
1
+ ["btcusd", "ethusd"]
@@ -0,0 +1,12 @@
1
+ {
2
+ "tokens": [
3
+ { "name": "PURR", "index": 0, "szDecimals": 2 },
4
+ { "name": "USDC", "index": 1, "szDecimals": 6 }
5
+ ],
6
+ "universe": [
7
+ {
8
+ "name": "PURR/USDC",
9
+ "tokens": [0, 1]
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "symbol": "BTC-USDT",
5
+ "baseCurrency": "BTC",
6
+ "quoteCurrency": "USDT",
7
+ "baseMinSize": "0.00001",
8
+ "quoteMinSize": "0.1",
9
+ "baseMaxSize": "10000000000",
10
+ "quoteMaxSize": "99999999",
11
+ "baseIncrement": "0.00000001",
12
+ "quoteIncrement": "0.000001",
13
+ "priceIncrement": "0.1",
14
+ "enableTrading": true
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "symbols": [
3
+ {
4
+ "symbol": "BTCUSDT",
5
+ "status": "TRADING",
6
+ "baseAsset": "BTC",
7
+ "quoteAsset": "USDT",
8
+ "baseAssetPrecision": 8,
9
+ "quoteAssetPrecision": 8,
10
+ "quotePrecision": 2,
11
+ "filters": [
12
+ {
13
+ "filterType": "PRICE_FILTER",
14
+ "tickSize": "0.01"
15
+ },
16
+ {
17
+ "filterType": "LOT_SIZE",
18
+ "minQty": "0.00001",
19
+ "maxQty": "9000",
20
+ "stepSize": "0.00001"
21
+ },
22
+ {
23
+ "filterType": "MIN_NOTIONAL",
24
+ "minNotional": "5",
25
+ "maxNotional": "9000000"
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "symbol": "ETHUSDT",
31
+ "status": "HALT",
32
+ "baseAsset": "ETH",
33
+ "quoteAsset": "USDT",
34
+ "baseAssetPrecision": 6,
35
+ "quoteAssetPrecision": 8,
36
+ "quotePrecision": 2,
37
+ "filters": []
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class Honeymaker::ClientTest < Minitest::Test
6
+ def test_default_options
7
+ assert_equal 5, Honeymaker::Client::OPTIONS[:request][:open_timeout]
8
+ assert_equal 30, Honeymaker::Client::OPTIONS[:request][:read_timeout]
9
+ assert_equal 10, Honeymaker::Client::OPTIONS[:request][:write_timeout]
10
+ end
11
+
12
+ def test_authenticated_with_credentials
13
+ client = Honeymaker::Client.new(api_key: "key", api_secret: "secret")
14
+ assert client.send(:authenticated?)
15
+ end
16
+
17
+ def test_not_authenticated_without_credentials
18
+ client = Honeymaker::Client.new
19
+ refute client.send(:authenticated?)
20
+ end
21
+
22
+ def test_not_authenticated_with_empty_credentials
23
+ client = Honeymaker::Client.new(api_key: "", api_secret: "")
24
+ refute client.send(:authenticated?)
25
+ end
26
+
27
+ def test_with_rescue_wraps_success
28
+ client = Honeymaker::Client.new
29
+ result = client.send(:with_rescue) { { "status" => "ok" } }
30
+ assert result.success?
31
+ assert_equal({ "status" => "ok" }, result.data)
32
+ end
33
+
34
+ def test_with_rescue_wraps_faraday_error
35
+ client = Honeymaker::Client.new
36
+ result = client.send(:with_rescue) { raise Faraday::TimeoutError, "timeout" }
37
+ assert result.failure?
38
+ end
39
+
40
+ def test_with_rescue_wraps_standard_error
41
+ client = Honeymaker::Client.new
42
+ result = client.send(:with_rescue) { raise StandardError, "boom" }
43
+ assert result.failure?
44
+ assert_equal ["boom"], result.errors
45
+ end
46
+
47
+ def test_hmac_sha256
48
+ client = Honeymaker::Client.new
49
+ sig = client.send(:hmac_sha256, "secret", "data")
50
+ expected = OpenSSL::HMAC.hexdigest("sha256", "secret", "data")
51
+ assert_equal expected, sig
52
+ end
53
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class Honeymaker::Clients::BinanceTest < Minitest::Test
6
+ def setup
7
+ @client = Honeymaker::Clients::Binance.new(api_key: "test_key", api_secret: "test_secret")
8
+ end
9
+
10
+ def test_url
11
+ assert_equal "https://api.binance.com", Honeymaker::Clients::Binance::URL
12
+ end
13
+
14
+ def test_exchange_information
15
+ stub_connection(:get, { "symbols" => [{ "symbol" => "BTCUSDT" }] })
16
+ result = @client.exchange_information
17
+ assert result.success?
18
+ assert_equal "BTCUSDT", result.data["symbols"].first["symbol"]
19
+ end
20
+
21
+ def test_new_order
22
+ stub_connection(:post, { "orderId" => 123, "status" => "FILLED" })
23
+ result = @client.new_order(symbol: "BTCUSDT", side: "BUY", type: "MARKET", quantity: "0.001")
24
+ assert result.success?
25
+ assert_equal 123, result.data["orderId"]
26
+ end
27
+
28
+ def test_query_order
29
+ stub_connection(:get, { "orderId" => 123, "status" => "FILLED" })
30
+ result = @client.query_order(symbol: "BTCUSDT", order_id: 123)
31
+ assert result.success?
32
+ end
33
+
34
+ def test_account_information
35
+ stub_connection(:get, { "balances" => [{ "asset" => "BTC", "free" => "0.5" }] })
36
+ result = @client.account_information
37
+ assert result.success?
38
+ assert_equal "BTC", result.data["balances"].first["asset"]
39
+ end
40
+
41
+ def test_cancel_order
42
+ stub_connection(:delete, { "orderId" => 123, "status" => "CANCELED" })
43
+ result = @client.cancel_order(symbol: "BTCUSDT", order_id: 123)
44
+ assert result.success?
45
+ end
46
+
47
+ def test_handles_api_error
48
+ connection = stub
49
+ connection.stubs(:get).raises(Faraday::ServerError.new("500", { status: 500, body: "Server Error" }))
50
+ @client.instance_variable_set(:@connection, connection)
51
+ result = @client.exchange_information
52
+ assert result.failure?
53
+ end
54
+
55
+ def test_headers_include_api_key
56
+ headers = @client.send(:headers)
57
+ assert_equal "test_key", headers[:"X-MBX-APIKEY"]
58
+ end
59
+
60
+ def test_headers_without_auth
61
+ client = Honeymaker::Clients::Binance.new
62
+ headers = client.send(:headers)
63
+ refute headers.key?(:"X-MBX-APIKEY")
64
+ end
65
+
66
+ def test_sign_params
67
+ sig = @client.send(:sign_params, { symbol: "BTCUSDT", timestamp: 1234567890 })
68
+ assert sig.is_a?(String)
69
+ assert_equal 64, sig.length
70
+ end
71
+
72
+ private
73
+
74
+ def stub_connection(method, body)
75
+ response = stub(body: body)
76
+ connection = stub
77
+ connection.stubs(method).returns(response)
78
+ @client.instance_variable_set(:@connection, connection)
79
+ end
80
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class Honeymaker::Clients::BinanceUsTest < Minitest::Test
6
+ def test_inherits_from_binance
7
+ client = Honeymaker::Clients::BinanceUs.new
8
+ assert_kind_of Honeymaker::Clients::Binance, client
9
+ end
10
+
11
+ def test_url
12
+ assert_equal "https://api.binance.us", Honeymaker::Clients::BinanceUs::URL
13
+ end
14
+
15
+ def test_exchange_information
16
+ client = Honeymaker::Clients::BinanceUs.new(api_key: "k", api_secret: "s")
17
+ response = stub(body: { "symbols" => [] })
18
+ connection = stub
19
+ connection.stubs(:get).returns(response)
20
+ client.instance_variable_set(:@connection, connection)
21
+
22
+ result = client.exchange_information
23
+ assert result.success?
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class Honeymaker::Clients::BingXTest < Minitest::Test
6
+ def setup
7
+ @client = Honeymaker::Clients::BingX.new(api_key: "test_key", api_secret: "test_secret")
8
+ end
9
+
10
+ def test_url
11
+ assert_equal "https://open-api.bingx.com", Honeymaker::Clients::BingX::URL
12
+ end
13
+
14
+ def test_get_symbols
15
+ stub_connection(:get, { "data" => { "symbols" => [{ "symbol" => "BTC-USDT" }] } })
16
+ result = @client.get_symbols
17
+ assert result.success?
18
+ end
19
+
20
+ def test_get_ticker
21
+ stub_connection(:get, { "data" => [{ "symbol" => "BTC-USDT" }] })
22
+ result = @client.get_ticker(symbol: "BTC-USDT")
23
+ assert result.success?
24
+ end
25
+
26
+ def test_get_balances
27
+ stub_connection(:get, { "data" => { "balances" => [{ "asset" => "BTC" }] } })
28
+ result = @client.get_balances
29
+ assert result.success?
30
+ end
31
+
32
+ def test_place_order
33
+ stub_connection(:post, { "data" => { "orderId" => "123" } })
34
+ result = @client.place_order(symbol: "BTC-USDT", side: "BUY", type: "MARKET", quantity: "0.001")
35
+ assert result.success?
36
+ end
37
+
38
+ def test_get_order
39
+ stub_connection(:get, { "data" => { "orderId" => "123" } })
40
+ result = @client.get_order(symbol: "BTC-USDT", order_id: "123")
41
+ assert result.success?
42
+ end
43
+
44
+ def test_cancel_order
45
+ stub_connection(:post, { "data" => { "orderId" => "123" } })
46
+ result = @client.cancel_order(symbol: "BTC-USDT", order_id: "123")
47
+ assert result.success?
48
+ end
49
+
50
+ def test_withdraw
51
+ stub_connection(:post, { "data" => { "id" => "w1" } })
52
+ result = @client.withdraw(coin: "BTC", address: "addr", amount: "0.1")
53
+ assert result.success?
54
+ end
55
+
56
+ private
57
+
58
+ def stub_connection(method, body)
59
+ response = stub(body: body)
60
+ connection = stub
61
+ connection.stubs(method).returns(response)
62
+ @client.instance_variable_set(:@connection, connection)
63
+ end
64
+ end