Architecture
September 2, 2026 · View on GitHub
wickra-exchange mirrors the tiering of the Wickra core and backtester: a Rust
core, native PyO3/napi bindings, and a C ABI hub that reaches C, C++, C#, Go, Java
and R — eight languages. There is no WASM binding (authenticated trading needs
raw sockets and secret keys; a browser sandbox forbids both).
Crates
crates/
├── wickra-exchange-core/ traits, types, credentials, the shared connectivity
│ machinery, and the per-exchange implementations
├── wickra-exchange/ facade crate — the stable public re-export
└── wickra-exchange-bench/ criterion benchmarks (signing / parse / filter)
The unified trait
One trait spans every venue; the implementation behind it is bespoke per exchange.
trait MarketData klines / ticker / order book + pull-based subscribe / poll_events
trait Execution place / cancel / query order, open orders, balances, positions
trait Exchange: MarketData + Execution
Exchange::new("<name>", credentials, options) is the factory over all ten
venues. The shape is identical everywhere; the auth scheme, WebSocket state
machine and symbol/filter mapping are unique per exchange, which is the
irreducible core that cannot be shared.
Two deliberate deviations from the Wickra tiering
- No WASM binding. See above. The browser-safe slice — public market data —
is already covered elsewhere (
wickra-wasmover a browser WebSocket). - Pull-based streams, not push callbacks. Subscriptions fill a bounded
buffer that the consumer drains with
poll_events() -> Vec<Event>. Push callbacks out of a background thread need bespoke per-language glue (Python GIL, Go cgo, Java JNI, C# delegates) and break entirely for single-threaded R. Pull lets the consumer own its loop, so the C ABI carries streaming as trivially as a synchronous call — across all eight languages, R included.
The transport abstraction
All network I/O goes through a Transport trait (HTTP + WebSocket). Production
uses a thin real-socket adapter; tests inject a MockTransport that replays
recorded fixtures. This is what lets the signing, parsing, filter-rounding,
WebSocket state machine, rate limiter and error paths all run offline, under
test, to near-total coverage — the only excluded lines are the thin real-socket
adapter itself.
Exact arithmetic
Order-layer price and quantity use rust_decimal::Decimal. Floats mis-round money
(scientific notation, 1e-8 drift) and exchanges reject mis-rounded values.
Indicator inputs stay f64; the boundary is the order layer.
Connectivity machinery (shared modules)
symbol (unified BTC/USDT ↔ BTCUSDT ↔ XBTUSD mapping), clock (server-time
sync, nonce, JWT TTL), ratelimiter (weight-based + 429/418 back-off),
retry/idempotency, instruments (exchangeInfo cache), orderbook (local L2
builder with diff-apply + gap detection + auto-resync), positions/reconcile,
observability (secret redaction + a health snapshot; there is no
tracing in this crate yet), error (a unified taxonomy mapping exchange
codes to an enum), and normalize.
Integration with the rest of Wickra
The differentiators are what make this more than "a typed ccxt":
PaperExchangeimplements the sameExchangetrait but simulates fills through thewickra-backtestengine — so a strategy runs paper ↔ live by swapping the implementation.- Microstructure feeds (
feeds) convert what the venue clients emit into the exact typed shapeswickra-coreconsumes (Trade,OrderBook,CrossSection), feeding the 514 indicators and the backtester with zero glue. The derivatives channels fold into aDerivativesTickfrom caller-supplied frames — no client subscribes to them yet. ReplayExchangedrives a recorded feed through the same trait.
A Rust trading backend depends on wickra-core, wickra-backtest-core and
wickra-exchange as three Cargo crates and composes them in one binary, no FFI.
Parity
Connectivity is I/O, so there is no byte-deterministic golden corpus. Parity is
proven by replay: recorded exchange responses in golden/replay/, with each
of the eight languages required to reproduce the normalised structs in
golden/expected/.