Skip to content

fix: OKX spot orders below 1 unit rejected with 51000 (sz precision misread as 0 for small lotSz) - #159

Open
lollipopkit wants to merge 2 commits into
OpenByteInc:mainfrom
lollipopkit:fix/okx-lotsz-precision
Open

fix: OKX spot orders below 1 unit rejected with 51000 (sz precision misread as 0 for small lotSz)#159
lollipopkit wants to merge 2 commits into
OpenByteInc:mainfrom
lollipopkit:fix/okx-lotsz-precision

Conversation

@lollipopkit

Copy link
Copy Markdown

Problem

Placing a spot market order through the OKX adapter fails with:

OKX error: {'code': '1', 'data': [{'sCode': '51000', 'sMsg': 'Parameter sz error', ...}], 'msg': 'All operations failed'}

This affects any spot order below one whole base unit — e.g. a 50 USDT quick-trade buy of BTC/USDT (~0.0008 BTC).

Root cause

OkxClient._normalize_order_size() infers the size precision from lotSz by string-parsing the normalized Decimal:

lot_sz_normalized = lot_sz.normalize()   # Decimal('0.00000001') -> Decimal('1E-8')
lot_sz_str = str(lot_sz_normalized)      # '1E-8' — no '.' present
if '.' in lot_sz_str:                    # not taken
    ...
else:
    size_precision = 0                   # precision misread as 0

Decimal.normalize() renders small steps in scientific notation, so BTC-USDT spot (lotSz=0.00000001) yields precision 0. _dec_str(sz, strict_precision=0) then quantizes 0.00081471 down to sz="0", which OKX rejects with 51000.

Fix

Derive the precision from the Decimal exponent instead of parsing the string representation:

exp = lot_sz.normalize().as_tuple().exponent
if isinstance(exp, int):
    size_precision = min(max(0, -exp), 18)

Handles 0.00000001 → 8, 0.000001 → 6, 1 → 0 (whole swap contracts), 10 → 0.

Testing

  • Added tests/test_okx_order_size_precision.py (3 regression tests, instrument cache pre-seeded so no network needed) — all pass; the first case fails on the old code.
  • Verified end-to-end against OKX demo trading (x-simulated-trading: 1) via /api/quick-trade/place-order: a 50 USDT market buy of BTC/USDT that previously failed with 51000 now fills correctly (0.00081677 BTC @ 61246).
Decimal.normalize() renders lotSz values like 0.00000001 in scientific
notation (1E-8). The string-based precision parser found no '.' in
'1E-8' and fell back to precision 0, so any spot order below one whole
unit was quantized to sz="0" and rejected by OKX with error 51000
(Parameter sz error). Derive precision from the Decimal exponent
instead, and add regression tests.
@lollipopkit
lollipopkit marked this pull request as ready for review July 2, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant