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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07736f839ae1cb3df2941e94e0170ebcea93e080631a42ce2dbc83ea85c8ca71
4
- data.tar.gz: 3574dff97c256b910fa3941ac4968295955ce8ff11997dc2b6f50ddb146aaf45
3
+ metadata.gz: e80ebeeb8222d978465b6fca4f757d13eeae098395f99c2ec2223196c55c2b4b
4
+ data.tar.gz: 5f00b757a6872a409bdce15b5e77c7370148195397b5827064e49b0f57379ea6
5
5
  SHA512:
6
- metadata.gz: 51079a9c330fb03e8bb806d30040b3774ea27244551b6a13443eb20fc3c061b41ac025802b04c2276e7c5412ed85895bb6d49dae310f05edc54a20e2595f447b
7
- data.tar.gz: 301512a5685a2f19858b7f60dc99c6f53b584acbfbcf275491f55488eeac534e856eaa3dbbc679dc28d53dbfe331c8327cd834a2f830c478949e29ee421e3741
6
+ metadata.gz: 428f23c82dd3d1ae22f5fa05f00cd0036691fd397541b058d541ac7f5ba09936a464959a8c81ff11fa46ccab1c68fbfefe4d8fe7f97f06084fb32aca6e994112
7
+ data.tar.gz: a79cbc1061092903465d37be4f1a0627332a05f583bbbd3289b1f46319e76623ad99563f13d2101dcdb9eb07cabc5038d50ec8a9229dc12c1bc32c719f2dd877
Binary file
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "base64"
5
+ require "securerandom"
6
+
7
+ module Honeymaker
8
+ class Client
9
+ OPTIONS = {
10
+ request: {
11
+ open_timeout: 5,
12
+ read_timeout: 30,
13
+ write_timeout: 10
14
+ }
15
+ }.freeze
16
+
17
+ attr_reader :api_key, :api_secret
18
+
19
+ def initialize(api_key: nil, api_secret: nil, proxy: nil, logger: nil)
20
+ @api_key = api_key
21
+ @api_secret = api_secret
22
+ @proxy = proxy
23
+ @logger = logger
24
+ end
25
+
26
+ private
27
+
28
+ def with_rescue
29
+ Result::Success.new(yield)
30
+ rescue Faraday::Error => e
31
+ body = e.respond_to?(:response_body) ? e.response_body : nil
32
+ error_message = (body && !body.empty?) ? body : e.message.to_s
33
+ error_message = "Unknown API error" if error_message.nil? || error_message.empty?
34
+ status = e.respond_to?(:response_status) ? e.response_status : nil
35
+ Result::Failure.new(error_message, data: { status: status })
36
+ rescue StandardError => e
37
+ msg = e.message
38
+ Result::Failure.new((msg && !msg.empty?) ? msg : "Unknown error")
39
+ end
40
+
41
+ def connection
42
+ @connection ||= build_client_connection(self.class::URL)
43
+ end
44
+
45
+ def build_client_connection(url, content_type_match: nil)
46
+ Faraday.new(url: url, **OPTIONS) do |config|
47
+ config.proxy = @proxy if @proxy
48
+ config.request :json
49
+ if content_type_match
50
+ config.response :json, content_type: content_type_match
51
+ else
52
+ config.response :json
53
+ end
54
+ config.response :raise_error
55
+ config.response :logger, @logger, headers: false, bodies: false, log_level: :debug if @logger
56
+ config.adapter :net_http_persistent do |http|
57
+ http.idle_timeout = 100
58
+ end
59
+ end
60
+ end
61
+
62
+ def authenticated?
63
+ @api_key && !@api_key.empty? && @api_secret && !@api_secret.empty?
64
+ end
65
+
66
+ def timestamp_ms
67
+ (Time.now.utc.to_f * 1_000).to_i
68
+ end
69
+
70
+ def hmac_sha256(secret, data)
71
+ OpenSSL::HMAC.hexdigest("sha256", secret, data)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Honeymaker
4
+ module Clients
5
+ class Binance < Client
6
+ URL = "https://api.binance.com"
7
+
8
+ def exchange_information(symbol: nil, symbols: nil, permissions: nil, show_permission_sets: nil, symbol_status: nil)
9
+ with_rescue do
10
+ response = connection.get do |req|
11
+ req.url "/api/v3/exchangeInfo"
12
+ req.headers = headers
13
+ req.params = {
14
+ symbol: symbol,
15
+ symbols: symbols&.to_json,
16
+ permissions: permissions&.to_json,
17
+ showPermissionSets: show_permission_sets,
18
+ symbolStatus: symbol_status
19
+ }.compact
20
+ end
21
+ response.body
22
+ end
23
+ end
24
+
25
+ def symbol_price_ticker(symbol: nil, symbols: nil)
26
+ with_rescue do
27
+ response = connection.get do |req|
28
+ req.url "/api/v3/ticker/price"
29
+ req.headers = headers
30
+ req.params = { symbol: symbol, symbols: symbols&.to_json }.compact
31
+ end
32
+ response.body
33
+ end
34
+ end
35
+
36
+ def symbol_order_book_ticker(symbol: nil, symbols: nil)
37
+ with_rescue do
38
+ response = connection.get do |req|
39
+ req.url "/api/v3/ticker/bookTicker"
40
+ req.headers = headers
41
+ req.params = { symbol: symbol, symbols: symbols&.to_json }.compact
42
+ end
43
+ response.body
44
+ end
45
+ end
46
+
47
+ def candlestick_data(symbol:, interval:, start_time: nil, end_time: nil, time_zone: 0, limit: 500)
48
+ with_rescue do
49
+ response = connection.get do |req|
50
+ req.url "/api/v3/klines"
51
+ req.headers = headers
52
+ req.params = {
53
+ symbol: symbol, interval: interval,
54
+ startTime: start_time, endTime: end_time,
55
+ timeZone: time_zone, limit: limit
56
+ }.compact
57
+ end
58
+ response.body
59
+ end
60
+ end
61
+
62
+ def account_information(omit_zero_balances: false, recv_window: 5000)
63
+ with_rescue do
64
+ response = connection.get do |req|
65
+ req.url "/api/v3/account"
66
+ req.headers = headers
67
+ req.params = {
68
+ omitZeroBalances: omit_zero_balances,
69
+ recvWindow: recv_window,
70
+ timestamp: timestamp_ms
71
+ }.compact
72
+ req.params[:signature] = sign_params(req.params)
73
+ end
74
+ response.body
75
+ end
76
+ end
77
+
78
+ def account_trade_list(symbol:, order_id: nil, start_time: nil, end_time: nil, from_id: nil, limit: 500, recv_window: 5000)
79
+ with_rescue do
80
+ response = connection.get do |req|
81
+ req.url "/api/v3/myTrades"
82
+ req.headers = headers
83
+ req.params = {
84
+ symbol: symbol, orderId: order_id,
85
+ startTime: start_time, endTime: end_time,
86
+ fromId: from_id, limit: limit,
87
+ recvWindow: recv_window, timestamp: timestamp_ms
88
+ }.compact
89
+ req.params[:signature] = sign_params(req.params)
90
+ end
91
+ response.body
92
+ end
93
+ end
94
+
95
+ def query_order(symbol:, order_id: nil, orig_client_order_id: nil, recv_window: 5000)
96
+ with_rescue do
97
+ response = connection.get do |req|
98
+ req.url "/api/v3/order"
99
+ req.headers = headers
100
+ req.params = {
101
+ symbol: symbol, orderId: order_id,
102
+ origClientOrderId: orig_client_order_id,
103
+ recvWindow: recv_window, timestamp: timestamp_ms
104
+ }.compact
105
+ req.params[:signature] = sign_params(req.params)
106
+ end
107
+ response.body
108
+ end
109
+ end
110
+
111
+ def all_orders(symbol:, order_id: nil, start_time: nil, end_time: nil, limit: 500, recv_window: 5000)
112
+ with_rescue do
113
+ response = connection.get do |req|
114
+ req.url "/api/v3/allOrders"
115
+ req.headers = headers
116
+ req.params = {
117
+ symbol: symbol, orderId: order_id,
118
+ startTime: start_time, endTime: end_time,
119
+ limit: limit, recvWindow: recv_window, timestamp: timestamp_ms
120
+ }.compact
121
+ req.params[:signature] = sign_params(req.params)
122
+ end
123
+ response.body
124
+ end
125
+ end
126
+
127
+ def new_order(symbol:, side:, type:, time_in_force: nil, quantity: nil, quote_order_qty: nil,
128
+ price: nil, new_client_order_id: nil, strategy_id: nil, strategy_type: nil,
129
+ stop_price: nil, trailing_delta: nil, iceberg_qty: nil, new_order_resp_type: nil,
130
+ self_trade_prevention_mode: nil, recv_window: 5000)
131
+ with_rescue do
132
+ response = connection.post do |req|
133
+ req.url "/api/v3/order"
134
+ req.headers = headers
135
+ req.params = {
136
+ symbol: symbol, side: side, type: type,
137
+ timeInForce: time_in_force, quantity: quantity,
138
+ quoteOrderQty: quote_order_qty, price: price,
139
+ newClientOrderId: new_client_order_id,
140
+ strategyId: strategy_id, strategyType: strategy_type,
141
+ stopPrice: stop_price, trailingDelta: trailing_delta,
142
+ icebergQty: iceberg_qty, newOrderRespType: new_order_resp_type,
143
+ selfTradePreventionMode: self_trade_prevention_mode,
144
+ recvWindow: recv_window, timestamp: timestamp_ms
145
+ }.compact
146
+ req.params[:signature] = sign_params(req.params)
147
+ end
148
+ response.body
149
+ end
150
+ end
151
+
152
+ def cancel_order(symbol:, order_id: nil, orig_client_order_id: nil, new_client_order_id: nil,
153
+ cancel_restrictions: nil, recv_window: 5000)
154
+ with_rescue do
155
+ response = connection.delete do |req|
156
+ req.url "/api/v3/order"
157
+ req.headers = headers
158
+ req.params = {
159
+ symbol: symbol, orderId: order_id,
160
+ origClientOrderId: orig_client_order_id,
161
+ newClientOrderId: new_client_order_id,
162
+ cancelRestrictions: cancel_restrictions,
163
+ recvWindow: recv_window, timestamp: timestamp_ms
164
+ }.compact
165
+ req.params[:signature] = sign_params(req.params)
166
+ end
167
+ response.body
168
+ end
169
+ end
170
+
171
+ def get_all_coins_information(recv_window: 5000)
172
+ with_rescue do
173
+ response = connection.get do |req|
174
+ req.url "/sapi/v1/capital/config/getall"
175
+ req.headers = headers
176
+ req.params = { recvWindow: recv_window, timestamp: timestamp_ms }
177
+ req.params[:signature] = sign_params(req.params)
178
+ end
179
+ response.body
180
+ end
181
+ end
182
+
183
+ def api_description(recv_window: 5000)
184
+ with_rescue do
185
+ response = connection.get do |req|
186
+ req.url "/sapi/v1/account/apiRestrictions"
187
+ req.headers = headers
188
+ req.params = { recvWindow: recv_window, timestamp: timestamp_ms }
189
+ req.params[:signature] = sign_params(req.params)
190
+ end
191
+ response.body
192
+ end
193
+ end
194
+
195
+ def get_withdraw_addresses(recv_window: 5000)
196
+ with_rescue do
197
+ response = connection.get do |req|
198
+ req.url "/sapi/v1/capital/withdraw/address/list"
199
+ req.headers = headers
200
+ req.params = { recvWindow: recv_window, timestamp: timestamp_ms }
201
+ req.params[:signature] = sign_params(req.params)
202
+ end
203
+ response.body
204
+ end
205
+ end
206
+
207
+ def withdraw(coin:, address:, amount:, network: nil, address_tag: nil, recv_window: 5000)
208
+ with_rescue do
209
+ response = connection.post do |req|
210
+ req.url "/sapi/v1/capital/withdraw/apply"
211
+ req.headers = headers
212
+ req.params = {
213
+ coin: coin, address: address, amount: amount,
214
+ network: network, addressTag: address_tag,
215
+ recvWindow: recv_window, timestamp: timestamp_ms
216
+ }.compact
217
+ req.params[:signature] = sign_params(req.params)
218
+ end
219
+ response.body
220
+ end
221
+ end
222
+
223
+ private
224
+
225
+ def headers
226
+ if authenticated?
227
+ { "X-MBX-APIKEY": @api_key, Accept: "application/json", "Content-Type": "application/json" }
228
+ else
229
+ { Accept: "application/json", "Content-Type": "application/json" }
230
+ end
231
+ end
232
+
233
+ def sign_params(params)
234
+ return unless @api_secret
235
+ query = Faraday::Utils.build_query(params)
236
+ hmac_sha256(@api_secret, query)
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Honeymaker
4
+ module Clients
5
+ class BinanceUs < Binance
6
+ URL = "https://api.binance.us"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Honeymaker
4
+ module Clients
5
+ class BingX < Client
6
+ URL = "https://open-api.bingx.com"
7
+
8
+ def get_symbols
9
+ get_public("/openApi/spot/v1/common/symbols")
10
+ end
11
+
12
+ def get_ticker(symbol: nil)
13
+ get_public("/openApi/spot/v2/market/ticker", { symbol: symbol })
14
+ end
15
+
16
+ def get_depth(symbol:, limit: nil)
17
+ get_public("/openApi/spot/v1/market/depth", { symbol: symbol, limit: limit })
18
+ end
19
+
20
+ def get_klines(symbol:, interval:, start_time: nil, end_time: nil, limit: nil)
21
+ get_public("/openApi/spot/v1/market/kline", {
22
+ symbol: symbol, interval: interval,
23
+ startTime: start_time, endTime: end_time, limit: limit
24
+ })
25
+ end
26
+
27
+ def get_balances
28
+ get_signed("/openApi/spot/v1/account/balance")
29
+ end
30
+
31
+ def place_order(symbol:, side:, type:, quantity: nil, quote_order_qty: nil, price: nil,
32
+ time_in_force: nil, client_order_id: nil)
33
+ post_signed("/openApi/spot/v1/trade/order", {
34
+ symbol: symbol, side: side, type: type,
35
+ quantity: quantity, quoteOrderQty: quote_order_qty,
36
+ price: price, timeInForce: time_in_force,
37
+ newClientOrderId: client_order_id
38
+ })
39
+ end
40
+
41
+ def get_order(symbol:, order_id: nil, client_order_id: nil)
42
+ get_signed("/openApi/spot/v1/trade/query", {
43
+ symbol: symbol, orderId: order_id, clientOrderID: client_order_id
44
+ })
45
+ end
46
+
47
+ def cancel_order(symbol:, order_id: nil, client_order_id: nil)
48
+ post_signed("/openApi/spot/v1/trade/cancel", {
49
+ symbol: symbol, orderId: order_id, clientOrderID: client_order_id
50
+ })
51
+ end
52
+
53
+ def withdraw(coin:, address:, amount:, network: nil, wallet_type: nil, tag: nil)
54
+ post_signed("/openApi/wallets/v1/capital/withdraw/apply", {
55
+ coin: coin, address: address, amount: amount,
56
+ network: network, walletType: wallet_type, tag: tag
57
+ })
58
+ end
59
+
60
+ private
61
+
62
+ def get_public(path, params = {})
63
+ with_rescue do
64
+ response = connection.get do |req|
65
+ req.url path
66
+ req.params = params.compact
67
+ end
68
+ response.body
69
+ end
70
+ end
71
+
72
+ def get_signed(path, params = {})
73
+ with_rescue do
74
+ params = params.compact.merge(timestamp: timestamp_ms)
75
+ params[:signature] = hmac_sha256(@api_secret, Faraday::Utils.build_query(params))
76
+
77
+ response = connection.get do |req|
78
+ req.url path
79
+ req.headers = { "X-BX-APIKEY": @api_key }
80
+ req.params = params
81
+ end
82
+ response.body
83
+ end
84
+ end
85
+
86
+ def post_signed(path, params = {})
87
+ with_rescue do
88
+ params = params.compact.merge(timestamp: timestamp_ms)
89
+ params[:signature] = hmac_sha256(@api_secret, Faraday::Utils.build_query(params))
90
+
91
+ response = connection.post do |req|
92
+ req.url path
93
+ req.headers = { "X-BX-APIKEY": @api_key, "Content-Type": "application/json" }
94
+ req.params = params
95
+ end
96
+ response.body
97
+ end
98
+ end
99
+
100
+ def connection
101
+ @connection ||= build_client_connection(URL, content_type_match: //)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,128 @@
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:, 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, 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 withdraw(coin:, transfer_type:, address:, size:, chain: nil, tag: nil, client_oid: nil)
60
+ post_signed("/api/v2/spot/wallet/withdrawal", {
61
+ coin: coin, transferType: transfer_type, address: address,
62
+ size: size, chain: chain, tag: tag, clientOid: client_oid
63
+ })
64
+ end
65
+
66
+ private
67
+
68
+ def get_public(path, params = {})
69
+ with_rescue do
70
+ response = connection.get do |req|
71
+ req.url path
72
+ req.headers = unauthenticated_headers
73
+ req.params = params.compact
74
+ end
75
+ response.body
76
+ end
77
+ end
78
+
79
+ def get_signed(path, params = {})
80
+ with_rescue do
81
+ params = params.compact
82
+ query_string = params.empty? ? "" : "?#{Faraday::Utils.build_query(params)}"
83
+ ts = timestamp_ms.to_s
84
+ pre_sign = "#{ts}GET#{path}#{query_string}"
85
+
86
+ response = connection.get do |req|
87
+ req.url path
88
+ req.headers = signed_headers(ts, pre_sign)
89
+ req.params = params
90
+ end
91
+ response.body
92
+ end
93
+ end
94
+
95
+ def post_signed(path, body = {})
96
+ with_rescue do
97
+ body = body.compact
98
+ ts = timestamp_ms.to_s
99
+ pre_sign = "#{ts}POST#{path}#{body.to_json}"
100
+
101
+ response = connection.post do |req|
102
+ req.url path
103
+ req.headers = signed_headers(ts, pre_sign)
104
+ req.body = body
105
+ end
106
+ response.body
107
+ end
108
+ end
109
+
110
+ def unauthenticated_headers
111
+ { Accept: "application/json", "Content-Type": "application/json" }
112
+ end
113
+
114
+ def signed_headers(timestamp, pre_sign)
115
+ mac = Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", @api_secret, pre_sign))
116
+ {
117
+ "ACCESS-KEY": @api_key,
118
+ "ACCESS-SIGN": mac,
119
+ "ACCESS-TIMESTAMP": timestamp,
120
+ "ACCESS-PASSPHRASE": @passphrase,
121
+ Accept: "application/json",
122
+ "Content-Type": "application/json",
123
+ locale: "en-US"
124
+ }
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,117 @@
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 withdraw(currency:, amount:, address:, address_memo: nil, destination: nil)
56
+ post_signed("/account/v1/withdraw/apply", {
57
+ currency: currency, amount: amount,
58
+ destination: destination || "To Digital Address",
59
+ address: address, address_memo: address_memo
60
+ })
61
+ end
62
+
63
+ private
64
+
65
+ def get_public(path, params = {})
66
+ with_rescue do
67
+ response = connection.get do |req|
68
+ req.url path
69
+ req.params = params.compact
70
+ end
71
+ response.body
72
+ end
73
+ end
74
+
75
+ def get_signed(path, params = {})
76
+ with_rescue do
77
+ ts = timestamp_ms.to_s
78
+ query_string = params.compact.empty? ? "" : "?#{Faraday::Utils.build_query(params.compact)}"
79
+ pre_sign = "#{ts}##{@memo}##{query_string}"
80
+
81
+ response = connection.get do |req|
82
+ req.url path
83
+ req.headers = signed_headers(ts, pre_sign)
84
+ req.params = params.compact
85
+ end
86
+ response.body
87
+ end
88
+ end
89
+
90
+ def post_signed(path, body = {})
91
+ with_rescue do
92
+ ts = timestamp_ms.to_s
93
+ body_json = body.compact.to_json
94
+ pre_sign = "#{ts}##{@memo}##{body_json}"
95
+
96
+ response = connection.post do |req|
97
+ req.url path
98
+ req.headers = signed_headers(ts, pre_sign)
99
+ req.body = body.compact
100
+ end
101
+ response.body
102
+ end
103
+ end
104
+
105
+ def signed_headers(timestamp, pre_sign)
106
+ signature = hmac_sha256(@api_secret, pre_sign)
107
+ {
108
+ "X-BM-KEY": @api_key,
109
+ "X-BM-SIGN": signature,
110
+ "X-BM-TIMESTAMP": timestamp,
111
+ Accept: "application/json",
112
+ "Content-Type": "application/json"
113
+ }
114
+ end
115
+ end
116
+ end
117
+ end