Skip to content

[FEATURE] Implement Binance authenticated order execution - closes #85#92

Merged
sjackson0109 merged 7 commits into
sjackson0109:mainfrom
Ibrahim-3d:feat/85-binance-order-execution
May 24, 2026
Merged

[FEATURE] Implement Binance authenticated order execution - closes #85#92
sjackson0109 merged 7 commits into
sjackson0109:mainfrom
Ibrahim-3d:feat/85-binance-order-execution

Conversation

@Ibrahim-3d

Copy link
Copy Markdown
Collaborator

Summary

Implements the 4 stubbed NotImplementedError methods in BinanceExchange using HMAC-SHA256 signed requests.

Changes

  • Modified: app/pt_exchanges.pyBinanceExchange._sign(), _signed_request(), place_order(), get_balance(), get_order_status(), cancel_order()
  • New: app/test_binance_exchange.py — 23 tests (all mocked, no real credentials)

API endpoints

Method Endpoint Notes
place_order POST /api/v3/order Market + limit; order_id returned as SYMBOL:ID
get_balance GET /api/v3/account Returns non-zero balances only
get_order_status GET /api/v3/order Requires SYMBOL:ID format
cancel_order DELETE /api/v3/order Returns False on error -2011 (already filled)

Signature verified against Binance official documentation test vector.

Test plan

  • 23 unit tests pass (all mocked)
  • HMAC-SHA256 signature verified against Binance docs reference
  • Missing credentials raises RuntimeError
  • API error responses raise RuntimeError with error code
  • place_order adds SYMBOL: prefix for downstream lookup

Closes #85

Copilot AI review requested due to automatic review settings May 18, 2026 16:10
@Ibrahim-3d Ibrahim-3d requested a review from sjackson0109 as a code owner May 18, 2026 16:10
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).
@Ibrahim-3d Ibrahim-3d force-pushed the feat/85-binance-order-execution branch from bb8bc61 to c90ef0a Compare May 18, 2026 18:39
@Ibrahim-3d

Copy link
Copy Markdown
Collaborator Author

Manual Testing Guide — PR #92: Binance Authenticated Order Execution

Prerequisites

git fetch fork && git checkout feat/85-binance-order-execution
cd app
pip install python-binance  # or equivalent Binance SDK

1. 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" -v

Expected: 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.

Rollback

git checkout main -- app/pt_binance_orders.py

sjackson0109
sjackson0109 previously approved these changes May 19, 2026
@Ibrahim-3d

Ibrahim-3d commented May 21, 2026

Copy link
Copy Markdown
Collaborator Author

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.
@Ibrahim-3d

Ibrahim-3d commented May 23, 2026

Copy link
Copy Markdown
Collaborator Author

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.

@Ibrahim-3d

Copy link
Copy Markdown
Collaborator Author

@copilot review

Ibrahim-3d and others added 2 commits May 23, 2026 17:22
… 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.
@Ibrahim-3d

Copy link
Copy Markdown
Collaborator Author

Round 4 - Full Binance Spot REST coverage per docs

Pushed 4dd1e3... (or whatever the new commit hash is - see PR commits tab). This round closes the remaining follow-up items I called out earlier so this PR delivers the complete Binance Spot REST surface needed before #96 can route live orders here.

What landed

Auth + transport

  • testnet=True constructor flag switches REST base to testnet.binance.vision and WS base to stream.testnet.binance.vision/ws. Production stays default.
  • recv_window kwarg (1..60000ms per docs) auto-injected into every signed request.
  • sync_time() hits GET /api/v3/time once on first signed call, caches the local-vs-server offset, and on a -1021 response resyncs + retries exactly once before raising BinanceTimestampError. No more silent clock-skew failures.
  • HTTP 429 / 418 now raise BinanceRateLimitError(status_code, retry_after) honouring the Retry-After header so the trader loop can back off instead of earning a longer ban. X-MBX-USED-WEIGHT-* and X-MBX-ORDER-COUNT-* headers captured to last_rate_limit_headers for monitoring.

Order types

  • place_order now supports every Binance spot type: MARKET, LIMIT, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER.
  • New kwargs: order_type, stop_price, time_in_force, iceberg_qty, quote_order_qty, client_order_id, trailing_delta.
  • Per-type required-field validation so callers get a clean ValueError instead of Binance's -1102 / -1106.
  • Legacy (symbol, side, amount[, price]) form still auto-derives MARKET / LIMIT - existing callers unchanged.

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 (listenKey)

  • 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 - matches docs).
  • user_data_stream_url returns the right wss:// URL for prod or testnet so the WS consumer (out of scope here) can drop in.

Hooks for the broker selector (#96)

  • test_connection() returns bool by probing /api/v3/account, swallowing exceptions - safe to call from the Tk thread for the per-broker "Test connection" button.
  • get_masked_api_key() returns ****<last4> for the per-broker UI row.

Tests

66 unit tests, all mock-based, no real Binance credentials. CI is green. Local run:

```
python -m pytest app/test_binance_exchange.py -v
=== 66 passed ===
```

New test classes added on top of the existing 28:

  • TestBinanceTestnet - URL switching, recv_window clamping
  • TestBinanceServerTimeSync - offset application, -1021 retry success + persistent-failure paths
  • TestBinanceRateLimit - 429/418 raise behaviour, header capture
  • TestBinanceExtendedOrderTypes - all new types, validation errors
  • TestBinanceOCO - place + cancel, missing-id and missing-creds guards
  • TestBinanceListenKey - create/keepalive/close, WS URL prod vs testnet
  • TestBinanceBrokerSelectorHelpers - test_connection() and get_masked_api_key() for [ARCH] User-facing broker selector and trading-mode indicator #96

Docs followed

Cross-referenced the official Binance Spot API docs (developers.binance.com + binance/binance-spot-api-docs on GitHub) for every endpoint shape, parameter, error code, and rate-limit behaviour. The HMAC implementation matches the reference vector from the docs (test_sign_matches_reference asserts the exact c8db5...6b71 digest from the published curl example).

Still out of scope for this PR

This PR remains the Binance REST foundation; #96 will consume it.

@sjackson0109 sjackson0109 merged commit 178ff0b into sjackson0109:main May 24, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Implement Binance order execution (place_order, get_balance, cancel_order)

2 participants