When to Use
Use this skill when a trading system sends orders to CME Globex — futures and options
on futures such as ES, CL, ZB — and you are assembling the order entry message
yourself rather than handing an order to a vendor OMS that already fills in the
exchange-mandated fields. It covers the four gateway checks a hand-rolled CME client
most often gets wrong:
- Operator ID (Tag 50 / iLink 3
SenderID) absent or malformed — CME Rule 576. - Manual Order Indicator (Tag 1028) absent, or a team/ATS ID used on a manually entered order — CME Rule 536.B.
- Price banding implemented as a two-sided check, which rejects perfectly legal resting orders that the exchange would accept.
- Market with Protection treated as an unbounded market order, so the residual that rests at the protection limit is invisible to position and risk logic.
When NOT to Use
- As a session layer. This module validates and assembles fields. It does not
encode SBE, run the FIXP session, sequence messages, or recover gaps — see
fix-protocol-session-management-across-venues. - For stop and stop-limit orders. Stop orders on Globex have their own protection
and trigger semantics;
process_orderrejects any order type other thanLIMITandMARKETrather than silently treating it as a limit. - As an idempotency mechanism.
cl_ord_idis passed through, not tracked. Whether a resubmission after a lost response duplicates an order is out of scope — seeorder-placement-idempotency. - As a source of contract parameters. Tick size, Price Band Variation and protection points are inputs. They come from CME's published product reference files and change; nothing here derives or guesses them.
- As your only pre-trade risk gate. These are exchange-conformance checks, not
capital, exposure or drawdown limits — see
sec-rule-15c3-5-risk-controls-us.
Prerequisites
- A CME Globex Firm ID, iLink session, and Operator IDs registered in the Exchange Fee System (EFS). The value transmitted must match the registration exactly.
- Knowledge of which Operator IDs are registered to an individual and which to a team/ATS — the two are not interchangeable across manual and automated orders.
- Per-symbol
ContractSpec: tick size (minimum price increment), Price Band Variation, and Market-with-Protection points, refreshed from CME's product reference files. - Python 3.10+. Standard library only —
decimal,logging,dataclasses.
Workflow
-
Load contract specifications. Build a
ContractSpecper symbol. The constructor rejects a non-positive tick size or negative band/protection values, because a zero-width band silently rejects every order and a zero tick makes every price off-tick. -
Construct the engine. Pass
team_operator_idsif you know which IDs are team/ATS registrations — the manual/automated pairing check is skipped when the set is empty rather than guessed at. Passpermitted_operator_id_symbolsif the advisory notice in force for your firm differs from the default_ - : @. -
State Tag 1028 explicitly on every order.
manual_order_indicatorhas no default.Truemeans a human entered the order;Falsemeans it was generated or routed without direct human interaction — anything an execution algorithm produces isFalse. An order that leaves it unset is rejected before transmission, because CME rejects the message anyway and a guessed audit-trail value is worse than a refusal. -
Call
process_orderwith the market state the exchange will judge the order against.reference_priceis the CME Banding Reference Price — the last trade, else the best bid/offer, else the settlement price — and is required for a limit order.current_bid/current_askare needed only for a market order, and only on the side the protection limit is computed from. -
Handle the rejection by type, not by string.
OperatorIdErrorandManualOrderIndicatorErrormean a registration or configuration fault: the same order will fail again, so do not retry it — escalate.PriceBandingErrorandTickConformanceErrormean the price is wrong for the current market: re-price and resubmit. A bareCmeOrderValidationErroron a market-data argument means the quote is unusable, so refresh it before retrying. -
Account for the Market-with-Protection residual. On a market order the returned
priceisNone— a market order carries no Tag 44 — andprotection_price_limitsays where unfilled quantity will rest as a limit order. Feed that into position tracking. Ifprotection_limit_outside_bandis set, the residual would rest outside the price band; the module flags and logs it rather than rejecting, because banding applies to price-based orders and a market order carries no price.
Common Pitfalls
- Checking the price band on both sides. CME rejects buys above BRP + PBV and sells
below BRP − PBV, and deliberately does not stop a bid below the market or an offer
above it. A symmetric
min <= price <= maxcheck rejects ordinary deep passive orders the exchange would have accepted — a silent loss of legitimate order flow that no exchange reject ever tells you about. - Trusting
price % tick_size == 0. In binary floating point5000.10 % 0.05is about 0.049999999, so a naive modulo check rejects a valid price for any product whose tick is not a power of two. Tick arithmetic here runs indecimal.Decimal. - Letting protection points off the tick. If protection points are not a whole number of ticks, the computed protection limit is off-tick. This module rounds it toward the market — down for a buy, up for a sell — so rounding can only tighten protection.
- Sending a market order as if it were unbounded. Globex fills a market order only inside the protected range and rests the remainder at the limit of that range. A client that does not model this ends up holding a resting limit order it never placed.
- Trimming whitespace off an Operator ID and sending it anyway. The transmitted value must match the EFS registration exactly; a padded ID is a configuration fault, so it is rejected rather than normalised.
- Distinguishing Operator IDs by letter case. CME Operator IDs are not case sensitive,
so
DESK_01anddesk_01are the same registration. The team/ATS check here compares case-insensitively for the same reason. - Omitting Tag 1028. In-scope iLink order entry messages without it, or with an invalid value, are rejected — and a team/ATS Operator ID may only submit automated messages.
Verification
- Submit an order with
manual_order_indicator=Noneand confirm it is rejected before transmission rather than defaulted. - Submit a limit buy far below the reference price and confirm it is accepted — the
regression case a two-sided band check gets wrong — then a limit buy one tick above
BRP + PBV and confirm
PriceBandingError. - Submit a limit price off the product's tick (e.g. 5000.10 on a 0.25-tick product) and
confirm
TickConformanceError. - Submit a market buy and confirm
price is None,ord_type == "MARKET", andprotection_price_limit == best_offer + protection_points, on-tick. - Run
python -m unittest discover -s skills/cme-globex-futures-api-integration/scripts.