Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

[HFT]Scanner

Real-time cross-exchange (Binance / Kraken) order book scanner and arbitrage-spread detector, in C++20.

For the reasoning behind non-obvious design decisions, see docs/DESIGN.md. For a chronological record of bugs found and fixed, see docs/CHANGELOG.md.

Architecture

BinanceClient (own thread, own io_context)  --\
  '-- REST snapshot fetch on a dedicated       -->  moodycamel::ConcurrentQueue<MarketEvent>  -->  ArbitrageEngine (own thread)
      thread, never blocks the io_context                                                            |-- OrderBook (Binance) -- sync state machine, U/u bridging
KrakenClient  (own thread, own io_context)  --/                                                       |-- OrderBook (Kraken)  -- sync state machine, checksum-validated
  '-- native "type":"snapshot" message                                                                `-- spread check -> spdlog
      handles its own sync, no REST needed
  • Networking: Boost.Beast/Boost.Asio, one WebSocket connection and one dedicated thread per exchange.
  • JSON: simdjson (ondemand API). Prices/quantities go straight from simdjson's string_view into std::from_chars (FastParse.hpp) -- no std::string/std::stod round-trip on the hot path.
  • Thread handoff: moodycamel::ConcurrentQueue. Delta events are trivially-copyable and allocation-free; snapshot events (rare, once per connect) carry a shared_ptr<SnapshotData>.
  • Order book: two preallocated, fixed arrays (bids desc, asks asc) per exchange, no locks or node allocations -- the strategy thread is their only reader and writer.
  • Decision gate: depth-weighted VWAP, per-venue taker fees, an explicit slippage buffer, lot-step truncation, and minimum-order checks precede every execution request. A raw crossed quote alone is never sufficient.
  • Risk gate: every order passes through RiskManager (open-order cap, rate limit, exposure ceiling, kill switch) before it's placed.
  • Execution: market orders only, dual-leg arbitrage attempts tracked through fill/reject with legging protection. See docs/DESIGN.md.

Build

Dependencies pulled automatically via FetchContent: simdjson, concurrentqueue, spdlog.

Boost and OpenSSL are not vendored (too large / better handled by your system package manager or vcpkg):

# using vcpkg
vcpkg install boost-beast boost-asio openssl

cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=<vcpkg-root>/scripts/buildsystems/vcpkg.cmake
cmake --build build --config Release

Run the test suite (all fixture-based, no network required):

ctest --test-dir build -C Release --output-on-failure

Run

.\build\Release\hft_scanner.exe

Connects to Binance's btcusdt@depth stream and Kraken's book channel for BTC/USD, and logs a warning line only when a depth-weighted trade remains profitable after both taker fees and the configured slippage buffer (5bps minimum net edge by default, set in main.cpp).

Environment variables

# Performance / capture (all opt-in)
HFT_BUSY_SPIN=1                  # strategy uses _mm_pause() while the queue is empty
HFT_PIN_THREADS=1                # best-effort core affinity for network/strategy workers
HFT_RECORD_PATH=run.hftcap       # asynchronously capture binary raw WS frames

# Execution (off by default -- see docs/DESIGN.md before enabling)
BINANCE_API_KEY, BINANCE_API_SECRET
KRAKEN_API_KEY,  KRAKEN_API_SECRET
HFT_ENABLE_EXECUTION=1           # required to place real orders at all

# Testnet validation (Binance only -- Kraken has no Spot sandbox)
HFT_USE_TESTNET=1                # points Binance at testnet; Kraken execution
                                  # is never constructed in this mode, regardless
                                  # of Kraken credentials present
HFT_TESTNET_PLACE_TEST_ORDER=1   # fires one real diagnostic order on testnet funds

Never hardcode credentials, never paste them into a chat, never commit them. Read main.cpp's comment block above the execution wiring before ever setting HFT_ENABLE_EXECUTION=1 against a funded account.

AccountReconciler polls Binance /api/v3/account and Kraken /0/private/Balance every 30s when execution is enabled; InventoryManager fails closed until both replies arrive and rejects any order that would take either venue's balance below 15% of its initial allocation.

Status

Verified with real regression tests (ctest, all fixture/oracle-based, no network required): order book (including array capacity-eviction), Kraken checksum (against Kraken's own worked example), HMAC signing (against RFC 4231), risk manager limits, trading rules, market replay, Binance and Kraken order-response parsing.

Verified live: Binance's execution path (HFT_USE_TESTNET=1 + HFT_TESTNET_PLACE_TEST_ORDER=1) -- confirmed against a real testnet fill, 2026-09-01. See docs/CHANGELOG.md for what that run caught and fixed.

Not yet verified:

  • Kraken's execution path against a live connection -- no Spot sandbox exists (only demo-futures.kraken.com, which is Futures-only). Fixture tests (kraken_order_parsing_test) are the practical ceiling for pre-live confidence right now; closing this fully means either a small, tightly RiskLimits-bounded real order, or a local mock WS/REST server.
  • Reconnect backoff under an actual sustained outage (implemented, unexercised beyond reasoning through it).
  • Kraken's cancel-before-conclude reconciliation step is fire-and-forget, not response-correlated (sends cancel_order, sleeps 300ms, re-checks -- shrinks but doesn't eliminate the race it targets).

Not implemented:

  • Multi-pair orchestration -- hardcoded to one Binance/Kraken pair. Scaling this is an open design question (thread-per-pair vs. a shared strategy thread), not a small change, given how much of RiskManager and ExecutionEngine currently assume a single pair.
  • Limit orders -- market orders only. Not purely additive: the legging/reconciliation logic assumes orders resolve near-instantly, an assumption a resting limit order breaks. Adding this means revisiting that logic alongside the order type, not after.

About

Cross-exchange (Binance/Kraken) HFT arbitrage scanner in C++20 — real-time order book sync, checksum-validated, risk-gated execution engine. Binance execution verified live on testnet; Kraken execution unverified (no Spot sandbox exists).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages