Event-Driven Design
Havklo delivers all data through a channel-based event system. Your application receives events and reacts - no polling required.Copy
let mut events = client.events();
while let Some(event) = events.recv().await {
match event {
Event::Market(market) => { /* price data */ }
Event::Connection(conn) => { /* lifecycle */ }
Event::Subscription(sub) => { /* channel status */ }
Event::Private(priv_) => { /* account data */ }
Event::L3(l3) => { /* order-level */ }
Event::BufferOverflow { dropped_count } => { /* backpressure */ }
}
}
Event Types
- Market
- Connection
- Private
Real-time market data:Usage:
Copy
pub enum MarketEvent {
OrderbookSnapshot {
symbol: String,
snapshot: OrderbookSnapshot,
},
OrderbookUpdate {
symbol: String,
snapshot: OrderbookSnapshot,
},
ChecksumMismatch {
symbol: String,
expected: u32,
computed: u32,
},
Heartbeat,
}
Copy
Event::Market(MarketEvent::OrderbookUpdate { symbol, snapshot }) => {
let spread = snapshot.spread();
let mid = snapshot.mid_price();
println!("{}: spread={:?}, mid={:?}", symbol, spread, mid);
}
WebSocket lifecycle:Usage:
Copy
pub enum ConnectionEvent {
Connected {
api_version: String,
connection_id: String,
},
Disconnected {
reason: DisconnectReason,
},
Reconnecting {
attempt: u32,
delay: Duration,
},
ReconnectFailed {
error: String,
},
SubscriptionsRestored {
count: usize,
},
CircuitBreakerOpen {
trips: u32,
},
}
Copy
Event::Connection(ConnectionEvent::Disconnected { reason }) => {
log::warn!("Connection lost: {:?}", reason);
}
Event::Connection(ConnectionEvent::SubscriptionsRestored { count }) => {
log::info!("Reconnected, restored {} subscriptions", count);
}
Authenticated account events:
Copy
pub enum PrivateEvent {
Execution {
order_id: String,
symbol: String,
side: Side,
price: Decimal,
qty: Decimal,
fee: Decimal,
},
BalanceUpdate {
asset: String,
balance: Decimal,
},
}
Private events require API key authentication.
OrderbookSnapshot
The snapshot contains full orderbook state:Copy
pub struct OrderbookSnapshot {
pub symbol: String,
pub timestamp: String, // ISO 8601
pub bids: Vec<Level>, // High to low
pub asks: Vec<Level>, // Low to high
pub checksum: u32,
pub state: OrderbookState,
}
impl OrderbookSnapshot {
pub fn best_bid(&self) -> Option<&Level>;
pub fn best_ask(&self) -> Option<&Level>;
pub fn spread(&self) -> Option<Decimal>;
pub fn mid_price(&self) -> Option<Decimal>;
}
Copy
Event::Market(MarketEvent::OrderbookUpdate { symbol, snapshot }) => {
// Access computed values
let spread = snapshot.spread().unwrap_or_default();
// Access raw levels
for bid in snapshot.bids.iter().take(5) {
println!("Bid: {} @ {}", bid.qty, bid.price);
}
}
Backpressure
The event channel has bounded capacity (default 1024). If your handler is slow:Copy
Event::BufferOverflow { dropped_count } => {
log::warn!("Dropped {} events - handler too slow", dropped_count);
}
BufferOverflow means you’re not keeping up. Consider processing faster, increasing capacity, or filtering events.Event Filtering
Reduce noise at subscription time:Copy
let client = KrakenClient::builder(symbols)
.with_event_filter(
EventFilter::new()
.only_market() // No connection events
.exclude_heartbeats() // No heartbeat spam
)
.connect()
.await?;