feat(clob): full CLOB order-type support (time-in-force, stp, MARKET)#10
Merged
Conversation
Phase 1 of full CLOB order-type support. - New core/order_types.py: Side, OrderType (MARKET/LIMIT only), TimeInForce (GTC/FOK/IOC/PO), SelfTradePrevention, OrderStatus IntEnums with on-chain integer values; name<->int maps; alias-aware parsers (parse_side/parse_order_type/parse_time_in_force/parse_stp); and validate_order_combo() encoding the type1 x type2 matrix. - clob.py read paths (_transform_order_from_api, _format_order_data) now route through the shared maps via _enum_to_name -> enum_int_to_name, eliminating four duplicated inline enum dicts that could drift. - Remove the speculative 2:"STOP"/3:"STOPLIMIT" labels: those values do not exist in the on-chain OrderType enum and the NewOrder struct has no trigger-price field. Unknown integers now surface as "UNKNOWN(<n>)" instead of a fabricated label. Pure additive refactor — no write-path behavior change. Full unit coverage for the new module; make test/lint/mypy green.
Phase 2 of full CLOB order-type support.
- add_order gains optional time_in_force ("GTC" default / FOK / IOC / PO,
aliases accepted) and stp ("CANCEL_TAKER" default / CANCEL_MAKER /
CANCEL_BOTH / CANCEL_NONE) params. The previously hardcoded type2=0 /
stp=0 in the NewOrder struct are now resolved from these.
- New _resolve_order_modifiers helper parses both modifiers and runs
validate_order_combo, rejecting invalid (type1, type2, price) combos
client-side before any transaction is sent.
- MARKET hardening: a MARKET order must be IOC/FOK and carry no price; the
pre-flight balance check is skipped for a MARKET BUY (quote notional is
unknown without a price) and retained everywhere else.
- Defaults preserve today's behavior (GTC, CANCEL_TAKER, LIMIT) — no
existing call site changes.
Tests cover each TIF/alias, each stp mode, MARKET IOC/FOK balance
handling, and pre-flight rejection of invalid combos. make test/lint/mypy
green.
Phase 3 of full CLOB order-type support. - _build_order_tuple now resolves per-order order_type (default LIMIT), time_in_force (default GTC) and stp (default CANCEL_TAKER) via the shared _resolve_order_modifiers helper, validating each combo, and also returns the order's balance requirement (None for a MARKET BUY, whose notional is unknown). _process_orders_for_batch aggregates that instead of computing price*amount inline. - _process_replacement gains the same per-replacement order_type/ time_in_force/stp handling and MARKET-BUY balance skip. - Both paths previously hardcoded type1=1/type2=0/stp=0. - Add add_order_list as a clearer alias for add_limit_order_list, since the batch path is no longer limit-only; docstrings document the new keys. Defaults preserve today's behavior. Tests cover mixed-type batches, per-order TIF/stp, batch-wide rejection of invalid combos, and TIF/stp in cancel_add_list. make test/lint/mypy green.
Phase 4 of full CLOB order-type support. - replace_order docstring now states that cancelReplaceOrder carries only a new price and quantity, so the replacement inherits the original order's type1/time_in_force/stp; callers wanting to change those should use cancel_add_list. No new params (the contract cannot carry them). - Add tests confirming the order-type revert codes (T-POOA-01, T-T2PO-01, T-STPR-01, T-FOKF-01) are present in errors.json, that _parse_revert_reason maps a raw revert to "<code>: <description>", and that sanitize_error_message preserves the code. No production logic change beyond the docstring — the revert mapping already existed via _parse_revert_reason. make test/lint/mypy green.
Phase 5 of full CLOB order-type support. - README: new "Order Types & Time-in-Force" section (order_type / time_in_force / stp params, combo-validity table, MARKET/PO/stp examples, add_order_list alias, replace inheritance note); updated add_order signature in the affected-methods list; states STOP/STOPLIMIT are not supported. - CLAUDE.md: five new Non-Obvious Decisions (order_types.py as the single enum home; STOP/STOPLIMIT absent on-chain; TIF/stp defaults + client-side combo validation; MARKET BUY balance skip; replace cannot change type/TIF/stp) and an order_types.py row in the Key File Reference. No new config/env var, so env.example is unchanged. Full suite (1110), lint, and mypy all green.
Close the six coverage gaps introduced by the order-type work, all error/edge branches: - invalid stp rejected in add_order (_resolve_order_modifiers stp path) - MARKET BUY in a batch (price 0, balance skipped) and invalid per-order order_type rejected (_build_order_tuple) - cancel_add_list MARKET BUY, plus invalid order_type / time_in_force rejection (_process_replacement) clob.py coverage 99% -> 100%. make test/lint/mypy green.
Pre-existing link issues surfaced by `make docs-build` (unrelated to the order-type work): - python-sdk-user-guide.md: escape `Result\[T\]` heading so Markdown stops parsing `[T]` as an undefined reference link; repoint the README link to the GitHub URL (../README.md is outside docs_dir). - sdk-caching.md: repoint the stale monorepo README paths to the Python and TypeScript GitHub READMEs. - websocket.md: link to the in-site rest-api.md instead of the non-existent /en/apiv2/RestApi.md. `make docs-build` now reports "No issues found".
Synced via scripts/version_manager.py across VERSION, pyproject.toml, src/dexalot_sdk/__init__.py, and uv.lock. Minor bump for the new CLOB order-type surface (time_in_force / stp / MARKET hardening).
Verified against Dexalot/contracts ITradePairs.sol: the contract Type1
enum is {MARKET, LIMIT, STOP, STOPLIMIT} — STOP/STOPLIMIT are real enum
members (reserved/unused, no trigger-price field, never enabled per pair),
not fabricated labels. An earlier change dropped them from the read map on
the mistaken premise that they don't exist on-chain.
Read paths now label type1 2/3 as STOP/STOPLIMIT again (faithful to the
contract enum), while the write-side OrderType enum still omits them so the
SDK never originates a stop order. Tests/docs updated in a later commit.
Verified against Dexalot/contracts TradePairs.sol: the contract does NOT
reject MARKET+GTC/PO or a MARKET order carrying a price — it ignores type2
and price for MARKET (no revert). The previous client-side rules ("MARKET
must be IOC/FOK", "MARKET must not specify a price") were stricter than the
chain and, because time_in_force defaults to GTC, rejected the natural
default MARKET order.
validate_order_combo now enforces only the contract-faithful invariant —
LIMIT requires a price — and defers everything else (per-pair allowed
types, Post-Only, self-trade, FOK fill) to on-chain reverts. Tests/docs
updated in a later commit.
The contract STP enum spells modes without underscores (CANCELTAKER, CANCELMAKER, CANCELBOTH) and uses bare NONE. Accept those spellings as input aliases alongside the readable CANCEL_* canonical names, so values copied from the contract parse directly. Also note in the enum docstring that the integer values are now confirmed against ITradePairs.sol.
Follow-up to the three contract-reconciliation fixes. Tests: - read map: assert type1 2/3 label as STOP/STOPLIMIT; write enum still omits them; genuinely-unknown ints -> UNKNOWN(<n>). - validate_order_combo: MARKET is permissive (GTC/PO/price all accepted); only LIMIT-requires-price enforced (single + batch). - add_order: default MARKET (GTC) succeeds; unknown time_in_force/stp still rejected pre-send. - parse_stp: accept contract spellings CANCELTAKER/CANCELMAKER/CANCELBOTH. Docs (README + CLAUDE.md): - STOP/STOPLIMIT are reserved-but-unplaceable contract enum members (not "do not exist"); reads label them, writes omit them. - Replace the IOC/FOK-only MARKET combo table with the actual rule (LIMIT requires price; MARKET ignores type2/price; PO/allowed-type/FOK/STP enforced on-chain). Note stp contract-spelling aliases. clob.py and order_types.py at 100% coverage; full suite (1117), lint, mypy, and docs-build all green.
hsyndeniz
approved these changes
Jun 17, 2026
ilkerulutas
approved these changes
Jun 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
core/order_types.pyas the single home for order enums (Side,OrderType,TimeInForceGTC/FOK/IOC/PO,SelfTradePrevention,OrderStatus), alias-aware parsers, andvalidate_order_combo; read and write paths share these maps so the integer↔label mapping cannot drift.add_ordergains optionaltime_in_force(default GTC) andstp(default CANCEL_TAKER), replacing the hardcodedtype2=0/stp=0.add_limit_order_list,cancel_add_list) to honour per-orderorder_type/time_in_force/stp; addadd_order_listas a clearer alias.replace_orderinherits the original order's type/TIF/stp (the contract'scancelReplaceOrdercarries only price and quantity).Dexalot/contracts(ITradePairs.sol/TradePairs.sol):type2andstpinteger values confirmed exact;STOP/STOPLIMITare reserved-but-unplaceableType1members (reads label them, writes omit them); relaxed client-side validation to the one rule the contract relies on (LIMIT requires a price); accept contract-spelling stp aliases.make docs-build.Quality Gates
Behavioural changes
time_in_forceandstpparameters are optional and default to today's behaviour (GTC / CANCEL_TAKER / LIMIT); no existing call site changes.time_in_forceand any supplied price on-chain, so the SDK no longer pre-rejects MARKET+GTC/PO or a MARKET order with a price (an earlier revision rejected the default MARKET order — fixed here). Per-pair allowed types, Post-Only, self-trade and FOK rules are enforced on-chain and surface as reverts (T-IVOT-01,T-POOA-01,T-T2PO-01,T-FOKF-01,T-STPR-01).type12/3 asSTOP/STOPLIMIT(faithful to the contractType1enum); the write API accepts only MARKET/LIMIT, so the SDK never originates a stop order. Genuinely-unrecognised enum integers render asUNKNOWN(<n>).