Project Skeleton: Build Your First HFT Microservice
Quick goal: assemble a tiny, end-to-end microservice that replays market data, runs a simple strategy, submits orders through a minimal gateway, logs decisions, and reports backtest PnL — all locally and reproducibly.
- Target reader: you — an engineer into Algorithmic Trading with beginner familiarity in
C++
,Python
,Java
,C
, andJS
. This screen gives you a low-friction C++ starting point and clear follow-ups for your other languages.
ASCII architecture (what we'll simulate locally):
[Synthetic Multicast Feed] --> [Feed Handler / Replay] --> [Strategy] --> [Order Gateway] --> [Simulated Exchange] | | `----> [Logger / Backtest Recorder] <-'
Think of it like a small pit crew: the feed handler hands the tire (price) to the mechanic (strategy); the mechanic decides whether to pit (trade) and the pit-box (order gateway) enforces safety checks.
Why C++ here?
- C++ shows hot-path structure (tight loops, low allocation). Beginners: treat this as a clear, opinionated starting point; later you can prototype in
Python
for fast experiments or rewrite hotspots back into C++.
What the provided C++ program does (run it as main.cpp):
- Generates a deterministic synthetic feed (
generate_feed
) — reproducible like a unit test. - Computes a
simple_sma
over aSMA_WINDOW
and runs a tiny mean-reversion strategy: when price deviates from the SMA byTHRESH
, it places aBUY
orSELL
order. - Submits orders to a naive
submit_order
gateway which enforcesMAX_ORDER_SIZE
and a simple rate limitMAX_ORDERS_PER_SEC
. - Executes orders immediately (simulated exchange), updates
position
andcash
, and logs events. - Prints a final backtest summary (final PnL) so you can iterate quickly.
Why this is useful to you (language crosswalks):
- C++: shows how to keep the hot path allocation-light —
deque
for SMA window, plain structs forTick
/Order
. - Python: port the same logic into
pandas
or a tight loop withnumpy
for fast prototyping; keep the same knobs (SMA_WINDOW
,THRESH
) so results are comparable. - Java/C: similar structure applies — use arrays/pools for low-allocation paths.
- JS: great for visualization and teaching — replay the same tick vector in a browser and draw live PnL charts.
Exercises & challenges (try these after running the C++ program):
- Tweak
SMA_WINDOW
andTHRESH
to see how trade frequency and PnL change. - Reduce
MAX_ORDERS_PER_SEC
to simulate an exchange throttling you — watch rejections. - Port the strategy loop to Python (keep random seed same) and compare final PnL — are they identical?
- Replace immediate execution with a simple matching engine: keep an
order_book
vector and match orders at best price. - Add an
audit
event (append-only) that recordstimestamp_ns
,decision
,reason
,trace_id
— then export to CSV. - For fun: change the strategy to a momentum rule (buy when price > SMA + x) — which performs better on this synthetic feed?
Mini-challenges tailored to your background:
- If you like Java: implement the
Order
andFeed
as small POJOs and run the replay loop in aScheduledExecutorService
. - If you like Python: re-implement
generate_feed
withnumpy.random.default_rng(42)
and vectorize SMA vianumpy.convolve
. - If you like JS: visualize the replay and PnL using
d3
or a simple HTML canvas — great for demoing to teammates.
Next steps after this skeleton
- Replace synthetic feed with real multicast capture (lab later covers
PF_RING
/DPDK
). - Harden the gateway: add pre-trade rules, per-client token buckets, and immutable audit logs.
- Add unit tests and a deterministic CI job that compiles the C++ and runs the backtest with fixed seeds.
Try this now
- Run the C++ program above. Then try one change: halve
SMA_WINDOW
and re-run — what happens to order count and PnL?
Happy hacking — this tiny microservice is your playground for moving from prototypes to production-ready HFT components. Feel free to port pieces to Python/Java/JS to learn tradeoffs and iterate fast.
xxxxxxxxxx
}
using namespace std;
using ns = chrono::nanoseconds;
using clk = chrono::high_resolution_clock;
struct Tick {
ns ts;
double price;
};
struct Order {
string side; // "BUY" or "SELL"
int size;
double price;
ns ts;
};
// Simple console logger (hot-path should be lighter in real HFT)
void log_event(const string &s) {
auto now = chrono::duration_cast<ns>(clk::now().time_since_epoch()).count();
cout << "[" << now << "] " << s << "\n";