honeymaker 0.9.7 → 0.9.10
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/lib/honeymaker/clients/kraken.rb +64 -0
- data/lib/honeymaker/version.rb +1 -1
- data/test/honeymaker/clients/kraken_client_test.rb +47 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bc5bacd0fa2240b323691f78b301b4aadf6d4e13229a05ab1edb621e20aaa89d
|
|
4
|
+
data.tar.gz: b1a21915e693afd3cadb1e40aa317991db4ea667f08f82974d9607d60770be2e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d25a9d2f3a4626d684c10b0eda8eecd3c57ca037506869bce682e8c0f2516c61b1122b1536c899f729c7a2642ea3a3ba9427ef816cab8e8f7b1fede7818cf979
|
|
7
|
+
data.tar.gz: 5e4f0f34aef020640c8d9deea8fb8b1bb596cbc0b37c477005313590d10a55b5c51f2d144e21dfad0ba182929e7a2df470026fb0a7da39e4b6e44f3a5221b699
|
|
@@ -121,6 +121,47 @@ module Honeymaker
|
|
|
121
121
|
})
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
+
# Reconcile orders that QueryOrders has already dropped (Kraken removes terminal
|
|
125
|
+
# orders within hours) against TradesHistory, which is authoritative for executions
|
|
126
|
+
# and effectively unbounded in retention. Returns ONLY orders that actually executed,
|
|
127
|
+
# keyed by ordertxid, each as a per-order executed aggregate. Orders with no trades
|
|
128
|
+
# (never filled / truly gone) are simply absent from the result.
|
|
129
|
+
#
|
|
130
|
+
# `start` should be a unix timestamp bounding the lookback (e.g. the order's creation
|
|
131
|
+
# time) so we don't page the user's entire trade history.
|
|
132
|
+
def closed_orders_from_trades(order_ids:, start: nil, max_pages: 20)
|
|
133
|
+
wanted = Array(order_ids)
|
|
134
|
+
return Result::Success.new({}) if wanted.empty?
|
|
135
|
+
|
|
136
|
+
by_order = Hash.new { |h, k| h[k] = [] }
|
|
137
|
+
offset = 0
|
|
138
|
+
pages = 0
|
|
139
|
+
loop do
|
|
140
|
+
result = get_trades_history(start: start, ofs: offset)
|
|
141
|
+
return result if result.failure?
|
|
142
|
+
|
|
143
|
+
errors = result.data["error"]
|
|
144
|
+
return Result::Failure.new(*errors) if errors.is_a?(Array) && errors.any?
|
|
145
|
+
|
|
146
|
+
trades = result.data.dig("result", "trades") || {}
|
|
147
|
+
break if trades.empty?
|
|
148
|
+
|
|
149
|
+
trades.each_value do |t|
|
|
150
|
+
otxid = t["ordertxid"]
|
|
151
|
+
by_order[otxid] << t if wanted.include?(otxid)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Do NOT early-exit when each id has been *seen* — a partial fill can have trades on
|
|
155
|
+
# later pages. Page the whole [start, now] window (bounded by `start` + max_pages).
|
|
156
|
+
pages += 1
|
|
157
|
+
offset += trades.size
|
|
158
|
+
count = result.data.dig("result", "count").to_i
|
|
159
|
+
break if offset >= count || pages >= max_pages
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
Result::Success.new(by_order.transform_values { |trades| aggregate_trades(trades) })
|
|
163
|
+
end
|
|
164
|
+
|
|
124
165
|
def get_withdraw_addresses(asset: nil, method: nil)
|
|
125
166
|
post_private("/0/private/WithdrawAddresses", { nonce: nonce, asset: asset, method: method })
|
|
126
167
|
end
|
|
@@ -145,6 +186,29 @@ module Honeymaker
|
|
|
145
186
|
|
|
146
187
|
private
|
|
147
188
|
|
|
189
|
+
def aggregate_trades(trades)
|
|
190
|
+
first = trades.first
|
|
191
|
+
vol = trades.sum { |t| BigDecimal(t["vol"].to_s) }
|
|
192
|
+
cost = trades.sum { |t| BigDecimal(t["cost"].to_s) }
|
|
193
|
+
fee = trades.sum { |t| BigDecimal(t["fee"].to_s) }
|
|
194
|
+
{
|
|
195
|
+
order_id: first["ordertxid"],
|
|
196
|
+
status: :closed, # only executed orders reach here
|
|
197
|
+
side: first["type"]&.to_sym, # buy / sell
|
|
198
|
+
order_type: parse_order_type(first["ordertype"]),
|
|
199
|
+
price: vol.zero? ? nil : cost / vol, # VWAP across (partial) fills
|
|
200
|
+
amount: nil, # original order size unknown from trades
|
|
201
|
+
quote_amount: nil,
|
|
202
|
+
amount_exec: vol,
|
|
203
|
+
quote_amount_exec: cost,
|
|
204
|
+
fee: fee,
|
|
205
|
+
pair: first["pair"],
|
|
206
|
+
trade_count: trades.size,
|
|
207
|
+
last_trade_at: trades.map { |t| t["time"].to_f }.max,
|
|
208
|
+
raw: { "trades" => trades }
|
|
209
|
+
}
|
|
210
|
+
end
|
|
211
|
+
|
|
148
212
|
def normalize_order(order_id, raw)
|
|
149
213
|
descr = raw["descr"] || {}
|
|
150
214
|
order_type = parse_order_type(descr["ordertype"])
|
data/lib/honeymaker/version.rb
CHANGED
|
@@ -126,6 +126,53 @@ class Honeymaker::Clients::KrakenTest < Minitest::Test
|
|
|
126
126
|
assert_equal first_key_first_nonce, second_key_first_nonce
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
def test_closed_orders_from_trades_aggregates_partial_fills
|
|
130
|
+
page = { "error" => [], "result" => { "count" => 2, "trades" => {
|
|
131
|
+
"TID-1" => { "ordertxid" => "OABC", "pair" => "XXBTZUSD", "type" => "buy",
|
|
132
|
+
"ordertype" => "limit", "price" => "60000.0", "cost" => "6.0",
|
|
133
|
+
"vol" => "0.0001", "fee" => "0.01", "time" => 1_700_000_000.0 },
|
|
134
|
+
"TID-2" => { "ordertxid" => "OABC", "pair" => "XXBTZUSD", "type" => "buy",
|
|
135
|
+
"ordertype" => "limit", "price" => "61000.0", "cost" => "4.0",
|
|
136
|
+
"vol" => "0.00006557", "fee" => "0.006", "time" => 1_700_000_100.0 } } } }
|
|
137
|
+
stub_connection(:post, page)
|
|
138
|
+
result = @client.closed_orders_from_trades(order_ids: ["OABC"], start: 1_699_999_000)
|
|
139
|
+
assert result.success?
|
|
140
|
+
agg = result.data["OABC"]
|
|
141
|
+
assert_equal :closed, agg[:status]
|
|
142
|
+
assert_equal :buy, agg[:side]
|
|
143
|
+
assert_equal :limit, agg[:order_type]
|
|
144
|
+
assert_equal BigDecimal("10.0"), agg[:quote_amount_exec] # 6 + 4
|
|
145
|
+
assert_equal BigDecimal("0.00016557"), agg[:amount_exec] # summed vol
|
|
146
|
+
assert_equal BigDecimal("0.016"), agg[:fee] # 0.01 + 0.006
|
|
147
|
+
assert_equal BigDecimal("10.0") / BigDecimal("0.00016557"), agg[:price] # VWAP = cost/vol
|
|
148
|
+
assert_equal "XXBTZUSD", agg[:pair]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_closed_orders_from_trades_ignores_unrelated_orders
|
|
152
|
+
stub_connection(:post, { "error" => [], "result" => { "count" => 1, "trades" => {
|
|
153
|
+
"TID-9" => { "ordertxid" => "OOTHER", "pair" => "XXBTZUSD", "type" => "buy",
|
|
154
|
+
"ordertype" => "limit", "price" => "1", "cost" => "1", "vol" => "1",
|
|
155
|
+
"fee" => "0", "time" => 1_700_000_000.0 } } } })
|
|
156
|
+
result = @client.closed_orders_from_trades(order_ids: ["OABC"], start: 1)
|
|
157
|
+
assert result.success?
|
|
158
|
+
assert_empty result.data
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_closed_orders_from_trades_aggregates_partial_fills_across_pages
|
|
162
|
+
page1 = Honeymaker::Result::Success.new({ "error" => [], "result" => { "count" => 2, "trades" => {
|
|
163
|
+
"TID-1" => { "ordertxid" => "OABC", "pair" => "XXBTZUSD", "type" => "buy", "ordertype" => "limit",
|
|
164
|
+
"price" => "60000.0", "cost" => "6.0", "vol" => "0.0001", "fee" => "0.01", "time" => 1.0 } } } })
|
|
165
|
+
page2 = Honeymaker::Result::Success.new({ "error" => [], "result" => { "count" => 2, "trades" => {
|
|
166
|
+
"TID-2" => { "ordertxid" => "OABC", "pair" => "XXBTZUSD", "type" => "buy", "ordertype" => "limit",
|
|
167
|
+
"price" => "61000.0", "cost" => "4.0", "vol" => "0.00006557", "fee" => "0.006", "time" => 2.0 } } } })
|
|
168
|
+
@client.stubs(:get_trades_history).returns(page1, page2) # Mocha returns successive values per call
|
|
169
|
+
result = @client.closed_orders_from_trades(order_ids: ["OABC"], start: 1, max_pages: 5)
|
|
170
|
+
assert result.success?
|
|
171
|
+
agg = result.data["OABC"]
|
|
172
|
+
assert_equal BigDecimal("10.0"), agg[:quote_amount_exec] # 6 + 4 across both pages
|
|
173
|
+
assert_equal BigDecimal("0.00016557"), agg[:amount_exec]
|
|
174
|
+
end
|
|
175
|
+
|
|
129
176
|
private
|
|
130
177
|
|
|
131
178
|
def assert_strictly_increasing(values)
|