{"id":50398896,"url":"https://github.com/alexjreid/pitboss","last_synced_at":"2026-05-30T22:02:19.092Z","repository":{"id":360685004,"uuid":"1250946672","full_name":"AlexJReid/pitboss","owner":"AlexJReid","description":"pitboss is a toy limit order book and matching engine","archived":false,"fork":false,"pushed_at":"2026-05-27T12:02:08.000Z","size":4284,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T13:25:39.907Z","etag":null,"topics":["experiment","matching-engine","order-book"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlexJReid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-27T05:30:10.000Z","updated_at":"2026-05-27T12:01:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AlexJReid/pitboss","commit_stats":null,"previous_names":["alexjreid/pitboss"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/AlexJReid/pitboss","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexJReid%2Fpitboss","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexJReid%2Fpitboss/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexJReid%2Fpitboss/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexJReid%2Fpitboss/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlexJReid","download_url":"https://codeload.github.com/AlexJReid/pitboss/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexJReid%2Fpitboss/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33711018,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["experiment","matching-engine","order-book"],"created_at":"2026-05-30T22:02:18.356Z","updated_at":"2026-05-30T22:02:19.081Z","avatar_url":"https://github.com/AlexJReid.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pitboss\n\n![boss](./pitboss.jpg)\n\npitboss is a tiny deterministic matching engine in C.\n\nIt takes commands, writes them to a journal, emits events, and rebuilds the book\nby replaying the journal.\n\nRight now scope is narrow... one symbol, limit orders only, fixed capacities, and one\nmutation owner for sequencing, persistence, matching, and book updates.\nCommands are **input facts**, events are **engine decisions**, and the book is **derived state** rebuilt from the journal.\n\nSee [theory.md](./theory.md) for limit-order-book background,\n[docs.md](./docs.md) for protocol and architecture details, and\n[roadmap.md](./roadmap.md) for current status and planned work.\n\nSome mechanical C/docs/sanity tests and benchmarks were written with non-human assistance. The design/layout/trade offs are mostly human, for better or worse.\nI've taken some ideas from [monoblok](https://github.com/lexvicacom/monoblok).\n\n## Why this exists\n\npitboss is not an exchange. It is a matching-engine core for looking at how\nordered systems sequence commands, produce events, persist decisions, and\nrebuild state.\n\nThere is no FIX, auth, clearing, risk engine, or market data. My focus is onkeeping matching, persistence, replay, and recovery simple enough to reason about.\n\nSome of the shape comes from a long-running interest in the ideas around event sourcing and [the LMAX architecture](https://martinfowler.com/articles/lmax.html), made famous in a classic 2011 write-up. Single-threaded in-memory business core, event sourcing, journal replay, and I/O pushed out to the edges. I have always found\nthat odd sounding approach interesting, and I have also seen a degree of\nbandwagon jumping around it, a decade after it was a thing.\n\nThis project is not trying to build a photocopier version of a 2010\napproach in 2026. In C, many of the Java workarounds are plainly nonsense: there is no\nJVM object model to fight, no garbage collector to placate, and no reason to\ncopy ceremony from a different runtime. pitboss does not use a ring\nbuffer as its core architecture just because that was the famous part as blind\ncopying would miss the point. The choices here are simple: single-owner\nmutation instead of lock-free core data structures, minimum allocation, no\nhot-path allocation in matching, fixed pools when capacity matters, and\nordinary arrays and linked lists where they fit. Durability concerns still\nmatter: keep the mutation owner clear, make state replayable, keep I/O out of\nthe deterministic core, measure the hot path, and let data structures fit the\ndomain instead of the other way around.\n\n## What it does\n\n- Matches limit orders with price-time priority.\n- Writes sequenced commands to a binary journal.\n- Replays the journal to rebuild state.\n- Saves snapshots as recovery shortcuts.\n- Exposes a TCP gateway, monitor stream, and warm follower.\n- Uses fixed pools and explicit capacity errors.\n\n## Text protocol example\n\nClients send newline-delimited commands such as\n`NEW_LIMIT order_id side price qty`.\n\nOver TCP, an accepted command returns `+OK sequence event-count`, followed by\nthat many event lines. The sequence is the journal position; it is separate from\nthe order id.\n\nAssume the book already has resting ask order `19`: sell `9` at price `101`,\nand the next journal sequence is `19`.\n\n```text\n\u003e NEW_LIMIT 41 S 200 10\n\u003c +OK 19 2\n\u003c ACCEPTED 19 41\n\u003c RESTING 19 41 S 200 10\n\n\u003e NEW_LIMIT 42 B 500 10\n\u003c +OK 20 3\n\u003c ACCEPTED 20 42\n\u003c TRADE 20 42 19 101 9\n\u003c TRADE 20 42 41 200 1\n```\n\nOrder `41` is accepted at sequence `19` and rests because it does not cross.\nOrder `42` is a buy with limit `500`, so it can trade with asks priced `500` or\nless. It consumes the cheaper resting ask first: order `19` at maker price\n`101` for quantity `9`, then order `41` at maker price `200` for quantity `1`.\nThe buy is fully filled, so there is no `RESTING` event for order `42`.\n\n## Build\n\n```sh\ncmake -S . -B build\ncmake --build build\n```\n\n## Run\n\n```sh\nbuild/pitboss run input.txt\n```\n\nUse `-` for stdin:\n\n```sh\nprintf 'NEW_LIMIT 1 B 100 10\\nNEW_LIMIT 2 S 99 4\\nCANCEL 1\\n' | build/pitboss run -\n```\n\nThe CLI appends binary command records to `pitboss.journal` by default before\napplying them. Set `PITBOSS_JOURNAL=none` to disable journaling, or set it to a\ndifferent path.\n\nJournal writes use fixed 64-byte records and a 64 KiB buffer. The TCP gateway\nacks accepted commands only after their journal buffer flushes; the default\ngroup-commit timer is 1 ms. Set `PITBOSS_JOURNAL_FLUSH_MS=N` to change it, and\n`PITBOSS_JOURNAL_FSYNC=1` to fsync each flushed buffer.\n\nReplay a sequenced journal and print the same events again:\n\n```sh\nbuild/pitboss replay pitboss.journal\n```\n\nThe replay output format is described in [docs.md](./docs.md#replay-output).\n\nDump the derived book state after replaying a journal:\n\n```sh\nbuild/pitboss dump pitboss.journal\n```\n\n`build/pitboss dump` without a path reads `PITBOSS_JOURNAL` when it is set,\notherwise `pitboss.journal`.\n\nCreate a binary checkpoint of the current derived book, then recover from that\ncheckpoint plus any later journal records:\n\n```sh\nbuild/pitboss checkpoint pitboss.journal pitboss.snap\nbuild/pitboss recover pitboss.journal pitboss.snap\n```\n\nWhen `run` or `listen` append to an existing binary journal, pitboss first\nreplays that journal silently so sequence numbers and the in-memory book continue\nfrom the recovered state.\n\n## TCP Gateway\n\nThe TCP listener is an I/O shell around the deterministic core. Socket framing,\nparse work, and stateless validation can run outside the sequencer; sequence\nassignment, journal append, matching, book mutation, monitor fanout, and\nreplication fanout stay ordered on the libuv loop.\n\n```mermaid\nflowchart LR\n  client[\"client commands\"]\n  gate[\"gate / validation\"]\n  sequencer[\"sequencer\"]\n  journal[\"journal\"]\n  matcher[\"matcher\"]\n  events[\"events\"]\n  book[\"book state\"]\n  monitors[\"monitor clients\"]\n  replicas[\"warm followers\"]\n\n  client --\u003e gate --\u003e sequencer --\u003e journal --\u003e matcher --\u003e events --\u003e book\n  events --\u003e monitors\n  journal --\u003e replicas\n```\n\n```sh\nbuild/pitboss listen 127.0.0.1 17077\nbuild/pitboss listen 127.0.0.1 17077 17078\nbuild/pitboss listen 127.0.0.1 17077 17078 17079\nscripts/pitboss-client.py 127.0.0.1:17077\nscripts/pitboss-client.py 127.0.0.1:17077 -c 'NEW_LIMIT 1 B 100 10'\nscripts/start-replication-pair.sh\n```\n\nThe TCP listener handles `SIGINT` and `SIGTERM` as graceful shutdown requests:\nit closes the listeners and active sessions, then runs the normal journal close\npath. Set `PITBOSS_JOURNAL_FSYNC=1` when flushed journal buffers should be\nfsynced.\n\nWhen the optional monitor port is present, monitor clients receive\n`PITBOSS MONITOR 1`, a current book snapshot, then a read-only live event\nstream.\n\nWhen the optional replication port is present, warm followers can connect and\ntail the primary's sequenced binary journal stream to maintain their own journal.\n\n```sh\nPITBOSS_JOURNAL=follower.journal build/pitboss follow 127.0.0.1 17079\n```\n\nThe follower recovers its local journal, sends a byte/sequence cursor, then\nappends and applies primary records after that point. Client ingress remains\nsingle-primary.\n\nFor operator-driven promotion of follower to leader, inspect the follower journal and compare it with\nthe old primary monitor sequence. See `scripts/start-replication-pair.sh` and the temp utility scripts it generates.\n\n## Test\n\n```sh\nctest --test-dir build\nscripts/listener-smoke.sh\nscripts/monitor-smoke.sh\nscripts/replication-smoke.sh\n```\n\nUseful manual checks:\n\n```sh\nbuild/pitboss bench 100000\nbuild/pitboss bench-levels 1000000\nbuild/pitboss bench-workload matcher-only --commands 3000000 --active-orders 1000 --price-slots 750 --cross-rate 6\nbuild/pitboss bench-workload journaled --commands 3000000 --active-orders 1000 --price-slots 750 --cross-rate 6\nscripts/replication-bench.sh catchup 10000\nscripts/replication-bench.sh live 10000\nscripts/replication-bench.sh live-bulk 100000\n```\n\nThe Python client accepts multiple command-gateway endpoints and tries them in\norder. If the current connection drops, it reconnects to the next endpoint:\n\n```sh\nscripts/pitboss-client.py 127.0.0.1:17077 127.0.0.1:18077\nscripts/pitboss-client.py 127.0.0.1:17077 127.0.0.1:18077 -f commands.txt --strict\n```\n\n`scripts/start-replication-pair.sh` starts a local leader with command, monitor,\nand replication ports plus one follower. It writes journals, logs, and PID files\nunder a temp run directory. It also generates `env.sh` plus helper scripts for\nclient, monitor, promote, stop, journal info, and log tailing.\n\n`bench-workload matcher-only` measures the in-process matcher on a deterministic\nactive-book workload. `bench-workload journaled` includes append-only journal\nwrites on the same workload. Both report throughput, command mix, final active\norder count, and p50/p90/p99/worst latency.\n\nExample output from one M2 MacBook Air (2022) run, Release build. Treat these as\norientation numbers, not a benchmark claim.\n\n| Surface | Command | Workload | Throughput | Latency |\n| --- | --- | ---: | ---: | ---: |\n| Matcher only | `bench-workload matcher-only` | 3,000,000 commands | 10.4M cmd/s | p50 83ns, p99 167ns |\n| Journaled matcher | `bench-workload journaled` | 300,000 commands | 497k cmd/s | p50 1.5us, p99 4.9us |\n| Replication catch-up, journaled follower | `replication-bench.sh catchup` | 10,000 records | 203k records/s | batch catch-up |\n| Live tail, client-paced | `replication-bench.sh live` | 10,000 records | 9.3k records/s | one command at a time |\n| Live tail, bounded bulk | `replication-bench.sh live-bulk` | 10,000 records | 13.8k records/s | 256-command window |\n\n`replication-bench.sh live` sends one command at a time and waits for the client\nresponse. It is a conservative end-to-end loopback check, not a maximum fanout\nnumber.\n\n## Commands\n\nProtocol, matching semantics, architecture notes, test commands, and current\nlimitations live in [docs.md](./docs.md#commands).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexjreid%2Fpitboss","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexjreid%2Fpitboss","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexjreid%2Fpitboss/lists"}