FIX 报文¶
来源状态
正文摘录自 postman/Polaris-FIX-Document.postman_collection.json
(Pepperstone Crypto FIX API 机构接入指南)。
本地 ptrader-fix-server 检出目前仅有 README(无 src/)。
字段表在源码核验前视为已发布的客户端契约 —
见 待确认清单。
中英对照
下列协议术语、Tag 表与报文示例保留英文原文(FIX 线路契约)。导航与说明使用中文。
Market data — MarketDataRequest (35=V)¶
Three modes are supported, selected by SubscriptionRequestType (263). If 263 is omitted, the gateway defaults to snapshot mode.
| 263 | Mode | Server response |
|---|---|---|
0 |
Snapshot only | One 35=W per requested symbol; the subscription is not retained. |
1 |
Snapshot + updates | One 35=W per symbol, then a stream of 35=X. |
2 |
Unsubscribe | No payload; matching MDReqID is removed. |
Required fields¶
| Tag | Field | Req? | Notes |
|---|---|---|---|
262 |
MDReqID | Y | Must be unique while the subscription is active. |
263 |
SubscriptionRequestType | Cond. | One of 0 / 1 / 2. Defaults to 0. |
264 |
MarketDepth | N | 0 = full book; 1 = top of book; N = top-N levels. |
267 |
NoMDEntryTypes | N | Repeating group of 269. Defaults to {0 BID, 1 OFFER}. |
146 |
NoRelatedSym | Y | Repeating group of 55. At least one entry required. |
55 |
Symbol | Y | Format BASE_QUOTE, e.g. BTC_USDT. Case-insensitive (normalised to upper). |
Snapshot example¶
Inbound · MarketDataRequest (snapshot)
8=FIXT.1.1|9=...|35=V|34=40|49=CLIENT|56=PtradeFIXServer-AU|
262=md-001|263=0|264=10|267=2|269=0|269=1|146=1|55=BTC_USDT|10=...
Outbound · MarketDataSnapshotFullRefresh (35=W)
8=FIXT.1.1|9=...|35=W|34=41|49=PtradeFIXServer-AU|56=CLIENT|
262=md-001|55=BTC_USDT|1187=Y|268=4|
269=0|270=62000.10|271=0.85|290=1|
269=0|270=61999.90|271=2.10|290=2|
269=1|270=62000.80|271=0.50|290=1|
269=1|270=62001.20|271=1.20|290=2|10=...
Snapshot + updates¶
The gateway re-publishes the entire top-N book on every tick, rather than emitting per-level diffs. This costs a small amount of bandwidth for guaranteed correctness under packet loss.
Outbound · MarketDataIncrementalRefresh (35=X)
8=FIXT.1.1|9=...|35=X|34=52|49=PtradeFIXServer-AU|56=CLIENT|
262=md-sub-001|268=2|
279=0|269=0|55=BTC_USDT|278=md-sub-001-BTC_USDT-0-1|270=62001.20|271=0.40|290=1|
279=0|269=1|55=BTC_USDT|278=md-sub-001-BTC_USDT-1-1|270=62001.80|271=0.30|290=1|10=...
Rejects¶
The gateway pre-validates requested symbols against the live catalog. The first symbol not in the catalog causes the entire request to be rejected — no subscription state is registered. Reject codes on MDReqRejReason (281):
| 281 | Meaning | Trigger |
|---|---|---|
0 |
UNKNOWN_SYMBOL | Symbol missing, empty group, or not in the active catalog. |
1 |
DUPLICATE_MDREQID | An active subscription already exists with the same MDReqID. |
4 |
UNSUPPORTED_SUBSCRIPTIONREQUESTTYPE | Missing MDReqID, or 263 value not in {0,1,2}. |
6 |
UNSUPPORTED_MDUPDATETYPE | MDUpdateType (265) present but not 0 or 1. |
8 |
UNSUPPORTED_MDENTRYTYPE | No requested entry types can be served. |
Outbound · MarketDataRequestReject (35=Y)
8=FIXT.1.1|9=...|35=Y|34=61|49=PtradeFIXServer-AU|56=CLIENT|
262=md-sub-001|281=0|58=Unknown symbol: NOSUCH_USDT|10=...
NOTE: To unsubscribe, send
35=Vwith263=2and the sameMDReqIDas the original subscription.
NewOrderSingle (35=D)¶
WARNING: Supported order types: MARKET (
40=1) and LIMIT (40=2) only. STOP, STOP_LIMIT and other conditional types are rejected withExecutionReport (35=8, 150=8, 39=8, 103=11 UNSUPPORTED_ORDER_CHARACTERISTIC).
Required fields¶
| Tag | Field | Req? | Notes |
|---|---|---|---|
11 |
ClOrdID | Y | Client-assigned, unique per account. Reuse triggers a duplicate-order reject; see below. |
55 |
Symbol | Y | Format BASE_QUOTE, e.g. BTC_USDT. |
54 |
Side | Y | 1 = BUY, 2 = SELL |
40 |
OrdType | Y | 1 = MARKET, 2 = LIMIT |
21 |
HandlInst | Y | Required by FIX dictionary. Typical value 1. Server does not branch on this. |
38 |
OrderQty | Cond. | Required for LIMIT and for MARKET by base quantity. Must be > 0. Mutually exclusive with 152. |
152 |
CashOrderQty | Cond. | Quote-currency amount for MARKET (e.g. "buy 1000 USDT of BTC"). Must be > 0. Not allowed on LIMIT. |
44 |
Price | Cond. | Required for LIMIT; must be > 0. Ignored on MARKET (safe to send 44=0 for Java/proto3 clients). |
59 |
TimeInForce | N | 0 = DAY, 1 = GTC (default), 3 = IOC, 4 = FOK |
60 |
TransactTime | Y | UTC YYYYMMDD-HH:MM:SS.sss. |
Placement flow¶
Before acknowledging, the gateway runs:
- Structural validation — required fields, value ranges, exclusivity of
38/152. - Symbol + trading-rule check against the catalog.
- Duplicate-
ClOrdIDguard — the id is claimed atomically; a collision returns a reject without touching the incumbent order. - Pre-lock of the required balance on the asset service.
- Publish to the matching engine.
On success, the gateway forwards the matching engine's first ExecutionReport (150=0 NEW, 39=0 NEW). All subsequent lifecycle events (fills, partial fills, cancels, expirations) are driven by the matching engine and arrive as further ExecutionReports.
Inbound · NewOrderSingle (LIMIT BUY)
8=FIXT.1.1|9=...|35=D|34=70|49=CLIENT|56=PtradeFIXServer-AU|
11=cli-10001|55=BTC_USDT|54=1|21=1|38=0.50|40=2|44=62000.00|59=1|60=20260515-09:05:00.000|10=...
Outbound · ExecutionReport (NEW ack)
8=FIXT.1.1|9=...|35=8|34=71|49=PtradeFIXServer-AU|56=CLIENT|
37=1234567|17=exec-...|11=cli-10001|55=BTC_USDT|54=1|40=2|38=0.50|44=62000.00|
150=0|39=0|14=0|151=0.50|6=0|60=20260515-09:05:00.100|10=...
Duplicate ClOrdID¶
Sending a NewOrderSingle with a ClOrdID already known to the gateway returns:
Outbound · ExecutionReport (duplicate ClOrdID)
8=FIXT.1.1|9=...|35=8|34=72|49=PtradeFIXServer-AU|56=CLIENT|
37=O-cli-10001-...|17=rejected-...|11=cli-10001|55=BTC_USDT|54=1|40=2|
150=8|39=8|14=0|151=0|6=0|103=6|58=Duplicate ClOrdID: cli-10001|10=...
NOTE: The incumbent live order with this
ClOrdIDis not affected and remains cancellable. Make sure yourClOrdIDgenerator is collision-free per account.
OrderCancelRequest (35=F)¶
Cancel a working order by referencing its ClOrdID.
Required fields¶
| Tag | Field | Req? | Notes |
|---|---|---|---|
41 |
OrigClOrdID | Y | The ClOrdID of the original NewOrderSingle. |
11 |
ClOrdID | Y | A new unique id for this cancel request itself. |
55 |
Symbol | Y | Echo the symbol of the original order. |
54 |
Side | Y | Required by the dictionary; not used for routing. |
60 |
TransactTime | Y | UTC YYYYMMDD-HH:MM:SS.sss |
Response semantics¶
Cancel is asynchronous:
- The gateway does local validation only (order known, state is cancellable, session is authenticated).
- On success, it forwards the request to the matching engine and marks the order
PENDING_CANCELlocally. NoExecutionReportis produced synchronously. - The real
ExecutionReport (150=4 Canceled, 39=4 Canceled)arrives once the matching engine confirms. - If local validation fails, an
OrderCancelReject (35=9)is returned synchronously with a specificCxlRejReason (102).
Cancel-reject reasons¶
| 102 | Meaning | Trigger |
|---|---|---|
1 |
UNKNOWN_ORDER | OrigClOrdID is missing, or the order is not in the gateway's tracked cache. |
0 |
TOO_LATE_TO_CANCEL | The order is already in a terminal state (FILLED / CANCELED / REJECTED / EXPIRED). |
3 |
ORDER_ALREADY_IN_PENDING_CANCEL_OR_PENDING_REPLACE_STATUS | A previous cancel for the same order is still in flight (double-tap). |
99 |
OTHER | Session not authenticated, or downstream publish failed. |
NOTE: OrdStatus (39) on
35=9mirrors the underlying order's current state, not the rejected cancel:39=0if the order is stillNEW,39=2ifFILLED,39=4ifCANCELED,39=6ifPENDING_CANCEL,39=8only when the order was never known to the gateway.
Inbound · OrderCancelRequest
8=FIXT.1.1|9=...|35=F|34=80|49=CLIENT|56=PtradeFIXServer-AU|
41=cli-10001|11=cli-10001-cxl-1|55=BTC_USDT|54=1|60=20260522-09:06:00.000|10=...
Outbound · ExecutionReport (Canceled, driven by matching engine)
8=FIXT.1.1|9=...|35=8|34=81|49=PtradeFIXServer-AU|56=CLIENT|
37=1234567|17=cxl-...|41=cli-10001|11=cli-10001-cxl-1|55=BTC_USDT|54=1|
150=4|39=4|14=0|151=0|6=0|10=...
Outbound · OrderCancelReject (order already terminal)
8=FIXT.1.1|9=...|35=9|34=83|49=PtradeFIXServer-AU|56=CLIENT|
37=1234567|41=cli-10001|11=cli-10001-cxl-2|39=4|434=1|102=0|
58=Order not cancelable in current state|10=...
11 vs 41 on Canceled ER¶
On both the synchronous OrderCancelReject and the asynchronous ExecutionReport (150=4):
11 ClOrdID— the id of the cancel request itself.41 OrigClOrdID— the id of the original order.
OrderMassCancelRequest (35=q)¶
Cancel every working order on the session that matches a given symbol (optionally narrowed to one side). The gateway iterates internally, dispatches one cancel per affected order, and enumerates them in the outbound OrderMassCancelReport (35=r) via the NoAffectedOrders (534) repeating group.
Only MassCancelRequestType = 1 (CANCEL_ORDERS_FOR_A_SECURITY) is supported.
Required fields¶
| Tag | Field | Req? | Notes |
|---|---|---|---|
11 |
ClOrdID | Y | Echoed back in 35=r as both 11 and 1369 MassActionReportID. |
530 |
MassCancelRequestType | Y | Only 1 supported today. |
55 |
Symbol | Y | Required when 530=1. |
54 |
Side | N | Optional. 1 = BUY, 2 = SELL. Omit to sweep both sides. |
60 |
TransactTime | Y | UTC YYYYMMDD-HH:MM:SS.sss |
Response¶
| Outcome | Fields |
|---|---|
| Accepted | 35=r · 531=1 · 533=N · one 534/41/535 entry per affected order |
Rejected — unsupported 530 |
35=r · 531=0 · 532=0 MASS_CANCEL_NOT_SUPPORTED |
Rejected — missing Symbol |
35=r · 531=0 · 532=1 INVALID_OR_UNKNOWN_SECURITY |
Rejected — unsupported Side |
35=r · 531=0 · 532=99 OTHER |
WARNING:
35=ris an ACK of the mass-cancel request, not of each individual cancel outcome. Every affected order still generates its ownExecutionReport (150=4 Canceled)asynchronously from the matching engine — just like a single35=F. Use the534group to know whichOrigClOrdIDs to expect.
Inbound · OrderMassCancelRequest
8=FIXT.1.1|9=...|35=q|34=120|49=CLIENT|56=PtradeFIXServer-AU|
11=mc-001|530=1|55=BTC_USDT|54=2|60=20260522-09:10:00.000|10=...
Outbound · OrderMassCancelReport (accepted)
8=FIXT.1.1|9=...|35=r|34=121|49=PtradeFIXServer-AU|56=CLIENT|
11=mc-001|1369=mc-001|530=1|531=1|55=BTC_USDT|533=2|
534=2|41=cli-10001|535=1234567|41=cli-10002|535=1234568|10=...
ExecutionReport (35=8)¶
The gateway emits an ExecutionReport for each lifecycle transition of your order.
| ExecType (150) | OrdStatus (39) | Meaning |
|---|---|---|
0 NEW |
0 NEW |
Order accepted by the book. |
F TRADE |
1 PARTIALLY_FILLED |
A partial fill occurred. |
F TRADE |
2 FILLED |
Final fill; the order is closed. |
4 CANCELED |
4 CANCELED |
Cancel confirmed by the matching engine. |
C EXPIRED |
C EXPIRED |
TIF-driven expiration (IOC / FOK unfilled). |
8 REJECTED |
8 REJECTED |
Order rejected; carries OrdRejReason (103). |
Common fields¶
| Tag | Field | Notes |
|---|---|---|
37 |
OrderID | Server-assigned order id. Use it to reconcile against back-office / REST. |
17 |
ExecID | Unique per ExecutionReport message. |
11 |
ClOrdID | Echo of the inbound ClOrdID. On a Canceled ER, this is the cancel-request id; 41 carries the original order id. |
55 |
Symbol | Canonical upper-case symbol id. |
14 |
CumQty | Cumulative filled base quantity. |
151 |
LeavesQty | Remaining open base quantity. Always 0 on terminal status. |
6 |
AvgPx | Average fill price. 0 whenever CumQty = 0. |
31 / 32 |
LastPx / LastQty | Present only on TRADE ERs (150 ∈ {F, 1, 2}). |
60 |
TransactTime | UTC timestamp at emit time. |
103 |
OrdRejReason | Present on rejects. See reject reference below. |
Coherence guarantees¶
On every outbound ExecutionReport, the gateway guarantees the following invariants regardless of upstream behaviour:
LeavesQty (151) = 0wheneverOrdStatusis terminal (FILLED / CANCELED / REJECTED / EXPIRED).AvgPx (6) = 0wheneverCumQty (14) = 0.ExecType (150)is coerced to mirrorOrdStatus (39)when upstream reportsNEWalongside a terminal status (this happens for IOC/FOK orders that don't match anything; you receive a single ER with150=C, 39=C, not a contradictory pair).LastPx (31) / LastQty (32)are stripped on non-TRADE ERs.
You can drive your OMS off (39, 150, 6, 151) without needing to reconcile contradictory upstream signals.
SecurityListRequest (35=x)¶
Fetch the currently active spot symbol catalog as a single SecurityList (35=y). Useful at session warm-up. Only SecurityListRequestType = 4 (AllSecurities) is supported.
Required fields¶
| Tag | Field | Req? | Notes |
|---|---|---|---|
320 |
SecurityReqID | Y | Echoed in the outbound 35=y. |
559 |
SecurityListRequestType | Y | Must be integer 4. Any other value → reject. |
Response¶
| 560 | Meaning | When |
|---|---|---|
0 |
VALID_REQUEST | Symbols returned in the 146 NoRelatedSym group. |
1 |
INVALID_OR_UNSUPPORTED_REQUEST | 559 ≠ 4. |
2 |
NO_INSTRUMENTS_FOUND | Catalog is empty (should not occur under normal operation). |
Each entry inside 146 carries the canonical upper-case Symbol (55). Additional instrument metadata (lot size, tick size, etc.) should be fetched from the REST catalog endpoint.
Inbound · SecurityListRequest
Outbound · SecurityList (accepted)
8=FIXT.1.1|9=...|35=y|34=131|49=PtradeFIXServer-AU|56=CLIENT|
320=sl-001|322=SLR-...|560=0|393=3|893=Y|146=3|
55=BTC_USDT|55=ETH_USDT|55=SOL_USDT|10=...
NOTE: QuickFIX/J note: the constant
SecurityListRequestType.SYMBOL = 0means "by symbol", not "all". Send the integer4on the wire.
Reject codes¶
Session-level Reject (35=3)¶
Produced by the QuickFIX/J engine before your message reaches business logic. See also Reject (35=3) in Session lifecycle for the message shape and companion fields.
The full set of SessionRejectReason (373) values the gateway can emit under strict validation is below. In practice you will spend most of your time with 0/1/2/5/11.
| 373 | Meaning | Cause |
|---|---|---|
0 |
INVALID_TAG_NUMBER | Tag number is not a defined FIX field. |
1 |
REQUIRED_TAG_MISSING | A dictionary-required tag is absent from the message. |
2 |
TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE | Legal tag used on the wrong 35=. |
3 |
UNDEFINED_TAG | Custom / user-defined tag with AllowUnknownMsgFields=N. |
4 |
TAG_SPECIFIED_WITHOUT_A_VALUE | Tag present but value empty. |
5 |
VALUE_IS_INCORRECT | Field value out of the enum defined by the dictionary. |
6 |
INCORRECT_DATA_FORMAT_FOR_VALUE | Value type mismatch (e.g. non-numeric where int is expected, malformed UTC timestamp). |
9 |
COMPID_PROBLEM | SenderCompID (49) or TargetCompID (56) does not match the allow-list for this session. |
10 |
SENDINGTIME_ACCURACY_PROBLEM | SendingTime (52) is too far from the gateway clock (default window is 2 min). |
11 |
INVALID_MSGTYPE | Unknown or unsupported 35= value. |
13 |
TAG_APPEARS_MORE_THAN_ONCE | Same tag appears twice in a single message body. |
14 |
TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER | Header, body, or trailer tag placed in the wrong section. |
15 |
REPEATING_GROUP_FIELDS_OUT_OF_ORDER | Repeating-group entries not delivered in dictionary-declared order. |
16 |
INCORRECT_NUMINGROUP_COUNT_FOR_REPEATING_GROUP | NoXxx count disagrees with the actual number of entries. |
18 |
INVALID_UNSUPPORTED_APPLICATION_VERSION | ApplVerID (1128) negotiation failed (must resolve to FIX 5.0 SP2). |
99 |
OTHER | Catch-all for validation failures not classified above. |
Order reject on 35=8¶
| 103 | Meaning | Cause |
|---|---|---|
0 |
BROKER_EXCHANGE_OPTION | Generic validation failure (blank ClOrdID, LIMIT missing / non-positive price, LIMIT with 152, etc.). |
1 |
UNKNOWN_SYMBOL | Symbol not in the active catalog. |
6 |
DUPLICATE_ORDER | ClOrdID collides with an order already tracked. |
11 |
UNSUPPORTED_ORDER_CHARACTERISTIC | Unsupported OrdType (e.g. 40=4 STOP_LIMIT) or TimeInForce. |
13 |
INCORRECT_QUANTITY | Missing / non-positive OrderQty, or MARKET with both 38 and 152. |
99 |
OTHER | Balance pre-lock failed, downstream publish failed, or session not authenticated. |
Business message reject (35=j)¶
Emitted when an application message exceeds the per-API-key inbound rate limit. The message carries BusinessRejectReason (380) = 99 (Other) and a Text describing the throttle.
Quick reference¶
| You send | Server responds |
|---|---|
Logon without 553 |
35=5 Logout · 58=… |
| Field value not in FIX dictionary enum | 35=3 · 373=5 VALUE_IS_INCORRECT |
| Unknown tag | 35=3 · 373=2 INVALID_TAG_NUMBER |
Duplicate ClOrdID on 35=D |
35=8 · 150=8, 39=8 · 103=6 DUPLICATE_ORDER |
35=D with 40=4 STOP_LIMIT |
35=8 · 150=8, 39=8 · 103=11 UNSUPPORTED_ORDER_CHARACTERISTIC |
LIMIT missing / non-positive Price |
35=8 · 103=0 BROKER_EXCHANGE_OPTION |
MARKET with neither 38 nor 152, or both |
35=8 · 103=13 INCORRECT_QUANTITY |
| Cancel of unknown order | 35=9 · 434=1 · 102=1 UNKNOWN_ORDER |
| Cancel of already-terminal order | 35=9 · 434=1 · 102=0 TOO_LATE_TO_CANCEL |
| Double-tap cancel | 35=9 · 434=1 · 102=3 PENDING_CANCEL_OR_REPLACE |
Duplicate MDReqID |
35=Y · 281=1 DUPLICATE_MDREQID |
35=V unknown symbol |
35=Y · 281=0 UNKNOWN_SYMBOL |
35=q with 530 ≠ 1 |
35=r · 531=0 · 532=0 MASS_CANCEL_NOT_SUPPORTED |
35=x with 559 ≠ 4 |
35=y · 560=1 INVALID_OR_UNSUPPORTED_REQUEST |
| Rate limit exceeded | 35=j · 380=99 · 58=Rate limit exceeded… |
Field reference¶
Essential fields consumed or emitted by the gateway. Anything not listed here is either dictionary-only or ignored by the application layer.
Header¶
| Tag | Field | Notes |
|---|---|---|
8 |
BeginString | FIXT.1.1 |
34 |
MsgSeqNum | Strictly monotonic per direction; persisted. |
35 |
MsgType | See overview table. |
49 |
SenderCompID | Your id inbound; PtradeFIXServer-<SITE> outbound. |
52 |
SendingTime | UTC YYYYMMDD-HH:MM:SS.sss |
56 |
TargetCompID | PtradeFIXServer-<SITE> inbound; your id outbound. |
1128 |
ApplVerID | 9 = FIX.5.0SP2 |
1137 |
DefaultApplVerID | Set on Logon. |
Session admin¶
| Tag | Field | Notes |
|---|---|---|
7 |
BeginSeqNo | Start of resend range. |
16 |
EndSeqNo | End of resend range (0 = open-ended). |
98 |
EncryptMethod | 0 None |
108 |
HeartBtInt | Seconds. |
112 |
TestReqID | Echoed on the answering Heartbeat. |
141 |
ResetSeqNumFlag | Y / N |
553 |
Username | API key. Required on Logon. |
Order entry¶
| Tag | Field | Notes |
|---|---|---|
11 |
ClOrdID | Client-assigned, unique per account. |
21 |
HandlInst | Dictionary requires it; typical value 1. |
38 |
OrderQty | Base quantity. Mutually exclusive with 152 on MARKET. |
40 |
OrdType | 1=MARKET · 2=LIMIT |
44 |
Price | LIMIT only. Ignored on MARKET. |
54 |
Side | 1=BUY · 2=SELL |
55 |
Symbol | BASE_QUOTE |
59 |
TimeInForce | 0=DAY · 1=GTC · 3=IOC · 4=FOK |
60 |
TransactTime | UTC YYYYMMDD-HH:MM:SS.sss |
152 |
CashOrderQty | Quote-currency amount; MARKET only. |
Execution report¶
| Tag | Field | Notes |
|---|---|---|
6 |
AvgPx | 0 when CumQty=0. |
14 |
CumQty | Cumulative filled base quantity. |
17 |
ExecID | Unique per ER message. |
31 / 32 |
LastPx / LastQty | TRADE ERs only. |
37 |
OrderID | Server-assigned id. |
39 |
OrdStatus | 0NEW · 1PART · 2FILLED · 4CANCELED · 6PENDING_CANCEL · 8REJECTED · CEXPIRED |
41 |
OrigClOrdID | Set on Canceled ER (original order id). |
103 |
OrdRejReason | See reject reference. |
150 |
ExecType | 0NEW · 4CANCELED · 8REJECTED · CEXPIRED · FTRADE |
151 |
LeavesQty | 0 on terminal status. |
Market data¶
| Tag | Field | Notes |
|---|---|---|
262 |
MDReqID | Unique per active subscription. |
263 |
SubscriptionRequestType | 0Snapshot · 1Snapshot+Updates · 2Unsubscribe |
264 |
MarketDepth | 0Full · 1Top · NTop-N |
269 |
MDEntryType | 0BID · 1OFFER · 2TRADE |
270 |
MDEntryPx | Price per entry. |
271 |
MDEntrySize | Size per entry. |
281 |
MDReqRejReason | See reject reference. |
290 |
MDEntryPositionNo | Depth level, 1-based, per side. |