In a couple of my recent posts I talked about figuring out what is next in my career and my adventure into solo mining cryptocurrency. Two themes came out of those posts: I like giving my computers something interesting to do while I’m not sitting at them, and I wanted to spend serious time learning how far agentic AI programming tools can be pushed without letting them turn a codebase into a plate of spaghetti. Those two interests collided over the last month and turned into my latest project: Kestrel.
Before going any further I’ll start with the same kind of disclaimer I gave in my solo mining post: nothing here is financial advice, and I am under no illusion that throwing a trading bot at the stock or crypto markets is an easy way to print money. In fact, one of the most interesting parts of building Kestrel has been watching it prove (with math and recorded market data) why several obvious trading ideas lose money once fees, slippage, and network latency enter the picture.
There is quite a bit going on in this project, so this post will be the first in a multi-part series. Today I’d like to introduce what Kestrel is at a high level, how it is put together, how to get it up and running, and preview what I plan to cover in upcoming posts.
What Is Kestrel?
BLUF – Kestrel is a C++23 library and command-line tool (kestrel_app) for gathering stock and cryptocurrency price data, backtesting trading strategies, paper trading on live feeds, and executing live orders.
At the time of writing Kestrel talks to three providers across both crypto and traditional US equities:
- Binance.US – Crypto spot market data (REST historical bars, live WebSocket quotes and bars, and public trade history) along with full live order execution.
- Kraken – Crypto spot market data over REST and WebSocket. Paper trading works today; the live trading venue is still a stub on the TODO list.
- Charles Schwab – US stocks and ETFs via Schwab’s OAuth API and WebSocket streamer, complete with US equity market-hours awareness. Paper trading works out of the box, and the live trading venue has placed real test market orders (kept behind an explicit config flag (
[schwab] live_trading = true) while I finish verifying resting limits, stops, and OCO –one-cancels-other– pairs).
How Is Kestrel Put Together?
In my last job I worked on embedded C++ applications that followed a strict, time-bound three-step control loop:
- Read information from the inputs
- Decide what to do based on current state
- Write commands out to the outputs
When you squint at an automated trading system, it wants to be the exact same loop. Market data comes in, a strategy decides whether to act, and orders go out.
The core design goal in Kestrel was to keep those boundaries clean behind interfaces so a strategy never knows (or cares) whether it is running in a historical backtest, paper trading on live data, or trading real money:
data::DataSourcehandles the read side: pulling historical OHLCV bars, looking up symbol tick/lot rules, and streaming live quotes or closed bars over WebSockets. Provider-specific quirks (like whether Bitcoin is calledBTCUSDT,XXBTZUSD, orBTC/USD) stop at the edge; inside Kestrel a pair always has the formBTC/USDand a stock isAAPL.StrategyandRunnerhandle the decision step. WebSocket threads only push incoming events onto a queue. A singleRun()thread pulls events off one at a time and calls hooks on the strategy (OnBar,OnQuote,OnFill,OnOrderUpdate,OnTimer, and session open/close hooks). Because one thread runs the strategy sequentially, strategy code doesn’t have to juggle mutexes.trading::TradingVenuehandles the write side: placing and cancelling market, limit, stop, and OCO orders, and reporting balances and fills.
When you switch [general] mode in the TOML configuration file from "paper" to "live", nothing in the strategy changes. A factory swaps PaperTradingVenue (which simulates fills, maker/taker fees, slippage, and latency against live quotes) for the live venue. For a backtest, ReplayDataSource feeds recorded bars and quotes from a PostgreSQL / TimescaleDB database into the exact same Runner on a simulated clock.
Rather than letting a strategy talk straight to an exchange, the Runner also puts safety layers in front of the venue (OrderTracker, SessionGatedVenue, and RiskGatedVenue) to track open orders, block stock orders outside market hours, and enforce [risk] limits and kill switches. Live runs flat-out refuse to start without a [risk] section configured.
A Quick Look At Running Kestrel
Building and running Kestrel on Linux (or Ubuntu under WSL, where I spend a lot of my time) takes CMake 3.25+ and GCC 12+ or Clang 16+:
cmake -S . -B build && cmake --build build -j
cp config/kestrel.example.toml config/kestrel.tomlCode language: Bash (bash)
kestrel_app uses CLI11 so each subcommand only does the one job you ask of it. Paper mode requires zero API credentials on Kraken or Binance.US:
# Pull the last 24 hourly bars for the configured symbols:
./build/kestrel_app -c config/kestrel.toml bars 1h 24
# Stream live quotes for 60 seconds (add --store to save to TimescaleDB):
./build/kestrel_app -c config/kestrel.toml stream 60
# Backtest a strategy over a historical window:
./build/kestrel_app -c config/kestrel.toml backtest ma_crossover \
--from 2026-09-01 --to 2026-10-01
# Run a strategy on live market data (paper or live depending on config):
./build/kestrel_app -c config/kestrel.toml run ma_crossoverCode language: Bash (bash)
Side Note: When run stops on Ctrl-C or SIGTERM, Kestrel cancels any open entry orders so nothing fills unintentionally after you walk away, while intentionally leaving protective exit orders (like a position’s stop-loss) resting on the book.
What Is Coming Up In This Series?
Rather than turning this introductory post into a novel, I plan to break the most interesting parts of building and testing Kestrel out into their own posts:
- Building C++23 With AI Coding Agents (Without Making Spaghetti) – I didn’t hide the involvement of AI coding agents (Claude, Gemini, and OpenCode) in Kestrel’s git history. I’ll walk through how I set the house rules early with
.clang-format,.clang-tidy,ASan/UBSan/TSansanitizers, an offline Docker test environment, and code reviews to catch subtle concurrency bugs and overly optimistic LLM assumptions. - C++ Under the Hood: Money, Latency, and Safety – Why IEEE-754 floating-point math doesn’t belong anywhere near order sizes (
Decimal), why Kestrel never blindly retries a timed-out order placement, how keeping TLS sessions warm shaved hundreds of milliseconds off calls to an API hosted in Tokyo, and how Kestrel batches tick-by-tick quotes into TimescaleDB while keeping sub-millisecond arrival order intact. - Integrating Crypto Exchanges and Charles Schwab – What it took to put Binance.US, Kraken, and Schwab behind one interface, including dealing with Schwab’s 7-day OAuth refresh cycle, market-hours gating, and the places where live API behavior differed from the documentation.
- When Trading Strategies Meet Reality – Kestrel currently includes five strategies (
ma_crossover,market_maker,breakout,momentum_rotation, andmean_reversion). In a dedicated post I’ll dig into the backtest and live test numbers: why adding realistic network latency completely flipped a “profitable” market maker on its head, how a short live run verified the simulator’s post-fill markouts, and which slower daily strategies actually held up after costs.
Conclusion
Kestrel is already doing real work on paper and live accounts, and TODO.md still has plenty left to tackle (including finishing the remaining live order and margin-account checks on Schwab, building out Kraken’s live trading venue, wiring Binance.US and Schwab’s WebSocket execution streams behind StreamExecutions so live runs don’t have to poll REST for fills, and setting up TimescaleDB compression and retention policies before quotes fills up my disk). It has been a fun way to combine modern C++, networking, databases, and performance measurement while putting agentic coding tools through a real stress test.
If you want to poke around ahead of the next posts, the code, design docs, and strategy research notes are all up on GitHub under an MIT license at github.com/trprice/kestrel. Let me know if you have any questions or topics you’d particularly like me to cover first, and stay tuned for more!
