use std::sync::Arc;
use crate::core::types::{AccountType, ExchangeId, ExchangeResult, ExchangeError};
use crate::core::traits::{Credentials, CoreConnector, WebSocketConnector};
use crate::l3::open::crypto::cex::binance::BinanceConnector;
use crate::l3::open::crypto::cex::bybit::BybitConnector;
use crate::l3::open::crypto::cex::okx::OkxConnector;
use crate::l3::open::crypto::cex::kucoin::KuCoinConnector;
use crate::l3::open::crypto::cex::kraken::KrakenConnector;
use crate::l3::open::crypto::cex::coinbase::CoinbaseConnector;
use crate::l3::open::crypto::cex::gateio::GateioConnector;
use crate::l3::open::crypto::cex::bitfinex::BitfinexConnector;
use crate::l3::open::crypto::cex::bitstamp::BitstampConnector;
use crate::l3::open::crypto::cex::gemini::GeminiConnector;
use crate::l3::open::crypto::cex::mexc::MexcConnector;
use crate::l3::open::crypto::cex::htx::HtxConnector;
use crate::l3::open::crypto::cex::bitget::BitgetConnector;
use crate::l3::open::crypto::cex::bingx::BingxConnector;
use crate::l3::open::crypto::cex::crypto_com::CryptoComConnector;
use crate::l3::open::crypto::cex::upbit::UpbitConnector;
use crate::l3::open::crypto::cex::deribit::DeribitConnector;
use crate::l3::open::crypto::cex::hyperliquid::HyperliquidConnector;
use crate::l3::open::crypto::dex::lighter::LighterConnector;
use crate::l3::open::crypto::dex::dydx::DydxConnector;
use crate::l2::paid::polygon::PolygonConnector;
use crate::l1::free::finnhub::FinnhubConnector;
use crate::l1::paid::tiingo::TiingoConnector;
use crate::l1::paid::twelvedata::TwelvedataConnector;
use crate::l3::gated::stocks::us::alpaca::AlpacaConnector;
use crate::l3::gated::stocks::india::dhan::DhanConnector;
use crate::l1::free::krx::KrxConnector;
use crate::l2::free::moex::{MoexConnector, MoexWebSocket};
use crate::l3::gated::forex::dukascopy::DukascopyConnector;
use crate::l1::paid::alphavantage::{AlphaVantageConnector, AlphaVantageAuth};
use crate::l3::open::prediction::polymarket::PolymarketConnector;
use crate::l1::free::yahoo::YahooFinanceConnector;
use crate::l2::paid::cryptocompare::CryptoCompareConnector;
use crate::l3::open::crypto::cex::binance::BinanceWebSocket;
use crate::l3::open::crypto::cex::bybit::BybitWebSocket;
use crate::l3::open::crypto::cex::okx::OkxWebSocket;
use crate::l3::open::crypto::cex::kucoin::KuCoinWebSocket;
use crate::l3::open::crypto::cex::kraken::KrakenWebSocket;
use crate::l3::open::crypto::cex::gateio::GateioWebSocket;
use crate::l3::open::crypto::cex::bitfinex::BitfinexWebSocket;
use crate::l3::open::crypto::cex::bitstamp::BitstampWebSocket;
use crate::l3::open::crypto::cex::gemini::GeminiWebSocket;
use crate::l3::open::crypto::cex::mexc::MexcWebSocket;
use crate::l3::open::crypto::cex::htx::HtxWebSocket;
use crate::l3::open::crypto::cex::bitget::BitgetWebSocket;
use crate::l3::open::crypto::cex::bingx::BingxWebSocket;
use crate::l3::open::crypto::cex::crypto_com::CryptoComWebSocket;
use crate::l3::open::crypto::cex::upbit::UpbitWebSocket;
use crate::l3::open::crypto::cex::deribit::DeribitWebSocket;
use crate::l3::open::crypto::cex::hyperliquid::HyperliquidWebSocket;
use crate::l3::open::crypto::cex::coinbase::CoinbaseWebSocket;
use crate::l3::open::crypto::dex::dydx::DydxWebSocket;
use crate::l3::open::crypto::dex::lighter::LighterWebSocket;
use crate::l1::free::yahoo::YahooFinanceWebSocket;
use crate::l2::paid::cryptocompare::CryptoCompareWebSocket;
pub(crate) struct ConnectorFactory;
impl ConnectorFactory {
pub(crate) async fn create_public(id: ExchangeId, testnet: bool) -> ExchangeResult<Arc<dyn CoreConnector>> {
match id {
ExchangeId::Binance => {
let c = BinanceConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bybit => {
let c = BybitConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::OKX => {
let c = OkxConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::KuCoin => {
let c = KuCoinConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Kraken => {
let c = KrakenConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::GateIO => {
let c = GateioConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bitfinex => {
let c = BitfinexConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::MEXC => {
let c = MexcConnector::public().await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::HTX => {
let c = HtxConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::BingX => {
let c = BingxConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::CryptoCom => {
let c = CryptoComConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Upbit => {
let c = UpbitConnector::public().await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Deribit => {
let c = DeribitConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::HyperLiquid => {
let c = HyperliquidConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Dydx => {
let c = DydxConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bitget => {
let c = BitgetConnector::public().await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bitstamp => {
let c = BitstampConnector::public().await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Coinbase => {
let c = CoinbaseConnector::public().await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Gemini => {
let c = GeminiConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Lighter => {
let c = LighterConnector::public(testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Alpaca => {
let c = AlpacaConnector::crypto_only();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::YahooFinance => {
let c = YahooFinanceConnector::new();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::DefiLlama => {
Err(ExchangeError::UnsupportedOperation(
"DefiLlama has moved to dig2feed crate".into()
))
}
ExchangeId::Polymarket => {
let c = PolymarketConnector::public();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Polygon => {
Err(ExchangeError::Auth(
"Polygon requires API key".into()
))
}
ExchangeId::Finnhub => {
Err(ExchangeError::Auth(
"Finnhub requires API key".into()
))
}
ExchangeId::Tiingo => {
Err(ExchangeError::Auth(
"Tiingo requires API key".into()
))
}
ExchangeId::Twelvedata => {
let c = TwelvedataConnector::demo();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::AlphaVantage => {
Err(ExchangeError::Auth(
"AlphaVantage requires API key".into()
))
}
ExchangeId::CryptoCompare => {
let c = CryptoCompareConnector::public();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::AngelOne => {
Err(ExchangeError::Auth(
"AngelOne requires credentials".into()
))
}
ExchangeId::Zerodha => {
Err(ExchangeError::Auth(
"Zerodha requires credentials".into()
))
}
ExchangeId::Upstox => {
Err(ExchangeError::Auth(
"Upstox requires credentials".into()
))
}
ExchangeId::Dhan => {
Err(ExchangeError::Auth(
"Dhan requires credentials".into()
))
}
ExchangeId::Fyers => {
Err(ExchangeError::Auth(
"Fyers requires credentials".into()
))
}
ExchangeId::Oanda => {
Err(ExchangeError::Auth(
"Oanda requires credentials".into()
))
}
ExchangeId::Dukascopy => {
let c = DukascopyConnector::new();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::JQuants => {
Err(ExchangeError::Auth(
"JQuants requires credentials".into()
))
}
ExchangeId::Krx => {
#[allow(deprecated)]
let c = KrxConnector::new_public();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Moex => {
let c = MoexConnector::new_public();
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Tinkoff => {
Err(ExchangeError::Auth(
"Tinkoff requires credentials".into()
))
}
ExchangeId::Ib => {
Err(ExchangeError::Auth(
"Interactive Brokers requires credentials".into()
))
}
ExchangeId::Futu => {
Err(ExchangeError::Auth(
"Futu requires OpenD TCP+Protobuf connection - create FutuConnector manually".into()
))
}
ExchangeId::Bls => {
Err(ExchangeError::Auth(
"BLS is a data feed - use BlsConnector directly".into()
))
}
ExchangeId::Coinglass => {
Err(ExchangeError::Auth(
"Coinglass requires API key".into()
))
}
ExchangeId::WhaleAlert => {
Err(ExchangeError::UnsupportedOperation(
"WhaleAlert has been extracted to the dig2onchain-data crate".into()
))
}
ExchangeId::Fred => {
Err(ExchangeError::Auth(
"Fred connector has been removed - intelligence_feeds module is no longer available".into()
))
}
ExchangeId::Bitquery => {
Err(ExchangeError::UnsupportedOperation(
"Bitquery has been extracted to the dig2onchain-data crate".into()
))
}
ExchangeId::Custom(_) => {
Err(ExchangeError::Auth(
"Custom exchange IDs not supported by factory - create manually".into()
))
}
}
}
pub(crate) async fn create_authenticated(
id: ExchangeId,
credentials: Credentials,
) -> ExchangeResult<Arc<dyn CoreConnector>> {
let testnet = credentials.testnet;
match id {
ExchangeId::Binance => {
let c = BinanceConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bybit => {
let c = BybitConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::OKX => {
let c = OkxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::KuCoin => {
let c = KuCoinConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Kraken => {
let c = KrakenConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::GateIO => {
let c = GateioConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bitfinex => {
let c = BitfinexConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::MEXC => {
let c = MexcConnector::new(Some(credentials)).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::HTX => {
let c = HtxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::BingX => {
let c = BingxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::CryptoCom => {
let c = CryptoComConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Upbit => {
let c = UpbitConnector::new(Some(credentials), "kr").await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Deribit => {
let c = DeribitConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::HyperLiquid => {
let c = HyperliquidConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bitget => {
let c = BitgetConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Bitstamp => {
let c = BitstampConnector::new(Some(credentials)).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Coinbase => {
let c = CoinbaseConnector::new(Some(credentials)).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Gemini => {
let c = GeminiConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Dydx => {
let c = DydxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Lighter => {
Self::create_public(id, testnet).await
}
ExchangeId::AlphaVantage => {
let auth = AlphaVantageAuth::new(credentials.api_key);
let c = AlphaVantageConnector::new(auth);
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::CryptoCompare => {
let auth = crate::l2::paid::cryptocompare::CryptoCompareAuth::new(credentials.api_key);
let c = CryptoCompareConnector::new(auth);
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Polygon => {
let c = PolygonConnector::new(credentials, testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Finnhub => {
let c = FinnhubConnector::new(credentials).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Tiingo => {
let c = TiingoConnector::new(credentials).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Twelvedata => {
let c = TwelvedataConnector::new(credentials.api_key);
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Alpaca => {
Err(ExchangeError::Auth(
"Alpaca connector uses from_env() - set environment variables instead".into()
))
}
ExchangeId::AngelOne => {
Err(ExchangeError::Auth(
"AngelOne requires complex authentication with TOTP - create manually".into()
))
}
ExchangeId::Zerodha => {
Err(ExchangeError::Auth(
"Zerodha requires OAuth authentication - create manually".into()
))
}
ExchangeId::Upstox => {
Err(ExchangeError::Auth(
"Upstox requires OAuth authentication - create manually".into()
))
}
ExchangeId::Dhan => {
let c = DhanConnector::new(credentials, testnet).await?;
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Fyers => {
Err(ExchangeError::Auth(
"Fyers requires special authentication setup - create manually".into()
))
}
ExchangeId::JQuants => {
Err(ExchangeError::Auth(
"JQuants requires email/password authentication - create manually".into()
))
}
ExchangeId::Krx => {
Err(ExchangeError::Auth(
"KRX requires special authentication - create manually".into()
))
}
ExchangeId::Moex => {
Err(ExchangeError::Auth(
"Moex connector requires special authentication - create manually".into()
))
}
ExchangeId::Tinkoff => {
Err(ExchangeError::Auth(
"Tinkoff requires token authentication - create manually".into()
))
}
ExchangeId::Oanda => {
Err(ExchangeError::Auth(
"Oanda requires practice mode specification - create manually".into()
))
}
ExchangeId::Dukascopy => {
Self::create_public(id, testnet).await
}
ExchangeId::Ib => {
Err(ExchangeError::Auth(
"Interactive Brokers requires TWS/Gateway connection - create manually".into()
))
}
ExchangeId::Futu => {
Err(ExchangeError::Auth(
"Futu requires OpenD TCP+Protobuf connection - create FutuConnector manually with FutuAuth".into()
))
}
ExchangeId::YahooFinance => {
Self::create_public(id, testnet).await
}
ExchangeId::DefiLlama => {
Err(ExchangeError::UnsupportedOperation(
"DefiLlama has moved to dig2feed crate".into()
))
}
ExchangeId::Polymarket => {
let poly_creds = crate::l3::open::prediction::polymarket::PolymarketCredentials::new(
credentials.api_key.clone(),
credentials.api_secret.clone(),
credentials.passphrase.clone().unwrap_or_default(),
String::new(), );
let c = PolymarketConnector::authenticated(poly_creds);
Ok(Arc::new(c) as Arc<dyn CoreConnector>)
}
ExchangeId::Coinglass => {
Err(ExchangeError::Auth(
"Coinglass connector has been removed - intelligence_feeds module is no longer available".into()
))
}
ExchangeId::WhaleAlert => {
Err(ExchangeError::UnsupportedOperation(
"WhaleAlert has been extracted to the dig2onchain-data crate".into()
))
}
ExchangeId::Fred => {
Err(ExchangeError::Auth(
"Fred connector has been removed - intelligence_feeds module is no longer available".into()
))
}
ExchangeId::Bitquery => {
Err(ExchangeError::UnsupportedOperation(
"Bitquery has been extracted to the dig2onchain-data crate".into()
))
}
ExchangeId::Bls => {
Err(ExchangeError::UnsupportedOperation(
"BLS is a data feed without standard connector traits - use BlsConnector directly".into()
))
}
ExchangeId::Custom(_) => {
Err(ExchangeError::Auth(
"Custom exchange IDs not supported by factory - create manually".into()
))
}
}
}
pub(crate) async fn create_websocket(
id: ExchangeId,
account_type: AccountType,
testnet: bool,
) -> ExchangeResult<Arc<dyn WebSocketConnector>> {
match id {
ExchangeId::Binance => {
let ws = BinanceWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Bybit => {
let ws = BybitWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::GateIO => {
let ws = GateioWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Bitfinex => {
let ws = BitfinexWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Bitget => {
let ws = BitgetWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::BingX => {
let ws = BingxWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Deribit => {
let ws = DeribitWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::KuCoin => {
let ws = KuCoinWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::OKX => {
let ws = OkxWebSocket::new(None, testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::HTX => {
let ws = HtxWebSocket::new(None, testnet, account_type)?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Coinbase => {
let ws = CoinbaseWebSocket::new(None).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Bitstamp => {
let ws = BitstampWebSocket::new().await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::MEXC => {
let ws = MexcWebSocket::new(None, crate::core::types::AccountType::Spot).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Gemini => {
let ws = GeminiWebSocket::new_market_data(testnet).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Kraken => {
let ws = KrakenWebSocket::new(None, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Upbit => {
let ws = UpbitWebSocket::new(None, "sg").await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::HyperLiquid => {
let ws = HyperliquidWebSocket::new(testnet);
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::CryptoCom => {
let ws = CryptoComWebSocket::new(None, false);
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Dydx => {
let ws = DydxWebSocket::new(testnet, account_type).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Lighter => {
let ws = LighterWebSocket::public(testnet).await?;
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Polymarket => {
Err(ExchangeError::UnsupportedOperation(
"Polymarket ClobWebSocket does not implement WebSocketConnector — use ClobWebSocket directly".into()
))
}
ExchangeId::YahooFinance => {
let ws = YahooFinanceWebSocket::new();
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::CryptoCompare => {
let ws = CryptoCompareWebSocket::new();
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Twelvedata => {
Err(ExchangeError::UnsupportedOperation(
"TwelvedataWebSocket requires an API key — construct directly with TwelvedataWebSocket::new(api_key)".into()
))
}
ExchangeId::Finnhub => {
Err(ExchangeError::UnsupportedOperation(
"FinnhubWebSocket requires credentials — construct directly with FinnhubWebSocket::new(credentials)".into()
))
}
ExchangeId::Polygon => {
Err(ExchangeError::UnsupportedOperation(
"PolygonWebSocket requires credentials — construct directly with PolygonWebSocket::new(credentials, realtime)".into()
))
}
ExchangeId::Tiingo => {
Err(ExchangeError::UnsupportedOperation(
"TiingoWebSocket requires TiingoAuth — construct directly via TiingoWebSocket::new_iex/new_forex/new_crypto".into()
))
}
ExchangeId::Moex => {
let ws = MoexWebSocket::new_public();
Ok(Arc::new(ws) as Arc<dyn WebSocketConnector>)
}
ExchangeId::Alpaca => {
Err(ExchangeError::UnsupportedOperation(
"AlpacaWebSocket requires AlpacaAuth — construct directly with AlpacaWebSocket::new(auth)".into()
))
}
ExchangeId::Dhan => {
Err(ExchangeError::UnsupportedOperation(
"DhanWebSocket requires access token — construct directly with DhanWebSocket::new(token)".into()
))
}
ExchangeId::Ib => {
Err(ExchangeError::UnsupportedOperation(
"IBWebSocket requires a TWS/Gateway URL — construct directly with IBWebSocket::new(url)".into()
))
}
ExchangeId::AlphaVantage
| ExchangeId::AngelOne
| ExchangeId::Zerodha
| ExchangeId::Upstox
| ExchangeId::Fyers
| ExchangeId::Oanda
| ExchangeId::Dukascopy
| ExchangeId::JQuants
| ExchangeId::Krx
| ExchangeId::Tinkoff
| ExchangeId::Futu
| ExchangeId::Bls
| ExchangeId::Coinglass
| ExchangeId::WhaleAlert
| ExchangeId::Fred
| ExchangeId::Bitquery
| ExchangeId::DefiLlama => Err(ExchangeError::UnsupportedOperation(
format!("{id:?} has no WebSocket implementation in digdigdig3")
)),
ExchangeId::Custom(_) => Err(ExchangeError::UnsupportedOperation(
"Custom exchange IDs not supported by WebSocket factory".into()
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::connector_manager::ConnectorRegistry;
#[tokio::test]
async fn test_create_public_binance() {
let result = ConnectorFactory::create_public(ExchangeId::Binance, false).await;
assert!(result.is_ok(), "Should create Binance public connector");
let connector = result.unwrap();
assert_eq!(connector.exchange_id(), ExchangeId::Binance);
}
#[tokio::test]
async fn test_create_authenticated_binance() {
let credentials = Credentials::new("test_key", "test_secret");
let result = ConnectorFactory::create_authenticated(
ExchangeId::Binance,
credentials
).await;
match result {
Ok(connector) => {
assert_eq!(connector.exchange_id(), ExchangeId::Binance);
}
Err(e) => {
println!("Expected error with test credentials: {:?}", e);
}
}
}
#[tokio::test]
async fn test_factory_coverage_all_51_exchanges() {
let registry = ConnectorRegistry::default();
let all_metas = registry.list_all();
assert_eq!(all_metas.len(), 47, "Registry should have 47 connectors");
println!("\n=== Testing Factory Coverage for 47 Exchanges ===\n");
let mut public_success = 0;
let mut public_requires_auth = 0;
let mut auth_attempted = 0;
for meta in all_metas {
println!("Testing {}", meta.name);
let public_result = ConnectorFactory::create_public(meta.id, false).await;
match public_result {
Ok(_) => {
println!(" ✓ Public connector created");
public_success += 1;
}
Err(ExchangeError::Auth(_)) => {
println!(" ⚠ Requires authentication");
public_requires_auth += 1;
let credentials = Credentials::new("dummy_key", "dummy_secret");
let auth_result = ConnectorFactory::create_authenticated(
meta.id,
credentials
).await;
match auth_result {
Ok(_) => {
println!(" ✓ Authenticated connector created (may fail at runtime)");
auth_attempted += 1;
}
Err(e) => {
println!(" ✗ Auth failed (expected with dummy creds): {:?}", e);
auth_attempted += 1;
}
}
}
Err(e) => {
panic!("Unexpected error creating public connector for {}: {:?}", meta.name, e);
}
}
}
println!("\n=== Factory Coverage Summary ===");
println!("Public connectors created: {}", public_success);
println!("Require authentication: {}", public_requires_auth);
println!("Auth connectors attempted: {}", auth_attempted);
println!("Total coverage: {}/47", public_success + public_requires_auth);
assert_eq!(
public_success + public_requires_auth,
47,
"Factory should handle all 47 exchanges"
);
}
#[tokio::test]
async fn test_create_authenticated_with_passphrase() {
let credentials = Credentials::new("test_key", "test_secret")
.with_passphrase("test_passphrase");
let result = ConnectorFactory::create_authenticated(
ExchangeId::OKX,
credentials
).await;
match result {
Ok(connector) => {
assert_eq!(connector.exchange_id(), ExchangeId::OKX);
}
Err(e) => {
println!("Expected error with test credentials: {:?}", e);
}
}
}
#[tokio::test]
async fn test_factory_creates_correct_type() {
let result = ConnectorFactory::create_public(ExchangeId::Bybit, false).await;
assert!(result.is_ok());
let connector = result.unwrap();
assert_eq!(connector.exchange_id(), ExchangeId::Bybit);
}
#[tokio::test]
async fn test_factory_multiple_creation() {
let conn1 = ConnectorFactory::create_public(ExchangeId::Binance, false).await.unwrap();
let conn2 = ConnectorFactory::create_public(ExchangeId::Bybit, false).await.unwrap();
let conn3 = ConnectorFactory::create_public(ExchangeId::OKX, false).await.unwrap();
assert_eq!(conn1.exchange_id(), ExchangeId::Binance);
assert_eq!(conn2.exchange_id(), ExchangeId::Bybit);
assert_eq!(conn3.exchange_id(), ExchangeId::OKX);
}
}