Binance FIX API for Institutional Traders: Complete Guide
Master Binance FIX API for institutional crypto trading. Learn authentication, order routing, response parsing, and error handling with Python code examples.
Master Binance FIX API for institutional crypto trading. Learn authentication, order routing, response parsing, and error handling with Python code examples.
If you're moving serious volume — think hundreds of BTC per day — the standard Binance REST API starts feeling like a garden hose when you need a fire main. That's where FIX API comes in. Financial Information eXchange (FIX) protocol is the backbone of institutional trading in both traditional finance and, increasingly, crypto. Binance's FIX API endpoint gives institutional clients sub-millisecond order routing, dedicated infrastructure, and the kind of reliability that high-frequency desks and market makers actually need.
FIX (Financial Information eXchange) is a messaging protocol developed in the early 1990s for real-time electronic trading. It has been the standard in equities, FX, and derivatives for three decades — and now Binance has brought it to crypto. Unlike REST, which is stateless and HTTP-based, FIX uses persistent TCP connections, meaning your orders travel over a socket that stays open rather than opening and closing with every request. The result is dramatically lower latency and far higher throughput.
Binance FIX API is only available to institutional clients who have completed KYB onboarding. Retail accounts cannot access fix-oe.binance.com. Contact Binance institutional sales if you are processing significant monthly volume and need dedicated infrastructure.
Before writing a single line of code, you need institutional access. Binance requires a formal application, KYB (Know Your Business) verification, and typically a minimum volume threshold. Once approved, you receive credentials for the Order Entry (OE) endpoint at fix-oe.binance.com and optionally the Market Data (MD) endpoint. Your API key and secret are used to sign the Logon message — Binance does not use traditional API key headers like the REST API does. Platforms like Bybit and OKX have comparable institutional FIX programs worth evaluating during your venue selection phase.
| Endpoint | Host | Port | Purpose |
|---|---|---|---|
| Order Entry | fix-oe.binance.com | 9000 | Place, cancel, query orders |
| Market Data | fix-md.binance.com | 9000 | L2 order book, trade stream |
| Drop Copy | fix-dc.binance.com | 9000 | Execution report copy feed |
| Testnet OE | fix-oe.testnet.binance.vision | 9000 | Testing and staging |
Authentication on Binance FIX API uses HMAC-SHA256 signing embedded in the Logon message, similar in concept to REST API signing but formatted as FIX tags. The signature payload combines specific message fields joined by the SOH delimiter. The simplefix library in Python makes building FIX messages significantly easier than rolling your own byte-level implementation. Install it with pip install simplefix before running the examples below.
import simplefix
import socket
import ssl
import hashlib
import hmac
import time
# Binance FIX API credentials
API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
FIX_HOST = "fix-oe.binance.com"
FIX_PORT = 9000
SENDER_COMP_ID = "your_client_id" # assigned by Binance on onboarding
TARGET_COMP_ID = "BINANCE"
def build_logon(seq_num: int) -> bytes:
# Create and sign a FIX Logon (MsgType=A) message
ts = str(int(time.time() * 1000))
# Signature payload: fields joined with SOH delimiter (\x01)
raw_data = f"{ts}\x01{SENDER_COMP_ID}\x01{TARGET_COMP_ID}\x01{seq_num}\x01A"
signature = hmac.new(
API_SECRET.encode("utf-8"),
raw_data.encode("utf-8"),
hashlib.sha256
).hexdigest()
msg = simplefix.FixMessage()
msg.append_pair(8, "FIX.4.4")
msg.append_pair(35, "A") # MsgType = Logon
msg.append_pair(49, SENDER_COMP_ID) # SenderCompID
msg.append_pair(56, TARGET_COMP_ID) # TargetCompID
msg.append_pair(34, seq_num) # MsgSeqNum
msg.append_pair(52, ts) # SendingTime
msg.append_pair(98, 0) # EncryptMethod = None
msg.append_pair(108, 30) # HeartBtInt = 30 seconds
msg.append_pair(95, len(signature)) # RawDataLength
msg.append_pair(96, signature) # RawData = HMAC-SHA256 hex
msg.append_pair(554, API_KEY) # Password = your API key
return msg.encode()
# Establish TLS connection to Binance FIX OE endpoint
context = ssl.create_default_context()
raw_sock = socket.create_connection((FIX_HOST, FIX_PORT), timeout=10)
tls_sock = context.wrap_socket(raw_sock, server_hostname=FIX_HOST)
# Send Logon and read acknowledgement
tls_sock.sendall(build_logon(seq_num=1))
response = tls_sock.recv(4096)
print(f"Logon response received: {len(response)} bytes")
Once logged on, order placement is straightforward. New Order Single (tag 35=D) is the workhorse message for limit and market orders. Every order needs a client-assigned ClOrdID (tag 11) — this is your internal reference that Binance echoes back in all execution reports for that order. Many trading systems use a timestamp-based ID or a UUID. Signal services like VoiceOfChain push trade signals that feed directly into this order pipeline, triggering FIX execution the moment a signal fires rather than waiting for manual action.
def place_limit_order(
sock: ssl.SSLSocket,
seq_num: int,
symbol: str,
side: str,
quantity: float,
price: float
) -> str:
# Send a New Order Single (D) — returns ClOrdID for tracking
cl_ord_id = f"VOC-{int(time.time() * 1000)}"
ts = str(int(time.time() * 1000))
side_code = "1" if side.upper() == "BUY" else "2"
msg = simplefix.FixMessage()
msg.append_pair(8, "FIX.4.4")
msg.append_pair(35, "D") # MsgType = NewOrderSingle
msg.append_pair(49, SENDER_COMP_ID)
msg.append_pair(56, TARGET_COMP_ID)
msg.append_pair(34, seq_num) # MsgSeqNum
msg.append_pair(52, ts) # SendingTime
msg.append_pair(11, cl_ord_id) # ClOrdID — your internal order ID
msg.append_pair(55, symbol) # Symbol e.g. "BTCUSDT"
msg.append_pair(54, side_code) # Side: 1=Buy, 2=Sell
msg.append_pair(60, ts) # TransactTime
msg.append_pair(38, str(quantity)) # OrderQty
msg.append_pair(40, "2") # OrdType: 2=Limit
msg.append_pair(44, str(price)) # Price
msg.append_pair(59, "1") # TimeInForce: 1=GTC
sock.sendall(msg.encode())
return cl_ord_id
# Example: Buy 0.5 BTC at $64,500 on Binance institutional OE
ord_id = place_limit_order(
tls_sock, seq_num=2,
symbol="BTCUSDT",
side="BUY",
quantity=0.5,
price=64500.0
)
print(f"Order submitted — ClOrdID: {ord_id}")
For market orders, change tag 40 to '1' (Market) and omit the price field (tag 44). For immediate-or-cancel execution, set tag 59 to '3'. Binance FIX also supports Order Cancel Request (MsgType=F) and Order Cancel/Replace Request (MsgType=G) for modifying live orders without cancelling and resubmitting — a significant latency advantage over REST when working within tight execution windows.
Every order action generates an Execution Report (MsgType=8). Reading and acting on these correctly is where most FIX integrations either work well or fail quietly. The key fields are ExecType (tag 150) and OrdStatus (tag 39) — together they tell you exactly what happened to your order. A fill comes back as ExecType=F, a rejection as OrdStatus=8. Build your handler before going live — silent failures on institutional-size positions are very expensive.
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("fix_handler")
EXEC_TYPE_LABELS = {
b"0": "New",
b"1": "Partial Fill",
b"4": "Cancelled",
b"8": "Rejected",
b"F": "Trade (fill)",
}
ORD_REJECT_REASONS = {
b"0": "Broker option",
b"1": "Unknown symbol",
b"3": "Order exceeds limit",
b"6": "Duplicate order",
b"11": "Insufficient funds",
b"99": "Other",
}
def handle_execution_report(msg: simplefix.FixMessage) -> dict:
exec_type = msg.get(150)
ord_status = msg.get(39)
result = {
"cl_ord_id": msg.get(11, b"").decode(),
"order_id": msg.get(37, b"").decode(),
"symbol": msg.get(55, b"").decode(),
"exec_type": EXEC_TYPE_LABELS.get(exec_type, str(exec_type)),
"cum_qty": float(msg.get(14, b"0")),
"leaves_qty":float(msg.get(151, b"0")),
"avg_px": float(msg.get(6, b"0")),
}
if ord_status == b"8": # Rejected
reason_code = msg.get(103)
reason = ORD_REJECT_REASONS.get(reason_code, b"Unknown")
result["error"] = reason.decode() if isinstance(reason, bytes) else reason
logger.error(f"Order rejected [{result['cl_ord_id']}]: {result['error']}")
elif exec_type == b"F": # Full or partial fill
logger.info(
f"Fill: {result['cl_ord_id']} | "
f"filled={result['cum_qty']} @ avg ${result['avg_px']} | "
f"remaining={result['leaves_qty']}"
)
return result
def receive_messages(sock: ssl.SSLSocket):
parser = simplefix.FixParser()
while True:
try:
data = sock.recv(4096)
if not data:
raise ConnectionResetError("Server closed connection")
parser.append_buffer(data)
while True:
msg = parser.get_message()
if msg is None:
break
msg_type = msg.get(35)
if msg_type == b"8": # Execution Report
handle_execution_report(msg)
elif msg_type == b"0": # Heartbeat — must echo
logger.debug("Heartbeat received")
elif msg_type == b"3": # Session-level Reject
logger.warning(f"Session reject: {msg.get(58)}")
except (socket.timeout, ConnectionResetError) as e:
logger.error(f"Connection lost: {e} — reconnect required")
break
Always implement a heartbeat handler. Binance will disconnect sessions that fail to respond to Heartbeat (MsgType=0) or TestRequest (MsgType=1) messages within the HeartBtInt specified at logon (recommended: 30 seconds). A silent disconnect with open positions and no reconnect logic is the most dangerous failure mode in any FIX integration.
Not every Binance integration needs FIX. If you are running a basic bot trading a few times per hour, the REST API is perfectly adequate. FIX becomes essential when latency and throughput are the real bottleneck. Bybit and OKX both offer FIX for institutional clients, and the message structure is similar enough that a well-architected FIX client can support multiple venues with minimal changes. Gate.io and KuCoin remain REST-only for most institutional clients as of 2025, though WebSocket execution is available on both.
| Feature | FIX API | REST API |
|---|---|---|
| Connection | Persistent TCP/TLS | Stateless HTTP |
| Latency | < 1ms typical | 5–50ms typical |
| Throughput | Thousands of orders/sec | Rate-limited (1200 req/min) |
| Setup complexity | High | Low |
| Access | Institutional only | All accounts |
| Execution reports | Pushed in real-time | Must poll or use WebSocket |
| Best for | HFT, market making, large desks | Retail bots, low-frequency strategies |
For algo traders operating somewhere in the middle — a systematic fund running 50–200 orders per day — a hybrid approach often works well. Use REST for account management, balance queries, and non-time-sensitive operations, while routing live order execution through FIX. VoiceOfChain's signal output integrates cleanly at this layer: signals arrive via the platform's API, get validated, then get dispatched to the FIX socket for execution with minimal latency overhead between signal and fill.
Binance FIX API is not a weekend project — it requires institutional access, solid understanding of the FIX protocol, and careful session management. But for desks handling serious volume, the latency and throughput improvements are decisive. The three code examples above cover the core of what you need: an authenticated session, order submission, and execution report handling with proper error logic. Build on testnet first, validate every message field against Binance's official FIX specification, and implement heartbeat handling before anything else. When paired with a reliable signal source like VoiceOfChain, a FIX-based execution layer gives you the full stack — from signal to fill — with institutional-grade speed.