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.

Sourcemm-fix-spec.md, rev. 2026-08-27
ScopeOrder-entry & drop-copy sessions
AudienceMM 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.4 or FIXT.1.1 (5.0 SP2). Fixed per session, not negotiable on the wire.
  • SenderCompID / TargetCompID pair — this triple with BeginString is your permanent session identity
  • Username(553) / Password(554) — access key and secret key for Logon
  • HeartBtInt(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 — TargetCompID is PROPHETX. SenderCompID is your choice: reuse an existing FIX SenderCompID if you already have one from another venue, or ask integration support to assign a generic one. ProphetX runs numbered FIX sessions — your chosen SenderCompID gets a _1 suffix 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 separate SenderCompID — a _DC suffix 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

FieldTagValue
BeginString8FIX.4.4, or FIXT.1.1 with DefaultApplVerID(1128)=9
Username553Access key
Password554Secret key — validated on logon; failure ends the connection
HeartBtInt108As 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 own SenderCompID:TargetCompID order — on your outgoing messages, it's reversed: SenderCompID(49)=YOURCOMPID, TargetCompID(56)=PROPHETX. The field values in the table above (TargetCompID is PROPHETX, SenderCompID is 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=Y on 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 a Logout(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=j
  • BusinessRejectReason(380)=0
  • Text(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)

ValueFormat≈0.65 probConstraint
1Cents (0–100)650 < p < 100
2American odds-186Exact integer, |odds| ≥ 100
3Decimal odds1.5385> 1
4Probability0.650 < 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 on ExecID(17).

NewOrderSingle (35=D) — full field table

FieldTagReqNotes
ClOrdID11YUnique per session
Account1YSubmit user id — absent or mismatched → OrdRejReason=15
Symbol55YLine / instrument identifier
Side54YBuy(1) only — Sell(2) is rejected
OrdType40Y2=Limit, 1=Market
OrderQty38YStake
Price44CRequired for limit; must not be set on market (except as carried by a quote)
PriceFormat6001YSee table above
TimeInForce59N1=GTC or 4=FOK only. Absent → defaults to GTC, not Day. The ack always states the TIF applied.
ExecInst18NE=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.
QuoteID117CMarket orders: references a prior Quote
Parties453…NFirm / 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 as 35=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 omits OrigClOrdID(41) — even when it followed your Cancel Request. If your stack expects tag 11 to strictly echo the request, correlate on OrderID(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

SituationAnswerReason field
New order refused (business)8 ExecType=8OrdRejReason(103)
Cancel / replace refused9 OrderCancelRejectCxlRejReason(102)
Malformed / unroutable / internal failurej BusinessMessageRejectBusinessRejectReason(380)
Structurally invalid message3 Session RejectSessionRejectReason(373)

OrdRejReason(103)

This is the complete list — no other value is ever sent.

ValueMeaning
1Unknown symbol / line
2Exchange closed / market not open
3Order exceeds limit (stake / exposure)
6Duplicate order (reused ClOrdID, different economics)
13Incorrect quantity (below min / above max)
15Unknown account
18Invalid price increment
99Other — see Text(58)

CxlRejReason(102)

This is the complete list — no other value is ever sent.

ValueMeaningWhat triggers it
0Too late to cancelThe order is already terminal — matched (fully or partially), already cancelled, inactive, or invalid.
1Unknown orderNo record of the order — unrecognized ClOrdID/OrderID or invalid reference. OrdStatus(39) on the reject is forced to Rejected(8) only in this case.
2Other (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.

ValueMeaning
0Other — incl. rate limit; see Text(58)
4Application not available — transient, safe to retry
6Not 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 using OrderID(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:

FieldMeaning
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.


Did this page help you?