HFTFramework: L2 Backtesting and ZeroMQ Strategy Bridges for Avellaneda-Stoikov Research
HFTFramework utilized for research on " A reinforcement learning approach to improve the performance of the Avellaneda-Stoikov market-making algorithm "
At a glance
- What is it?
- HFTFramework is a Java and Python research platform for market-making experiments, built around L2 tick-level backtesting and a ZeroMQ bridge that lets Python strategies drive the Java engine. Its own README states it has not been validated in live trading.
- Who is it for?
- Adopt HFTFramework if you need to backtest market-making logic against L2 tick data with the same code path used for live connector interfacing, and if you accept that the README explicitly says the framework has not been validated in a live trading environment. Do not adopt it if you need a production execution stack or a maintained release cadence: the only tagged release is V0 from July 2022.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Jupyter Notebook, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The Problem: Testing Market-Making Quotes Without Rewriting Your Stack
Market-making research has an awkward property. The strategy you want to study, whether it is ConstantSpread, LinearConstantSpread or an Avellaneda-Stoikov variant, depends on microstructure detail that coarse OHLC bars destroy: queue position, spread at the moment of quoting, and the depth available on each side. HFTFramework targets exactly that gap. The README states the framework performs backtesting at the L2 tick data level using the same codebase as the one used for live market interfacing. That single sentence is the whole pitch. You write an algorithm once, run it against historical depth, and later point the same class at a connector.
The intended audience is narrow and technical. The repository description ties it to research on a reinforcement learning approach to improving the Avellaneda-Stoikov market-making algorithm, and the topics list includes avellaneda-stoikov, market-maker and deep-learning. This is not a retail bot. It is a lab bench for someone who wants to compare quoting policies under identical depth conditions, and who is willing to compile Java to do it.
Architecture: Java Core, ZeroMQ Bus, Optional Python Brains
The framework is split by language and by role. Java holds the algorithmic trading framework, the trading algorithms, the executables and the connectors under java/common/connectors. Python holds strategy code and the python_algo package. Connectors can run in the same process or remotely through ZeroMQ, which is what makes the language split practical rather than cosmetic.
The python_algo bridge is documented with concrete socket semantics. A Java PUB socket feeds a Python SUB socket with market data events: depth, trade, execution reports and candles. Order and quote commands travel the other way, from Python PUSH to Java PULL, and the README notes this direction is asynchronous. A third pair, Java REP against Python REQ, handles synchronous calls such as a portfolio snapshot. Transport is TCP by default on localhost ports 7700 to 7703, or IPC over Unix domain sockets when both sides share a host and you want lower latency. The codec is JSON by default, with MessagePack offered as an alternative that the README describes as roughly three times faster to parse.
That is a sensible decomposition. The expensive, latency-sensitive matching and book-keeping stay in Java; the strategy logic can live in Python where the research tooling is. The cost is a serialization boundary on every event, and the README's own framing of the REP/REQ channel as synchronous tells you it is not the path to use inside a tight quoting loop.
Getting a Backtest Running: JAR, Two Environment Variables, Parquet Data
The documented path is short. Compile and package the Backtest module under java/executables/Backtest; the resulting artifact lands at java/executables/Backtest/target/Backtest.jar. Point the environment variable LAMBDA_JAR_PATH at that file. Prepare a data folder containing Parquet files and point LAMBDA_DATA_PATH at it. The repository ships an example data set under data/ for reference.
With those in place, a Java run is a single command against a JSON config:
java -jar Backtest.jar example_ConstantSpread.json
The example config java/executables/Backtest/example_ConstantSpread.json is the template to copy. The Python entry point is different in shape. You instantiate the strategy and call test() with an instrument and a datetime window:
constant_spread = ConstantSpread(algorithm_info='test_main') output_test = constant_spread.test( instrument_pk='btcusdt_kraken', start_date=datetime.datetime(year=2023, day=13, month=11, hour=9), end_date=datetime.datetime(year=2023, day=13, month=11, hour=15), )
Adding a new Java algorithm is not a config change. The README says you must create a class extending Algorithm.java and register it in the getAlgorithm method of TradingAlgorithmsProvider.java. The README also points to a separate repository, HFTFramework_privateAlgosExample, for keeping custom algorithms out of the main tree. If you skip the provider registration step, the JAR has no way to resolve your class name from the JSON.
The Live-Trading Caveat Is the Most Important Line in the README
The README contains an explicit warning: the framework has not been validated in a live trading environment, and users should proceed with caution and assume all associated risks. Take that literally. Everything the project claims about live interfacing is structural. Connectors exist, the ZeroMQ transport exists, and the code path is shared with backtesting. What does not exist, per the project's own statement, is evidence from a real account.
There are practical consequences. A backtest that shares code with live trading removes one class of bug, the kind where your simulation fills orders your executor never would. It does not remove the rest: exchange rate limits, partial fills, disconnects, and the difference between historical depth and the depth you actually face. The framework gives you L2 granularity in the backtest, which is better than bar data, but the README does not document a slippage or latency model, so how optimistic the simulated fills are is something you have to determine from the code yourself.
There is also a maintenance signal worth weighing. The only tagged release listed is V0, dated July 2022. The last push is September 2026, so the repository is active, but releases are not the unit of change here. If your team depends on versioned artifacts, you are depending on the main branch.
Where It Fits Against a General-Purpose Backtester
The natural alternative is a Python event-driven backtester such as Nautilus Trader or Backtrader, or a research notebook stack built on pandas and a matching engine you write yourself. The difference is not language preference. A general-purpose backtester usually models the market as a stream of trades and quotes that your strategy observes, with the fill model bolted on afterward. HFTFramework inverts the emphasis: the market engine and the connectors are first-class, and the strategy is the plug-in, which is why the README can claim one codebase for both backtest and live.
If your research question is a quoting policy under L2 depth, that inversion is worth the Java build step. If your question is signal generation on daily bars, it is pure overhead. The python_algo bridge is the compromise position: you get the Java engine and write the strategy in Python, at the cost of a ZeroMQ hop and a serialization format you must keep consistent on both sides. The README's own note that the PUSH/PULL direction is asynchronous is the detail to design around; treating order submission as fire-and-forget changes how you track fills.
Licence and the Cost of Keeping a Fork Alive
The repository is Apache-2.0. That permits commercial use and modification, and it includes an explicit patent grant, which matters if you intend to build on the Avellaneda-Stoikov work commercially. It also means you can keep a private fork without publishing your strategy code, which is consistent with the separate privateAlgosExample repository the README points to. This is a description of the licence text, not legal advice; have counsel review anything you ship.
Upgrade cost is the real budget line. There is no release cadence to pin to, so an upgrade means diffing main. The surface you would need to re-verify is concrete: the Algorithm base class you extend, the getAlgorithm provider method, the JSON schema behind example_ConstantSpread.json, and the message classes in python_algo (DepthMsg, TradeMsg, ExecutionReportMsg, CandleMsg, OrderRequestCmd). Any of those changing breaks a custom algorithm, and because the JAR path is supplied through LAMBDA_JAR_PATH rather than resolved by a package manager, a stale JAR will silently run old code against a new data set. The README mentions Java and Python unit test workflows in its badges, so there is CI, but it validates the framework, not your fork of it.
What to Check Before You Commit a Quarter to This
Verify the data path first. The README assumes Parquet files under data/ with an instrument identifier like btcusdt_kraken; if your venue's depth history is not in that shape, the conversion work lands on you before any strategy runs. Second, confirm the provider registration flow end to end by building the JAR, adding a trivial algorithm to TradingAlgorithmsProvider.getAlgorithm, and running java -jar Backtest.jar against your own JSON. Third, decide whether your strategy belongs in Java or behind the ZeroMQ bridge, because that choice determines whether you are debugging a JVM or a socket protocol when fills look wrong. Fourth, read the fill logic in the market engine, since the README documents L2 granularity but not a latency model, and a market-making backtest is only as good as its queue assumption. None of these steps require the project to be live-validated. They require you to treat it as what it says it is: a research framework with a shared code path, not a trading system.
Editorial conclusion
Adopt HFTFramework if you need to backtest market-making logic against L2 tick data with the same code path used for live connector interfacing, and if you accept that the README explicitly says the framework has not been validated in a live trading environment. Do not adopt it if you need a production execution stack or a maintained release cadence: the only tagged release is V0 from July 2022. Before committing, verify that the data/ folder contains Parquet files for your instrument, that LAMBDA_JAR_PATH and LAMBDA_DATA_PATH resolve on your machine, and that your algorithm is registered in TradingAlgorithmsProvider.getAlgorithm, otherwise the Backtest JAR will not find it.
Community notes