FIX protocol engine for TypeScript
The FIX engine
TypeScript deserves.
A correct session layer, zero dependencies, and an API that feels like 2026. The resend, gap-fill, and sequence-reset edge cases handled, not the ones that quietly break lesser engines at 3am.
$ npm install fixwire
Why fixwire
Most FIX libraries parse messages. The hard part is the session.
Encoding tag=value is a weekend. The session layer is the swamp: resend requests, gap-fill versus sequence-reset, recovery after a dirty disconnect. fixwire gets that part right, and proves it with tests.
Right where it counts
Gap detection and ResendRequest, true replay with PossDup and OrigSendingTime, SequenceReset gap-fill, CompID and checksum validation, and recovery after a dirty reconnect. The failure modes that corrupt an order flow are tested, not hoped for.
Typed and modern
An async, event-driven API with fully typed messages and events, order builders, and human-readable logging from a FIX dictionary. It reads like TypeScript, not a port of a 2004 Java engine.
Zero dependencies
The entire engine ships with no runtime dependencies. Nothing transitive to audit, no surprise breakage upstream. You can read the whole thing in an afternoon before you trust it with real orders.
In practice
Connect, send, and get filled in a dozen lines.
import { Session, MsgType, Tag, newOrderSingle, Side, OrdType } from 'fixwire'
const session = new Session({
senderCompId: 'CLIENT',
targetCompId: 'VENUE',
host: 'fix.venue.com',
port: 9876,
tls: true,
reconnect: true,
})
session.on('logon', () => {
session.send(MsgType.NewOrderSingle, newOrderSingle({
clOrdId: 'order-1',
symbol: 'BTC/USD',
side: Side.Buy,
orderQty: 1,
ordType: OrdType.Limit,
price: 65000,
}))
})
session.on('app', (msg) => {
if (msg.msgType === MsgType.ExecutionReport) {
console.log('fill', msg.get(Tag.ClOrdID), msg.get(Tag.OrdStatus))
}
})
session.connect()import { Acceptor, MsgType, Tag } from 'fixwire'
const acceptor = new Acceptor({
senderCompId: 'SERVER',
targetCompId: 'CLIENT',
port: 9876,
})
acceptor.on('session', (session) => {
// Wire handlers before the counterparty logs on.
session.on('app', (msg) => {
if (msg.msgType === MsgType.NewOrderSingle) {
session.send(MsgType.ExecutionReport, [
[Tag.ClOrdID, msg.get(Tag.ClOrdID) ?? ''],
[Tag.ExecType, '0'],
[Tag.OrdStatus, '0'],
])
}
})
})
acceptor.listen()import { parseDictionary, describe } from 'fixwire'
import { readFileSync } from 'node:fs'
// Point it at any QuickFIX data dictionary.
const dict = parseDictionary(readFileSync('FIX44.xml', 'utf8'))
session.on('message', (msg) => {
console.log(describe(msg, dict))
// Side=1(BUY) ClOrdID=order-1 Symbol=BTC/USD OrdType=2(LIMIT)
})What is inside
A complete session engine, both sides of the wire.
Session state machine
Logon, logout, heartbeats, test requests, sequence tracking.
Resend & gap-fill
Real replay with PossDup, admin runs gap-filled correctly.
Auto-reconnect
Exponential backoff on unexpected drops, never after a clean logout.
Inbound validation
Checksum, CompID, BeginString and required-tag checks with Reject.
Pluggable stores
In-memory or file-backed sequence and message stores. Bring your own.
FIX dictionary
Parse any QuickFIX spec, readable logs, typed-constant codegen.
TCP, TLS & WebSocket
Pluggable transport, run over a socket, or over WebSocket in the browser.
Initiator + Acceptor
Client and server roles, FIX 4.2 through 5.0 (FIXT.1.1).
Repeating groups
Extract group entries with getGroups, build them with the group() helper.
Authentication
Username, Password and RawData on logon; gate connections with an acceptor callback.
Multi-counterparty
One acceptor serves many initiators, learning each CompID from its Logon.
Message validation
Dictionary-driven required-field checks on inbound and outbound messages.
Components & nesting
Dictionary expands components and preserves nested repeating groups.
Observability
A typed event stream for logging, plus sent / received / reject / reconnect counters.
Message allowlist
Reject unexpected inbound message types with a session Reject.
Mutual TLS
Client certificates on both the initiator and the acceptor.
Proof, not promises
The nasty cases are the test suite.
A FIX library is only as trustworthy as its handling of the moments that go wrong. Every one of these is a real scenario fixwire is tested against, including a full round-trip of its own acceptor talking to its own initiator.
replays application messages with PossDup on ResendRequest
discards a message with a bad checksum
logs out on a sequence number that is too low
logs out on a CompID mismatch
reconnects after an unexpected disconnect
rejects a message missing SendingTime
FIXT.1.1 logon carries DefaultApplVerID
acceptor and initiator complete a full round-trip
completes a logon + order round-trip over WebSocket
... and 29 more ✓ 39 passing · 0 failing
Interoperability
Proven against the engines the market actually runs.
fixwire logs on, trades, and logs out against independent, production FIX engines, passing their strict data-dictionary validation. Different codebases, different languages, no shared heritage with fixwire. It is verified across protocol versions too.
Build on a FIX layer you can trust.
Open source, MIT licensed, zero dependencies. Clone it, read it, and connect your first venue today.