Oracle
A CEX index-price feed — multi-venue WebSockets, a robust aggregate, and Redis as the handoff.
server/oracle — TypeScript. Subscribes to several centralised exchanges over WebSocket,
aggregates a per-asset index price, and publishes it to Redis for the engine.
Binance ─┐
OKX ─┼──WS──► oracle ──robust aggregate──► Redis ──► engine
Bybit ─┘ ├─ mark price
├─ funding
└─ liquidationThe engine never talks to a CEX directly. That keeps market-data concerns — reconnects, venue outages, rate limits — out of the matching path.
Assets
BTC, ETH, SOL today. These are the engine's asset names; the interface registry keys the
Canton wrappers (CBTC, CETH, CSOL) and reconciles them in one alias layer.
Running
cd server/oracle
npm install
# print one round to stdout without touching Redis — the best first check
npm run once
# run the loop, publishing to Redis
npm run start
# one market only
npm run start -- --market BTConce waits up to 15s for the first WS frames from every venue before its single tick.
Without that wait the tick fires while sockets are still connecting and every asset prints
"m":"na".
A venue set that never delivers still exits (with a [once] WARNING) rather than hanging. And
once binds no HTTP port, so it is safe to run beside a live oracle.
Redis output
The engine reads per-asset keys from Redis. A missing or stale key is what makes the engine unable to compute a mark price — the first thing to check when funding or liquidation looks frozen.
Adding an asset or an exchange
Add the asset to the oracle's market list, with each venue's symbol for it (venues disagree:
BTCUSDT vs BTC-USD vs BTC-USDT).
Verify with npm run once — an asset showing "m":"na" means no venue delivered a usable frame.
Add the corresponding market to the engine's markets.json with its szDecimals and
maxLeverage.
Add it to the interface seed and the token registry.
Full walkthrough: Add a market.
Why aggregate at all
A single venue is a single point of manipulation and a single point of outage. Aggregating across venues with a robust statistic — rather than a mean — means one venue printing a wick cannot move the index enough to trigger liquidations.
This matters most precisely when it is hardest to test: a venue outage during high volatility.
npm run once with a venue deliberately blocked is a cheap way to confirm the degradation path
behaves.