crypto_exc_all 0.1.1

Unified cryptocurrency exchange SDK facade
Documentation

crypto_exc_all

统一加密货币交易所 SDK facade。外部业务只依赖 crypto_exc_all,由根 crate 自动加载不同交易所的 API key,并在内部转成对应的 okx_rs / binance_rs / bitget_rs client。

发布结构

本项目按三层发布:

  • okx_rs: OKX 交易所 SDK crate。
  • binance_rs: Binance USDⓈ-M Futures SDK crate。
  • bitget_rs: Bitget V2 Futures SDK crate。
  • crypto_exc_all: 统一入口 crate,通过版本依赖引用各交易所 SDK。

本地开发使用 path + version 依赖;发布时先发布子交易所 crate,再发布 crypto_exc_all

[dependencies]
crypto_exc_all = "0.1"

当前根 crate 的默认 feature 会启用 OKX、Binance 和 Bitget:

crypto_exc_all = { version = "0.1", default-features = true }

只启用 Binance:

crypto_exc_all = { version = "0.1", default-features = false, features = ["binance"] }

只启用 Bitget:

crypto_exc_all = { version = "0.1", default-features = false, features = ["bitget"] }

环境变量

OKX:

OKX_API_KEY=...
OKX_API_SECRET=...
OKX_PASSPHRASE=...
OKX_SIMULATED_TRADING=1

也支持 OKX 模拟盘变量:

OKX_SIMULATED_API_KEY=...
OKX_SIMULATED_API_SECRET=...
OKX_SIMULATED_PASSPHRASE=...

Binance:

BINANCE_API_KEY=...
BINANCE_API_SECRET=...
BINANCE_PROXY_URL=socks5h://127.0.0.1:7897

BINANCE_PROXY_URL 可省略;如果传入 socks5://,SDK 会自动规范化成 socks5h://

Bitget:

BITGET_API_KEY=...
BITGET_API_SECRET=...
BITGET_PASSPHRASE=...
BITGET_PRODUCT_TYPE=USDT-FUTURES
BITGET_PROXY_URL=socks5h://127.0.0.1:7897
BITGET_WS_PUBLIC_URL=wss://ws.bitget.com/v2/ws/public
BITGET_WS_PRIVATE_URL=wss://ws.bitget.com/v2/ws/private

BITGET_PRODUCT_TYPE 可省略,根 adapter 默认使用 USDT-FUTURES。也支持 .env 中的小写/既有混合大小写变量名:bitget_api_keybitget_api_secretbitget_passphrasebitget_PASSPHRASEBITGET_WS_PUBLIC_URL / BITGET_WS_PRIVATE_URL 可省略,bitget_rs 会默认使用 Bitget V2 public/private WebSocket 主域名。

统一调用

use crypto_exc_all::{CryptoSdk, ExchangeId, Instrument};

#[tokio::main]
async fn main() -> crypto_exc_all::Result<()> {
    let sdk = CryptoSdk::from_env()?;
    let instrument = Instrument::perp("BTC", "USDT");

    let ticker = sdk
        .market(ExchangeId::Bitget)?
        .ticker(&instrument)
        .await?;

    println!("{} {}", ticker.exchange_symbol, ticker.last_price);

    Ok(())
}

遍历所有已配置交易所:

use crypto_exc_all::{CryptoSdk, Instrument};

#[tokio::main]
async fn main() -> crypto_exc_all::Result<()> {
    let sdk = CryptoSdk::from_env()?;
    let instrument = Instrument::perp("BTC", "USDT");

    for exchange in sdk.configured_exchanges() {
        let ticker = sdk.market(exchange)?.ticker(&instrument).await?;
        println!("{exchange}: {} {}", ticker.exchange_symbol, ticker.last_price);
    }

    Ok(())
}

运行示例:

cargo run --example unified_market

统一持仓、交易和订单查询入口:

use crypto_exc_all::{
    CancelOrderRequest, CryptoSdk, EnsureOrderMarginModeRequest, ExchangeId, Instrument,
    MarginMode, OrderSide, PlaceOrderRequest, PositionMode,
    CandleQuery, FillListQuery, FundingRateQuery, MarketStatsQuery, OrderBookQuery,
    OrderListQuery, PrepareOrderSettingsRequest, SetLeverageRequest, SetPositionModeRequest,
    TimeInForce,
};

#[tokio::main]
async fn main() -> crypto_exc_all::Result<()> {
    let sdk = CryptoSdk::from_env()?;
    let instrument = Instrument::perp("BTC", "USDT");

    let positions = sdk
        .positions(ExchangeId::Bitget)?
        .list(Some(&instrument))
        .await?;
    println!("positions={positions:?}");

    let book = sdk
        .market(ExchangeId::Bitget)?
        .orderbook(OrderBookQuery::new(instrument.clone()).with_limit(20))
        .await?;
    println!("best_bid={:?} best_ask={:?}", book.bids.first(), book.asks.first());

    let candles = sdk
        .market(ExchangeId::Bitget)?
        .candles(CandleQuery::new(instrument.clone(), "1m").with_limit(100))
        .await?;
    println!("candles={candles:?}");

    let funding = sdk
        .market(ExchangeId::Bitget)?
        .funding_rate(&instrument)
        .await?;
    println!("funding={funding:?}");

    let funding_history = sdk
        .market(ExchangeId::Bitget)?
        .funding_rate_history(FundingRateQuery::new(instrument.clone()).with_limit(20))
        .await?;
    println!("funding_history={funding_history:?}");

    let mark_price = sdk
        .market(ExchangeId::Bitget)?
        .mark_price(&instrument)
        .await?;
    println!("mark_price={mark_price:?}");

    let open_interest = sdk
        .market(ExchangeId::Bitget)?
        .open_interest(&instrument)
        .await?;
    println!("open_interest={open_interest:?}");

    let sentiment_query = MarketStatsQuery::new(instrument.clone(), "5m").with_limit(20);
    let long_short = sdk
        .market(ExchangeId::Bitget)?
        .long_short_ratio(sentiment_query.clone())
        .await?;
    println!("long_short={long_short:?}");

    let taker_volume = sdk
        .market(ExchangeId::Bitget)?
        .taker_buy_sell_volume(sentiment_query)
        .await?;
    println!("taker_volume={taker_volume:?}");

    let open_orders = sdk
        .orders(ExchangeId::Bitget)?
        .open(OrderListQuery::for_instrument(instrument.clone()).with_limit(20))
        .await?;
    println!("open_orders={open_orders:?}");

    let fills = sdk
        .fills(ExchangeId::Bitget)?
        .list(FillListQuery::for_instrument(instrument.clone()).with_limit(20))
        .await?;
    println!("fills={fills:?}");

    let leverage = sdk
        .account(ExchangeId::Bitget)?
        .set_leverage(
            SetLeverageRequest::new(instrument.clone(), "20")
                .with_margin_mode(MarginMode::Cross)
                .with_margin_coin("USDT"),
        )
        .await?;
    println!("leverage={leverage:?}");

    let order_margin_mode = sdk
        .account(ExchangeId::Bitget)?
        .ensure_order_margin_mode(
            EnsureOrderMarginModeRequest::new(instrument.clone(), MarginMode::Cross)
                .with_product_type("USDT-FUTURES")
                .with_margin_coin("USDT"),
        )
        .await?;
    println!("order_margin_mode={order_margin_mode:?}");

    let order_settings = sdk
        .account(ExchangeId::Bitget)?
        .prepare_order_settings(
            PrepareOrderSettingsRequest::new(instrument.clone())
                .with_position_mode(PositionMode::Hedge)
                .with_margin_mode(MarginMode::Cross)
                .with_leverage("20")
                .with_product_type("USDT-FUTURES")
                .with_margin_coin("USDT")
                .with_position_side("long"),
        )
        .await?;
    println!("order_settings={order_settings:?}");

    let position_mode = sdk
        .account(ExchangeId::Bitget)?
        .set_position_mode(
            SetPositionModeRequest::new(PositionMode::Hedge)
                .with_product_type("USDT-FUTURES"),
        )
        .await?;
    println!("position_mode={position_mode:?}");

    let order = sdk
        .trade(ExchangeId::Bitget)?
        .place_order(
            PlaceOrderRequest::limit(instrument.clone(), OrderSide::Buy, "0.001", "60000")
                .with_time_in_force(TimeInForce::PostOnly)
                .with_client_order_id("my-client-order-id"),
        )
        .await?;

    if let Some(order_id) = order.order_id {
        sdk.trade(ExchangeId::Bitget)?
            .cancel_order(CancelOrderRequest::by_order_id(instrument, order_id))
            .await?;
    }

    Ok(())
}

当前统一能力

  • 自动读取 OKX / Binance / Bitget 凭证。
  • CryptoSdk::from_env() / CryptoSdk::from_config()
  • sdk.configured_exchanges()
  • 统一 Instrument,自动映射交易所 symbol:
    • Binance 永续:BTCUSDT
    • OKX 永续:BTC-USDT-SWAP
    • Bitget USDT 永续:BTCUSDT
  • 统一 market ticker:
    • sdk.market(exchange)?.ticker(&instrument).await
  • 统一 market orderbook 和 candles:
    • sdk.market(exchange)?.orderbook(query).await
    • sdk.market(exchange)?.candles(query).await
  • 统一 derivatives market metrics:
    • sdk.market(exchange)?.funding_rate(&instrument).await
    • sdk.market(exchange)?.funding_rate_history(query).await
    • sdk.market(exchange)?.mark_price(&instrument).await
    • sdk.market(exchange)?.open_interest(&instrument).await
  • 统一 market sentiment stats:
    • sdk.market(exchange)?.long_short_ratio(query).await
    • sdk.market(exchange)?.taker_buy_sell_volume(query).await
  • 统一 account balances:
    • sdk.account(exchange)?.balances().await
  • 统一账户交易设置:
    • sdk.account(exchange)?.capabilities()
    • sdk.account(exchange)?.set_leverage(request).await
    • sdk.account(exchange)?.set_position_mode(request).await
    • sdk.account(exchange)?.set_symbol_margin_mode(request).await
    • sdk.account(exchange)?.ensure_order_margin_mode(request).await
    • sdk.account(exchange)?.prepare_order_settings(request).await
  • 统一 positions:
    • sdk.positions(exchange)?.list(Some(&instrument)).await
  • 统一基础下单/撤单:
    • sdk.trade(exchange)?.place_order(request).await
    • sdk.trade(exchange)?.cancel_order(request).await
  • 统一订单查询:
    • sdk.orders(exchange)?.get(query).await
    • sdk.orders(exchange)?.open(query).await
    • sdk.orders(exchange)?.history(query).await
  • 统一成交明细查询:
    • sdk.fills(exchange)?.list(query).await
  • 统一事件流:
    • sdk.events(exchange)?.connect(subscriptions).await
    • 当前先支持 Bitget,OKX/Binance 事件流会明确返回 Unsupported
  • 统一错误入口 crypto_exc_all::Error
  • raw 逃生口:
    • crypto_exc_all::raw::okx
    • crypto_exc_all::raw::binance
    • crypto_exc_all::raw::bitget

crypto_exc_all::raw::bitget 暴露 bitget_rs 的原生 V2 REST/WebSocket wrapper,覆盖 Bitget Futures market/account/trade、计划单/条件单/TPSL、历史持仓、VIP fee rate、子账户合约资产、Spot wallet/asset、public notices、common trade-rate,以及 V2 public/private WebSocket URL、login、ping/pong、subscribe/unsubscribe、place-order/cancel-order trade helper、trade ack parser、ticker/orders/account/positions/books/trade/candle/fill/orders-algo/adl-noti/positions-history typed event parser、运行中动态订阅/取消订阅、私有连接登录重放和 ack gate、入站消息超时重连、连接内失败重连次数限制、基础重连订阅重放、public/private manager 分层、统一事件转发、pending/active 订阅 registry 和细分连接状态。统一 facade 当前稳定暴露跨交易所 ticker / orderbook / candles / funding rate / funding rate history / mark price / open interest / long-short ratio / taker buy-sell volume / balances / set leverage / set position mode / set symbol margin mode / ensure order margin mode / prepare order settings / positions / place_order / cancel_order / order detail / open orders / order history / fills,并先为 Bitget 暴露统一 event stream,把 ticker/orderbook/trades/orders/account/positions/fills 推送映射为统一事件且保留 raw;不同交易所的账户配置语义通过 capabilities() 暴露,OKX 这类没有 symbol 级独立 margin-mode switch 的交易所会返回 Unsupported,策略层可优先使用 prepare_order_settings 一次性处理持仓模式、保证金模式和杠杆预配置。

测试

根 crate 包含外部调用场景集成测试:测试代码只引入 crypto_exc_all,通过 mock HTTP 同时调用 OKX、Binance 和 Bitget 的统一 ticker、orderbook、candles、funding rate、funding rate history、mark price、open interest、long-short ratio、taker buy-sell volume、balances、set leverage、set position mode、set symbol margin mode、ensure order margin mode、prepare order settings、positions、place_order、cancel_order、order detail、open orders、order history 和 fills 接口,并通过本地 mock WebSocket 覆盖 Bitget 统一 event stream。

cargo test -p crypto_exc_all -- --nocapture

继续接入交易所

新增 Bybit、Hyperliquid 等交易所时,按 Exchange Integration Playbook 执行。该文档记录了本轮迭代沉淀下来的 crate 命名、dependency alias、adapter、测试、发布和安全检查流程。