08 / 12Prism Systems · FinTech & Banking
An exchange core that matches, settles and replays to the exact same state
A price-time priority central limit order book with limit, market, stop, stop-limit, IOC, FOK, DAY and post-only orders, self-trade prevention, price bands and market collars, a double-entry multi-currency ledger with funds holds and maker/taker fees, a FIX 4.4 session layer and TCP gateway, and a deterministic input journal that replays to an identical state hash.
A live BTC-USD order book with market makers, takers and stops
Three market makers quote post-only ladders and six takers trade against them. Send your own orders through a real FIX session, plant a stop cluster and let a whale sell through it, or pull the makers and watch the spread blow out.
What's inside the engine
Real algorithms, not mock-ups. The simulator above runs the exact same code you import.
Price-time priority
Price levels are kept sorted best-first with binary search, each level is a FIFO queue, and cancels are O(1) through lazy deletion with periodic compaction.
Every order type
Limit, market (converted to IOC with a 2% collar), stop and stop-limit, with GTC, IOC, FOK (pre-checked for full liquidity) and DAY, plus post-only that rejects instead of taking.
Pre-trade risk
Tick and lot checks, a fat-finger price band around the last price, max order size, funds holds at the worst possible price, and self-trade prevention (cancel-resting or cancel-taker).
Double-entry ledger
Each fill posts balanced legs for base, quote and maker/taker fees. Balances split into available and held, and verify() proves every currency sums to zero.
FIX 4.4
Encoder and parser with BodyLength and CheckSum, a session with Logon, sequence-gap ResendRequest, session Reject, and D/F/G mapped to ExecutionReport and OrderCancelReject.
Cancel / replace
Reducing quantity keeps queue priority, changing price or increasing size re-queues at the back, and a replace that crosses matches immediately.
Stops that cascade
Stops trigger on the last trade price and fire as market or limit orders, which can move the price and trigger the next stop in the same command.
Deterministic replay
Commands are journaled before execution. Replaying the journal into a fresh engine reproduces the same SHA-256 hash over books, stops, balances and sequence numbers.
From request to result
Validate
Symbol, side, type and TIF are checked, then tick, lot, price band and max size. A market order gets a collar price so it can never sweep the whole book.
Hold funds
Buyers hold quote at the worst price plus the max fee, sellers hold base. Orders without funds are rejected before touching the book.
Match
The order walks the opposite side best price first and FIFO within a level, applying self-trade prevention. FOK checks available quantity first.
Settle
Each fill posts a balanced journal entry: base and quote move between accounts, fees go to the fee account, unused holds are released.
Rest or finish
Leftover GTC/DAY quantity rests in the book, IOC remainders cancel, then stops are checked against the new last price.
System design
Every box is a module in src/. Data flows left to right.
Ship it in your own stack
Zero dependencies. Works in Node.js 18+ and modern browsers.
import { Engine, FixSession, FixClient, toUnits } from "prism-matching-engine";
const ex = new Engine({ fees: { maker: 2, taker: 10 }, stp: "cancel-resting" }); // bps
ex.ledger.deposit("alice", "BTC", BigInt(toUnits("2")));
ex.ledger.deposit("bob", "USD", BigInt(toUnits("200000")));
ex.submit({ account: "alice", symbol: "BTC-USD", side: "sell", px: "65000.50", qty: "1.2" });
const r = ex.submit({ account: "bob", symbol: "BTC-USD", side: "buy", type: "market", qty: "0.5" });
r.trades; // [{ id: "T1", px: 6500050000000, qty: 50000000, buyer: "bob", seller: "alice", ... }]
r.reports; // execution reports: new / trade / canceled / rejected / replaced / triggered
ex.submit({ account: "alice", symbol: "BTC-USD", side: "sell", type: "stop", stopPx: "64980", qty: "0.3" });
ex.replace("O1", { qty: "1.0" }); // smaller qty keeps queue priority
ex.book("BTC-USD").depth(5); // { bids: [{ px, qty, orders }], asks: [...] }
ex.ledger.verify(); // { ok: true } — every currency sums to zero
const again = Engine.replay(ex.input, {}, seedDeposits);
again.stateHash() === ex.stateHash(); // true
const fix = new FixSession(ex), cl = new FixClient();
fix.handle(cl.logon());
fix.handle(cl.order({ clOrdId: "a1", account: "bob", symbol: "BTC-USD", side: "buy", px: "65000.50", qty: "0.1", tif: "IOC" }));$ npx github:krishyaa06/prism-matching-engine demo
$ npx github:krishyaa06/prism-matching-engine book --secs 30
$ npx github:krishyaa06/prism-matching-engine fix examples/orders.fix
$ npx github:krishyaa06/prism-matching-engine bench --orders 300000
$ npx github:krishyaa06/prism-matching-engine replay
$ npx github:krishyaa06/prism-matching-engine serve --port 9878# use the CLI without installing
$ npx github:krishyaa06/prism-matching-engine --help
# or add the engine to your project
$ npm install github:krishyaa06/prism-matching-engine
# run the simulator locally
$ git clone https://github.com/krishyaa06/prism-matching-engine.git && cd prism-matching-engine
$ npx serve . # then open http://localhost:3000