honeymaker 0.1.0 → 0.3.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 +4 -0
- data/lib/honeymaker/client.rb +96 -0
- data/lib/honeymaker/clients/binance.rb +288 -0
- data/lib/honeymaker/clients/binance_us.rb +9 -0
- data/lib/honeymaker/clients/bingx.rb +136 -0
- data/lib/honeymaker/clients/bitget.rb +157 -0
- data/lib/honeymaker/clients/bitmart.rb +146 -0
- data/lib/honeymaker/clients/bitrue.rb +130 -0
- data/lib/honeymaker/clients/bitvavo.rb +160 -0
- data/lib/honeymaker/clients/bybit.rb +166 -0
- data/lib/honeymaker/clients/coinbase.rb +219 -0
- data/lib/honeymaker/clients/gemini.rb +99 -0
- data/lib/honeymaker/clients/hyperliquid.rb +65 -0
- data/lib/honeymaker/clients/kraken.rb +162 -0
- data/lib/honeymaker/clients/kucoin.rb +180 -0
- data/lib/honeymaker/clients/mexc.rb +166 -0
- data/lib/honeymaker/exchanges/binance.rb +11 -17
- data/lib/honeymaker/exchanges/bitget.rb +2 -2
- data/lib/honeymaker/exchanges/bitrue.rb +6 -16
- data/lib/honeymaker/exchanges/bitvavo.rb +2 -2
- data/lib/honeymaker/exchanges/bybit.rb +2 -2
- data/lib/honeymaker/exchanges/coinbase.rb +5 -9
- data/lib/honeymaker/exchanges/gemini.rb +4 -6
- data/lib/honeymaker/exchanges/kraken.rb +5 -9
- data/lib/honeymaker/exchanges/kucoin.rb +2 -2
- data/lib/honeymaker/exchanges/mexc.rb +11 -17
- data/lib/honeymaker/utils.rb +9 -0
- data/lib/honeymaker/version.rb +1 -1
- data/lib/honeymaker.rb +38 -0
- data/test/fixtures/bingx_symbols.json +26 -0
- data/test/fixtures/bitget_symbols.json +28 -0
- data/test/fixtures/bitmart_symbols.json +26 -0
- data/test/fixtures/bitrue_exchange_info.json +34 -0
- data/test/fixtures/bitvavo_markets.json +16 -0
- data/test/fixtures/bybit_instruments.json +23 -0
- data/test/fixtures/coinbase_products.json +24 -0
- data/test/fixtures/gemini_symbol_detail.json +9 -0
- data/test/fixtures/gemini_symbols.json +1 -0
- data/test/fixtures/hyperliquid_spot_meta.json +12 -0
- data/test/fixtures/kucoin_symbols.json +17 -0
- data/test/fixtures/mexc_exchange_info.json +40 -0
- data/test/honeymaker/client_test.rb +53 -0
- data/test/honeymaker/clients/binance_client_test.rb +101 -0
- data/test/honeymaker/clients/binance_us_client_test.rb +25 -0
- data/test/honeymaker/clients/bingx_client_test.rb +82 -0
- data/test/honeymaker/clients/bitget_client_test.rb +109 -0
- data/test/honeymaker/clients/bitmart_client_test.rb +96 -0
- data/test/honeymaker/clients/bitrue_client_test.rb +69 -0
- data/test/honeymaker/clients/bitvavo_client_test.rb +105 -0
- data/test/honeymaker/clients/bybit_client_test.rb +102 -0
- data/test/honeymaker/clients/coinbase_client_test.rb +132 -0
- data/test/honeymaker/clients/gemini_client_test.rb +83 -0
- data/test/honeymaker/clients/honeymaker_client_registry_test.rb +44 -0
- data/test/honeymaker/clients/hyperliquid_client_test.rb +65 -0
- data/test/honeymaker/clients/kraken_client_test.rb +84 -0
- data/test/honeymaker/clients/kucoin_client_test.rb +106 -0
- data/test/honeymaker/clients/mexc_client_test.rb +93 -0
- data/test/honeymaker/clients/validation_test.rb +182 -0
- data/test/honeymaker/exchanges/binance_us_test.rb +40 -0
- data/test/honeymaker/exchanges/bingx_test.rb +53 -0
- data/test/honeymaker/exchanges/bitget_test.rb +52 -0
- data/test/honeymaker/exchanges/bitmart_test.rb +52 -0
- data/test/honeymaker/exchanges/bitrue_test.rb +53 -0
- data/test/honeymaker/exchanges/bitvavo_test.rb +52 -0
- data/test/honeymaker/exchanges/bybit_test.rb +43 -0
- data/test/honeymaker/exchanges/coinbase_test.rb +52 -0
- data/test/honeymaker/exchanges/gemini_test.rb +48 -0
- data/test/honeymaker/exchanges/hyperliquid_test.rb +52 -0
- data/test/honeymaker/exchanges/kucoin_test.rb +43 -0
- data/test/honeymaker/exchanges/mexc_test.rb +64 -0
- data/test/honeymaker/utils_test.rb +38 -0
- data/test/test_helper.rb +1 -0
- metadata +74 -3
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
def test_get_trade_fills
|
|
57
|
+
stub_connection(:get, { "data" => [{ "tradeId" => "t1" }] })
|
|
58
|
+
result = @client.get_trade_fills(symbol: "BTC-USDT")
|
|
59
|
+
assert result.success?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_deposit_history
|
|
63
|
+
stub_connection(:get, [{ "coin" => "BTC" }])
|
|
64
|
+
result = @client.deposit_history
|
|
65
|
+
assert result.success?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_withdraw_history
|
|
69
|
+
stub_connection(:get, [{ "coin" => "ETH" }])
|
|
70
|
+
result = @client.withdraw_history
|
|
71
|
+
assert result.success?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def stub_connection(method, body)
|
|
77
|
+
response = stub(body: body)
|
|
78
|
+
connection = stub
|
|
79
|
+
connection.stubs(method).returns(response)
|
|
80
|
+
@client.instance_variable_set(:@connection, connection)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::BitgetTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@client = Honeymaker::Clients::Bitget.new(
|
|
8
|
+
api_key: "test_key", api_secret: "test_secret", passphrase: "test_pass"
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_url
|
|
13
|
+
assert_equal "https://api.bitget.com", Honeymaker::Clients::Bitget::URL
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_accepts_passphrase
|
|
17
|
+
assert_equal "test_pass", @client.passphrase
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_get_coins
|
|
21
|
+
stub_connection(:get, { "data" => [{ "coin" => "BTC" }] })
|
|
22
|
+
result = @client.get_coins
|
|
23
|
+
assert result.success?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_get_symbols
|
|
27
|
+
stub_connection(:get, { "data" => [{ "symbol" => "BTCUSDT" }] })
|
|
28
|
+
result = @client.get_symbols
|
|
29
|
+
assert result.success?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_get_account_assets
|
|
33
|
+
stub_connection(:get, { "data" => [{ "coin" => "BTC", "available" => "0.5" }] })
|
|
34
|
+
result = @client.get_account_assets
|
|
35
|
+
assert result.success?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_place_order
|
|
39
|
+
stub_connection(:post, { "data" => { "orderId" => "123" } })
|
|
40
|
+
result = @client.place_order(symbol: "BTCUSDT", side: "buy", order_type: "market", size: "0.001")
|
|
41
|
+
assert result.success?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_place_order_with_quote_size
|
|
45
|
+
stub_connection(:post, { "data" => { "orderId" => "456" } })
|
|
46
|
+
result = @client.place_order(symbol: "BTCUSDT", side: "buy", order_type: "market", quote_size: "100")
|
|
47
|
+
assert result.success?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_get_order
|
|
51
|
+
stub_connection(:get, { "data" => [{ "orderId" => "123" }] })
|
|
52
|
+
result = @client.get_order(order_id: "123")
|
|
53
|
+
assert result.success?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_cancel_order
|
|
57
|
+
stub_connection(:post, { "data" => { "orderId" => "123" } })
|
|
58
|
+
result = @client.cancel_order(symbol: "BTCUSDT", order_id: "123")
|
|
59
|
+
assert result.success?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_get_candles
|
|
63
|
+
stub_connection(:get, { "data" => [] })
|
|
64
|
+
result = @client.get_candles(symbol: "BTCUSDT", granularity: "1min")
|
|
65
|
+
assert result.success?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_withdraw
|
|
69
|
+
stub_connection(:post, { "data" => { "orderId" => "w1" } })
|
|
70
|
+
result = @client.withdraw(coin: "BTC", address: "addr", size: "0.1")
|
|
71
|
+
assert result.success?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_get_fills
|
|
75
|
+
stub_connection(:get, { "data" => [{ "tradeId" => "t1" }] })
|
|
76
|
+
result = @client.get_fills(symbol: "BTCUSDT")
|
|
77
|
+
assert result.success?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_deposit_list
|
|
81
|
+
stub_connection(:get, { "data" => [{ "coin" => "BTC" }] })
|
|
82
|
+
result = @client.deposit_list
|
|
83
|
+
assert result.success?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_withdrawal_list
|
|
87
|
+
stub_connection(:get, { "data" => [{ "coin" => "ETH" }] })
|
|
88
|
+
result = @client.withdrawal_list
|
|
89
|
+
assert result.success?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_signed_headers_include_passphrase
|
|
93
|
+
ts = "1234567890"
|
|
94
|
+
headers = @client.send(:signed_headers, ts, "payload")
|
|
95
|
+
assert_equal "test_key", headers[:"ACCESS-KEY"]
|
|
96
|
+
assert_equal "test_pass", headers[:"ACCESS-PASSPHRASE"]
|
|
97
|
+
assert_equal ts, headers[:"ACCESS-TIMESTAMP"]
|
|
98
|
+
assert headers.key?(:"ACCESS-SIGN")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def stub_connection(method, body)
|
|
104
|
+
response = stub(body: body)
|
|
105
|
+
connection = stub
|
|
106
|
+
connection.stubs(method).returns(response)
|
|
107
|
+
@client.instance_variable_set(:@connection, connection)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::BitMartTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@client = Honeymaker::Clients::BitMart.new(
|
|
8
|
+
api_key: "test_key", api_secret: "test_secret", memo: "test_memo"
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_url
|
|
13
|
+
assert_equal "https://api-cloud.bitmart.com", Honeymaker::Clients::BitMart::URL
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_accepts_memo
|
|
17
|
+
assert_equal "test_memo", @client.memo
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_get_symbols_details
|
|
21
|
+
stub_connection(:get, { "data" => { "symbols" => [{ "symbol" => "BTC_USDT" }] } })
|
|
22
|
+
result = @client.get_symbols_details
|
|
23
|
+
assert result.success?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_get_ticker
|
|
27
|
+
stub_connection(:get, { "data" => [{ "symbol" => "BTC_USDT" }] })
|
|
28
|
+
result = @client.get_ticker(symbol: "BTC_USDT")
|
|
29
|
+
assert result.success?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_get_wallet
|
|
33
|
+
stub_connection(:get, { "data" => { "wallet" => [{ "id" => "BTC" }] } })
|
|
34
|
+
result = @client.get_wallet
|
|
35
|
+
assert result.success?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_submit_order
|
|
39
|
+
stub_connection(:post, { "data" => { "order_id" => 123 } })
|
|
40
|
+
result = @client.submit_order(symbol: "BTC_USDT", side: "buy", type: "market", notional: "100")
|
|
41
|
+
assert result.success?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_get_order
|
|
45
|
+
stub_connection(:post, { "data" => { "order_id" => 123 } })
|
|
46
|
+
result = @client.get_order(order_id: "123")
|
|
47
|
+
assert result.success?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_cancel_order
|
|
51
|
+
stub_connection(:post, { "data" => true })
|
|
52
|
+
result = @client.cancel_order(symbol: "BTC_USDT", order_id: "123")
|
|
53
|
+
assert result.success?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_withdraw
|
|
57
|
+
stub_connection(:post, { "data" => { "withdraw_id" => "w1" } })
|
|
58
|
+
result = @client.withdraw(currency: "BTC", amount: "0.1", address: "addr")
|
|
59
|
+
assert result.success?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_get_trades
|
|
63
|
+
stub_connection(:get, { "data" => [{ "tradeId" => "t1" }] })
|
|
64
|
+
result = @client.get_trades(symbol: "BTC_USDT")
|
|
65
|
+
assert result.success?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_deposit_list
|
|
69
|
+
stub_connection(:get, { "data" => { "records" => [{ "currency" => "BTC" }] } })
|
|
70
|
+
result = @client.deposit_list
|
|
71
|
+
assert result.success?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_withdraw_list
|
|
75
|
+
stub_connection(:get, { "data" => { "records" => [{ "currency" => "ETH" }] } })
|
|
76
|
+
result = @client.withdraw_list
|
|
77
|
+
assert result.success?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_signed_headers_include_memo_in_signature
|
|
81
|
+
ts = "1234567890"
|
|
82
|
+
pre_sign = "#{ts}#test_memo#body"
|
|
83
|
+
headers = @client.send(:signed_headers, ts, pre_sign)
|
|
84
|
+
assert_equal "test_key", headers[:"X-BM-KEY"]
|
|
85
|
+
assert headers.key?(:"X-BM-SIGN")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def stub_connection(method, body)
|
|
91
|
+
response = stub(body: body)
|
|
92
|
+
connection = stub
|
|
93
|
+
connection.stubs(method).returns(response)
|
|
94
|
+
@client.instance_variable_set(:@connection, connection)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::BitrueTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@client = Honeymaker::Clients::Bitrue.new(api_key: "test_key", api_secret: "test_secret")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_url
|
|
11
|
+
assert_equal "https://openapi.bitrue.com", Honeymaker::Clients::Bitrue::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
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_symbol_price_ticker
|
|
21
|
+
stub_connection(:get, { "symbol" => "BTCUSDT", "price" => "50000" })
|
|
22
|
+
result = @client.symbol_price_ticker(symbol: "BTCUSDT")
|
|
23
|
+
assert result.success?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_account_information
|
|
27
|
+
stub_connection(:get, { "balances" => [{ "asset" => "BTC" }] })
|
|
28
|
+
result = @client.account_information
|
|
29
|
+
assert result.success?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_new_order
|
|
33
|
+
stub_connection(:post, { "orderId" => "123" })
|
|
34
|
+
result = @client.new_order(symbol: "BTCUSDT", side: "BUY", type: "MARKET", quantity: "0.001")
|
|
35
|
+
assert result.success?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_query_order
|
|
39
|
+
stub_connection(:get, { "orderId" => "123", "status" => "FILLED" })
|
|
40
|
+
result = @client.query_order(symbol: "BTCUSDT", order_id: "123")
|
|
41
|
+
assert result.success?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_cancel_order
|
|
45
|
+
stub_connection(:delete, { "orderId" => "123" })
|
|
46
|
+
result = @client.cancel_order(symbol: "BTCUSDT", order_id: "123")
|
|
47
|
+
assert result.success?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_account_trade_list
|
|
51
|
+
stub_connection(:get, [{ "symbol" => "BTCUSDT", "id" => 1 }])
|
|
52
|
+
result = @client.account_trade_list(symbol: "BTCUSDT")
|
|
53
|
+
assert result.success?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_headers_include_api_key
|
|
57
|
+
headers = @client.send(:auth_headers)
|
|
58
|
+
assert_equal "test_key", headers[:"X-MBX-APIKEY"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def stub_connection(method, body)
|
|
64
|
+
response = stub(body: body)
|
|
65
|
+
connection = stub
|
|
66
|
+
connection.stubs(method).returns(response)
|
|
67
|
+
@client.instance_variable_set(:@connection, connection)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::BitvavoTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@client = Honeymaker::Clients::Bitvavo.new(api_key: "test_key", api_secret: "test_secret")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_url
|
|
11
|
+
assert_equal "https://api.bitvavo.com", Honeymaker::Clients::Bitvavo::URL
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_get_markets
|
|
15
|
+
stub_connection(:get, [{ "market" => "BTC-EUR" }])
|
|
16
|
+
result = @client.get_markets
|
|
17
|
+
assert result.success?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_get_ticker_price
|
|
21
|
+
stub_connection(:get, [{ "market" => "BTC-EUR", "price" => "50000" }])
|
|
22
|
+
result = @client.get_ticker_price
|
|
23
|
+
assert result.success?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_get_balance
|
|
27
|
+
stub_connection(:get, [{ "symbol" => "BTC", "available" => "0.5" }])
|
|
28
|
+
result = @client.get_balance
|
|
29
|
+
assert result.success?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_place_order
|
|
33
|
+
stub_connection(:post, { "orderId" => "123" })
|
|
34
|
+
result = @client.place_order(market: "BTC-EUR", side: "buy", order_type: "market", amount_quote: "100")
|
|
35
|
+
assert result.success?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_get_order
|
|
39
|
+
stub_connection(:get, { "orderId" => "123", "status" => "filled" })
|
|
40
|
+
result = @client.get_order(market: "BTC-EUR", order_id: "123")
|
|
41
|
+
assert result.success?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_cancel_order
|
|
45
|
+
stub_connection(:delete, { "orderId" => "123" })
|
|
46
|
+
result = @client.cancel_order(market: "BTC-EUR", order_id: "123")
|
|
47
|
+
assert result.success?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_withdraw
|
|
51
|
+
stub_connection(:post, { "success" => true })
|
|
52
|
+
result = @client.withdraw(symbol: "BTC", amount: "0.1", address: "addr")
|
|
53
|
+
assert result.success?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_get_assets
|
|
57
|
+
stub_connection(:get, [{ "symbol" => "BTC" }])
|
|
58
|
+
result = @client.get_assets
|
|
59
|
+
assert result.success?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_get_trades
|
|
63
|
+
stub_connection(:get, [{ "id" => "t1", "side" => "buy" }])
|
|
64
|
+
result = @client.get_trades(market: "BTC-EUR")
|
|
65
|
+
assert result.success?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_get_deposit_history
|
|
69
|
+
stub_connection(:get, [{ "symbol" => "BTC", "amount" => "1.0" }])
|
|
70
|
+
result = @client.get_deposit_history
|
|
71
|
+
assert result.success?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_get_withdrawal_history
|
|
75
|
+
stub_connection(:get, [{ "symbol" => "ETH", "amount" => "2.0" }])
|
|
76
|
+
result = @client.get_withdrawal_history
|
|
77
|
+
assert result.success?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_signed_headers_include_access_window
|
|
81
|
+
ts = "1234567890"
|
|
82
|
+
headers = @client.send(:signed_headers, ts, "payload")
|
|
83
|
+
assert_equal "test_key", headers[:"BITVAVO-ACCESS-KEY"]
|
|
84
|
+
assert_equal "10000", headers[:"BITVAVO-ACCESS-WINDOW"]
|
|
85
|
+
assert headers.key?(:"BITVAVO-ACCESS-SIGNATURE")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_signing_payload_format
|
|
89
|
+
# Verify the signing payload matches Bitvavo's expected format
|
|
90
|
+
sig1 = @client.send(:hmac_sha256, "test_secret", "1234567890GET/v2/balance")
|
|
91
|
+
sig2 = @client.send(:hmac_sha256, "test_secret", "1234567890POST/v2/order{\"market\":\"BTC-EUR\"}")
|
|
92
|
+
assert sig1.is_a?(String)
|
|
93
|
+
assert sig2.is_a?(String)
|
|
94
|
+
refute_equal sig1, sig2
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def stub_connection(method, body)
|
|
100
|
+
response = stub(body: body)
|
|
101
|
+
connection = stub
|
|
102
|
+
connection.stubs(method).returns(response)
|
|
103
|
+
@client.instance_variable_set(:@connection, connection)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::BybitTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@client = Honeymaker::Clients::Bybit.new(api_key: "test_key", api_secret: "test_secret")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_url
|
|
11
|
+
assert_equal "https://api.bybit.com", Honeymaker::Clients::Bybit::URL
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_instruments_info
|
|
15
|
+
stub_connection(:get, { "result" => { "list" => [{ "symbol" => "BTCUSDT" }] } })
|
|
16
|
+
result = @client.instruments_info(category: "spot")
|
|
17
|
+
assert result.success?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_tickers
|
|
21
|
+
stub_connection(:get, { "result" => { "list" => [{ "lastPrice" => "50000" }] } })
|
|
22
|
+
result = @client.tickers(category: "spot", symbol: "BTCUSDT")
|
|
23
|
+
assert result.success?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_wallet_balance
|
|
27
|
+
stub_connection(:get, { "result" => { "list" => [{ "coin" => [{ "coin" => "BTC" }] }] } })
|
|
28
|
+
result = @client.wallet_balance(account_type: "UNIFIED")
|
|
29
|
+
assert result.success?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_create_order
|
|
33
|
+
stub_connection(:post, { "result" => { "orderId" => "123" } })
|
|
34
|
+
result = @client.create_order(category: "spot", symbol: "BTCUSDT", side: "Buy", order_type: "Market", qty: "0.001")
|
|
35
|
+
assert result.success?
|
|
36
|
+
assert_equal "123", result.data["result"]["orderId"]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_get_order
|
|
40
|
+
stub_connection(:get, { "result" => { "list" => [{ "orderId" => "123" }] } })
|
|
41
|
+
result = @client.get_order(category: "spot", order_id: "123")
|
|
42
|
+
assert result.success?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_cancel_order
|
|
46
|
+
stub_connection(:post, { "result" => { "orderId" => "123" } })
|
|
47
|
+
result = @client.cancel_order(category: "spot", symbol: "BTCUSDT", order_id: "123")
|
|
48
|
+
assert result.success?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_kline
|
|
52
|
+
stub_connection(:get, { "result" => { "list" => [] } })
|
|
53
|
+
result = @client.kline(category: "spot", symbol: "BTCUSDT", interval: "60")
|
|
54
|
+
assert result.success?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_withdraw
|
|
58
|
+
stub_connection(:post, { "result" => { "id" => "w1" } })
|
|
59
|
+
result = @client.withdraw(coin: "BTC", chain: "BTC", address: "addr", amount: "0.1")
|
|
60
|
+
assert result.success?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_execution_list
|
|
64
|
+
stub_connection(:get, { "result" => { "list" => [{ "execId" => "e1" }] } })
|
|
65
|
+
result = @client.execution_list(category: "spot")
|
|
66
|
+
assert result.success?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_deposit_records
|
|
70
|
+
stub_connection(:get, { "result" => { "rows" => [{ "coin" => "BTC" }] } })
|
|
71
|
+
result = @client.deposit_records
|
|
72
|
+
assert result.success?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_withdraw_records
|
|
76
|
+
stub_connection(:get, { "result" => { "rows" => [{ "coin" => "ETH" }] } })
|
|
77
|
+
result = @client.withdraw_records
|
|
78
|
+
assert result.success?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_signed_headers_include_api_key
|
|
82
|
+
headers = @client.send(:signed_headers, "GET", { foo: "bar" })
|
|
83
|
+
assert_equal "test_key", headers[:"X-BAPI-API-KEY"]
|
|
84
|
+
assert headers.key?(:"X-BAPI-SIGN")
|
|
85
|
+
assert headers.key?(:"X-BAPI-TIMESTAMP")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_unauthenticated_client
|
|
89
|
+
client = Honeymaker::Clients::Bybit.new
|
|
90
|
+
headers = client.send(:signed_headers, "GET", {})
|
|
91
|
+
refute headers.key?(:"X-BAPI-SIGN")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def stub_connection(method, body)
|
|
97
|
+
response = stub(body: body)
|
|
98
|
+
connection = stub
|
|
99
|
+
connection.stubs(method).returns(response)
|
|
100
|
+
@client.instance_variable_set(:@connection, connection)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class Honeymaker::Clients::CoinbaseTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
# Use ECDSA key for testing
|
|
8
|
+
@key = OpenSSL::PKey::EC.generate("prime256v1")
|
|
9
|
+
@client = Honeymaker::Clients::Coinbase.new(
|
|
10
|
+
api_key: "test_key",
|
|
11
|
+
api_secret: @key.to_pem
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_url
|
|
16
|
+
assert_equal "https://api.coinbase.com", Honeymaker::Clients::Coinbase::URL
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_get_order
|
|
20
|
+
stub_connection(:get, { "order" => { "order_id" => "abc", "status" => "FILLED" } })
|
|
21
|
+
result = @client.get_order(order_id: "abc")
|
|
22
|
+
assert result.success?
|
|
23
|
+
assert_equal "abc", result.data["order"]["order_id"]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_create_order
|
|
27
|
+
stub_connection(:post, { "success" => true, "order_id" => "xyz" })
|
|
28
|
+
result = @client.create_order(
|
|
29
|
+
client_order_id: "c1", product_id: "BTC-USD",
|
|
30
|
+
side: "BUY", order_configuration: { market_market_ioc: { quote_size: "100" } }
|
|
31
|
+
)
|
|
32
|
+
assert result.success?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_cancel_orders
|
|
36
|
+
stub_connection(:post, { "results" => [{ "success" => true }] })
|
|
37
|
+
result = @client.cancel_orders(order_ids: ["abc"])
|
|
38
|
+
assert result.success?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_list_public_products
|
|
42
|
+
stub_connection(:get, { "products" => [{ "product_id" => "BTC-USD" }] })
|
|
43
|
+
result = @client.list_public_products
|
|
44
|
+
assert result.success?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_get_public_product
|
|
48
|
+
stub_connection(:get, { "product_id" => "BTC-USD" })
|
|
49
|
+
result = @client.get_public_product(product_id: "BTC-USD")
|
|
50
|
+
assert result.success?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_get_api_key_permissions
|
|
54
|
+
stub_connection(:get, { "can_trade" => true })
|
|
55
|
+
result = @client.get_api_key_permissions
|
|
56
|
+
assert result.success?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_list_accounts
|
|
60
|
+
stub_connection(:get, { "accounts" => [] })
|
|
61
|
+
result = @client.list_accounts
|
|
62
|
+
assert result.success?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_send_money
|
|
66
|
+
stub_connection(:post, { "data" => { "id" => "tx1" } })
|
|
67
|
+
result = @client.send_money(account_id: "acc1", to: "addr", amount: "0.1", currency: "BTC")
|
|
68
|
+
assert result.success?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_list_fills
|
|
72
|
+
stub_connection(:get, { "fills" => [{ "trade_id" => "f1", "product_id" => "BTC-USD" }] })
|
|
73
|
+
result = @client.list_fills(product_id: "BTC-USD")
|
|
74
|
+
assert result.success?
|
|
75
|
+
assert_equal "f1", result.data["fills"].first["trade_id"]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_list_transactions
|
|
79
|
+
stub_connection(:get, { "data" => [{ "id" => "tx1", "type" => "send" }] })
|
|
80
|
+
result = @client.list_transactions(account_id: "acc1")
|
|
81
|
+
assert result.success?
|
|
82
|
+
assert_equal "tx1", result.data["data"].first["id"]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_handles_api_error
|
|
86
|
+
connection = stub
|
|
87
|
+
connection.stubs(:get).raises(Faraday::ClientError.new("401", { status: 401, body: "Unauthorized" }))
|
|
88
|
+
@client.instance_variable_set(:@connection, connection)
|
|
89
|
+
result = @client.list_accounts
|
|
90
|
+
assert result.failure?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_ecdsa_key_detected
|
|
94
|
+
assert @client.send(:ecdsa_key?)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_non_ecdsa_key_not_detected
|
|
98
|
+
client = Honeymaker::Clients::Coinbase.new(api_key: "k", api_secret: "not-a-pem-key")
|
|
99
|
+
refute client.send(:ecdsa_key?)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_ed25519_returns_nil_without_rbnacl
|
|
103
|
+
client = Honeymaker::Clients::Coinbase.new(api_key: "k", api_secret: Base64.encode64("short"))
|
|
104
|
+
# rbnacl may or may not be installed; either way should not raise
|
|
105
|
+
result = client.send(:ed25519_signing_key)
|
|
106
|
+
# Returns nil if rbnacl not installed or key is invalid
|
|
107
|
+
assert_nil result
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_unauthenticated_headers
|
|
111
|
+
client = Honeymaker::Clients::Coinbase.new
|
|
112
|
+
stub_connection_on(client, :get, { "products" => [] })
|
|
113
|
+
result = client.list_public_products
|
|
114
|
+
assert result.success?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def stub_connection(method, body)
|
|
120
|
+
response = stub(body: body)
|
|
121
|
+
connection = stub
|
|
122
|
+
connection.stubs(method).returns(response)
|
|
123
|
+
@client.instance_variable_set(:@connection, connection)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def stub_connection_on(client, method, body)
|
|
127
|
+
response = stub(body: body)
|
|
128
|
+
connection = stub
|
|
129
|
+
connection.stubs(method).returns(response)
|
|
130
|
+
client.instance_variable_set(:@connection, connection)
|
|
131
|
+
end
|
|
132
|
+
end
|