Guide

FIX connectivity

The same accounts, guards and error codes as the Trading API, over FIX 4.4 for systems that already speak it. This guide takes a FIX engine from first logon to a certified order flow. Every tag and value here comes from the FIX reference; nothing is added.

#Overview

Endpoint
api.your-brand.com:5201, TLS 1.2+
Version
FIX 4.4, standard header and trailer
Credential
Trading API key (qk_…) with scope fix
Sessions
57=QUOTE market data · 57=TRADE orders

Your broker's API access panel shows the exact host, the port, the TargetCompID (written OCTOLIT in this guide) and the id of your key. Messages in this guide are written with | for the SOH delimiter (0x01) and … where a value is yours.

#Two sessions

One TCP connection carries one session type. Open two connections for a full setup: a QUOTE session that only streams prices, and a TRADE session that only carries orders and reports. The CompIDs are the same on both; TargetSubID (57) chooses the type.

TagValue
49 SenderCompIDK<key id> - the numeric id of your Trading API key, e.g. K12
56 TargetCompIDThe CompID from the panel; this guide writes it as OCTOLIT
57 TargetSubIDQUOTE or TRADE
554 PasswordThe API key itself. Scope fix is required; the key's IP allow-list and expiry apply exactly as for REST.
141 ResetSeqNumFlagY. Sessions are not resumed across reconnects - start both sides at 1 every time.
108 HeartBtInt5 - 120 seconds; 30 is a good default.
Reconnect means re-logon
Because sequence numbers reset on every logon, there is no gap fill. After a disconnect, log on again with 141=Y, re-send your market data subscriptions, and send 35=AF (OrderMassStatusRequest) and 35=AN (RequestForPositions) to rebuild your state from the platform's answers.

#Logon

  1. Open TLS to the endpoint

    The endpoint speaks TLS only (1.2 or newer). Verify the certificate chain as you would for HTTPS; the host name is the API host of your brand.

  2. Send 35=A within a few seconds

    Logon - TRADE session
    8=FIX.4.4 | 35=A | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=1 | 98=0 | 108=30 | 141=Y | 554=qk_... |
    Logon - QUOTE session
    8=FIX.4.4 | 35=A | 49=K12 | 56=OCTOLIT | 57=QUOTE | 34=1 | 98=0 | 108=30 | 141=Y | 554=qk_... |

    98=0 (no encryption inside the FIX layer - TLS does that), 108 your heartbeat, 141=Y, 554 the key.

  3. Read the answer

    Accepted: a 35=A back with the same 108. Refused: 35=5 Logout with the reason in 58 - one of the Trading API error codes, then the connection closes.

    Logout - refused logon
    8=FIX.4.4 | 35=5 | 49=OCTOLIT | 50=TRADE | 56=K12 | 34=2 | 58=invalid_api_key |
    • invalid_api_key - the key is wrong, revoked or expired, or lacks the fix scope.
    • ip_not_allowed - your address is not on the key's allow-list.
    • open_api_disabled - API access is switched off for the brand or the client.

#Heartbeats and test requests

Test Request and Heartbeat work as FIX 4.4 defines them. Send 35=0 every 108 seconds when nothing else was sent; answer a 35=1 TestRequest with a Heartbeat that echoes 112. The platform sends its own TestRequest when your side goes quiet, and a session that stops answering is closed.

Heartbeat
8=FIX.4.4 | 35=0 | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=7 |
TestRequest from the platform
8=FIX.4.4 | 35=1 | 49=OCTOLIT | 50=TRADE | 56=K12 | 34=9 | 112=TEST-1 |
Your answer
8=FIX.4.4 | 35=0 | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=8 | 112=TEST-1 |

Keep 108 short enough that a dead connection is noticed before your risk limits mind, and long enough that a slow link does not log you out - 30 seconds suits most.

#Order lifecycle

Your FIX client TRADE session · 57=TRADE Octolit platform validates · books · reports 35=D 11 ClOrdID · 55 · 54 · 38 · 40 35=8 150=0 New · 39=0 · 37 OrderID 35=8 150=… F · 4 · 5 35=8 150=8 Rejected · 103 · 58 Order states (39) 0 New 2 Filled 150=F · 31 / 32 · 721 pending limit / stop · 59=1 4 Cancelled 5 Replaced 35=F 35=G 8 Rejected

One NewOrderSingle, one or more ExecutionReports: 150=0 New first, then 150=F on a fill, 150=4 after a cancel, 150=5 after a replace, or 150=8 with the reason in 103 and the REST error code in 58.

35=D sent→ 150=0 New→ 150=F Filled· pending → 4 / 5· 150=8 Rejected
  1. NewOrderSingle (35=D)

    The minimum is 11 ClOrdID, 55 symbol (X:EURUSD, the numeric id, or EUR-USD; or 48 + 22=8), 54 side, 38 quantity in units (lots × contract size), 40 type and 59=1. 1 is the trading account id - optional when the key's client has one account. Stop loss and take profit ride along as absolute prices in 9101 / 9102; 9103 picks the leverage when the symbol offers a choice.

    Market buy 10 000 EURUSD with SL and TP
    8=FIX.4.4 | 35=D | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=2 | 11=ord-20261111-001 | 1=55 | 55=X:EURUSD | 54=1 | 38=10000 | 40=1 | 59=1 | 9101=1.0700 | 9102=1.0900 | 60=20261111-09:30:00.000 |

    11 ClOrdID is the client_order_id of the REST API. The same value is idempotent across FIX and REST: a duplicate answers 150=8 103=6 instead of a second order.

  2. ExecutionReport 150=0 - New

    The platform accepted the order and assigned 37 OrderID. For a market order this report is followed at once by the fill; for a limit or stop order it is the resting state.

    ExecutionReport - New
    8=FIX.4.4 | 35=8 | 49=OCTOLIT | 50=TRADE | 56=K12 | 37=812 | 11=my-order-1 | 17=... | 150=0 | 39=0 | 1=55 | 55=X:EURUSD | 54=1 | 40=2 | 44=1.0750 | 38=10000 | 14=0 | 151=10000 | 6=0 | 721=812 | 9101=1.07 |
  3. ExecutionReport 150=F - Trade

    39=2 Filled, 31 the price, 32 the quantity, 14 cumulative, 151 leaves 0. 721 is the position id - keep it, you need it to close or to match 35=AP position reports.

    ExecutionReport - Filled
    8=FIX.4.4 | 35=8 | 49=OCTOLIT | 50=TRADE | 56=K12 | 37=812 | 11=ord-20261111-001 | 17=... | 150=F | 39=2 | 1=55 | 55=X:EURUSD | 54=1 | 38=10000 | 14=10000 | 151=0 | 31=1.0752 | 32=10000 | 6=1.0752 | 721=812 |
  4. ExecutionReport 150=8 - Rejected

    39=8, the FIX reason in 103 and the REST error code in 58. One error table serves both protocols, so your REST error handling applies unchanged.

    ExecutionReport - Rejected (market closed)
    8=FIX.4.4 | 35=8 | 49=OCTOLIT | 50=TRADE | 56=K12 | 37=0 | 11=ord-20261111-002 | 17=... | 150=8 | 39=8 | 103=2 | 58=market_closed | 55=X:EURUSD | 54=1 | 38=10000 |
  5. Close a position

    A close is a NewOrderSingle with 721 set to the position id and 54 the opposite side. 38 may be the whole position or less where partial closes are allowed. The answer is 150=0 then 150=F with OrderID C<closed row id> and 721 the position.

    Close position 812
    8=FIX.4.4 | 35=D | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=5 | 11=close-812 | 721=812 | 55=X:EURUSD | 54=2 | 38=10000 | 40=1 | 59=1 | 60=20261111-10:00:00.000 |

#Pending orders: replace and cancel

Limit buy 10 000 EURUSD at 1.0750 with a stop loss
8=FIX.4.4 | 35=D | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=2 | 11=my-order-1 | 1=55 | 55=X:EURUSD | 54=1 | 38=10000 | 40=2 | 44=1.0750 | 59=1 | 9101=1.0700 | 60=20261010-12:00:00.000 |
You sendTagsAnswer
35=G OrderCancelReplaceRequest11 new ClOrdID, 41 the order's ClOrdID (or 37), 44 / 99 new price, 9101 / 9102 new levels (0 removes). Quantity and type cannot change.150=5 Replaced; refused: 35=9 with 434=2
35=F OrderCancelRequest11 new ClOrdID, 41 (or 37)150=4 39=4; already filled: 35=9 102=0; unknown: 35=9 102=1
35=H OrderStatusRequest11 or 37150=I; unknown: 150=I 39=8 103=5
Replace: new limit price and stop loss
8=FIX.4.4 | 35=G | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=3 | 11=ord-20261111-001-r1 | 41=ord-20261111-001 | 44=1.0740 | 9101=1.0690 | 60=20261111-09:31:00.000 |
Cancel
8=FIX.4.4 | 35=F | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=4 | 11=ord-20261111-001-c1 | 41=ord-20261111-001 | 60=20261111-09:32:00.000 |

Each replace or cancel carries its own fresh 11; 41 names the order. A refused replace answers 35=9 434=2 - for example when the order is no longer pending; the next ExecutionReport is the truth.

#Positions and open orders

Rebuild your state after every logon, and reconcile it on a timer:

  • 35=AF OrderMassStatusRequest (584 request id, optional 1 / 55) → one 150=I per pending order and open position, 911 total, 912=Y on the last.
  • 35=AN RequestForPositions (710 request id, 724=0, optional 1 / 55) → 35=AO then one 35=AP per open position with 721, 704 / 705 long / short quantity, 730 open price and 9101 / 9102.
RequestForPositions
8=FIX.4.4 | 35=AN | 49=K12 | 56=OCTOLIT | 57=TRADE | 34=6 | 710=pos-1 | 724=0 |

Positions also change outside your session - the client in the trader, a stop loss, a take profit, a stop-out. Reconcile with 35=AN on a timer, or watch the account frames of the WebSocket stream, which tell you when something on an account changed.

#Market data (QUOTE session)

  1. Subscribe

    35=V with 262 request id, 263=1 (snapshot + updates; 0 snapshot only, 2 unsubscribe), 264=0, 265, 267 with 269=0 bid and 269=1 offer, and 146 + 55 per symbol. Up to 100 symbols per session.

    MarketDataRequest
    8=FIX.4.4 | 35=V | 49=K12 | 56=OCTOLIT | 57=QUOTE | 34=2 | 262=md-1 | 263=1 | 264=0 | 265=1 | 267=2 | 269=0 | 269=1 | 146=2 | 55=X:EURUSD | 55=X:XAUUSD |
  2. Snapshot, then increments

    One 35=W per symbol with the current bid and ask, then 35=X incremental refreshes - at most one per symbol every 250 ms. The prices are the trading prices (bid / ask with the account type's spread), the same values the Trading API and the WebSocket deliver.

    MarketDataSnapshotFullRefresh
    8=FIX.4.4 | 35=W | 49=OCTOLIT | 50=QUOTE | 56=K12 | 34=2 | 262=md-1 | 55=X:EURUSD | 268=2 | 269=0 | 270=1.07500 | 269=1 | 270=1.07512 |
  3. Unknown symbol

    MarketDataRequestReject
    8=FIX.4.4 | 35=Y | 49=OCTOLIT | 50=QUOTE | 56=K12 | 34=3 | 262=md-2 | 281=0 | 58=symbol_not_found |

    Symbol codes are listed by GET /api/open/v1/symbols; use X:EURUSD, the numeric id or EUR-USD.

#Reject reasons

103 OrdRejReason on a 150=8 report. 58 always carries the REST error code, which is the more precise of the two.

0OrdRejReasonRefused by the broker
1OrdRejReasonUnknown symbol
2OrdRejReasonMarket closed
3OrdRejReasonOrder exceeds limit
5OrdRejReasonUnknown order
6OrdRejReasonDuplicate ClOrdID
11OrdRejReasonUnsupported order characteristic
13OrdRejReasonIncorrect quantity
99OrdRejReasonOther - see tag 58 (REST error code)

Session-level problems come as 35=3 Reject (malformed message, bad tag) or 35=5 Logout (key revoked, limit hit); both carry 58.

#Limits

FIX
TransportTCP + TLS 1.2 or newer
VersionFIX 4.4
HeartBtInt (108)5 - 120 s
Messages per second per session50 (burst 100)
Logons per minute per address30
Connections per address20
Symbols per QUOTE session100
Quote updatesat most one X per symbol every 250 ms

Order guards are shared with REST and counted together: the per-key and per-account order actions per second below apply to FIX orders too, and whatever refuses an order over REST refuses it over FIX, with the same code in 58.

Trading API
Requests per minute, per key1 200 (broker-set, 10 - 12 000)
Requests per minute, per client2 x the key limit, across all keys
Order actions per second, per key10 (broker-set, 1 - 50)
Order actions per second, per trading account10 (broker-set, 1 - 50)
Keys per client5 (broker-set, 1 - 20)
Wrong keys per minute, per address60, then 429 too_many_invalid_keys
Symbols per GET /quotes100
Bars per GET /bars5 000
Stream ticketsingle use, 60 s

#Certification checklist

Run this against a demo account before your broker switches the key to live. Each line is one message pair you should be able to show in a log.

  • Logon on QUOTE and on TRADE with 141=Y; a refused logon (35=5) is handled without a reconnect storm (backoff, at most 30 logons per minute).
  • Heartbeats at 108; a TestRequest is answered with the same 112.
  • Market order: 150=0 then 150=F; 721 stored.
  • Limit order, replace (150=5), cancel (150=4); a cancel of a filled order answers 35=9 102=0 and is handled.
  • Reject 150=8 with 103 and 58 mapped to your error handling; duplicate 11 gives 103=6 and is not retried.
  • Close by 721, partial and full.
  • 35=AF and 35=AN after a reconnect rebuild the same state your system had before.
  • QUOTE: subscribe to 2 symbols, receive 35=W then 35=X; unsubscribe with 263=2; unknown symbol gives 35=Y.
  • Rate limits respected: no more than 50 messages per second per session, no more than 10 order actions per second per key.
  • Clean logout (35=5) at shutdown.

Build and parse single messages in the FIX message lab; ask your broker for a certification session when the list above is green.