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
0
runtime deps
39
tests passing
3
FIX versions
2
session roles
MIT
licensed
~1.3µs
per parse

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.

Correctness

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.

Developer experience

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.

Supply chain

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.

35=A

Session state machine

Logon, logout, heartbeats, test requests, sequence tracking.

35=2

Resend & gap-fill

Real replay with PossDup, admin runs gap-filled correctly.

reconnect

Auto-reconnect

Exponential backoff on unexpected drops, never after a clean logout.

35=3

Inbound validation

Checksum, CompID, BeginString and required-tag checks with Reject.

store

Pluggable stores

In-memory or file-backed sequence and message stores. Bring your own.

xml

FIX dictionary

Parse any QuickFIX spec, readable logs, typed-constant codegen.

tcp / ws

TCP, TLS & WebSocket

Pluggable transport, run over a socket, or over WebSocket in the browser.

4.2 / 4.4 / 5.0

Initiator + Acceptor

Client and server roles, FIX 4.2 through 5.0 (FIXT.1.1).

NoXXX

Repeating groups

Extract group entries with getGroups, build them with the group() helper.

553 / 554

Authentication

Username, Password and RawData on logon; gate connections with an acceptor callback.

CompID

Multi-counterparty

One acceptor serves many initiators, learning each CompID from its Logon.

dict

Message validation

Dictionary-driven required-field checks on inbound and outbound messages.

component

Components & nesting

Dictionary expands components and preserves nested repeating groups.

metrics

Observability

A typed event stream for logging, plus sent / received / reject / reconnect counters.

allow

Message allowlist

Reject unexpected inbound message types with a session Reject.

mTLS

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.

$ npm test
detects a sequence gap and recovers via gap-fill
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.

Engine
quickfixgoGo✓
QuickFIXC++✓
QuickFIX/JJava✓
Protocol version
FIX 4.2logon → fill✓
FIX 4.4logon → fill✓
FIX 5.0 · FIXT.1.1logon → fill✓

Build on a FIX layer you can trust.

Open source, MIT licensed, zero dependencies. Clone it, read it, and connect your first venue today.

Get started on GitHub