digdigdig3
Multi-exchange connector library — unified async Rust API for 22 production crypto exchanges
- stocks/forex/prediction (validated subset). Single
ExchangeHubasync pool exposing all connectors with self-declared, empirically-validated capabilities.
Version: 0.2.3 Edition: Rust 2021 License: MIT OR Apache-2.0 Repository: https://github.com/ZENG3LD/digdigdig3
Quick start
use ;
let hub = new;
hub.connect_full.await?;
let conn = hub.rest.unwrap;
// Three equivalent ways to pass a symbol:
// Raw (zero allocation, exchange-native):
let ticker = conn.get_ticker.await?;
// Canonical (exchange-agnostic):
let sym = new;
let ticker = conn.get_ticker.await?;
// Macro:
let ticker = conn.get_ticker.await?;
println!;
Architecture
Hub-first API
ExchangeHub is THE entry point. Pool internals are pub(crate) — consumers cannot bypass.
| Method | Purpose |
|---|---|
connect_full(id, accounts, testnet) |
Wires REST + WS for an exchange |
connect_full_validated(...) |
Same but rejects exchanges without validation stamp |
rest(id) -> Option<Arc<dyn CoreConnector>> |
Typed REST dispatch |
ws(id, account) -> Option<Arc<dyn WebSocketConnector>> |
WS dispatch |
capabilities(id) -> Option<ConnectorCapabilities> |
Discover what an exchange supports |
list_connected() -> Vec<ExchangeId> |
Enumerate active connections |
shutdown(id) |
Releases REST + WS |
SymbolInput — raw or canonical, per-call
Every per-symbol method accepts SymbolInput<'_>:
Both variants land in the same connector code path via SymbolInput::resolve(exchange, account_type) -> Cow<str>.
Raw → Cow::Borrowed (no allocation). Canonical → Cow::Owned via SymbolNormalizer::to_exchange.
Per-call dispatch — caller can mix Raw and Canonical for different exchanges in a loop, no method rename needed.
For long-lived storage (e.g. StreamSpec.symbol), use OwnedSymbolInput (same Raw/Canonical variants, owned data).
Per-exchange normalization rules in core::utils::symbol_normalizer (22 sub-modules).
Examples: "BTCUSDT" (Binance), "BTC-USDT" (OKX), "BTC_USDT" (Gate.io), "tBTCUSD" (Bitfinex), "BTC-PERPETUAL" (Deribit).
WebSocket: UniversalWsTransport
All 9 migrated WS connectors share UniversalWsTransport<P: WsProtocol> framework owning:
- connect/reconnect/backoff
- ping scheduler + subscription replay
- frame dispatch (no
_ => Ok(None)catch-alls — unmatched topics warn) - tracing on every frame
Each exchange implements thin WsProtocol (~6 methods, ~400-900 LOC) vs the old bespoke loops
(800-1500 LOC each).
Capabilities = empirical truth
HasCapabilities::validation_status() -> Option<ValidationStamp> exposes per-method/stream
validation from the e2e_smoke harness, embedded as data/validation_snapshot.json.
let stamp = hub.capabilities.and_then;
match stamp.as_ref.and_then
Validated coverage (e2e_smoke 2026-05-17)
22 in-scope exchanges (no API keys needed for public data):
| Group | Exchanges | REST OK | WS Connected | Real Data Flowing |
|---|---|---|---|---|
| L3-open CEX (18) | Binance, Bybit, OKX, KuCoin, Kraken, Coinbase, Gate.io, Gemini, MEXC, HTX, Bitget, BingX, Crypto.com, Upbit, Bitfinex, Bitstamp, Deribit, HyperLiquid | 15/18 | 17/18 | 13/17 |
| L3-open DEX (2) | dYdX, Lighter | 1/2 | 2/2 | 2/2 |
| L3-open Pred (1) | Polymarket | 0/1 | 0/1 | — |
| L2-free (1) | MOEX | 1/1 | 0/1 | — |
L1/L2-paid + L3-gated (21 exchanges) compile-validated only; functional validation deferred until API keys available.
Levels
| Level | Description | Auth |
|---|---|---|
| L1 | OHLCV/ticker only — no orderbook | Often API key |
| L2 | L1 + orderbook | Often API key |
| L3 open | Full stack, public data | None for data; key for trading |
| L3 gated | Full stack, all data | Required |
43 connectors total across all levels.
Documentation
CLAUDE.md— architectural principles + test plan + scope (full detail)docs/plans/wave0-foundation.md— UniversalWsTransport designdocs/plans/phase-alpha-symbol-decoupling.md— Symbol decoupling designdocs/plans/smoke_v8_findings_spec.md— original consumer feedback specexamples/exchange_hub_demo.rs— minimal hub usageexamples/e2e_smoke.rs— full validation harnessexamples/full_smoke.rs— parallel 48-exchange smoke
Validation harness
Generates e2e_smoke_post_zeta.txt and a regenerated data/validation_snapshot.json.
Feature flags
| Feature | Default | Purpose |
|---|---|---|
onchain-evm |
yes | EVM provider (HyperLiquid signing) |
onchain-cosmos |
no | Cosmos provider (dYdX) |
onchain-starknet |
no | StarkNet provider (Lighter) |
grpc |
no | tonic transport (Tinkoff) |
websocket |
yes | WS enablement |
Architecture invariants
- No
_ => Ok(None)catch-alls in WS dispatch. Unmatched topic →tracing::warn!. - No
std::sync::Mutexacross.await. Tokio sync only. - Symbol normalization lives outside connectors. Connectors take raw exchange-native strings.
- Capabilities are derived, not declared. Topic registry determines stream support; ValidationStamp records observed reality.
Levels of test coverage
| Layer | What | Command |
|---|---|---|
| Compile gate | 0 errors, 0 warnings | RUSTFLAGS="-D warnings" cargo check --all-targets --all-features |
| Unit tests | Fixture-based parser tests | cargo test --lib --all-features |
| Live validation | Real API + content inspection | ./target/release/examples/e2e_smoke.exe |
Known limitations
- L3-gated connectors (Alpaca, Zerodha, OANDA, IB, ...) compile but are not functionally validated — requires API keys.
- Some L3-open methods limited by exchange API access: HyperLiquid
get_tickerneeds asset_index → coin mapping (deferred). - Symbol normalizer assumes canonical
(base, quote)model. Options (Deribit) require concrete instrument_name viaSymbol::with_raw. - HTX server-pong reply hook not in framework yet — auto-reconnect compensates.
- MOEX FAST WS may need RU ISP routing for events.
Author / repository
- Repo: https://github.com/ZENG3LD/digdigdig3
- License: MIT OR Apache-2.0