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,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Honeymaker
|
|
4
|
+
module Clients
|
|
5
|
+
class Bitget < Client
|
|
6
|
+
URL = "https://api.bitget.com"
|
|
7
|
+
|
|
8
|
+
attr_reader :passphrase
|
|
9
|
+
|
|
10
|
+
def initialize(api_key: nil, api_secret: nil, passphrase: nil, proxy: nil, logger: nil)
|
|
11
|
+
super(api_key: api_key, api_secret: api_secret, proxy: proxy, logger: logger)
|
|
12
|
+
@passphrase = passphrase
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_coins
|
|
16
|
+
get_public("/api/v2/spot/public/coins")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get_symbols
|
|
20
|
+
get_public("/api/v2/spot/public/symbols")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get_tickers(symbol: nil)
|
|
24
|
+
get_public("/api/v2/spot/market/tickers", { symbol: symbol })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def get_orderbook(symbol:, limit: nil)
|
|
28
|
+
get_public("/api/v2/spot/market/orderbook", { symbol: symbol, limit: limit })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def get_candles(symbol:, granularity:, start_time: nil, end_time: nil, limit: nil)
|
|
32
|
+
get_public("/api/v2/spot/market/candles", {
|
|
33
|
+
symbol: symbol, granularity: granularity,
|
|
34
|
+
startTime: start_time, endTime: end_time, limit: limit
|
|
35
|
+
})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def get_account_assets(coin: nil)
|
|
39
|
+
get_signed("/api/v2/spot/account/assets", { coin: coin })
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def place_order(symbol:, side:, order_type:, size: nil, quote_size: nil, price: nil, force: nil, client_oid: nil)
|
|
43
|
+
post_signed("/api/v2/spot/trade/place-order", {
|
|
44
|
+
symbol: symbol, side: side, orderType: order_type,
|
|
45
|
+
size: size, quoteSize: quote_size, price: price, force: force, clientOid: client_oid
|
|
46
|
+
})
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def get_order(order_id: nil, client_oid: nil)
|
|
50
|
+
get_signed("/api/v2/spot/trade/orderInfo", { orderId: order_id, clientOid: client_oid })
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def cancel_order(symbol:, order_id: nil, client_oid: nil)
|
|
54
|
+
post_signed("/api/v2/spot/trade/cancel-order", {
|
|
55
|
+
symbol: symbol, orderId: order_id, clientOid: client_oid
|
|
56
|
+
})
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def get_fills(symbol: nil, order_id: nil, start_time: nil, end_time: nil, limit: nil, id_less_than: nil)
|
|
60
|
+
get_signed("/api/v2/spot/trade/fills", {
|
|
61
|
+
symbol: symbol, orderId: order_id,
|
|
62
|
+
startTime: start_time, endTime: end_time, limit: limit, idLessThan: id_less_than
|
|
63
|
+
})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def deposit_list(coin: nil, start_time: nil, end_time: nil, limit: nil, id_less_than: nil)
|
|
67
|
+
get_signed("/api/v2/spot/wallet/deposit-records", {
|
|
68
|
+
coin: coin, startTime: start_time, endTime: end_time, limit: limit, idLessThan: id_less_than
|
|
69
|
+
})
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def withdrawal_list(coin: nil, start_time: nil, end_time: nil, limit: nil, id_less_than: nil)
|
|
73
|
+
get_signed("/api/v2/spot/wallet/withdrawal-records", {
|
|
74
|
+
coin: coin, startTime: start_time, endTime: end_time, limit: limit, idLessThan: id_less_than
|
|
75
|
+
})
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def withdraw(coin:, address:, size:, transfer_type: nil, chain: nil, tag: nil, client_oid: nil)
|
|
79
|
+
post_signed("/api/v2/spot/wallet/withdrawal", {
|
|
80
|
+
coin: coin, transferType: transfer_type, address: address,
|
|
81
|
+
size: size, chain: chain, tag: tag, clientOid: client_oid
|
|
82
|
+
})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def validate_trading_credentials
|
|
88
|
+
result = get_account_assets
|
|
89
|
+
return Result::Failure.new("Invalid trading credentials") if result.failure?
|
|
90
|
+
result.data["code"] == "00000" ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def validate_read_credentials
|
|
94
|
+
validate_trading_credentials
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def get_public(path, params = {})
|
|
98
|
+
with_rescue do
|
|
99
|
+
response = connection.get do |req|
|
|
100
|
+
req.url path
|
|
101
|
+
req.headers = unauthenticated_headers
|
|
102
|
+
req.params = params.compact
|
|
103
|
+
end
|
|
104
|
+
response.body
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def get_signed(path, params = {})
|
|
109
|
+
with_rescue do
|
|
110
|
+
params = params.compact
|
|
111
|
+
query_string = params.empty? ? "" : "?#{Faraday::Utils.build_query(params)}"
|
|
112
|
+
ts = timestamp_ms.to_s
|
|
113
|
+
pre_sign = "#{ts}GET#{path}#{query_string}"
|
|
114
|
+
|
|
115
|
+
response = connection.get do |req|
|
|
116
|
+
req.url path
|
|
117
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
118
|
+
req.params = params
|
|
119
|
+
end
|
|
120
|
+
response.body
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def post_signed(path, body = {})
|
|
125
|
+
with_rescue do
|
|
126
|
+
body = body.compact
|
|
127
|
+
ts = timestamp_ms.to_s
|
|
128
|
+
pre_sign = "#{ts}POST#{path}#{body.to_json}"
|
|
129
|
+
|
|
130
|
+
response = connection.post do |req|
|
|
131
|
+
req.url path
|
|
132
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
133
|
+
req.body = body
|
|
134
|
+
end
|
|
135
|
+
response.body
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def unauthenticated_headers
|
|
140
|
+
{ Accept: "application/json", "Content-Type": "application/json" }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def signed_headers(timestamp, pre_sign)
|
|
144
|
+
mac = Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", @api_secret, pre_sign))
|
|
145
|
+
{
|
|
146
|
+
"ACCESS-KEY": @api_key,
|
|
147
|
+
"ACCESS-SIGN": mac,
|
|
148
|
+
"ACCESS-TIMESTAMP": timestamp,
|
|
149
|
+
"ACCESS-PASSPHRASE": @passphrase,
|
|
150
|
+
Accept: "application/json",
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
locale: "en-US"
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Honeymaker
|
|
4
|
+
module Clients
|
|
5
|
+
class BitMart < Client
|
|
6
|
+
URL = "https://api-cloud.bitmart.com"
|
|
7
|
+
|
|
8
|
+
attr_reader :memo
|
|
9
|
+
|
|
10
|
+
def initialize(api_key: nil, api_secret: nil, memo: nil, proxy: nil, logger: nil)
|
|
11
|
+
super(api_key: api_key, api_secret: api_secret, proxy: proxy, logger: logger)
|
|
12
|
+
@memo = memo
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_symbols_details
|
|
16
|
+
get_public("/spot/v1/symbols/details")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get_ticker(symbol: nil)
|
|
20
|
+
get_public("/spot/quotation/v3/ticker", { symbol: symbol })
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get_depth(symbol:, limit: nil)
|
|
24
|
+
get_public("/spot/quotation/v3/books", { symbol: symbol, limit: limit })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def get_klines(symbol:, step:, before: nil, after_time: nil, limit: nil)
|
|
28
|
+
get_public("/spot/quotation/v3/lite-klines", {
|
|
29
|
+
symbol: symbol, step: step, before: before, after: after_time, limit: limit
|
|
30
|
+
})
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_wallet
|
|
34
|
+
get_signed("/spot/v1/wallet")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def submit_order(symbol:, side:, type:, size: nil, notional: nil, price: nil, client_order_id: nil)
|
|
38
|
+
post_signed("/spot/v2/submit_order", {
|
|
39
|
+
symbol: symbol, side: side, type: type,
|
|
40
|
+
size: size, notional: notional, price: price,
|
|
41
|
+
client_order_id: client_order_id
|
|
42
|
+
})
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def get_order(order_id:)
|
|
46
|
+
post_signed("/spot/v2/order_detail", { orderId: order_id })
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def cancel_order(symbol:, order_id: nil, client_order_id: nil)
|
|
50
|
+
post_signed("/spot/v3/cancel_order", {
|
|
51
|
+
symbol: symbol, order_id: order_id, client_order_id: client_order_id
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def get_trades(symbol:, order_mode: nil, start_time: nil, end_time: nil, limit: nil, recv_window: nil)
|
|
56
|
+
get_signed("/spot/v2/trades", {
|
|
57
|
+
symbol: symbol, orderMode: order_mode,
|
|
58
|
+
startTime: start_time, endTime: end_time, N: limit, recvWindow: recv_window
|
|
59
|
+
})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def deposit_list(currency: nil, n: nil, status: nil)
|
|
63
|
+
get_signed("/account/v1/deposit-withdraw/detail", {
|
|
64
|
+
currency: currency, N: n, type: "deposit", operation_type: "deposit", status: status
|
|
65
|
+
})
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def withdraw_list(currency: nil, n: nil, status: nil)
|
|
69
|
+
get_signed("/account/v1/deposit-withdraw/detail", {
|
|
70
|
+
currency: currency, N: n, type: "withdraw", operation_type: "withdraw", status: status
|
|
71
|
+
})
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def withdraw(currency:, amount:, address:, address_memo: nil, destination: nil)
|
|
75
|
+
post_signed("/account/v1/withdraw/apply", {
|
|
76
|
+
currency: currency, amount: amount,
|
|
77
|
+
destination: destination || "To Digital Address",
|
|
78
|
+
address: address, address_memo: address_memo
|
|
79
|
+
})
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def validate_trading_credentials
|
|
85
|
+
result = get_wallet
|
|
86
|
+
return Result::Failure.new("Invalid trading credentials") if result.failure?
|
|
87
|
+
result.data["code"] == 1000 ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def validate_read_credentials
|
|
91
|
+
validate_trading_credentials
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def get_public(path, params = {})
|
|
95
|
+
with_rescue do
|
|
96
|
+
response = connection.get do |req|
|
|
97
|
+
req.url path
|
|
98
|
+
req.params = params.compact
|
|
99
|
+
end
|
|
100
|
+
response.body
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def get_signed(path, params = {})
|
|
105
|
+
with_rescue do
|
|
106
|
+
ts = timestamp_ms.to_s
|
|
107
|
+
query_string = params.compact.empty? ? "" : "?#{Faraday::Utils.build_query(params.compact)}"
|
|
108
|
+
pre_sign = "#{ts}##{@memo}##{query_string}"
|
|
109
|
+
|
|
110
|
+
response = connection.get do |req|
|
|
111
|
+
req.url path
|
|
112
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
113
|
+
req.params = params.compact
|
|
114
|
+
end
|
|
115
|
+
response.body
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def post_signed(path, body = {})
|
|
120
|
+
with_rescue do
|
|
121
|
+
ts = timestamp_ms.to_s
|
|
122
|
+
body_json = body.compact.to_json
|
|
123
|
+
pre_sign = "#{ts}##{@memo}##{body_json}"
|
|
124
|
+
|
|
125
|
+
response = connection.post do |req|
|
|
126
|
+
req.url path
|
|
127
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
128
|
+
req.body = body.compact
|
|
129
|
+
end
|
|
130
|
+
response.body
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def signed_headers(timestamp, pre_sign)
|
|
135
|
+
signature = hmac_sha256(@api_secret, pre_sign)
|
|
136
|
+
{
|
|
137
|
+
"X-BM-KEY": @api_key,
|
|
138
|
+
"X-BM-SIGN": signature,
|
|
139
|
+
"X-BM-TIMESTAMP": timestamp,
|
|
140
|
+
Accept: "application/json",
|
|
141
|
+
"Content-Type": "application/json"
|
|
142
|
+
}
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Honeymaker
|
|
4
|
+
module Clients
|
|
5
|
+
class Bitrue < Client
|
|
6
|
+
URL = "https://openapi.bitrue.com"
|
|
7
|
+
|
|
8
|
+
def exchange_information
|
|
9
|
+
get_public("/api/v1/exchangeInfo")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def symbol_price_ticker(symbol: nil)
|
|
13
|
+
get_public("/api/v1/ticker/price", { symbol: symbol })
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def symbol_order_book_ticker(symbol: nil)
|
|
17
|
+
get_public("/api/v1/ticker/bookTicker", { symbol: symbol })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def candlestick_data(symbol:, interval:, start_time: nil, end_time: nil, limit: 500)
|
|
21
|
+
get_public("/api/v1/market/kline", {
|
|
22
|
+
symbol: symbol, scale: interval, startTime: start_time,
|
|
23
|
+
endTime: end_time, limit: limit
|
|
24
|
+
})
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def account_information(recv_window: 5000)
|
|
28
|
+
get_signed("/api/v1/account", { recvWindow: recv_window })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def query_order(symbol:, order_id: nil, orig_client_order_id: nil, recv_window: 5000)
|
|
32
|
+
get_signed("/api/v1/order", {
|
|
33
|
+
symbol: symbol, orderId: order_id,
|
|
34
|
+
origClientOrderId: orig_client_order_id, recvWindow: recv_window
|
|
35
|
+
})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def new_order(symbol:, side:, type:, time_in_force: nil, quantity: nil, quote_order_qty: nil,
|
|
39
|
+
price: nil, new_client_order_id: nil, recv_window: 5000)
|
|
40
|
+
post_signed("/api/v1/order", {
|
|
41
|
+
symbol: symbol, side: side, type: type,
|
|
42
|
+
timeInForce: time_in_force, quantity: quantity,
|
|
43
|
+
quoteOrderQty: quote_order_qty, price: price,
|
|
44
|
+
newClientOrderId: new_client_order_id, recvWindow: recv_window
|
|
45
|
+
})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def cancel_order(symbol:, order_id: nil, orig_client_order_id: nil, recv_window: 5000)
|
|
49
|
+
delete_signed("/api/v1/order", {
|
|
50
|
+
symbol: symbol, orderId: order_id,
|
|
51
|
+
origClientOrderId: orig_client_order_id, recvWindow: recv_window
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def account_trade_list(symbol:, start_time: nil, end_time: nil, from_id: nil, limit: 500, recv_window: 5000)
|
|
56
|
+
get_signed("/api/v1/myTrades", {
|
|
57
|
+
symbol: symbol, startTime: start_time, endTime: end_time,
|
|
58
|
+
fromId: from_id, limit: limit, recvWindow: recv_window
|
|
59
|
+
})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def validate_trading_credentials
|
|
65
|
+
result = account_information
|
|
66
|
+
result.success? ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def validate_read_credentials
|
|
70
|
+
validate_trading_credentials
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def get_public(path, params = {})
|
|
74
|
+
with_rescue do
|
|
75
|
+
response = connection.get do |req|
|
|
76
|
+
req.url path
|
|
77
|
+
req.params = params.compact
|
|
78
|
+
end
|
|
79
|
+
response.body
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def get_signed(path, params = {})
|
|
84
|
+
with_rescue do
|
|
85
|
+
response = connection.get do |req|
|
|
86
|
+
req.url path
|
|
87
|
+
req.headers = auth_headers
|
|
88
|
+
req.params = params.compact.merge(timestamp: timestamp_ms)
|
|
89
|
+
req.params[:signature] = sign_params(req.params)
|
|
90
|
+
end
|
|
91
|
+
response.body
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def post_signed(path, params = {})
|
|
96
|
+
with_rescue do
|
|
97
|
+
response = connection.post do |req|
|
|
98
|
+
req.url path
|
|
99
|
+
req.headers = auth_headers
|
|
100
|
+
req.params = params.compact.merge(timestamp: timestamp_ms)
|
|
101
|
+
req.params[:signature] = sign_params(req.params)
|
|
102
|
+
end
|
|
103
|
+
response.body
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def delete_signed(path, params = {})
|
|
108
|
+
with_rescue do
|
|
109
|
+
response = connection.delete do |req|
|
|
110
|
+
req.url path
|
|
111
|
+
req.headers = auth_headers
|
|
112
|
+
req.params = params.compact.merge(timestamp: timestamp_ms)
|
|
113
|
+
req.params[:signature] = sign_params(req.params)
|
|
114
|
+
end
|
|
115
|
+
response.body
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def auth_headers
|
|
120
|
+
{ "X-MBX-APIKEY": @api_key, Accept: "application/json", "Content-Type": "application/json" }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def sign_params(params)
|
|
124
|
+
return unless @api_secret
|
|
125
|
+
query = Faraday::Utils.build_query(params)
|
|
126
|
+
hmac_sha256(@api_secret, query)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Honeymaker
|
|
4
|
+
module Clients
|
|
5
|
+
class Bitvavo < Client
|
|
6
|
+
URL = "https://api.bitvavo.com"
|
|
7
|
+
ACCESS_WINDOW = "10000"
|
|
8
|
+
|
|
9
|
+
def get_assets
|
|
10
|
+
get_public("/v2/assets")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_markets(market: nil)
|
|
14
|
+
get_public("/v2/markets", { market: market })
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get_ticker_price(market: nil)
|
|
18
|
+
get_public("/v2/ticker/price", { market: market })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get_ticker_book(market: nil)
|
|
22
|
+
get_public("/v2/ticker/book", { market: market })
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def get_candles(market:, interval:, start_time: nil, end_time: nil, limit: nil)
|
|
26
|
+
get_public("/v2/#{market}/candles", {
|
|
27
|
+
interval: interval, start: start_time, end: end_time, limit: limit
|
|
28
|
+
})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def get_balance(symbol: nil)
|
|
32
|
+
get_signed("/v2/balance", { symbol: symbol })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def place_order(market:, side:, order_type:, amount: nil, amount_quote: nil, price: nil,
|
|
36
|
+
time_in_force: nil, client_order_id: nil)
|
|
37
|
+
post_signed("/v2/order", {
|
|
38
|
+
market: market, side: side, orderType: order_type,
|
|
39
|
+
amount: amount, amountQuote: amount_quote, price: price,
|
|
40
|
+
timeInForce: time_in_force, clientOrderId: client_order_id
|
|
41
|
+
})
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_order(market:, order_id:)
|
|
45
|
+
get_signed("/v2/order", { market: market, orderId: order_id })
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def cancel_order(market:, order_id:)
|
|
49
|
+
delete_signed("/v2/order", { market: market, orderId: order_id })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_trades(market:, limit: nil, start_time: nil, end_time: nil, trade_id_from: nil, trade_id_to: nil)
|
|
53
|
+
get_signed("/v2/trades", {
|
|
54
|
+
market: market, limit: limit, start: start_time, end: end_time,
|
|
55
|
+
tradeIdFrom: trade_id_from, tradeIdTo: trade_id_to
|
|
56
|
+
})
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def get_deposit_history(symbol: nil, limit: nil, start_time: nil, end_time: nil)
|
|
60
|
+
get_signed("/v2/deposit", { symbol: symbol, limit: limit, start: start_time, end: end_time })
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def get_withdrawal_history(symbol: nil, limit: nil, start_time: nil, end_time: nil)
|
|
64
|
+
get_signed("/v2/withdrawal", { symbol: symbol, limit: limit, start: start_time, end: end_time })
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def withdraw(symbol:, amount:, address:, payment_id: nil)
|
|
68
|
+
post_signed("/v2/withdrawal", {
|
|
69
|
+
symbol: symbol, amount: amount, address: address, paymentId: payment_id
|
|
70
|
+
})
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def validate_trading_credentials
|
|
76
|
+
result = get_balance
|
|
77
|
+
result.success? ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validate_read_credentials
|
|
81
|
+
validate_trading_credentials
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def get_public(path, params = {})
|
|
85
|
+
with_rescue do
|
|
86
|
+
response = connection.get do |req|
|
|
87
|
+
req.url path
|
|
88
|
+
req.headers = unauthenticated_headers
|
|
89
|
+
req.params = params.compact
|
|
90
|
+
end
|
|
91
|
+
response.body
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def get_signed(path, params = {})
|
|
96
|
+
with_rescue do
|
|
97
|
+
params = params.compact
|
|
98
|
+
query_string = params.any? ? "?#{Faraday::Utils.build_query(params)}" : ""
|
|
99
|
+
ts = timestamp_ms.to_s
|
|
100
|
+
payload = "#{ts}GET#{path}#{query_string}"
|
|
101
|
+
|
|
102
|
+
response = connection.get do |req|
|
|
103
|
+
req.url path
|
|
104
|
+
req.headers = signed_headers(ts, payload)
|
|
105
|
+
req.params = params
|
|
106
|
+
end
|
|
107
|
+
response.body
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def post_signed(path, body = {})
|
|
112
|
+
with_rescue do
|
|
113
|
+
body = body.compact
|
|
114
|
+
body_string = body.to_json
|
|
115
|
+
ts = timestamp_ms.to_s
|
|
116
|
+
payload = "#{ts}POST#{path}#{body_string}"
|
|
117
|
+
|
|
118
|
+
response = connection.post do |req|
|
|
119
|
+
req.url path
|
|
120
|
+
req.headers = signed_headers(ts, payload)
|
|
121
|
+
req.body = body
|
|
122
|
+
end
|
|
123
|
+
response.body
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def delete_signed(path, params = {})
|
|
128
|
+
with_rescue do
|
|
129
|
+
params = params.compact
|
|
130
|
+
query_string = params.any? ? "?#{Faraday::Utils.build_query(params)}" : ""
|
|
131
|
+
ts = timestamp_ms.to_s
|
|
132
|
+
payload = "#{ts}DELETE#{path}#{query_string}"
|
|
133
|
+
|
|
134
|
+
response = connection.delete do |req|
|
|
135
|
+
req.url path
|
|
136
|
+
req.headers = signed_headers(ts, payload)
|
|
137
|
+
req.params = params
|
|
138
|
+
end
|
|
139
|
+
response.body
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def unauthenticated_headers
|
|
144
|
+
{ Accept: "application/json", "Content-Type": "application/json" }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def signed_headers(timestamp, payload)
|
|
148
|
+
signature = hmac_sha256(@api_secret, payload)
|
|
149
|
+
{
|
|
150
|
+
"BITVAVO-ACCESS-KEY": @api_key,
|
|
151
|
+
"BITVAVO-ACCESS-SIGNATURE": signature,
|
|
152
|
+
"BITVAVO-ACCESS-TIMESTAMP": timestamp,
|
|
153
|
+
"BITVAVO-ACCESS-WINDOW": ACCESS_WINDOW,
|
|
154
|
+
Accept: "application/json",
|
|
155
|
+
"Content-Type": "application/json"
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|