Low-latency systems in C++ — market data, lock-free concurrency, and the measurements behind both
LinkedIn • Email • TradingView
Software engineer, five years in, mostly in the two industries where being wrong is expensive: finance and healthcare. I have built clinical systems handling patient records — where a bug is not a rollback, it is someone's medical history — and trading infrastructure where the cost of being wrong is measured in basis points. Both taught the same lesson from opposite ends. Confidence is not evidence.
That is roughly how I ended up here. I spent years writing C# and .NET, and at some point started wondering why processing a market data event takes microseconds in one language and nanoseconds in another. The answer had very little to do with the languages and everything to do with cache lines, memory ordering, and how often you ask the operating system for permission to continue. Rather than take anyone's word for it, I started rebuilding the pieces myself and measuring each one.
That is what most of these repositories are. Not tutorials, not libraries anyone asked for — the things I wanted to understand well enough to build from scratch, with benchmarks run on my own hardware instead of numbers quoted from a book.
I have a habit of not believing things, including my own results. A green test suite means nothing until you have broken the code on purpose and watched it turn red — so I do that first, and only then trust the clean run. The same scepticism goes for consensus in markets: my most-read writing has been the contrarian calls, and the ones worth publishing are the ones where I could show the work rather than assert a view. Several times that habit has forced me to publish a result I did not want. Cache-line padding bought 1.16x, not the 2-3x everyone repeats. An optimisation I was sure about changed the throughput by nothing at all. Those went in the READMEs too.
Lately I have been working where software engineering, quantitative methods and machine learning overlap — mean reversion, volatility forecasting, regime detection — and building the low-latency systems underneath them. That intersection is where I want to be.
cpp-hft — a trading engine, rewritten in C++
I had a working market data and market making engine in C#. It handled about 3,500 events per second, end to end in roughly 200 microseconds, which was fine. Then I wondered how much of that was the problem and how much was the runtime.
So I started porting it, beginning with the parts where C++ actually earns its keep: the ring buffer, the order book, and the path between them. Measured over 500,000 simulated Binance messages flowing through two real threads, the whole chain — parse the JSON, cross the ring buffer, update the book, run the strategy, produce a quote — now takes 417 nanoseconds at the median, and sustains 11.4 million events per second.
The honest caveat, which the repo states too: the C# number includes the network hop and this one does not, so it is not a clean comparison. The tail latency (p99 and beyond) is mostly the scheduler preempting the process, since I have no CPU isolation or thread pinning on this machine.
The repo also collects the lessons that got me there, each with a reproducible
benchmark. My favourite is the one that taught me the most: a formally
incorrect memory_order that passed twenty out of twenty test runs on x86-64
and would have broken silently on ARM. Nothing caught it except
ThreadSanitizer. At this level, the bugs that matter often produce no signal at
all — correct output, clean build, passing tests, and wrong on hardware you do
not own.
C++20, CMake, atomics, ThreadSanitizer, Valgrind
OpenHFT-Lab — the C# engine it came from
The original. A live Binance WebSocket feed into lock-free ring buffers, an L2 order book, market making and triangular arbitrage strategies, and a real-time dashboard over SignalR.
It is still the most complete system I have built, and the one people have found most useful — it is by some distance the most starred thing on this profile. Writing it is what made me curious about the layer below it.
C#/.NET 8, unsafe pointers, volatile and memory barriers, SignalR
spsc-ring — one lock-free queue, done properly
A single-producer single-consumer ring buffer, which is the smallest piece of the puzzle above and the one where every detail is visible.
It moves 462 million items per second between two threads — about 25 times what the same handoff costs through a mutex. But the number I care about more is this: I deliberately broke the memory ordering, confirmed ThreadSanitizer reported the race, and only then trusted the clean run on the correct version. A passing sanitizer proves nothing unless you know it would have failed.
I also ran it against the live Binance order book for BTC. Three hundred and sixteen updates applied to a local book with zero sequence gaps, which is the only real proof that the thing works — a dropped or reordered message corrupts the book silently, and the exchange's own sequence numbers are what tell you.
Two findings I wrote down because they contradicted what I expected. Cache-line padding bought 1.16x here, not the 2-3x that gets quoted around. And the latency I measured turned out to be almost entirely my consumer's waiting policy, not the queue — changing it cut the 99th percentile by 184x without touching the ring at all.
C++20, atomics and memory ordering, Catch2, Google Benchmark
thread-pool — concurrency primitives from scratch
A header-only thread pool built on std::jthread, with cooperative
cancellation modelled on .NET's CancellationToken — the one thing I genuinely
missed coming from C#.
The interesting part was measuring where the time actually goes. Dispatching a
task costs about 30 microseconds, and 95% of that is the thread wakeup, not
the queue and not the allocation. That single measurement killed two
optimisations I had planned and pointed at the one that worked: batching. A
parallel for_each that submits one task per chunk instead of one per element
runs roughly 1,255 times faster on fine-grained work.
I have kept the negative results in the README as well, since they were the
useful part. Replacing std::function with a custom task type halved the
allocations and changed the throughput by nothing at all.
C++20, jthread and stop_token, ASan, TSan
regime-discovery-cpp — finding market regimes without labels
Every trading day becomes a feature vector over a trailing window — realised volatility, momentum, drawdown, skewness. Index those with HNSW, cluster them with HDBSCAN, and market regimes fall out as dense regions of the space. No labels, no supervision, nothing told it what a "crisis" looks like.
Validated across 33 years of SPY. Both the nearest-neighbour index and the clustering are written from scratch, with an exact brute-force implementation kept alongside as the oracle that keeps the approximate one honest.
C++20, HNSW, HDBSCAN, 33 years of daily data
rag-system-cpp — vector search, hand-written
A vector similarity search engine built from nothing, with a local RAG pipeline on top as a demonstration. Everything runs on the machine; no external APIs.
The point was never the RAG part. It was the numeric core: contiguous cache-friendly storage, a hand-written AVX2 SIMD kernel that dispatches on what the CPU actually supports at startup, and an HNSW index. As in the regime work, an exact index sits next to the approximate one so that recall can be measured rather than assumed.
C++20, AVX2 intrinsics, runtime CPU dispatch, HNSW
automatic-differentiation-engine — how backpropagation actually works
Reverse-mode automatic differentiation in C++17, with no dependencies beyond a test framework. It builds the computation graph implicitly as you write expressions, then differentiates exactly in one backward sweep — and it is complete enough to train a small neural network end to end.
I built this because I wanted to stop treating loss.backward() as a magic
incantation. It is neither symbolic differentiation nor finite differences, and
understanding precisely why turned out to be worth the weekend.
C++17, reverse-mode AD, GoogleTest
Day job: Senior .NET Developer — distributed systems, event-driven architecture, and the SQL performance work that tends to come with both.
On the quantitative side I finished Columbia's Financial Engineering and Risk Management coursework, covering derivatives pricing, Itô calculus, Markowitz and CAPM, VaR and CVaR, and Monte Carlo methods. I also run a statistical arbitrage strategy on QQQ/IWM against an Interactive Brokers paper account — Kalman filter for the dynamic hedge ratio, HMM regime filter on top, 1.77 out-of-sample Sharpe. The binding constraint there is transaction cost modelling rather than signal quality, which is the part most write-ups skip.
Self-taught throughout. The order that works for me is intuition first, then the mathematics, then the code. Doing it in the other direction produces something that runs and teaches me nothing.
Day to day: C++20 for anything where latency matters, C#/.NET in production, and Python for research and anything statistical.
The specifics that come up most: lock-free data structures and memory ordering, cache-aware data layout, SIMD, and measuring any of it honestly — which usually means ThreadSanitizer, Valgrind, and Google Benchmark, plus enough scepticism to break my own code on purpose before trusting a green run.
I publish on macro and market microstructure on LinkedIn, usually where the two meet. Recent pieces: DXY accumulation, systemic risk in private credit, and a bear case on MicroStrategy.


