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,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module Honeymaker
|
|
7
|
+
module Clients
|
|
8
|
+
class Kraken < Client
|
|
9
|
+
URL = "https://api.kraken.com"
|
|
10
|
+
|
|
11
|
+
def query_orders_info(txid:, trades: nil, userref: nil, consolidate_taker: true)
|
|
12
|
+
post_private("/0/private/QueryOrders", {
|
|
13
|
+
nonce: nonce, trades: trades, userref: userref,
|
|
14
|
+
txid: txid, consolidate_taker: consolidate_taker
|
|
15
|
+
})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_order(ordertype:, type:, volume:, pair:, userref: nil, cl_ord_id: nil,
|
|
19
|
+
displayvol: nil, price: nil, price2: nil, trigger: nil, leverage: nil,
|
|
20
|
+
reduce_only: nil, stptype: nil, oflags: [], timeinforce: nil,
|
|
21
|
+
starttm: nil, expiretm: nil, close: nil, close_price: nil,
|
|
22
|
+
close_price2: nil, deadline: nil, validate: nil)
|
|
23
|
+
post_private("/0/private/AddOrder", {
|
|
24
|
+
"nonce" => nonce, "ordertype" => ordertype, "type" => type,
|
|
25
|
+
"volume" => volume, "pair" => pair, "userref" => userref,
|
|
26
|
+
"cl_ord_id" => cl_ord_id, "displayvol" => displayvol,
|
|
27
|
+
"price" => price, "price2" => price2, "trigger" => trigger,
|
|
28
|
+
"leverage" => leverage, "reduce_only" => reduce_only,
|
|
29
|
+
"stptype" => stptype,
|
|
30
|
+
"oflags" => oflags.any? ? oflags.join(",") : nil,
|
|
31
|
+
"timeinforce" => timeinforce, "starttm" => starttm,
|
|
32
|
+
"expiretm" => expiretm, "close[ordertype]" => close,
|
|
33
|
+
"close[price]" => close_price, "close[price2]" => close_price2,
|
|
34
|
+
"deadline" => deadline, "validate" => validate
|
|
35
|
+
})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cancel_order(txid: nil, cl_ord_id: nil)
|
|
39
|
+
post_private("/0/private/CancelOrder", { nonce: nonce, txid: txid, cl_ord_id: cl_ord_id })
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_tradable_asset_pairs(pairs: nil, info: nil, country_code: nil)
|
|
43
|
+
get_public("/0/public/AssetPairs", {
|
|
44
|
+
pair: pairs ? pairs.join(",") : nil, info: info, country_code: country_code
|
|
45
|
+
})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_asset_info(assets: nil, aclass: nil)
|
|
49
|
+
get_public("/0/public/Assets", { asset: assets ? assets.join(",") : nil, aclass: aclass })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_ticker_information(pair: nil)
|
|
53
|
+
get_public("/0/public/Ticker", { pair: pair })
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def get_extended_balance
|
|
57
|
+
post_private("/0/private/BalanceEx", { nonce: nonce })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def get_ohlc_data(pair:, interval: nil, since: nil)
|
|
61
|
+
get_public("/0/public/OHLC", { pair: pair, interval: interval, since: since })
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def get_trades_history(type: nil, trades: nil, start: nil, end_time: nil, ofs: nil)
|
|
65
|
+
post_private("/0/private/TradesHistory", {
|
|
66
|
+
nonce: nonce, type: type, trades: trades,
|
|
67
|
+
start: start, end: end_time, ofs: ofs
|
|
68
|
+
})
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def get_ledgers(asset: nil, type: nil, start: nil, end_time: nil, ofs: nil)
|
|
72
|
+
post_private("/0/private/Ledgers", {
|
|
73
|
+
nonce: nonce, asset: asset, type: type,
|
|
74
|
+
start: start, end: end_time, ofs: ofs
|
|
75
|
+
})
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def get_withdraw_addresses(asset: nil, method: nil)
|
|
79
|
+
post_private("/0/private/WithdrawAddresses", { nonce: nonce, asset: asset, method: method })
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def get_withdraw_methods(asset: nil)
|
|
83
|
+
post_private("/0/private/WithdrawMethods", { nonce: nonce, asset: asset })
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def withdraw(asset:, key:, amount:, address: nil)
|
|
87
|
+
post_private("/0/private/Withdraw", { nonce: nonce, asset: asset, key: key, amount: amount, address: address })
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def validate_trading_credentials
|
|
93
|
+
result = get_extended_balance
|
|
94
|
+
return Result::Failure.new("Invalid trading credentials") if result.failure?
|
|
95
|
+
|
|
96
|
+
errors = result.data["error"]
|
|
97
|
+
if errors.is_a?(Array) && errors.none?
|
|
98
|
+
Result::Success.new(true)
|
|
99
|
+
else
|
|
100
|
+
Result::Failure.new("Invalid trading credentials")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def validate_read_credentials
|
|
105
|
+
validate_trading_credentials
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def get_public(path, params = {})
|
|
109
|
+
with_rescue do
|
|
110
|
+
response = connection.get do |req|
|
|
111
|
+
req.url path
|
|
112
|
+
req.headers = public_headers
|
|
113
|
+
req.params = params.compact
|
|
114
|
+
end
|
|
115
|
+
response.body
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def post_private(path, body = {})
|
|
120
|
+
with_rescue do
|
|
121
|
+
response = connection.post do |req|
|
|
122
|
+
req.url path
|
|
123
|
+
req.body = URI.encode_www_form(body.compact)
|
|
124
|
+
req.headers = private_headers(req.path, req.body)
|
|
125
|
+
end
|
|
126
|
+
response.body
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def nonce
|
|
131
|
+
(Time.now.utc.to_f * 1_000_000).to_i
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def private_headers(path, body)
|
|
135
|
+
return public_headers unless authenticated?
|
|
136
|
+
|
|
137
|
+
request_nonce = URI.decode_www_form(body).to_h["nonce"]
|
|
138
|
+
data = "#{request_nonce}#{body}"
|
|
139
|
+
message = path + Digest::SHA256.digest(data)
|
|
140
|
+
decoded_key = Base64.decode64(@api_secret)
|
|
141
|
+
begin
|
|
142
|
+
hmac = OpenSSL::HMAC.digest("sha512", decoded_key, message)
|
|
143
|
+
rescue OpenSSL::HMACError
|
|
144
|
+
return public_headers
|
|
145
|
+
end
|
|
146
|
+
signature = Base64.strict_encode64(hmac)
|
|
147
|
+
|
|
148
|
+
{
|
|
149
|
+
"API-Key": @api_key,
|
|
150
|
+
"API-Sign": signature,
|
|
151
|
+
Accept: "application/json",
|
|
152
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
153
|
+
"User-Agent": "Honeymaker Ruby"
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def public_headers
|
|
158
|
+
{ Accept: "application/json", "Content-Type": "application/json", "User-Agent": "Honeymaker Ruby" }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Honeymaker
|
|
4
|
+
module Clients
|
|
5
|
+
class Kucoin < Client
|
|
6
|
+
URL = "https://api.kucoin.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_symbols
|
|
16
|
+
get_public("/api/v2/symbols")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get_ticker(symbol:)
|
|
20
|
+
get_public("/api/v1/market/orderbook/level1", { symbol: symbol })
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get_all_tickers
|
|
24
|
+
get_public("/api/v1/market/allTickers")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def get_klines(symbol:, type:, start_at: nil, end_at: nil)
|
|
28
|
+
get_public("/api/v1/market/candles", {
|
|
29
|
+
symbol: symbol, type: type, startAt: start_at, endAt: end_at
|
|
30
|
+
})
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_accounts(currency: nil, type: nil)
|
|
34
|
+
get_signed("/api/v1/accounts", { currency: currency, type: type })
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def place_order(client_oid:, side:, symbol:, type:, size: nil, funds: nil, price: nil,
|
|
38
|
+
time_in_force: nil, stp: nil)
|
|
39
|
+
post_signed("/api/v1/orders", {
|
|
40
|
+
clientOid: client_oid, side: side, symbol: symbol, type: type,
|
|
41
|
+
size: size, funds: funds, price: price,
|
|
42
|
+
timeInForce: time_in_force, stp: stp
|
|
43
|
+
})
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def get_order(order_id:)
|
|
47
|
+
get_signed("/api/v1/orders/#{order_id}")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def cancel_order(order_id:)
|
|
51
|
+
delete_signed("/api/v1/orders/#{order_id}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def get_currencies
|
|
55
|
+
get_public("/api/v3/currencies")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_withdrawal_quotas(currency:, chain: nil)
|
|
59
|
+
get_signed("/api/v1/withdrawals/quotas", { currency: currency, chain: chain })
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get_fills(order_id: nil, symbol: nil, side: nil, type: nil, start_at: nil, end_at: nil,
|
|
63
|
+
trade_type: nil, limit: nil, current_page: nil)
|
|
64
|
+
get_signed("/api/v1/fills", {
|
|
65
|
+
orderId: order_id, symbol: symbol, side: side, type: type,
|
|
66
|
+
startAt: start_at, endAt: end_at, tradeType: trade_type,
|
|
67
|
+
pageSize: limit, currentPage: current_page
|
|
68
|
+
})
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def get_deposits(currency: nil, start_at: nil, end_at: nil, status: nil, current_page: nil, page_size: nil)
|
|
72
|
+
get_signed("/api/v1/deposits", {
|
|
73
|
+
currency: currency, startAt: start_at, endAt: end_at,
|
|
74
|
+
status: status, currentPage: current_page, pageSize: page_size
|
|
75
|
+
})
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def get_withdrawals(currency: nil, start_at: nil, end_at: nil, status: nil, current_page: nil, page_size: nil)
|
|
79
|
+
get_signed("/api/v1/withdrawals", {
|
|
80
|
+
currency: currency, startAt: start_at, endAt: end_at,
|
|
81
|
+
status: status, currentPage: current_page, pageSize: page_size
|
|
82
|
+
})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def withdraw(currency:, address:, amount:, chain: nil, memo: nil)
|
|
86
|
+
post_signed("/api/v1/withdrawals", {
|
|
87
|
+
currency: currency, address: address, amount: amount,
|
|
88
|
+
chain: chain, memo: memo
|
|
89
|
+
})
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def validate_trading_credentials
|
|
95
|
+
result = get_accounts(type: "trade")
|
|
96
|
+
return Result::Failure.new("Invalid trading credentials") if result.failure?
|
|
97
|
+
result.data["code"] == "200000" ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def validate_read_credentials
|
|
101
|
+
validate_trading_credentials
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def get_public(path, params = {})
|
|
105
|
+
with_rescue do
|
|
106
|
+
response = connection.get do |req|
|
|
107
|
+
req.url path
|
|
108
|
+
req.headers = unauthenticated_headers
|
|
109
|
+
req.params = params.compact
|
|
110
|
+
end
|
|
111
|
+
response.body
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def get_signed(path, params = {})
|
|
116
|
+
with_rescue do
|
|
117
|
+
params = params.compact
|
|
118
|
+
query_string = params.empty? ? "" : "?#{Faraday::Utils.build_query(params)}"
|
|
119
|
+
ts = timestamp_ms.to_s
|
|
120
|
+
pre_sign = "#{ts}GET#{path}#{query_string}"
|
|
121
|
+
|
|
122
|
+
response = connection.get do |req|
|
|
123
|
+
req.url path
|
|
124
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
125
|
+
req.params = params
|
|
126
|
+
end
|
|
127
|
+
response.body
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def post_signed(path, body = {})
|
|
132
|
+
with_rescue do
|
|
133
|
+
body = body.compact
|
|
134
|
+
ts = timestamp_ms.to_s
|
|
135
|
+
pre_sign = "#{ts}POST#{path}#{body.to_json}"
|
|
136
|
+
|
|
137
|
+
response = connection.post do |req|
|
|
138
|
+
req.url path
|
|
139
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
140
|
+
req.body = body
|
|
141
|
+
end
|
|
142
|
+
response.body
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def delete_signed(path)
|
|
147
|
+
with_rescue do
|
|
148
|
+
ts = timestamp_ms.to_s
|
|
149
|
+
pre_sign = "#{ts}DELETE#{path}"
|
|
150
|
+
|
|
151
|
+
response = connection.delete do |req|
|
|
152
|
+
req.url path
|
|
153
|
+
req.headers = signed_headers(ts, pre_sign)
|
|
154
|
+
end
|
|
155
|
+
response.body
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def unauthenticated_headers
|
|
160
|
+
{ Accept: "application/json", "Content-Type": "application/json" }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def signed_headers(timestamp, pre_sign)
|
|
164
|
+
signature = Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", @api_secret, pre_sign))
|
|
165
|
+
signed_passphrase = Base64.strict_encode64(
|
|
166
|
+
OpenSSL::HMAC.digest("sha256", @api_secret, @passphrase)
|
|
167
|
+
)
|
|
168
|
+
{
|
|
169
|
+
"KC-API-KEY": @api_key,
|
|
170
|
+
"KC-API-SIGN": signature,
|
|
171
|
+
"KC-API-TIMESTAMP": timestamp,
|
|
172
|
+
"KC-API-PASSPHRASE": signed_passphrase,
|
|
173
|
+
"KC-API-KEY-VERSION": "2",
|
|
174
|
+
Accept: "application/json",
|
|
175
|
+
"Content-Type": "application/json"
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Honeymaker
|
|
4
|
+
module Clients
|
|
5
|
+
class Mexc < Client
|
|
6
|
+
URL = "https://api.mexc.com"
|
|
7
|
+
|
|
8
|
+
def get_all_coins_information
|
|
9
|
+
get_public("/api/v3/capital/config/getall")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def exchange_information
|
|
13
|
+
get_public("/api/v3/exchangeInfo")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def symbol_price_ticker(symbol: nil)
|
|
17
|
+
get_public("/api/v3/ticker/price", { symbol: symbol })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def symbol_order_book_ticker(symbol: nil)
|
|
21
|
+
get_public("/api/v3/ticker/bookTicker", { symbol: symbol })
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def candlestick_data(symbol:, interval:, start_time: nil, end_time: nil, limit: 500)
|
|
25
|
+
get_public("/api/v3/klines", {
|
|
26
|
+
symbol: symbol, interval: interval,
|
|
27
|
+
startTime: start_time, endTime: end_time, limit: limit
|
|
28
|
+
})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def account_information(recv_window: 5000)
|
|
32
|
+
get_signed("/api/v3/account", { recvWindow: recv_window })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def query_order(symbol:, order_id: nil, orig_client_order_id: nil, recv_window: 5000)
|
|
36
|
+
get_signed("/api/v3/order", {
|
|
37
|
+
symbol: symbol, orderId: order_id,
|
|
38
|
+
origClientOrderId: orig_client_order_id, recvWindow: recv_window
|
|
39
|
+
})
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def new_order(symbol:, side:, type:, time_in_force: nil, quantity: nil, quote_order_qty: nil,
|
|
43
|
+
price: nil, new_client_order_id: nil, recv_window: 5000)
|
|
44
|
+
post_signed("/api/v3/order", {
|
|
45
|
+
symbol: symbol, side: side, type: type,
|
|
46
|
+
timeInForce: time_in_force, quantity: quantity,
|
|
47
|
+
quoteOrderQty: quote_order_qty, price: price,
|
|
48
|
+
newClientOrderId: new_client_order_id, recvWindow: recv_window
|
|
49
|
+
})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def cancel_order(symbol:, order_id: nil, orig_client_order_id: nil,
|
|
53
|
+
new_client_order_id: nil, recv_window: 5000)
|
|
54
|
+
delete_signed("/api/v3/order", {
|
|
55
|
+
symbol: symbol, orderId: order_id,
|
|
56
|
+
origClientOrderId: orig_client_order_id,
|
|
57
|
+
newClientOrderId: new_client_order_id, recvWindow: recv_window
|
|
58
|
+
})
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def account_trade_list(symbol:, order_id: nil, start_time: nil, end_time: nil, limit: 500, recv_window: 5000)
|
|
62
|
+
get_signed("/api/v3/myTrades", {
|
|
63
|
+
symbol: symbol, orderId: order_id,
|
|
64
|
+
startTime: start_time, endTime: end_time, limit: limit, recvWindow: recv_window
|
|
65
|
+
})
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def deposit_history(coin: nil, status: nil, start_time: nil, end_time: nil, limit: 1000, recv_window: 5000)
|
|
69
|
+
get_signed("/api/v3/capital/deposit/hisrec", {
|
|
70
|
+
coin: coin, status: status,
|
|
71
|
+
startTime: start_time, endTime: end_time, limit: limit, recvWindow: recv_window
|
|
72
|
+
})
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def withdraw_history(coin: nil, status: nil, start_time: nil, end_time: nil, limit: 1000, recv_window: 5000)
|
|
76
|
+
get_signed("/api/v3/capital/withdraw/history", {
|
|
77
|
+
coin: coin, status: status,
|
|
78
|
+
startTime: start_time, endTime: end_time, limit: limit, recvWindow: recv_window
|
|
79
|
+
})
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def get_withdraw_addresses(recv_window: 5000)
|
|
83
|
+
get_signed("/api/v3/capital/withdraw/address", { recvWindow: recv_window })
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def withdraw(coin:, address:, amount:, network: nil, memo: nil, recv_window: 5000)
|
|
87
|
+
post_signed("/api/v3/capital/withdraw/apply", {
|
|
88
|
+
coin: coin, address: address, amount: amount,
|
|
89
|
+
network: network, memo: memo, recvWindow: recv_window
|
|
90
|
+
})
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def validate_trading_credentials
|
|
96
|
+
result = account_information
|
|
97
|
+
result.success? ? Result::Success.new(true) : Result::Failure.new("Invalid trading credentials")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def validate_read_credentials
|
|
101
|
+
validate_trading_credentials
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def get_public(path, params = {})
|
|
105
|
+
with_rescue do
|
|
106
|
+
response = connection.get do |req|
|
|
107
|
+
req.url path
|
|
108
|
+
req.headers = headers
|
|
109
|
+
req.params = params.compact
|
|
110
|
+
end
|
|
111
|
+
response.body
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def get_signed(path, params = {})
|
|
116
|
+
with_rescue do
|
|
117
|
+
response = connection.get do |req|
|
|
118
|
+
req.url path
|
|
119
|
+
req.headers = headers
|
|
120
|
+
req.params = params.compact.merge(timestamp: timestamp_ms)
|
|
121
|
+
req.params[:signature] = sign_params(req.params)
|
|
122
|
+
end
|
|
123
|
+
response.body
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def post_signed(path, params = {})
|
|
128
|
+
with_rescue do
|
|
129
|
+
response = connection.post do |req|
|
|
130
|
+
req.url path
|
|
131
|
+
req.headers = headers
|
|
132
|
+
req.params = params.compact.merge(timestamp: timestamp_ms)
|
|
133
|
+
req.params[:signature] = sign_params(req.params)
|
|
134
|
+
end
|
|
135
|
+
response.body
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def delete_signed(path, params = {})
|
|
140
|
+
with_rescue do
|
|
141
|
+
response = connection.delete do |req|
|
|
142
|
+
req.url path
|
|
143
|
+
req.headers = headers
|
|
144
|
+
req.params = params.compact.merge(timestamp: timestamp_ms)
|
|
145
|
+
req.params[:signature] = sign_params(req.params)
|
|
146
|
+
end
|
|
147
|
+
response.body
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def headers
|
|
152
|
+
if authenticated?
|
|
153
|
+
{ "X-MEXC-APIKEY": @api_key, Accept: "application/json", "Content-Type": "application/json" }
|
|
154
|
+
else
|
|
155
|
+
{ Accept: "application/json", "Content-Type": "application/json" }
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def sign_params(params)
|
|
160
|
+
return unless @api_secret
|
|
161
|
+
query = Faraday::Utils.build_query(params)
|
|
162
|
+
hmac_sha256(@api_secret, query)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -11,29 +11,23 @@ module Honeymaker
|
|
|
11
11
|
req.params = { permissions: "SPOT" }
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
response.body["symbols"].
|
|
15
|
-
|
|
16
|
-
status = product["status"]
|
|
17
|
-
|
|
18
|
-
filters = product["filters"]
|
|
19
|
-
price_filter = filters.find { |f| f["filterType"] == "PRICE_FILTER" }
|
|
20
|
-
lot_size_filter = filters.find { |f| f["filterType"] == "LOT_SIZE" }
|
|
21
|
-
notional_filter = filters.find { |f| %w[NOTIONAL MIN_NOTIONAL].include?(f["filterType"]) }
|
|
14
|
+
response.body["symbols"].filter_map do |product|
|
|
15
|
+
f = Utils.parse_filters(product["filters"])
|
|
22
16
|
|
|
23
17
|
{
|
|
24
|
-
ticker:
|
|
18
|
+
ticker: product["symbol"],
|
|
25
19
|
base: product["baseAsset"],
|
|
26
20
|
quote: product["quoteAsset"],
|
|
27
|
-
minimum_base_size:
|
|
28
|
-
minimum_quote_size:
|
|
29
|
-
maximum_base_size:
|
|
30
|
-
maximum_quote_size:
|
|
31
|
-
base_decimals: Utils.decimals(
|
|
21
|
+
minimum_base_size: f[:lot_size]["minQty"],
|
|
22
|
+
minimum_quote_size: f[:notional]["minNotional"],
|
|
23
|
+
maximum_base_size: f[:lot_size]["maxQty"],
|
|
24
|
+
maximum_quote_size: f[:notional]["maxNotional"],
|
|
25
|
+
base_decimals: Utils.decimals(f[:lot_size]["stepSize"]),
|
|
32
26
|
quote_decimals: product["quoteAssetPrecision"],
|
|
33
|
-
price_decimals: Utils.decimals(
|
|
34
|
-
available: status == "TRADING"
|
|
27
|
+
price_decimals: Utils.decimals(f[:price]["tickSize"]),
|
|
28
|
+
available: product["status"] == "TRADING"
|
|
35
29
|
}
|
|
36
|
-
end
|
|
30
|
+
end
|
|
37
31
|
end
|
|
38
32
|
end
|
|
39
33
|
|
|
@@ -9,7 +9,7 @@ module Honeymaker
|
|
|
9
9
|
with_rescue do
|
|
10
10
|
response = connection.get("/api/v2/spot/public/symbols")
|
|
11
11
|
|
|
12
|
-
response.body["data"].
|
|
12
|
+
response.body["data"].filter_map do |product|
|
|
13
13
|
{
|
|
14
14
|
ticker: product["symbol"],
|
|
15
15
|
base: product["baseCoin"],
|
|
@@ -23,7 +23,7 @@ module Honeymaker
|
|
|
23
23
|
price_decimals: product["pricePrecision"].to_i,
|
|
24
24
|
available: product["status"] == "online"
|
|
25
25
|
}
|
|
26
|
-
end
|
|
26
|
+
end
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -10,29 +10,19 @@ module Honeymaker
|
|
|
10
10
|
response = connection.get("/api/v1/exchangeInfo")
|
|
11
11
|
|
|
12
12
|
response.body["symbols"].filter_map do |product|
|
|
13
|
-
|
|
14
|
-
price_filter = filters.find { |f| f["filterType"] == "PRICE_FILTER" }
|
|
15
|
-
lot_size_filter = filters.find { |f| f["filterType"] == "LOT_SIZE" }
|
|
13
|
+
f = Utils.parse_filters(product["filters"] || [])
|
|
16
14
|
|
|
17
15
|
{
|
|
18
16
|
ticker: product["symbol"],
|
|
19
17
|
base: product["baseAsset"]&.upcase,
|
|
20
18
|
quote: product["quoteAsset"]&.upcase,
|
|
21
|
-
minimum_base_size:
|
|
22
|
-
minimum_quote_size:
|
|
23
|
-
maximum_base_size:
|
|
19
|
+
minimum_base_size: f[:lot_size]&.dig("minQty"),
|
|
20
|
+
minimum_quote_size: f[:lot_size]&.dig("minVal"),
|
|
21
|
+
maximum_base_size: f[:lot_size]&.dig("maxQty"),
|
|
24
22
|
maximum_quote_size: nil,
|
|
25
|
-
base_decimals:
|
|
26
|
-
Utils.decimals(lot_size_filter["stepSize"])
|
|
27
|
-
else
|
|
28
|
-
product["baseAssetPrecision"]
|
|
29
|
-
end,
|
|
23
|
+
base_decimals: f[:lot_size] ? Utils.decimals(f[:lot_size]["stepSize"]) : product["baseAssetPrecision"],
|
|
30
24
|
quote_decimals: product["quotePrecision"],
|
|
31
|
-
price_decimals:
|
|
32
|
-
Utils.decimals(price_filter["tickSize"])
|
|
33
|
-
else
|
|
34
|
-
product["quotePrecision"]
|
|
35
|
-
end,
|
|
25
|
+
price_decimals: f[:price] ? Utils.decimals(f[:price]["tickSize"]) : product["quotePrecision"],
|
|
36
26
|
available: product["status"] == "TRADING"
|
|
37
27
|
}
|
|
38
28
|
end
|
|
@@ -9,7 +9,7 @@ module Honeymaker
|
|
|
9
9
|
with_rescue do
|
|
10
10
|
response = connection.get("/v2/markets")
|
|
11
11
|
|
|
12
|
-
response.body.
|
|
12
|
+
response.body.filter_map do |product|
|
|
13
13
|
market = product["market"]
|
|
14
14
|
base, quote = market.split("-")
|
|
15
15
|
|
|
@@ -26,7 +26,7 @@ module Honeymaker
|
|
|
26
26
|
price_decimals: product["pricePrecision"] || 8,
|
|
27
27
|
available: product["status"] == "trading"
|
|
28
28
|
}
|
|
29
|
-
end
|
|
29
|
+
end
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -11,7 +11,7 @@ module Honeymaker
|
|
|
11
11
|
req.params = { category: "spot" }
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
response.body["result"]["list"].
|
|
14
|
+
response.body["result"]["list"].filter_map do |product|
|
|
15
15
|
lot_size_filter = product["lotSizeFilter"]
|
|
16
16
|
price_filter = product["priceFilter"]
|
|
17
17
|
|
|
@@ -28,7 +28,7 @@ module Honeymaker
|
|
|
28
28
|
price_decimals: Utils.decimals(price_filter["tickSize"]),
|
|
29
29
|
available: product["status"] == "Trading"
|
|
30
30
|
}
|
|
31
|
-
end
|
|
31
|
+
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
@@ -15,15 +15,11 @@ module Honeymaker
|
|
|
15
15
|
with_rescue do
|
|
16
16
|
response = connection.get("/api/v3/brokerage/market/products")
|
|
17
17
|
|
|
18
|
-
response.body["products"].
|
|
18
|
+
response.body["products"].filter_map do |product|
|
|
19
19
|
ticker = product["product_id"]
|
|
20
20
|
base, quote = ticker.split("-")
|
|
21
21
|
next if ASSET_BLACKLIST.include?(base)
|
|
22
22
|
|
|
23
|
-
base_increment = product["base_increment"]
|
|
24
|
-
quote_increment = product["quote_increment"]
|
|
25
|
-
price_increment = product["price_increment"]
|
|
26
|
-
|
|
27
23
|
{
|
|
28
24
|
ticker: ticker,
|
|
29
25
|
base: base,
|
|
@@ -32,12 +28,12 @@ module Honeymaker
|
|
|
32
28
|
minimum_quote_size: product["quote_min_size"],
|
|
33
29
|
maximum_base_size: product["base_max_size"],
|
|
34
30
|
maximum_quote_size: product["quote_max_size"],
|
|
35
|
-
base_decimals: Utils.decimals(base_increment),
|
|
36
|
-
quote_decimals: Utils.decimals(quote_increment),
|
|
37
|
-
price_decimals: Utils.decimals(price_increment),
|
|
31
|
+
base_decimals: Utils.decimals(product["base_increment"]),
|
|
32
|
+
quote_decimals: Utils.decimals(product["quote_increment"]),
|
|
33
|
+
price_decimals: Utils.decimals(product["price_increment"]),
|
|
38
34
|
available: true
|
|
39
35
|
}
|
|
40
|
-
end
|
|
36
|
+
end
|
|
41
37
|
end
|
|
42
38
|
end
|
|
43
39
|
|