honeymaker 0.4.0 → 0.5.1
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 +145 -6
- data/lib/honeymaker/client.rb +14 -0
- data/lib/honeymaker/clients/binance.rb +264 -2
- data/lib/honeymaker/clients/binance_us.rb +33 -0
- data/lib/honeymaker/clients/bingx.rb +100 -4
- data/lib/honeymaker/clients/bitget.rb +163 -2
- data/lib/honeymaker/clients/bitmart.rb +108 -2
- data/lib/honeymaker/clients/bitrue.rb +90 -2
- data/lib/honeymaker/clients/bitvavo.rb +80 -4
- data/lib/honeymaker/clients/bybit.rb +120 -2
- data/lib/honeymaker/clients/coinbase.rb +108 -2
- data/lib/honeymaker/clients/gemini.rb +85 -4
- data/lib/honeymaker/clients/hyperliquid.rb +69 -1
- data/lib/honeymaker/clients/kraken.rb +112 -2
- data/lib/honeymaker/clients/kraken_futures.rb +78 -0
- data/lib/honeymaker/clients/kucoin.rb +120 -2
- data/lib/honeymaker/clients/mexc.rb +85 -2
- data/lib/honeymaker/version.rb +1 -1
- data/lib/honeymaker.rb +3 -1
- data/test/honeymaker/clients/binance_client_test.rb +9 -2
- data/test/honeymaker/clients/bitget_client_test.rb +9 -3
- data/test/honeymaker/clients/bitmart_client_test.rb +7 -2
- data/test/honeymaker/clients/bitvavo_client_test.rb +2 -2
- data/test/honeymaker/clients/bybit_client_test.rb +7 -3
- data/test/honeymaker/clients/coinbase_client_test.rb +10 -3
- data/test/honeymaker/clients/honeymaker_client_registry_test.rb +1 -1
- data/test/honeymaker/clients/kraken_client_test.rb +2 -1
- data/test/honeymaker/clients/kraken_futures_client_test.rb +54 -0
- data/test/honeymaker/clients/kucoin_client_test.rb +8 -2
- data/test/honeymaker/clients/mexc_client_test.rb +6 -1
- metadata +17 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a49e9be8c0e38711e061c2ea61373bbce84dfe3f779c5e6933fdc2c8185b942b
|
|
4
|
+
data.tar.gz: c9dda3aa8c109c0db2bdcaf086f4b86fe95214cc7a6690d39b921e1182a08766
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7fe8a3cd0df1bea5355498e117887e854716d8050bdfe12009c9b1f0225771b1a18ad59030dfdd1418629df8db06805d79e38392ab99f3b300f93e4a87a52c3b
|
|
7
|
+
data.tar.gz: 49e17edfcc5982629034cc755c315fc772188cc9a232102478eaf984522ed4df261bb55818bb0e62dcbe0b9f3a9a15c20759ef2199015da70d48a1b8098a22b3
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Ruby clients for cryptocurrency exchange APIs. Originally extracted from [Deltab
|
|
|
8
8
|
|
|
9
9
|
## Supported Exchanges
|
|
10
10
|
|
|
11
|
-
Binance, Binance US, Kraken, Coinbase, Bybit, KuCoin, Bitget, MEXC, Bitvavo, Gemini, Hyperliquid, BingX, Bitrue, BitMart.
|
|
11
|
+
Binance, Binance US, Kraken, Kraken Futures, Coinbase, Bybit, KuCoin, Bitget, MEXC, Bitvavo, Gemini, Hyperliquid, BingX, Bitrue, BitMart.
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -18,24 +18,163 @@ gem "honeymaker"
|
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
|
+
### Market Data
|
|
22
|
+
|
|
21
23
|
```ruby
|
|
22
24
|
require "honeymaker"
|
|
23
25
|
|
|
24
|
-
# Get an exchange client
|
|
25
26
|
exchange = Honeymaker.exchange("binance")
|
|
26
|
-
|
|
27
|
-
# Fetch trading pair info (symbols, decimals, min/max amounts)
|
|
28
27
|
result = exchange.get_tickers_info
|
|
29
28
|
|
|
30
29
|
if result.success?
|
|
31
30
|
result.data.each do |ticker|
|
|
32
31
|
puts "#{ticker[:ticker]} — min: #{ticker[:minimum_quote_size]}, decimals: #{ticker[:base_decimals]}"
|
|
33
32
|
end
|
|
34
|
-
else
|
|
35
|
-
puts "Error: #{result.errors.join(', ')}"
|
|
36
33
|
end
|
|
37
34
|
```
|
|
38
35
|
|
|
36
|
+
### Balances
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
client = Honeymaker.client("binance", api_key: "...", api_secret: "...")
|
|
40
|
+
result = client.get_balances
|
|
41
|
+
|
|
42
|
+
if result.success?
|
|
43
|
+
result.data.each do |symbol, balance|
|
|
44
|
+
puts "#{symbol}: free=#{balance[:free]}, locked=#{balance[:locked]}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
# => { "BTC" => { free: BigDecimal("0.5"), locked: BigDecimal("0.1") }, ... }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Coinbase auto-resolves the default portfolio, or pass one explicitly:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
client.get_balances(portfolio_uuid: "...")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Placing Orders
|
|
57
|
+
|
|
58
|
+
Order placement returns a normalized `{ order_id:, raw: }` hash:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
client = Honeymaker.client("binance", api_key: "...", api_secret: "...")
|
|
62
|
+
|
|
63
|
+
result = client.new_order(symbol: "BTCUSDT", side: "BUY", type: "MARKET", quote_order_qty: "100")
|
|
64
|
+
if result.success?
|
|
65
|
+
puts result.data[:order_id] # => "BTCUSDT-123456"
|
|
66
|
+
puts result.data[:raw] # full exchange response
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Method names vary by exchange (`new_order`, `create_order`, `add_order`, `place_order`, `submit_order`) but the return format is the same.
|
|
71
|
+
|
|
72
|
+
### Querying Orders
|
|
73
|
+
|
|
74
|
+
Order queries return a normalized hash with unified status, amounts, and the raw response:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
result = client.query_order(symbol: "BTCUSDT", order_id: 123456)
|
|
78
|
+
if result.success?
|
|
79
|
+
order = result.data
|
|
80
|
+
order[:order_id] # => "BTCUSDT-123456"
|
|
81
|
+
order[:status] # => :open, :closed, :cancelled, :failed, :unknown
|
|
82
|
+
order[:side] # => :buy, :sell
|
|
83
|
+
order[:order_type] # => :market, :limit
|
|
84
|
+
order[:price] # => BigDecimal — avg fill price
|
|
85
|
+
order[:amount] # => BigDecimal — requested base qty (nil if quote-denominated)
|
|
86
|
+
order[:quote_amount] # => BigDecimal — requested quote qty (nil if base-denominated)
|
|
87
|
+
order[:amount_exec] # => BigDecimal — filled base qty
|
|
88
|
+
order[:quote_amount_exec] # => BigDecimal — filled quote qty
|
|
89
|
+
order[:raw] # => Hash — full exchange response
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Account History (Tax Reporting)
|
|
94
|
+
|
|
95
|
+
History endpoints for margin, futures, staking/earn, and other tax-relevant events:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
# Margin
|
|
99
|
+
client.margin_borrow_repay_history(type: "BORROW") # Binance
|
|
100
|
+
client.margin_interest_history # Binance, KuCoin, Bitget
|
|
101
|
+
client.margin_force_liquidation # Binance
|
|
102
|
+
|
|
103
|
+
# Futures
|
|
104
|
+
client.futures_income_history(income_type: "REALIZED_PNL") # Binance USDT-M
|
|
105
|
+
client.coin_futures_income_history # Binance Coin-M
|
|
106
|
+
client.futures_account_bills(product_type: "USDT-FUTURES") # Bitget
|
|
107
|
+
client.futures_income # BingX
|
|
108
|
+
|
|
109
|
+
# Staking / Earn
|
|
110
|
+
client.simple_earn_flexible_rewards # Binance
|
|
111
|
+
client.simple_earn_locked_subscriptions # Binance
|
|
112
|
+
client.earn_yield_history # Bybit
|
|
113
|
+
client.staking_rewards # Gemini
|
|
114
|
+
client.staking_rewards_history # Binance US
|
|
115
|
+
|
|
116
|
+
# Other
|
|
117
|
+
client.universal_transfer_history(type: "MAIN_MARGIN") # Binance
|
|
118
|
+
client.dust_log # Binance
|
|
119
|
+
client.asset_dividend # Binance
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Coverage varies by exchange. Kraken's `get_ledgers(type:)` covers margin, staking, and trades in a single endpoint. Coinbase's `list_transactions` covers 30+ event types.
|
|
123
|
+
|
|
124
|
+
### Kraken Futures
|
|
125
|
+
|
|
126
|
+
Kraken Futures uses separate API keys and a different auth scheme — it's a standalone client:
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
client = Honeymaker.client("kraken_futures", api_key: "...", api_secret: "...")
|
|
130
|
+
|
|
131
|
+
client.get_accounts # wallet balances, margin, PnL
|
|
132
|
+
client.get_fills # trade fills
|
|
133
|
+
client.get_open_positions # open positions
|
|
134
|
+
client.historical_funding_rates(symbol: "PF_XBTUSD") # funding rate history
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Credential Validation
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
client = Honeymaker.client("binance", api_key: "...", api_secret: "...")
|
|
141
|
+
result = client.validate(:trading)
|
|
142
|
+
result.success? # => true if credentials have trading permissions
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Rate Limits
|
|
146
|
+
|
|
147
|
+
Each exchange exposes rate limit metadata (milliseconds between requests):
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
Honeymaker::Clients::Binance.rate_limits
|
|
151
|
+
# => { default: 100, orders: 200 }
|
|
152
|
+
|
|
153
|
+
Honeymaker::Clients::Kraken.rate_limits
|
|
154
|
+
# => { default: 1000, orders: 1000 }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Proxy Support
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
client = Honeymaker.client("binance",
|
|
161
|
+
api_key: "...", api_secret: "...",
|
|
162
|
+
proxy: "http://proxy:8100"
|
|
163
|
+
)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Result Objects
|
|
167
|
+
|
|
168
|
+
All methods return `Result::Success` or `Result::Failure`:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
result = client.get_balances
|
|
172
|
+
result.success? # true/false
|
|
173
|
+
result.failure? # true/false
|
|
174
|
+
result.data # response payload
|
|
175
|
+
result.errors # array of error messages (empty on success)
|
|
176
|
+
```
|
|
177
|
+
|
|
39
178
|
## License
|
|
40
179
|
|
|
41
180
|
MIT
|
data/lib/honeymaker/client.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "openssl"
|
|
4
4
|
require "base64"
|
|
5
5
|
require "securerandom"
|
|
6
|
+
require "bigdecimal"
|
|
6
7
|
|
|
7
8
|
module Honeymaker
|
|
8
9
|
class Client
|
|
@@ -14,6 +15,11 @@ module Honeymaker
|
|
|
14
15
|
}
|
|
15
16
|
}.freeze
|
|
16
17
|
|
|
18
|
+
RATE_LIMITS = {
|
|
19
|
+
default: 100,
|
|
20
|
+
orders: 100
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
17
23
|
attr_reader :api_key, :api_secret
|
|
18
24
|
|
|
19
25
|
def initialize(api_key: nil, api_secret: nil, proxy: nil, logger: nil)
|
|
@@ -23,6 +29,14 @@ module Honeymaker
|
|
|
23
29
|
@logger = logger
|
|
24
30
|
end
|
|
25
31
|
|
|
32
|
+
def self.rate_limits
|
|
33
|
+
self::RATE_LIMITS
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get_balances
|
|
37
|
+
raise NotImplementedError, "#{self.class} must implement #get_balances"
|
|
38
|
+
end
|
|
39
|
+
|
|
26
40
|
def validate(type = :trading)
|
|
27
41
|
return Result::Failure.new("No credentials provided") unless authenticated?
|
|
28
42
|
|
|
@@ -4,6 +4,7 @@ module Honeymaker
|
|
|
4
4
|
module Clients
|
|
5
5
|
class Binance < Client
|
|
6
6
|
URL = "https://api.binance.com"
|
|
7
|
+
RATE_LIMITS = { default: 100, orders: 200 }.freeze
|
|
7
8
|
|
|
8
9
|
def exchange_information(symbol: nil, symbols: nil, permissions: nil, show_permission_sets: nil, symbol_status: nil)
|
|
9
10
|
with_rescue do
|
|
@@ -75,6 +76,22 @@ module Honeymaker
|
|
|
75
76
|
end
|
|
76
77
|
end
|
|
77
78
|
|
|
79
|
+
def get_balances
|
|
80
|
+
result = account_information(omit_zero_balances: true)
|
|
81
|
+
return result if result.failure?
|
|
82
|
+
|
|
83
|
+
balances = {}
|
|
84
|
+
Array(result.data["balances"]).each do |balance|
|
|
85
|
+
symbol = balance["asset"]
|
|
86
|
+
free = BigDecimal(balance["free"].to_s)
|
|
87
|
+
locked = BigDecimal(balance["locked"].to_s)
|
|
88
|
+
next if free.zero? && locked.zero?
|
|
89
|
+
balances[symbol] = { free: free, locked: locked }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
Result::Success.new(balances)
|
|
93
|
+
end
|
|
94
|
+
|
|
78
95
|
def account_trade_list(symbol:, order_id: nil, start_time: nil, end_time: nil, from_id: nil, limit: 500, recv_window: 5000)
|
|
79
96
|
with_rescue do
|
|
80
97
|
response = connection.get do |req|
|
|
@@ -104,7 +121,8 @@ module Honeymaker
|
|
|
104
121
|
}.compact
|
|
105
122
|
req.params[:signature] = sign_params(req.params)
|
|
106
123
|
end
|
|
107
|
-
response.body
|
|
124
|
+
raw = response.body
|
|
125
|
+
normalize_order("#{symbol}-#{raw['orderId']}", raw)
|
|
108
126
|
end
|
|
109
127
|
end
|
|
110
128
|
|
|
@@ -145,7 +163,8 @@ module Honeymaker
|
|
|
145
163
|
}.compact
|
|
146
164
|
req.params[:signature] = sign_params(req.params)
|
|
147
165
|
end
|
|
148
|
-
response.body
|
|
166
|
+
raw = response.body
|
|
167
|
+
{ order_id: "#{symbol}-#{raw['orderId']}", raw: raw }
|
|
149
168
|
end
|
|
150
169
|
end
|
|
151
170
|
|
|
@@ -363,8 +382,251 @@ module Honeymaker
|
|
|
363
382
|
end
|
|
364
383
|
end
|
|
365
384
|
|
|
385
|
+
# --- Margin ---
|
|
386
|
+
|
|
387
|
+
def margin_borrow_repay_history(type:, asset: nil, isolated_symbol: nil, start_time: nil, end_time: nil,
|
|
388
|
+
current: nil, size: nil, recv_window: 5000)
|
|
389
|
+
with_rescue do
|
|
390
|
+
response = connection.get do |req|
|
|
391
|
+
req.url "/sapi/v1/margin/borrow-repay"
|
|
392
|
+
req.headers = headers
|
|
393
|
+
req.params = {
|
|
394
|
+
type: type, asset: asset, isolatedSymbol: isolated_symbol,
|
|
395
|
+
startTime: start_time, endTime: end_time,
|
|
396
|
+
current: current, size: size,
|
|
397
|
+
recvWindow: recv_window, timestamp: timestamp_ms
|
|
398
|
+
}.compact
|
|
399
|
+
req.params[:signature] = sign_params(req.params)
|
|
400
|
+
end
|
|
401
|
+
response.body
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def margin_interest_history(asset: nil, isolated_symbol: nil, start_time: nil, end_time: nil,
|
|
406
|
+
current: nil, size: nil, archived: nil, recv_window: 5000)
|
|
407
|
+
with_rescue do
|
|
408
|
+
response = connection.get do |req|
|
|
409
|
+
req.url "/sapi/v1/margin/interestHistory"
|
|
410
|
+
req.headers = headers
|
|
411
|
+
req.params = {
|
|
412
|
+
asset: asset, isolatedSymbol: isolated_symbol,
|
|
413
|
+
startTime: start_time, endTime: end_time,
|
|
414
|
+
current: current, size: size, archived: archived,
|
|
415
|
+
recvWindow: recv_window, timestamp: timestamp_ms
|
|
416
|
+
}.compact
|
|
417
|
+
req.params[:signature] = sign_params(req.params)
|
|
418
|
+
end
|
|
419
|
+
response.body
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def margin_force_liquidation(start_time: nil, end_time: nil, isolated_symbol: nil,
|
|
424
|
+
current: nil, size: nil, recv_window: 5000)
|
|
425
|
+
with_rescue do
|
|
426
|
+
response = connection.get do |req|
|
|
427
|
+
req.url "/sapi/v1/margin/forceLiquidationRec"
|
|
428
|
+
req.headers = headers
|
|
429
|
+
req.params = {
|
|
430
|
+
startTime: start_time, endTime: end_time,
|
|
431
|
+
isolatedSymbol: isolated_symbol,
|
|
432
|
+
current: current, size: size,
|
|
433
|
+
recvWindow: recv_window, timestamp: timestamp_ms
|
|
434
|
+
}.compact
|
|
435
|
+
req.params[:signature] = sign_params(req.params)
|
|
436
|
+
end
|
|
437
|
+
response.body
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# --- Futures ---
|
|
442
|
+
|
|
443
|
+
def futures_income_history(symbol: nil, income_type: nil, start_time: nil, end_time: nil,
|
|
444
|
+
page: nil, limit: 1000, recv_window: 5000)
|
|
445
|
+
with_rescue do
|
|
446
|
+
response = usdt_futures_connection.get do |req|
|
|
447
|
+
req.url "/fapi/v1/income"
|
|
448
|
+
req.headers = headers
|
|
449
|
+
req.params = {
|
|
450
|
+
symbol: symbol, incomeType: income_type,
|
|
451
|
+
startTime: start_time, endTime: end_time,
|
|
452
|
+
page: page, limit: limit,
|
|
453
|
+
recvWindow: recv_window, timestamp: timestamp_ms
|
|
454
|
+
}.compact
|
|
455
|
+
req.params[:signature] = sign_params(req.params)
|
|
456
|
+
end
|
|
457
|
+
response.body
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def coin_futures_income_history(symbol: nil, income_type: nil, start_time: nil, end_time: nil,
|
|
462
|
+
page: nil, limit: 1000, recv_window: 5000)
|
|
463
|
+
with_rescue do
|
|
464
|
+
response = coin_futures_connection.get do |req|
|
|
465
|
+
req.url "/dapi/v1/income"
|
|
466
|
+
req.headers = headers
|
|
467
|
+
req.params = {
|
|
468
|
+
symbol: symbol, incomeType: income_type,
|
|
469
|
+
startTime: start_time, endTime: end_time,
|
|
470
|
+
page: page, limit: limit,
|
|
471
|
+
recvWindow: recv_window, timestamp: timestamp_ms
|
|
472
|
+
}.compact
|
|
473
|
+
req.params[:signature] = sign_params(req.params)
|
|
474
|
+
end
|
|
475
|
+
response.body
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# --- Simple Earn ---
|
|
480
|
+
|
|
481
|
+
def simple_earn_flexible_subscriptions(product_id: nil, purchase_id: nil, asset: nil,
|
|
482
|
+
start_time: nil, end_time: nil, current: nil, size: nil)
|
|
483
|
+
with_rescue do
|
|
484
|
+
response = connection.get do |req|
|
|
485
|
+
req.url "/sapi/v1/simple-earn/flexible/history/subscriptionRecord"
|
|
486
|
+
req.headers = headers
|
|
487
|
+
req.params = {
|
|
488
|
+
productId: product_id, purchaseId: purchase_id, asset: asset,
|
|
489
|
+
startTime: start_time, endTime: end_time,
|
|
490
|
+
current: current, size: size, timestamp: timestamp_ms
|
|
491
|
+
}.compact
|
|
492
|
+
req.params[:signature] = sign_params(req.params)
|
|
493
|
+
end
|
|
494
|
+
response.body
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def simple_earn_flexible_redemptions(product_id: nil, redeem_id: nil, asset: nil,
|
|
499
|
+
start_time: nil, end_time: nil, current: nil, size: nil)
|
|
500
|
+
with_rescue do
|
|
501
|
+
response = connection.get do |req|
|
|
502
|
+
req.url "/sapi/v1/simple-earn/flexible/history/redemptionRecord"
|
|
503
|
+
req.headers = headers
|
|
504
|
+
req.params = {
|
|
505
|
+
productId: product_id, redeemId: redeem_id, asset: asset,
|
|
506
|
+
startTime: start_time, endTime: end_time,
|
|
507
|
+
current: current, size: size, timestamp: timestamp_ms
|
|
508
|
+
}.compact
|
|
509
|
+
req.params[:signature] = sign_params(req.params)
|
|
510
|
+
end
|
|
511
|
+
response.body
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def simple_earn_locked_subscriptions(product_id: nil, purchase_id: nil, asset: nil,
|
|
516
|
+
start_time: nil, end_time: nil, current: nil, size: nil)
|
|
517
|
+
with_rescue do
|
|
518
|
+
response = connection.get do |req|
|
|
519
|
+
req.url "/sapi/v1/simple-earn/locked/history/subscriptionRecord"
|
|
520
|
+
req.headers = headers
|
|
521
|
+
req.params = {
|
|
522
|
+
productId: product_id, purchaseId: purchase_id, asset: asset,
|
|
523
|
+
startTime: start_time, endTime: end_time,
|
|
524
|
+
current: current, size: size, timestamp: timestamp_ms
|
|
525
|
+
}.compact
|
|
526
|
+
req.params[:signature] = sign_params(req.params)
|
|
527
|
+
end
|
|
528
|
+
response.body
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def simple_earn_locked_redemptions(product_id: nil, redeem_id: nil, asset: nil,
|
|
533
|
+
start_time: nil, end_time: nil, current: nil, size: nil)
|
|
534
|
+
with_rescue do
|
|
535
|
+
response = connection.get do |req|
|
|
536
|
+
req.url "/sapi/v1/simple-earn/locked/history/redemptionRecord"
|
|
537
|
+
req.headers = headers
|
|
538
|
+
req.params = {
|
|
539
|
+
productId: product_id, redeemId: redeem_id, asset: asset,
|
|
540
|
+
startTime: start_time, endTime: end_time,
|
|
541
|
+
current: current, size: size, timestamp: timestamp_ms
|
|
542
|
+
}.compact
|
|
543
|
+
req.params[:signature] = sign_params(req.params)
|
|
544
|
+
end
|
|
545
|
+
response.body
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# --- Transfers ---
|
|
550
|
+
|
|
551
|
+
def universal_transfer_history(type:, start_time: nil, end_time: nil, current: nil, size: nil, recv_window: 5000)
|
|
552
|
+
with_rescue do
|
|
553
|
+
response = connection.get do |req|
|
|
554
|
+
req.url "/sapi/v1/asset/transfer"
|
|
555
|
+
req.headers = headers
|
|
556
|
+
req.params = {
|
|
557
|
+
type: type, startTime: start_time, endTime: end_time,
|
|
558
|
+
current: current, size: size,
|
|
559
|
+
recvWindow: recv_window, timestamp: timestamp_ms
|
|
560
|
+
}.compact
|
|
561
|
+
req.params[:signature] = sign_params(req.params)
|
|
562
|
+
end
|
|
563
|
+
response.body
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
366
567
|
private
|
|
367
568
|
|
|
569
|
+
def usdt_futures_connection
|
|
570
|
+
@usdt_futures_connection ||= build_client_connection("https://fapi.binance.com")
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
def coin_futures_connection
|
|
574
|
+
@coin_futures_connection ||= build_client_connection("https://dapi.binance.com")
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def normalize_order(order_id, raw)
|
|
578
|
+
order_type = parse_order_type(raw["type"])
|
|
579
|
+
side = raw["side"]&.downcase&.to_sym
|
|
580
|
+
status = parse_order_status(raw["status"])
|
|
581
|
+
|
|
582
|
+
amount = BigDecimal(raw["origQty"].to_s)
|
|
583
|
+
amount = nil if amount.zero?
|
|
584
|
+
quote_amount = BigDecimal(raw["origQuoteOrderQty"].to_s)
|
|
585
|
+
quote_amount = nil if quote_amount.zero?
|
|
586
|
+
|
|
587
|
+
amount_exec = BigDecimal(raw["executedQty"].to_s)
|
|
588
|
+
quote_amount_exec = BigDecimal(raw["cummulativeQuoteQty"].to_s)
|
|
589
|
+
quote_amount_exec = nil if quote_amount_exec.negative?
|
|
590
|
+
|
|
591
|
+
price = BigDecimal(raw["price"].to_s)
|
|
592
|
+
if price.zero? && quote_amount_exec&.positive? && amount_exec.positive?
|
|
593
|
+
price = quote_amount_exec / amount_exec
|
|
594
|
+
end
|
|
595
|
+
price = nil if price.zero?
|
|
596
|
+
|
|
597
|
+
{
|
|
598
|
+
order_id: order_id,
|
|
599
|
+
status: status,
|
|
600
|
+
side: side,
|
|
601
|
+
order_type: order_type,
|
|
602
|
+
price: price,
|
|
603
|
+
amount: amount,
|
|
604
|
+
quote_amount: quote_amount,
|
|
605
|
+
amount_exec: amount_exec,
|
|
606
|
+
quote_amount_exec: quote_amount_exec,
|
|
607
|
+
raw: raw
|
|
608
|
+
}
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def parse_order_type(type)
|
|
612
|
+
case type
|
|
613
|
+
when "MARKET" then :market
|
|
614
|
+
when "LIMIT" then :limit
|
|
615
|
+
else :unknown
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def parse_order_status(status)
|
|
620
|
+
case status
|
|
621
|
+
when "PENDING_CANCEL" then :unknown
|
|
622
|
+
when "NEW", "PENDING_NEW", "PARTIALLY_FILLED" then :open
|
|
623
|
+
when "FILLED" then :closed
|
|
624
|
+
when "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH" then :cancelled
|
|
625
|
+
when "REJECTED" then :failed
|
|
626
|
+
else :unknown
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
|
|
368
630
|
def validate_trading_credentials
|
|
369
631
|
# Try cancelling a non-existent order — error -2011 (ORDER_DOES_NOT_EXIST) means key is valid with trade permission
|
|
370
632
|
result = cancel_order(symbol: "ETHBTC", order_id: "9999999999")
|
|
@@ -4,6 +4,39 @@ module Honeymaker
|
|
|
4
4
|
module Clients
|
|
5
5
|
class BinanceUs < Binance
|
|
6
6
|
URL = "https://api.binance.us"
|
|
7
|
+
|
|
8
|
+
# --- Staking ---
|
|
9
|
+
|
|
10
|
+
def staking_history(staking_type: nil, asset: nil, start_time: nil, end_time: nil, page: nil, limit: nil)
|
|
11
|
+
with_rescue do
|
|
12
|
+
response = connection.get do |req|
|
|
13
|
+
req.url "/staking/v1/history"
|
|
14
|
+
req.headers = headers
|
|
15
|
+
req.params = {
|
|
16
|
+
stakingType: staking_type, asset: asset,
|
|
17
|
+
startTime: start_time, endTime: end_time,
|
|
18
|
+
page: page, limit: limit, timestamp: timestamp_ms
|
|
19
|
+
}.compact
|
|
20
|
+
req.params[:signature] = sign_params(req.params)
|
|
21
|
+
end
|
|
22
|
+
response.body
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def staking_rewards_history(asset: nil, start_time: nil, end_time: nil, page: nil, limit: nil)
|
|
27
|
+
with_rescue do
|
|
28
|
+
response = connection.get do |req|
|
|
29
|
+
req.url "/staking/v1/rewardsHistory"
|
|
30
|
+
req.headers = headers
|
|
31
|
+
req.params = {
|
|
32
|
+
asset: asset, startTime: start_time, endTime: end_time,
|
|
33
|
+
page: page, limit: limit, timestamp: timestamp_ms
|
|
34
|
+
}.compact
|
|
35
|
+
req.params[:signature] = sign_params(req.params)
|
|
36
|
+
end
|
|
37
|
+
response.body
|
|
38
|
+
end
|
|
39
|
+
end
|
|
7
40
|
end
|
|
8
41
|
end
|
|
9
42
|
end
|