[FEATURE] Implement Binance authenticated order execution - closes #85#92
Conversation
Adds _sign() (HMAC-SHA256) and _signed_request() helpers to BinanceExchange. Implements 4 previously stubbed methods: - place_order: POST /api/v3/order, market and limit, compound order_id format - get_balance: GET /api/v3/account, filters zero balances - get_order_status: GET /api/v3/order, requires SYMBOL:ID format - cancel_order: DELETE /api/v3/order, handles -2011 (already filled) Signature verified against Binance documentation reference vector. 23 unit tests, all mocked (no real credentials required).
bb8bc61 to
c90ef0a
Compare
Manual Testing Guide — PR #92: Binance Authenticated Order ExecutionPrerequisitesgit fetch fork && git checkout feat/85-binance-order-execution
cd app
pip install python-binance # or equivalent Binance SDK1. Run unit tests (mock-based, no live keys needed)python -m pytest test_binance_order*.py -v 2>/dev/null || python -m pytest -k "binance" -vExpected: All tests pass without real API keys. 2. Smoke test — import and instantiate (offline)python -c "
from pt_binance_orders import BinanceOrderExecutor # adjust class name if needed
print('import ok')
# Should not attempt network connection at import or instantiation
exec = BinanceOrderExecutor(api_key='test', api_secret='test', testnet=True)
print('instantiation ok')
"3. Live test (optional — needs Binance testnet keys)If you have Binance testnet credentials: python -c "
from pt_binance_orders import BinanceOrderExecutor
exec = BinanceOrderExecutor(
api_key='YOUR_TESTNET_KEY',
api_secret='YOUR_TESTNET_SECRET',
testnet=True
)
# Place a small test order on testnet
result = exec.place_market_order('BTCUSDT', side='BUY', quantity=0.001)
print('order result:', result)
"Expected on testnet: Order placed and returned with an order ID. Rollbackgit checkout main -- app/pt_binance_orders.py |
|
Please hold this before merging. The Binance order code was sending raw float quantities/prices without symbol-aware rounding, which would get rejected by Binance with their cryptic -1013 filter error on most real pairs. Adding LOT_SIZE/PRICE_FILTER/MIN_NOTIONAL handling first so live orders actually go through. Update coming. |
Raw float quantities and prices were sent without symbol-aware rounding, causing Binance to reject many real orders with -1013 (Filter failure). - Fetch and cache per-symbol filters from /api/v3/exchangeInfo on first use; reuse cached dict for subsequent orders. - Round quantity DOWN to LOT_SIZE.stepSize (truncate, never round up so qty cannot exceed user intent). - Round limit price DOWN to PRICE_FILTER.tickSize. - Reject locally with RuntimeError when quantity < minQty or quantity * price < minNotional, surfacing a useful message instead of Binance's opaque -1013 / -1100. - Handle both legacy MIN_NOTIONAL and renamed NOTIONAL filter types (Binance migration in 2023). - Add 5 tests covering rounding, rejection, and filter caching.
|
LOT_SIZE / PRICE_FILTER / MIN_NOTIONAL handling is in. BinanceExchange now pulls each symbol's filters from /api/v3/exchangeInfo on first use and caches them, rounds the quantity DOWN to the LOT_SIZE step and the price DOWN to the PRICE_FILTER tick (never up, so we can't accidentally overshoot what the user asked for), and rejects the order locally with a clear message if it would fail minQty or minNotional. Also handles both the legacy MIN_NOTIONAL filter name and the renamed NOTIONAL one (Binance switched in 2023), so older and newer pairs both work. Added 5 new tests covering rounding, rejection, and caching - 28 total passing now. |
|
@copilot review |
… limit, all order types, OCO, listenKey Brings BinanceExchange to full feature parity with Binance Spot API docs so PR #92 covers everything needed before the broker selector in #96 wires it to the GUI. REST + auth - testnet=True flag in constructor flips REST base to testnet.binance.vision and WebSocket base to stream.testnet.binance.vision/ws. Default stays production. - recv_window kwarg (1..60000ms per Binance limits) auto-injected into every signed request alongside the server-time-corrected timestamp. - sync_time() hits GET /api/v3/time once on first signed call to cache the local-vs- server offset; -1021 responses trigger one auto-resync + retry before raising BinanceTimestampError. - HTTP 429 / 418 now raise BinanceRateLimitError carrying the status code and the Retry-After header so the trader loop can back off instead of hammering and earning a longer ban. - X-MBX-USED-WEIGHT-* and X-MBX-ORDER-COUNT-* response headers captured into last_rate_limit_headers for monitoring. Order types - place_order extended to support every Binance spot type: MARKET, LIMIT, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER. - Per-type required-field validation (price for limits, stop_price/trailing_delta for stops, both-vs-neither guard for MARKET amount/quoteOrderQty). - New kwargs: order_type, stop_price, time_in_force, iceberg_qty, quote_order_qty, client_order_id, trailing_delta. Legacy (symbol, side, amount[, price]) form still auto-derives MARKET/LIMIT for backwards compat. - LOT_SIZE / PRICE_FILTER / MIN_NOTIONAL rounding applies to every leg. OCO - place_oco_order targets the modern POST /api/v3/orderList/oco with the aboveType / belowType schema (Binance retired the flat /order/oco shape in 2024). - cancel_order_list deletes the whole list via DELETE /api/v3/orderList. User-data stream - create_listen_key / keepalive_listen_key / close_listen_key cover the POST/PUT/DELETE /api/v3/userDataStream lifecycle via a new _public_request helper (API-key header, no signature). - user_data_stream_url returns the right wss:// URL for prod or testnet. Broker-selector hooks (for #96) - test_connection() returns bool by probing /api/v3/account, never raising. - get_masked_api_key() returns "****<last4>" for the per-broker UI row. Tests - 66 unit tests, all mock-based, no real Binance credentials. Covers testnet toggle, time-sync offset, -1021 retry success + persistent-failure path, 429/418 rate-limit raising, weight-header capture, every new order type, validation errors, OCO place + cancel, listenKey lifecycle, broker-selector helpers, plus the existing 28 from previous round.
Round 4 - Full Binance Spot REST coverage per docsPushed What landedAuth + transport
Order types
OCO
User-data stream (listenKey)
Hooks for the broker selector (#96)
Tests66 unit tests, all mock-based, no real Binance credentials. CI is green. Local run: ``` New test classes added on top of the existing 28:
Docs followedCross-referenced the official Binance Spot API docs ( Still out of scope for this PR
This PR remains the Binance REST foundation; #96 will consume it. |
Summary
Implements the 4 stubbed
NotImplementedErrormethods inBinanceExchangeusing HMAC-SHA256 signed requests.Changes
app/pt_exchanges.py—BinanceExchange._sign(),_signed_request(),place_order(),get_balance(),get_order_status(),cancel_order()app/test_binance_exchange.py— 23 tests (all mocked, no real credentials)API endpoints
place_orderPOST /api/v3/orderSYMBOL:IDget_balanceGET /api/v3/accountget_order_statusGET /api/v3/orderSYMBOL:IDformatcancel_orderDELETE /api/v3/orderFalseon error -2011 (already filled)Signature verified against Binance official documentation test vector.
Test plan
RuntimeErrorRuntimeErrorwith error codeplace_orderaddsSYMBOL:prefix for downstream lookupCloses #85