exchange-apiws
Async Rust client for exchange REST APIs and WebSocket feeds.
KuCoin (Futures + Spot) is fully implemented. The crate is architected to be exchange-agnostic — adding a new exchange means implementing one trait and the shared runner handles the rest.
Features
KuCoin — REST
| Method | Endpoint |
|---|---|
get_balance(currency) |
GET /api/v1/account-overview |
get_account_overview(currency) |
GET /api/v1/account-overview |
get_position(symbol) |
GET /api/v1/position |
get_all_positions() |
GET /api/v1/positions |
fetch_klines(symbol, limit, granularity) |
GET /api/v1/kline/query |
fetch_klines_extended(...) |
paginated kline fetch |
get_orderbook_snapshot(symbol) |
GET /api/v1/level2/snapshot |
get_funding_rate(symbol) |
GET /api/v1/funding-rate/{symbol}/current |
get_mark_price(symbol) |
GET /api/v1/mark-price/{symbol}/current |
get_active_contracts() |
GET /api/v1/contracts/active |
get_contract(symbol) |
GET /api/v1/contracts/{symbol} |
get_ticker(symbol) |
GET /api/v1/ticker |
get_risk_limit_levels(symbol) |
GET /api/v1/contracts/risk-limit/{symbol} |
get_funding_history(symbol, max_count) |
GET /api/v1/funding-history |
place_order(...) |
POST /api/v1/orders |
close_position(symbol, qty, leverage) |
POST /api/v1/orders |
cancel_order(order_id) |
DELETE /api/v1/orders/{id} |
cancel_all_orders(symbol) |
DELETE /api/v1/orders?symbol=… |
get_open_orders(symbol) |
GET /api/v1/orders?status=active |
get_order(order_id) |
GET /api/v1/orders/{id} |
get_recent_fills(symbol) |
GET /api/v1/recentFills |
place_stop_order(...) |
POST /api/v1/stopOrders |
cancel_stop_order(order_id) |
DELETE /api/v1/stopOrders/{id} |
cancel_all_stop_orders(symbol) |
DELETE /api/v1/stopOrders?symbol=… |
set_auto_deposit(symbol, bool) |
POST /api/v1/position/changeAutoDeposit |
set_risk_limit_level(symbol, level) |
POST /api/v1/position/risk-limit-level/change |
KuCoin — WebSocket
| Subscription helper | Topic | Feed type |
|---|---|---|
trade_subscription(symbol) |
/contractMarket/execution:{sym} |
DataMessage::Trade |
ticker_subscription(symbol) |
/contractMarket/tickerV2:{sym} |
DataMessage::Ticker |
orderbook_depth_subscription(symbol, depth) |
/contractMarket/level2Depth{5|50}:{sym} |
DataMessage::OrderBook (snapshot) |
orderbook_l2_subscription(symbol) |
/contractMarket/level2:{sym} |
DataMessage::OrderBook (delta) |
order_updates_subscription() ⚑ |
/contractMarket/tradeOrders |
DataMessage::OrderUpdate |
position_subscription(symbol) ⚑ |
/contract/position:{sym} |
DataMessage::PositionChange |
balance_subscription() ⚑ |
/contractAccount/wallet |
DataMessage::BalanceUpdate |
⚑ Requires a private WS token — call client.get_ws_token_private().
Installation
[]
= "0.1"
= { = "1", = ["rt-multi-thread", "macros"] }
Set your credentials as environment variables:
KC_KEY=your_api_key
KC_SECRET=your_api_secret
KC_PASSPHRASE=your_passphrase
Quick start
REST
use ;
async
Public WebSocket feed
use Arc;
use ;
use ;
use ;
async
Private WebSocket feed (order fills + positions)
// Use get_ws_token_private() and add private subscriptions:
let token = client.get_ws_token_private.await?;
let conn = new;
let subs = vec!;
// ... same run_feed setup as above
Contract sizing
use calc_contracts;
let contracts = calc_contracts;
println!;
Placing orders
use ;
// Market order
client.place_order.await?;
// Limit order with IOC + STP
client.place_order.await?;
// Stop-market order (close on breach)
client.place_stop_order.await?;
Rate limits
REST
KuCoin enforces per-UID rate limits per resource pool. VIP0 Futures quota is 2,000 requests / 30 seconds. The client automatically:
- Retries transient failures with exponential backoff (3 attempts, 1.5× factor)
- Reads the
gw-ratelimit-resetheader on HTTP 429 and sleeps for the exact reset window - Returns
ExchangeError::Apiwith the KuCoin error code on non-200000 responses
WebSocket
KuCoin allows 100 client→server messages per 10 seconds per connection (subscribe, unsubscribe, ping). The runner enforces this with a sliding-window guard before every outbound send — subscriptions sent at startup are rate-limited too, so large subscription batches at connect time will be transparently throttled.
Error handling
All fallible functions return Result<T> where the error type is ExchangeError:
use ExchangeError;
match client.get_position.await
Authentication
KuCoin API v2 HMAC-SHA256 signing is implemented in auth::build_headers. The prehash string is {timestamp}{METHOD}{endpoint}{body}. The passphrase is itself HMAC-signed (not sent raw), which is the v2 requirement.
Credentials are loaded from environment variables with Credentials::from_env():
| Variable | Description |
|---|---|
KC_KEY |
API key |
KC_SECRET |
API secret |
KC_PASSPHRASE |
API passphrase |
KuCoin-specific notes
Leverage is a per-order field in KuCoin Futures, not an account setting. Pass leverage in place_order and close_position. Use set_risk_limit_level to change the max position size tier.
Inverse vs. linear contracts — calc_contracts uses a built-in contract_value table. Inverse (USD-margined) contracts like XBTUSDM have a multiplier of 1 USD. Linear (USDT-margined) contracts like XBTUSDTM express a base-coin multiplier (0.001 BTC per contract).
Private WS token expiry — WS tokens are valid for the lifetime of the connection. The runner reconnects automatically; call get_ws_token_private() again inside the reconnect flow if you need long-lived private feeds.
Roadmap
- Binance Futures REST + WS
- OKX REST + WS
- Bybit REST + WS
- KuCoin Unified Trade Account (UTA) endpoints
- KuCoin spot margin orders
- WebSocket order placement (
wsapi.kucoin.com) - Integration test suite with recorded WS frames
License
MIT — see LICENSE.