Quantpylib is now used by more than 500 quant traders to power their quantitative trading and research workflows - today I am happy to announce the first standardised exchange gateway wrapper on Quantcplib.
And guess what - only for the month of November - all new signups and existing readers can get complimentary access to the quantcplib repo by liking the post and commenting your github username. Will delete comment after invite is sent.
In the future, we will paywall it at >400USD with an access pass, similar to the quantpylib access pass. We will continue to work on the repo features - as the regulars know - once I start on a project, it will likely keep growing for years and years. As it gets more “institutional focus”, we will increase the access cost.
Over the years, the readers and I have used quantpylib to power their trading infrastructure, from trend following to market making systems, with its pros being its extremely ergonomic API and composability, allowing trading systems to inject quantpylib infra for custom trading pipelines.
While suitable for some hft/mft applications, in terms of raw performance and latency critical systems, the performance was necessarily lacking, Quantcplib aims to bring the same ergonomics but with performance aware engineering.
The first Binance client already brings the familiar the complete gateway contracts for market data, order submission and cancellation, private account streams, and local trading-state mirrors into the C++ library: looks familiar?
Low Latency Knobs
For most of the API, the functions look similar - so what changed? Well a lot, but it is worth talking about the major architectural difference. A typical trading system interacts with the network to receive market data, submit and cancel orders, and reconcile account state - how this happens matters.
In quantpylib, this is done via asynchronous coroutines and tasks in a single-threaded, cooperative multitasking fashion. The scheduling of async functions are performed via the asyncio event loop kernel - which adds scheduling and synchronisation overhead.
However, low latency trading applications are usually designed to take control over the network runtime IO. This allows more deterministic timing effects and software latency.
A strategy can share a thread with the network client, keeping the path from incoming data to a trading decision on one core. Alternatively, a dedicated network thread can service connections while strategy threads perform their own work. Lastly, applications built around coroutines can express pending requests as suspension points within the event loop (parallel to the Python asyncio approach).
Our network client exposes all of the necessary controls needed for the application to take control of the runtime event loop. It also gives the ability to configure between epoll-based waiting and direct userspace polling, choosing the right execution model and core affinities to match the corresponding kernel tuned setup.
Consider fetching an L2 order-book snapshot. The following excerpts request the same Binance USD-M endpoint:
constexpr auto target = "/fapi/v1/depth?symbol=BTCUSDT&limit=20";These examples expose the native HTTP response; the wrapper’s l2_book_get supplies a normalised BookUpdate through its callback interface.
A) Fused thread: submit directly, receive callbacks.
The strategy thread owns the network runtime. DirectCommandPolicy submits requests without an inter-thread command queue, and poll_once() advances I/O alongside strategy work:
using Http = net::HttpRuntime<net::SleepWait, net::DirectCommandPolicy>;
auto submitted = client.get(target, response_handler, options);
if (!submitted) return;
while (!done) {
auto polled = network.poll_once({32, 64, 0}); //take full control of network IO
if (polled.failed()) break;
strategy_step();
}
B) Separate threads: queue the request, consume the result on the strategy thread.
With one strategy thread submitting to a dedicated network owner, we select a bounded single-producer, single-consumer command queue:
using Http = net::HttpRuntime<net::SleepWait, net::SpscCommandPolicy<64>>;
std::jthread io([&](std::stop_token stop) {
network_error = network.run_until_stopped(stop);
});
auto request = client.get(target, options);
while (!request.ready()) {
strategy(); //do other things on the strategy thread while network thread polls
}
auto result = request.wait();
consume(std::move(result));C) Coroutine: suspend the request, keep the event loop approach
Using the same runtime configuration as A), the owned-response interface also supports co_await:
Task fetch_snapshot(Http::ClientHandle client,
net::HttpRequestOptions options) {
auto result = co_await client.get(target, options);
consume(std::move(result));
}A pending request suspends the coroutine; completion resumes it on the network owner. The coroutine supplies the control flow, while the application retains control over network progress.
Additionally, these integration choices sit on top of nonblocking TCP/TLS, persistent HTTP connection pools, incremental parsing and other performance aware implementation choices.
We intentionally do not have live documentation for quantcplib for now. This is because a) we are actively and rapidly developing the API, and hence subject to change and b) one can easily ask AI on “how to use” use cases. We will make one in the future.
For a fuller view of the Binance wrapper, check out the implemented examples:
Raw exchange requests:
https://github.com/hangukquant/quantcplib/blob/master/examples/binance_exchange.cpp
Gateway operations:
https://github.com/hangukquant/quantcplib/blob/master/examples/binance_gateway.cpp
Streams and mirrors:
https://github.com/hangukquant/quantcplib/blob/master/examples/binance_gateway_socket.cpp
As we write out more functionalities, we will also write equivalent market making guides using the cpp infrastructure as we did in quantpylib.





Github: @jppanier