crypto_exc_all 0.1.0

Unified cryptocurrency exchange SDK facade
Documentation
use crate::account::AccountFacade;
use crate::adapters::ExchangeClient;
use crate::config::SdkConfig;
use crate::error::{Error, Result};
use crate::exchange::ExchangeId;
use crate::market::MarketFacade;
use std::collections::HashMap;

pub struct CryptoSdk {
    clients: HashMap<ExchangeId, ExchangeClient>,
}

impl CryptoSdk {
    pub fn from_env() -> Result<Self> {
        Self::from_config(SdkConfig::from_env())
    }

    pub fn from_config(config: SdkConfig) -> Result<Self> {
        let mut clients = HashMap::new();

        #[cfg(feature = "okx")]
        if let Some(okx_config) = config.okx {
            let client = ExchangeClient::okx(okx_config)?;
            clients.insert(client.exchange_id(), client);
        }

        #[cfg(feature = "binance")]
        if let Some(binance_config) = config.binance {
            let client = ExchangeClient::binance(binance_config)?;
            clients.insert(client.exchange_id(), client);
        }

        Ok(Self { clients })
    }

    pub fn configured_exchanges(&self) -> Vec<ExchangeId> {
        let mut exchanges: Vec<_> = self.clients.keys().copied().collect();
        exchanges.sort_by_key(|exchange| exchange.as_str());
        exchanges
    }

    pub fn market(&self, exchange: ExchangeId) -> Result<MarketFacade<'_>> {
        Ok(MarketFacade::new(self.client(exchange)?))
    }

    pub fn account(&self, exchange: ExchangeId) -> Result<AccountFacade<'_>> {
        Ok(AccountFacade::new(self.client(exchange)?))
    }

    fn client(&self, exchange: ExchangeId) -> Result<&ExchangeClient> {
        self.clients
            .get(&exchange)
            .ok_or(Error::ExchangeNotConfigured(exchange))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::{BinanceExchangeConfig, OkxExchangeConfig};

    #[test]
    fn builds_sdk_from_explicit_config() {
        let sdk = CryptoSdk::from_config(SdkConfig {
            okx: Some(OkxExchangeConfig {
                api_key: "okx-key".to_string(),
                api_secret: "okx-secret".to_string(),
                passphrase: "okx-pass".to_string(),
                simulated: true,
                api_url: Some("http://127.0.0.1:1".to_string()),
                request_expiration_ms: Some(1_000),
            }),
            binance: Some(BinanceExchangeConfig {
                api_key: "binance-key".to_string(),
                api_secret: "binance-secret".to_string(),
                api_url: Some("http://127.0.0.1:1".to_string()),
                sapi_api_url: None,
                web_api_url: None,
                ws_stream_url: None,
                api_timeout_ms: Some(1_000),
                recv_window_ms: Some(5_000),
                proxy_url: None,
            }),
        })
        .unwrap();

        assert_eq!(
            sdk.configured_exchanges(),
            vec![ExchangeId::Binance, ExchangeId::Okx]
        );
        assert!(sdk.market(ExchangeId::Okx).is_ok());
        assert!(sdk.account(ExchangeId::Binance).is_ok());
    }
}