FIX Order Entry Onboarding
Market Maker Connectivity · FIX 4.4 / FIX 5.0 SP2
A step-by-step path from first contact with integration support to a certified, live order-entry session — covering session setup, the resilience logic your client must implement, order lifecycle, and the two optional tracks: parlay MM quoting and drop copy.
| Source | mm-fix-spec.md, rev. 2026-08-27 |
| Scope | Order-entry & drop-copy sessions |
| Audience | MM counterparty integration engineers |
00 · What you'll need from integration support
None of the following ship in the spec itself — they're assigned per counterparty at onboarding. Request all of it before writing any session code.
- Host/port and TLS details for the order-entry endpoint
- FIX version assignment —
FIX.4.4orFIXT.1.1(5.0 SP2). Fixed per session, not negotiable on the wire. SenderCompID/TargetCompIDpair — this triple with BeginString is your permanent session identityUsername(553)/Password(554)— access key and secret key for LogonHeartBtInt(108)value to configure- Your per-session order-entry rate limit, if one is being applied
- The outbound price format your session will report in — independent of the inbound format you choose per message (see §01)
- If back office needs a mirrored stream: a drop copy session request, naming which trading session it subscribes to (§06)
Nothing above is guessable. Endpoints, CompIDs, credentials and outbound price format are assigned per counterparty and are not published in the spec. Confirm each one explicitly with integration support before certification — a wrong assumption here (e.g. assuming outbound format matches your inbound choice) fails silently until reconciliation.
Sandbox defaults, as assigned to counterparties to date. Production values are assigned separately — confirm with integration support before pointing at prod.
- Host/port —
fix-acceptor.sandbox.prophetx.dev:9876. TLS is enabled; no client certificate is required.- FIX version — FIX 4.4 is supported.
- SenderCompID/TargetCompID —
TargetCompIDisPROPHETX.SenderCompIDis your choice: reuse an existing FIXSenderCompIDif you already have one from another venue, or ask integration support to assign a generic one. ProphetX runs numbered FIX sessions — your chosenSenderCompIDgets a_1suffix appended (e.g.ACME_OE→ACME_OE_1). Confirm with integration support whether this applies to your session; if it does and you connect without the suffix, ProphetX accepts the TLS handshake but silently drops the connection with no Logon response, Reject, or Logout — a connection-level symptom that looks like a network/firewall problem, not a CompID mismatch. A drop copy session (§06) is a separateSenderCompID— a_DCsuffix on your order-entry CompID is a common (not required) naming choice for it.- Username(553)/Password(554) — these are the same access key / secret key pair issued for REST integration, not a separate FIX credential. If you haven't generated REST API keys yet, request them from integration support first.
- HeartBtInt(108) — defaults to 10 seconds; integration support can configure a different value on request.
- Per-session rate limit — not fixed by default; confirm the value (if any) with integration support for your session.
01 · Session setup
ProphetX is the acceptor; you are the initiator. Get Logon exchanging cleanly before touching order entry.
Logon (35=A) fields
| Field | Tag | Value |
|---|---|---|
| BeginString | 8 | FIX.4.4, or FIXT.1.1 with DefaultApplVerID(1128)=9 |
| Username | 553 | Access key |
| Password | 554 | Secret key — validated on logon; failure ends the connection |
| HeartBtInt | 108 | As configured at onboarding |
Account(1) is not part of Logon — session identity comes from SenderCompID/TargetCompID plus credentials. Account(1) is required starting at order entry (§03), not before.
Session identity. BeginString:SenderCompID:TargetCompID — stable across reconnects. Execution reports survive a disconnect because they're keyed to this identity, not the TCP connection.
Whose perspective is that triple written from? When integration support hands you your session identity as a colon-separated string, confirm whether it's written from ProphetX's side or yours — the two are mirror images of each other, and mixing them up produces a connection that completes TLS but gets no Logon response. If you're told
FIX.4.4:PROPHETX:YOURCOMPID, that's ProphetX's ownSenderCompID:TargetCompIDorder — on your outgoing messages, it's reversed:SenderCompID(49)=YOURCOMPID,TargetCompID(56)=PROPHETX. The field values in the table above (TargetCompIDisPROPHETX,SenderCompIDis yours) are already from your side — this callout is about avoiding confusion if the same information comes to you phrased from ProphetX's side instead.
Sequence numbers. ProphetX keeps its own persistent store indefinitely — no scheduled reset. MsgSeqNum(34) is independent of the application-level delivery sequencing used for execution reports.
02 · Reconnect & sequencing logic
This is the part most integrations get wrong first — build it deliberately, not by porting a generic FIX client's defaults.
When to send ResetSeqNumFlag(141)=Y
- ProphetX's Logon response carries
MsgSeqNum(34)lower than the next inbound number you expect — that means ProphetX's own sequence state was reset - You've lost your own sequence store and cannot resume
Do not rely on tag 789. ProphetX never asserts
141=Yon its own Logon — a reset is always initiated by you, or by venue operations coordinated out of band beforehand.NextExpectedMsgSeqNum(789)is ignored if sent. If venue operations force a reset, you'll see aLogout(35=5)with the fixed text: "Sequence reset by venue operations. Reset your sequence numbers and reconnect with ResetSeqNumFlag(141)=Y." — you can match on it, but branch your reconnect logic on the sequence-number rule above, not on this string alone.
Resend window: 7 days
Older messages are pruned. A ResendRequest spanning outside the window gets SequenceReset-GapFill over the pruned portion. Disconnected longer than 7 days? Reconnect with 141=Y instead of attempting resume.
Execution reports don't need resend
They live in a durable per-session outbox and redeliver on reconnect regardless of sequence numbers. Dedup on ExecID(17) — a report may legitimately repeat after a reconnect.
Rate limiting
Order-entry messages (D/E/F/G/H/R/i/AJ) may carry a per-session limit agreed at onboarding. Session-layer messages are never throttled. A throttled request comes back as:
BusinessMessageReject—35=jBusinessRejectReason(380)=0Text(58): "rate limit exceeded for this session; retry after backing off"
Match on the text, not just the reason code — 380=0 is the generic "Other" value. The request never reached order processing, so it's always safe to retry the same ClOrdID once you've backed off.
03 · Order entry
Price format — tag 6001 (custom, required on every order-entry message)
| Value | Format | ≈0.65 prob | Constraint |
|---|---|---|---|
| 1 | Cents (0–100) | 65 | 0 < p < 100 |
| 2 | American odds | -186 | Exact integer, |odds| ≥ 100 |
| 3 | Decimal odds | 1.5385 | > 1 |
| 4 | Probability | 0.65 | 0 < p < 1 |
Inbound format is your choice per message; outbound format (how Price/LastPx/AvgPx report back) is fixed per session at onboarding and may differ. Prices convert in exact decimal, never float-rounded — a limit price that doesn't convert to exact integer American odds is rejected.
Order lifecycle
place (35=D)
│
▼
PendingNew (ExecType=A) ── provisional: order id delivered, not yet working
│
├──► New (ExecType=0) ── terminal acceptance, order is working
│ ├──► Trade (ExecType=F) one per fill
│ ├──► Canceled (ExecType=4) cancel / void / wipe
│ └──► Filled (OrdStatus=2) on last fill
│
└──► Rejected (ExecType=8) ── matching engine refused the provisionally-accepted order
PendingNew ≠ working. Don't treat an order as live until New arrives. Under load, a fill can be delivered before the New for the same order — order your view by
TransactTime(60)(microsecond precision), not arrival order, and dedup onExecID(17).
NewOrderSingle (35=D) — full field table
| Field | Tag | Req | Notes |
|---|---|---|---|
| ClOrdID | 11 | Y | Unique per session |
| Account | 1 | Y | Submit user id — absent or mismatched → OrdRejReason=15 |
| Symbol | 55 | Y | Line / instrument identifier |
| Side | 54 | Y | Buy(1) only — Sell(2) is rejected |
| OrdType | 40 | Y | 2=Limit, 1=Market |
| OrderQty | 38 | Y | Stake |
| Price | 44 | C | Required for limit; must not be set on market (except as carried by a quote) |
| PriceFormat | 6001 | Y | See table above |
| TimeInForce | 59 | N | 1=GTC or 4=FOK only. Absent → defaults to GTC, not Day. The ack always states the TIF applied. |
| ExecInst | 18 | N | E=take-only. 6 (make-only / ALO) is currently parsed but has no effect on order behavior — use order_strategy: "ALO" on the REST API instead (see Integration). Raise this with integration support if you need make-only enforced over FIX. |
| QuoteID | 117 | C | Market orders: references a prior Quote |
| Parties | 453… | N | Firm / customer attribution |
NewOrderList (35=E) — batch rules
Price format is set once at list level, applied to every order. Each order acks independently with its own PendingNew or reject for a business-level failure (e.g. unknown symbol, exceeds limit) — that rejects only that entry, the rest proceed. A structural failure on any single entry (a malformed field, a missing required tag) rejects the entire list in one reject, before any order in it is acknowledged individually — see FIX Order Entry §6.
20-order cap is all-or-nothing. A list of more than 20 orders (or an empty list) is rejected in full — every order in it, not just the ones past #20. Keep lists at ≤20; within that bound, orders still succeed or fail independently.
Cancel & Replace (35=F / 35=G)
Account(1)is optional on cancel/replace — an absent tag, or one matching the session's authorized user, is accepted. A conflicting value is refused as35=j, BusinessRejectReason=6, not as an ExecutionReport.- Replace is limit-only. Market-order replace is cancel-rejected.
- An order must be working (has received its New) before it can be replaced. Replace attempted mid-placement is transiently refused and retried briefly, or cancel-rejected if not yet open.
- A successful replace is one Replaced report (ExecType=5, OrdStatus=0, CumQty=0) — cumulative quantity does not carry across.
Correlate cancellations on OrderID, not ClOrdID. A venue-originated cancellation (void, wipe) has no request behind it. ProphetX reports
ClOrdID(11)= the order's own client order id and omitsOrigClOrdID(41)— even when it followed your Cancel Request. If your stack expects tag 11 to strictly echo the request, correlate onOrderID(37)instead, or raise this at onboarding.
Order Status (35=H) & Quote Request (35=R)
Status: query by ClOrdID (+ Side/Symbol) and/or OrderID. Three outcomes — found (ExecType=I), unknown order (OrdStatus=Rejected(8)), or malformed (BusinessMessageReject).
Quote Request: exactly one symbol per request. Reply is one-sided — OfferPx/OfferSize for a Buy request, BidPx/BidSize for a Sell request. Indicative and ephemeral; act on it via a market NewOrderSingle referencing the QuoteID before ValidUntilTime.
04 · Rejections & rate limits
Where a rejection lands depends entirely on where it failed — route your handling accordingly.
Routing table
| Situation | Answer | Reason field |
|---|---|---|
| New order refused (business) | 8 ExecType=8 | OrdRejReason(103) |
| Cancel / replace refused | 9 OrderCancelReject | CxlRejReason(102) |
| Malformed / unroutable / internal failure | j BusinessMessageReject | BusinessRejectReason(380) |
| Structurally invalid message | 3 Session Reject | SessionRejectReason(373) |
OrdRejReason(103)
This is the complete list — no other value is ever sent.
| Value | Meaning |
|---|---|
| 1 | Unknown symbol / line |
| 2 | Exchange closed / market not open |
| 3 | Order exceeds limit (stake / exposure) |
| 6 | Duplicate order (reused ClOrdID, different economics) |
| 13 | Incorrect quantity (below min / above max) |
| 15 | Unknown account |
| 18 | Invalid price increment |
| 99 | Other — see Text(58) |
CxlRejReason(102)
This is the complete list — no other value is ever sent.
| Value | Meaning | What triggers it |
|---|---|---|
| 0 | Too late to cancel | The order is already terminal — matched (fully or partially), already cancelled, inactive, or invalid. |
| 1 | Unknown order | No record of the order — unrecognized ClOrdID/OrderID or invalid reference. OrdStatus(39) on the reject is forced to Rejected(8) only in this case. |
| 2 | Other (broker option) | Catch-all for every other failure. |
BusinessRejectReason(380)
This is the complete list of values ProphetX actually sends. A fourth value, 5 (field missing, per the standard FIX enum), is defined in code but not currently wired up to any rejection path.
| Value | Meaning |
|---|---|
| 0 | Other — incl. rate limit; see Text(58) |
| 4 | Application not available — transient, safe to retry |
| 6 | Not Authorized — conflicting Account, or any message on a drop copy session |
SessionRejectReason(373)
Emitted by the underlying FIX session engine for protocol-layer parsing failures, not chosen by ProphetX's application logic — expect the standard FIX 4.4 enum here, not a curated subset.
Duplicate ClOrdID handling. Same ClOrdID + identical economics → treated as a benign redelivery, re-acknowledged idempotently (same ExecID repeats, dedups cleanly). Same ClOrdID + different economics → rejected, OrdRejReason=6.
05 · Parlay quoting (optional — MM quoting only)
A parlay is one instrument, 2–12 legs, one combined price, all-or-nothing payout. You don't place parlay orders — you quote, and ProphetX creates the order when a taker accepts.
Sequence
inquiry for a parlay price ── not FIX, existing channel
│
▼
you quote a price ladder ── MassQuote (35=i) ──► ProphetX
│
▼ (taker accepts, not FIX)
LAST LOOK: confirm or walk ── ExecutionReport, ExecType=A ◄── ProphetX
│
▼
you answer ── QuoteResponse (35=AJ) ──► ProphetX
│
▼
trade stands ── New, then Trade, then terminals ◄── ProphetX
MassQuote (35=i). One message quotes one parlay — exactly one QuoteSet, ≥1 rung. OfferPx must convert to exact integer American odds. Every submission gets a MassQuoteAcknowledgement (35=b) with QuoteStatus(297) Accepted/Rejected; a structurally malformed submission is refused earlier as 35=j against your QuoteID instead.
QuoteResponse (35=AJ). Answer with accept(1), counter(2), or pass(6). An acceptance needs a probability for every leg — the venue re-derives the price from them, and the product must reconstruct it. This isn't a hard rejection: a mismatch still goes through, it just loses auto-settlement on that order (settled manually instead). A counter can only move in the taker's favour and only within venue tolerance.
Correlate on OrderID — there's no ClOrdID here. The last-look prompt carries
ClOrdID(11)=NONE— you never sent a request. Answer usingOrderID(37)from the prompt as your QuoteID in the response, and correlate every subsequent report on OrderID. Answer promptly: the confirmation window started when the venue created the order, so your actual time is slightly less than ValidUntilTime shows.
Not supported for parlays
- Cancel (35=F) — refused as OrderCancelReject; no cancel route exists for a parlay
- Replace (35=G) — refused as OrderCancelReject
- Taking parlays over FIX — quoting only
- Multiple leg-probability sets on one acceptance
- Per-leg prices on a ladder — legs carry line ids only
06 · Drop copy (optional — back office / risk)
A separate, outbound-only session mirroring the ExecutionReport stream of one of your order-entry sessions. Request it alongside your order-entry session details.
Setup
- Own SenderCompID/TargetCompID, authenticated like any session
- Logs on with the same credentials as the trading desk it mirrors — no separate drop-copy identity exists
- The subscription (which trading session it mirrors) is configured at onboarding
- One trading session's flow can be sharded across several drop copy sessions for scale — raise expected volume with integration support
Behavior
- Receive-only — any application message you send back is refused as
35=j, BusinessRejectReason=6 - Never delays or blocks reports to the trading session, even if your back-office connection is slow or offline
- Reconnect recovers backlog the same way an order-entry session does
Identifying a copy
Same ExecID(17) as the primary stream — reconcile on it. Two fields exist only on the copy:
| Field | Meaning |
|---|---|
OnBehalfOfCompID(115) | TargetCompID of the trading session this report belongs to |
Account(1) | The order's sub-account, or the trading session's own account |
Mirrors ExecutionReport (all types) and OrderCancelReject. Does not mirror OrderStatus responses (ExecType=I), BusinessMessageReject, or Quote — and only mirrors activity from this FIX interface, not a full account feed.
07 · Certify & go live
Before integration support signs off, exercise the behaviors that don't show up in a happy-path test.
- Logon on both a fresh session and a reconnect using the existing sequence store
- Place, fill (partial + full), cancel, and replace a limit order; place a market order against a Quote
- Submit a 21-order NewOrderList and confirm the whole batch is rejected, not just order 21
- Disconnect mid-session and confirm backlogged execution reports redeliver on reconnect, deduped on ExecID
- Issue a ResendRequest inside the 7-day window and one spanning outside it (expect GapFill on the pruned portion)
- Exceed the configured rate limit and confirm you get 35=j / 380=0 and can safely retry the same ClOrdID
- Resend an identical ClOrdID (expect idempotent re-ack) and one with changed economics (expect OrdRejReason=6)
- Confirm reported Price/LastPx/AvgPx match the outbound format agreed at onboarding, independent of your inbound tag 6001 choice
- If quoting parlays: run a full MassQuote → last look → QuoteResponse cycle, including a pass and a countered acceptance
- If using drop copy: confirm it receives the mirrored stream, rejects any inbound application message, and never blocks the trading session
Sign off with integration support. Once the checklist above passes against the assigned test endpoint, confirm cutover timing and production credentials with ProphetX integration support before pointing your session at production.
Built from mm-fix-spec.md (rev. 2026-08-27). This guide sequences and restructures that spec for onboarding — it does not supersede it. Confirm any discrepancy against the source spec and with ProphetX integration support.
Updated 5 days ago
