hft_eb is a Linux-first event-driven high-frequency trading framework written in C++17.
It is built around a synchronous in-process event bus, dynamically loaded modules, and a shared core state layer for orders, positions, accounts, and market snapshots.
This README is intentionally optimized for developers who need to:
- understand the project quickly
- build and run it with minimal guesswork
- add or modify modules without reverse-engineering the whole repository
Related docs:
docs/README.md: documentation indexdocs/modules_overview.md: module responsibility mapdocs/plugins/README.md: plugin parameter index
The repository provides:
hft_engine: the main host process- dynamically loaded strategy / factor / risk / trade / replay modules
hft_trade_gateway: a standalone trade gateway processhft_md: a market data recorder that writes mmap-backed tick files- Python and Rust helper tools for research and data handling
Typical use cases:
- replay historical market data into strategies
- run simulation trading pipelines
- wire real trading adapters behind a process boundary
- develop strategy tree nodes or factor DAG nodes
- extend the framework with custom modules
At runtime, hft_engine does four things:
- loads a YAML config
- initializes shared core services and market snapshot storage
dlopens configured module.sofiles in order- drives the system by publishing polling events into the event bus
The main event loop in src/engine.cpp continuously publishes:
EVENT_POLL_GATEWAYEVENT_POLL_REPLAY
Modules subscribe to events and publish new ones synchronously through EventBus.
That means the execution chain is explicit and easy to extend, but also means module ordering and callback behavior matter.
Typical pipeline:
Replay / Market Data -> Strategy / Factor -> Portfolio (optional) -> Risk -> Trade -> Core State / Monitor
flowchart LR
subgraph Source["Data Source"]
MD["hft_md / live feed"]
MMAP[("mmap tick / kline files")]
end
subgraph Engine["hft_engine"]
BUS["EventBus"]
CORE["Core state<br/>orders / positions / accounts / snapshot"]
REPLAY["Replay / KlineReplay"]
STRAT["Strategy / StrategyTree / PyStrategy"]
FACTOR["Factor DAG"]
PORT["Portfolio"]
RISK["Risk"]
TRADE["SimTrade / GatewayPoll / Trade"]
MON["Monitor / SignalCsv / TestHarness"]
end
subgraph Gateway["Optional process"]
GW["hft_trade_gateway"]
end
MD --> MMAP
MMAP --> REPLAY
REPLAY --> BUS
BUS --> STRAT
BUS --> FACTOR
STRAT --> BUS
FACTOR --> BUS
BUS --> PORT
PORT --> BUS
BUS --> RISK
RISK --> BUS
BUS --> TRADE
TRADE --> BUS
BUS --> CORE
BUS --> MON
TRADE --> GW
hft_eb/
├── bin/ # build outputs: executables and shared libraries
├── build/ # CMake build directory
├── conf/ # YAML configs and symbol lists
├── core/ # shared core state, snapshot, protocol, IPC primitives
├── docs/ # architecture and module docs
├── hft_md/ # market data recorder
├── include/ # public engine/module interfaces
├── modules/ # loadable modules
├── py_tools/ # Python utilities
├── rust_tools/ # Rust utilities
├── src/ # engine host implementation
├── tests/ # tests and experiments
├── third_party/ # bundled dependencies and vendor code
└── trade_gateway/ # standalone trade gateway process
Modules implement IModule from include/framework.h:
class IModule {
public:
virtual ~IModule() = default;
virtual void init(EventBus* bus, const ConfigMap& config, ITimerService* timer_svc = nullptr) = 0;
virtual void start() {}
virtual void stop() {}
};Export the factory symbol with:
EXPORT_MODULE(MyModule)Use this for:
- replay modules
- risk modules
- trade modules
- monitor modules
- top-level strategy/factor orchestration modules
Strategy tree plugins implement IStrategyNode and export:
EXPORT_STRATEGY(MyStrategyNode)Use this when you want to add reusable strategy tree leaves instead of a full top-level module.
Engine config is YAML. For each plugin entry:
- scalar fields inside
configare flattened intoConfigMap - the full plugin config is also injected as
_yaml
This is important for secondary development:
- simple modules can read flat scalar strings directly
- complex modules can parse
_yamlthemselves
From the current CMakeLists.txt, the important targets are:
Executables:
hft_enginehft_trade_gatewayhft_trade_gateway_pinghft_recorderhft_reader
Core library:
libhft_core.so
Representative module libraries:
libmod_replay.solibmod_kline.solibmod_strategy.solibmod_strategy_tree.solibmod_strategy_tree_parallel.solibmod_py_strategy.solibmod_factor_dag.solibmod_portfolio.solibmod_risk.solibmod_trade.solibmod_sim_trade.solibmod_gateway_poll.solibmod_signal_csv.solibmod_test_harness.solibmod_event_sampler.solibmod_sweep_trader.so
Optional target:
libmod_kline_parquet_replay.sowhen Arrow / Parquet dependencies are available
Required:
- Linux
- CMake
>= 3.10 - C++17 compiler
pthreaddlrt- Python 3 development headers
Bundled in third_party/ and built from source by CMake:
yaml-cppnlohmann_jsonspdlogabseil-cpplibzmq
Environment-specific:
- CTP headers and libraries are expected under
third_party/ctp/ - some live-trading workflows require correct runtime library paths
Optional:
- Apache Arrow / Parquet support for parquet replay modules
Recommended build:
./build_release.shClean rebuild:
./build_release.sh cleanWhat the script does:
- creates
build/andbin/ - unsets common library path variables to avoid accidental linkage pollution
- runs CMake in Release mode
- builds with
make -j$(nproc)
Expected outputs:
bin/hft_enginebin/hft_trade_gatewaybin/lib*.so
cd bin
./hft_engine ../conf/config_sim_backtest.yaml./bin/hft_engine conf/config_factor_dag.yaml./bin/hft_engine conf/config_strategy_tree_parallel_perf_20260320.yamlExamples exist in:
conf/config_py_backtest.yamlconf/config_py_stock_mf_backtest.yamlconf/config_py_stock_cs_mf_backtest.yaml
./bin/hft_trade_gateway --config conf/trade_gateway_demo.yamlcd hft_md
./build.sh
./run.sh 20260325- Pass an explicit YAML config path whenever possible.
src/main.cppstill defaults toconfig.json, but normal project usage is YAML-based. - Plugin library paths are resolved from the current working directory because the engine directly
dlopens the configuredlibrarypath. - If your config uses bare names like
libmod_xxx.so, running frombin/is the safest choice. run.shstartsbin/hft_enginewithconf/config_real_test.yamland usespkill hft_enginefirst. Review it before using it in shared environments.- Live trading configs may include credentials or sensitive endpoints. Keep those out of commits.
Top-level fields commonly used by the engine:
symbols_file: symbol mapping file, defaults toconf/symbols.txtsnapshot: snapshot backend configtrading_hours: optional runtime windowplugins: ordered plugin list
Snapshot fields:
type:localorshmpath: shared memory path whentype: shmis_writer: writer/reader mode
Plugin fields:
name: human-readable module namelibrary: shared library pathenabled: optional, defaults totrueconfig: module-specific config
Minimal example:
symbols_file: "../conf/symbols.txt"
snapshot:
type: "local"
plugins:
- name: Replay
library: "libmod_replay.so"
enabled: true
config:
data_file: "data/market_data_20260319_night"
- name: Risk
library: "libmod_risk.so"
enabled: true
config:
max_orders_per_second: 100Recommended plugin order:
Replay / market dataStrategy / FactorPortfolioif you aggregate signals before order generationRiskTradeMonitor / output
If you are onboarding to the codebase, use this order:
- read
include/framework.h - read
src/engine.cpp - inspect one config in
conf/ - inspect one simple module and one complex module
Suggested starting files:
include/framework.hsrc/engine.cppmodules/replay/replay_module.cppmodules/risk/risk_module.cppmodules/strategy/simple_strategy.cppmodules/factor/factor_dag_module.cppmodules/trade/sim_trade_module.cpp
When adding a new module:
- implement
IModule - export it with
EXPORT_MODULE - add a CMake target
- place the output in
bin/ - register it from a YAML config
- run with a narrow config first
When adding a strategy tree node:
- implement
IStrategyNode - export it with
EXPORT_STRATEGY - add a dedicated shared library target
- load it through the strategy tree module config
- Keep module responsibilities narrow. The framework already gives you event dispatch, timer registration, and config injection.
- Prefer validating new logic in replay or sim-trade mode before touching live paths.
- Reuse existing configs in
conf/as templates instead of starting from scratch. - For plugin parameter details, use
docs/plugins/README.md. - For architecture decisions and boundaries, use
docs/README.md.
Python tools:
- data prep and research helpers in
py_tools/
Rust tools:
rust_tools/containshft_reader, with helpers around mmap/parquet workflows
These tools are not required to build the main engine, but they are useful for research and data-side development.
- New developer onboarding:
docs/README.md - Module-level responsibility map:
docs/modules_overview.md - Plugin config lookup:
docs/plugins/README.md - Trade gateway internals:
trade_gateway/ - Market data recorder internals:
hft_md/