Fix protocol financial trading
Skill hamzabellouch/agent-skills/Financial and Fintech Engineering/fix-protocol-financial-trading
Master high-frequency FIX (Financial Information eXchange) protocol messaging (FIX 4.2/4.4/5.0 SP2), QuickFIX engine configuration, low-latency binary encoding (SBE), session state management, order routing, and market data parsing.From its SKILL.md
npx -y skills add hamzabellouch/agent-skills --skill fix-protocol-financial-tradingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.0 KB, ~3.0k tokens by cl100k_base, as published. Nobody here has run it
FIX Protocol Financial Trading & Messaging Architecture
This skill package defines enterprise standards for high-frequency financial trading systems, order management systems (OMS), and execution management systems (EMS) utilizing FIX (Financial Information eXchange) protocol versions 4.2, 4.4, and 5.0 SP2 (FIXML / SBE).
1. Core Session & Application Architecture
Session Layer Management & Resend Recovery
- Session State Persistence: Maintain strictly synchronized sequence numbers (
MsgSeqNum, Tag 34) for incoming and outgoing messages. Store session state in memory-mapped files (MMAP) or persistent low-latency disk storage. - Sequence Gap Handling & Recovery:
- When an incoming
MsgSeqNumis greater than expected, immediately issue aResendRequest(MsgType2, Tag 35=2). - Respond to incoming
ResendRequestmessages by re-sending administrative messages asSequenceReset-GapFill(MsgType4, Tag 35=4, Tag 123GapFillFlag=Y) and re-sending application messages withPossDupFlag=Y(Tag 43).
- When an incoming
- Heartbeat & Test Request Loop: Send
Heartbeat(Tag 35=0) at specifiedHeartBtInt(Tag 108) intervals. If no activity is received withinHeartBtInt + Margin, issue aTestRequest(Tag 35=1) with a uniqueTestReqID(Tag 112). Drop session if response is absent.
Low-Latency Messaging Engineering
- Zero-GC & Zero-Copy Memory Management: Eliminate object allocations on hot execution paths. Utilize ring buffers (e.g., LMAX Disruptor pattern) and pre-allocated byte buffers for message serialization.
- Encoding Formats: Use SBE (Simple Binary Encoding) or FAST (FIX Adapted for STreaming) for ultra-low latency market data feeds over UDP multicast, replacing legacy tag-value pairs.
- Transport Security & Network Topologies: Enforce mTLS 1.3 or IPsec VPN tunnels across dedicated cross-connects (Equinix NY4, LD4, TY3). Enable
TCP_NODELAY(disable Nagle's algorithm) and pin thread CPU affinity.
2. Key FIX Tag Reference Matrix
| Tag # | Field Name | Required / Enum | Description |
|---|---|---|---|
| 8 | BeginString | FIX.4.2 / FIX.4.4 / FIXT.1.1 | Protocol version identifier |
| 9 | BodyLength | Integer | Length of message body between Tag 9 and Tag 10 |
| 35 | MsgType | D (New Order), 8 (ExecReport), 2 (ResendReq) | Message type code |
| 49 | SenderCompID | String | Submitting institution identifier |
| 56 | TargetCompID | String | Receiving counterparty/exchange identifier |
| 34 | MsgSeqNum | Integer (1-based counter) | Message sequence counter |
| 11 | ClOrdID | Unique String | Client assigned unique identifier for order |
| 55 | Symbol | String | Financial instrument ticker (e.g., AAPL, EUR/USD) |
| 54 | Side | 1=Buy, 2=Sell, 5=Sell Short | Order side specification |
| 40 | OrdType | 1=Market, 2=Limit, 3=Stop, P=Pegged | Order type execution style |
| 38 | OrderQty | Quantity / Decimal | Total quantity of shares/contracts |
| 44 | Price | Currency Price | Limit price per unit |
| 10 | CheckSum | 3-digit zero-padded String | Modulo 256 sum of all characters up to 10= |
3. Production Code Implementations
Python Implementation: Production QuickFIX Initiator for Order Routing
import sys
import time
import logging
import quickfix as fix
import quickfix44 as fix44
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
class FIXOrderRoutingApplication(fix.Application):
def __init__(self):
super().__init__()
self.session_id = None
def onCreate(self, sessionID: fix.SessionID):
logging.info(f"Session Created: {sessionID}")
def onLogon(self, sessionID: fix.SessionID):
logging.info(f"Logon Successful: {sessionID}")
self.session_id = sessionID
def onLogout(self, sessionID: fix.SessionID):
logging.warning(f"Session Logged Out: {sessionID}")
self.session_id = None
def toAdmin(self, message: fix.Message, sessionID: fix.SessionID):
msg_type = fix.MsgType()
message.getHeader().getField(msg_type)
if msg_type.getValue() == fix.MsgType_Logon:
# Add Username/Password credentials for authentication
message.setField(fix.RawData("SuperSecretAuthToken"))
message.setField(fix.RawDataLength(len("SuperSecretAuthToken")))
def fromAdmin(self, message: fix.Message, sessionID: fix.SessionID):
pass
def toApp(self, message: fix.Message, sessionID: fix.SessionID):
logging.info(f"Sending Outbound App Message: {message}")
def fromApp(self, message: fix.Message, sessionID: fix.SessionID):
self._route_app_message(message, sessionID)
def send_limit_order(self, cl_ord_id: str, symbol: str, side: str, qty: float, price: float):
"""
Constructs and sends a FIX 4.4 NewOrderSingle (MsgType D)
"""
if not self.session_id:
raise RuntimeError("Cannot send order: FIX Session not active")
order = fix44.NewOrderSingle()
order.setField(fix.ClOrdID(cl_ord_id))
# Side: 1=Buy, 2=Sell
side_val = fix.Side_BUY if side.upper() == "BUY" else fix.Side_SELL
order.setField(fix.Side(side_val))
order.setField(fix.TransactTime())
order.setField(fix.OrdType(fix.OrdType_LIMIT))
order.setField(fix.Symbol(symbol))
order.setField(fix.OrderQty(qty))
order.setField(fix.Price(price))
order.setField(fix.TimeInForce(fix.TimeInForce_DAY))
# Send order asynchronously via QuickFIX engine
success = fix.Session.sendToTarget(order, self.session_id)
if not success:
logging.error(f"Failed to submit ClOrdID: {cl_ord_id}")
else:
logging.info(f"Submitted Limit Order {cl_ord_id} for {symbol}")
def _route_app_message(self, message: fix.Message, sessionID: fix.SessionID):
msg_type = fix.MsgType()
message.getHeader().getField(msg_type)
if msg_type.getValue() == fix.MsgType_ExecutionReport:
exec_id = fix.ExecID()
cl_ord_id = fix.ClOrdID()
ord_status = fix.OrdStatus()
symbol = fix.Symbol()
cum_qty = fix.CumQty()
avg_px = fix.AvgPx()
message.getField(exec_id)
message.getField(cl_ord_id)
message.getField(ord_status)
message.getField(symbol)
message.getField(cum_qty)
message.getField(avg_px)
logging.info(
f"ExecutionReport Received | ExecID: {exec_id.getValue()} | "
f"ClOrdID: {cl_ord_id.getValue()} | Status: {ord_status.getValue()} | "
f"Symbol: {symbol.getValue()} | CumQty: {cum_qty.getValue()} | AvgPx: {avg_px.getValue()}"
)
def main():
settings = fix.SessionSettings("fix_initiator.cfg")
application = FIXOrderRoutingApplication()
store_factory = fix.FileStoreFactory(settings)
log_factory = fix.FileLogFactory(settings)
initiator = fix.SocketInitiator(application, store_factory, settings, log_factory)
logging.info("Starting FIX Socket Initiator...")
initiator.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
logging.info("Stopping FIX Initiator...")
initiator.stop()
if __name__ == "__main__":
main()
TypeScript Implementation: High-Throughput Zero-Allocation FIX Parser
export class FastFixParser {
private static readonly SOH = 0x01; // SOH (Start of Header \x01)
/**
* Fast zero-copy zero-allocation parser for Tag-Value FIX buffers
*/
public parse(buffer: Buffer): Map<number, string> {
const fields = new Map<number, string>();
let cursor = 0;
const len = buffer.length;
while (cursor < len) {
const tagStart = cursor;
while (cursor < len && buffer[cursor] !== 0x3d /* '=' */) {
cursor++;
}
if (cursor >= len) break;
const tag = parseInt(buffer.toString('ascii', tagStart, cursor), 10);
cursor++; // Skip '='
const valStart = cursor;
while (cursor < len && buffer[cursor] !== FastFixParser.SOH) {
cursor++;
}
const value = buffer.toString('utf8', valStart, cursor);
fields.set(tag, value);
cursor++; // Skip SOH
}
this.validateChecksum(buffer, fields.get(10));
return fields;
}
/**
* Computes checksum modulo 256 over all bytes up to tag 10=
*/
private validateChecksum(buffer: Buffer, expectedChecksum?: string): void {
if (!expectedChecksum) return;
const sohIndex = buffer.lastIndexOf('\x0110=');
if (sohIndex === -1) return;
let sum = 0;
for (let i = 0; i <= sohIndex; i++) {
sum += buffer[i];
}
const computed = (sum % 256).toString().padStart(3, '0');
if (computed !== expectedChecksum) {
throw new Error(`FIX Checksum Error: Computed=${computed}, Expected=${expectedChecksum}`);
}
}
/**
* Formats a raw key-value map into a raw FIX wire protocol Buffer
*/
public serialize(msgType: string, senderCompId: string, targetCompId: string, seqNum: number, tags: Map<number, string>): Buffer {
let bodyStr = `35=${msgType}\x0149=${senderCompId}\x0156=${targetCompId}\x0134=${seqNum}\x01`;
for (const [tag, val] of tags.entries()) {
if ([8, 9, 35, 49, 56, 34, 10].includes(tag)) continue;
bodyStr += `${tag}=${val}\x01`;
}
const bodyLength = Buffer.byteLength(bodyStr, 'ascii');
const headerStr = `8=FIX.4.4\x019=${bodyLength}\x01`;
const fullMsgWithoutChecksum = headerStr + bodyStr;
let sum = 0;
const msgBuffer = Buffer.from(fullMsgWithoutChecksum, 'ascii');
for (let i = 0; i < msgBuffer.length; i++) {
sum += msgBuffer[i];
}
const checksumStr = `10=${(sum % 256).toString().padStart(3, '0')}\x01`;
return Buffer.concat([msgBuffer, Buffer.from(checksumStr, 'ascii')]);
}
}
4. Anti-Patterns & Critical Mistakes
-
Dynamic Memory Allocation in the Hot Path
- Impact: Garbage collection pauses (GC stalls) cause execution latencies to spike from microseconds to hundreds of milliseconds, triggering slippage or rejected limit orders.
- Remediation: Pre-allocate object pools, fix ring buffer sizes, and use off-heap memory.
-
Incorrect Gap Fill Processing (
SequenceReset-GapFill)- Impact: Exchange disconnects the session or rejects subsequent valid orders due to unsynchronized sequence numbers.
- Remediation: Ensure admin messages (Logon, Heartbeat, TestRequest) are skipped using
GapFillFlag=Ywhile incrementingNewSeqNo(Tag 36) appropriately.
-
Synchronous Disk I/O on Session Persistence
- Impact: Blocking file appends stall the event loop thread during market volatility bursts.
- Remediation: Perform async I/O or flush memory-mapped files off the critical path using dedicated background writer threads.
-
Ignoring Sub-Millisecond Timestamp Precision
- Impact: Regulatory non-compliance under MiFID II (RTS 25) requiring clock synchronization accuracy within 100 microseconds for high-frequency trading.
- Remediation: Format
SendingTime(Tag 52) andTransactTime(Tag 60) with microsecond (YYYYMMDD-HH:MM:SS.uuuuuu) or nanosecond resolution against PTP (Precision Time Protocol / IEEE 1588) synchronized clocks.
5. Verification & Testing Playbook
- Mock Acceptor Integration: Connect Initiator against QuickFIX Acceptor harness or TT/FIX Simulator.
- Sequence Number Gap Injection Test: Force gap by sending
MsgSeqNum = N + 5and verify incomingResendRequestgeneration and properSequenceResethandling. - Latency Profiling: Measure end-to-end wire-to-cancel throughput using
perfand low-overhead tick counters (RDTSC).
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.