use std::sync::Arc;
use crate::core::types::{ExchangeId, ExchangeResult, ExchangeError};
use crate::core::traits::Credentials;
use crate::connector_manager::AnyConnector;
use crate::crypto::cex::binance::BinanceConnector;
use crate::crypto::cex::bybit::BybitConnector;
use crate::crypto::cex::okx::OkxConnector;
use crate::crypto::cex::kucoin::KuCoinConnector;
use crate::crypto::cex::kraken::KrakenConnector;
use crate::crypto::cex::coinbase::CoinbaseConnector;
use crate::crypto::cex::gateio::GateioConnector;
use crate::crypto::cex::bitfinex::BitfinexConnector;
use crate::crypto::cex::bitstamp::BitstampConnector;
use crate::crypto::cex::gemini::GeminiConnector;
use crate::crypto::cex::mexc::MexcConnector;
use crate::crypto::cex::htx::HtxConnector;
use crate::crypto::cex::bitget::BitgetConnector;
use crate::crypto::cex::bingx::BingxConnector;
use crate::crypto::cex::phemex::PhemexConnector;
use crate::crypto::cex::crypto_com::CryptoComConnector;
use crate::crypto::cex::upbit::UpbitConnector;
use crate::crypto::cex::deribit::DeribitConnector;
use crate::crypto::cex::hyperliquid::HyperliquidConnector;
use crate::crypto::dex::lighter::LighterConnector;
use crate::crypto::dex::paradex::ParadexConnector;
use crate::crypto::dex::dydx::DydxConnector;
use crate::stocks::us::polygon::PolygonConnector;
use crate::stocks::us::finnhub::FinnhubConnector;
use crate::stocks::us::tiingo::TiingoConnector;
use crate::stocks::us::twelvedata::TwelvedataConnector;
use crate::stocks::us::alpaca::AlpacaConnector;
use crate::stocks::india::dhan::DhanConnector;
use crate::stocks::korea::krx::KrxConnector;
use crate::stocks::russia::moex::MoexConnector;
use crate::forex::dukascopy::DukascopyConnector;
use crate::forex::alphavantage::{AlphaVantageConnector, AlphaVantageAuth};
use crate::prediction::polymarket::PolymarketConnector;
use crate::data_feeds::yahoo::YahooFinanceConnector;
use crate::data_feeds::cryptocompare::CryptoCompareConnector;
pub struct ConnectorFactory;
impl ConnectorFactory {
pub async fn create_public(id: ExchangeId, testnet: bool) -> ExchangeResult<Arc<AnyConnector>> {
match id {
ExchangeId::Binance => {
let c = BinanceConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Binance(Arc::new(c))))
}
ExchangeId::Bybit => {
let c = BybitConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Bybit(Arc::new(c))))
}
ExchangeId::OKX => {
let c = OkxConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::OKX(Arc::new(c))))
}
ExchangeId::KuCoin => {
let c = KuCoinConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::KuCoin(Arc::new(c))))
}
ExchangeId::Kraken => {
let c = KrakenConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Kraken(Arc::new(c))))
}
ExchangeId::GateIO => {
let c = GateioConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::GateIO(Arc::new(c))))
}
ExchangeId::Bitfinex => {
let c = BitfinexConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Bitfinex(Arc::new(c))))
}
ExchangeId::MEXC => {
let c = MexcConnector::public().await?;
Ok(Arc::new(AnyConnector::MEXC(Arc::new(c))))
}
ExchangeId::HTX => {
let c = HtxConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::HTX(Arc::new(c))))
}
ExchangeId::BingX => {
let c = BingxConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::BingX(Arc::new(c))))
}
ExchangeId::Phemex => {
let c = PhemexConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Phemex(Arc::new(c))))
}
ExchangeId::CryptoCom => {
let c = CryptoComConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::CryptoCom(Arc::new(c))))
}
ExchangeId::Upbit => {
let c = UpbitConnector::public().await?;
Ok(Arc::new(AnyConnector::Upbit(Arc::new(c))))
}
ExchangeId::Deribit => {
let c = DeribitConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Deribit(Arc::new(c))))
}
ExchangeId::HyperLiquid => {
let c = HyperliquidConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::HyperLiquid(Arc::new(c))))
}
ExchangeId::Dydx => {
let c = DydxConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Dydx(Arc::new(c))))
}
ExchangeId::Paradex => {
let c = ParadexConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Paradex(Arc::new(c))))
}
ExchangeId::Bitget => {
let c = BitgetConnector::public().await?;
Ok(Arc::new(AnyConnector::Bitget(Arc::new(c))))
}
ExchangeId::Bitstamp => {
let c = BitstampConnector::public().await?;
Ok(Arc::new(AnyConnector::Bitstamp(Arc::new(c))))
}
ExchangeId::Coinbase => {
let c = CoinbaseConnector::public().await?;
Ok(Arc::new(AnyConnector::Coinbase(Arc::new(c))))
}
ExchangeId::Gemini => {
let c = GeminiConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Gemini(Arc::new(c))))
}
ExchangeId::Lighter => {
let c = LighterConnector::public(testnet).await?;
Ok(Arc::new(AnyConnector::Lighter(Arc::new(c))))
}
ExchangeId::Jupiter | ExchangeId::Raydium | ExchangeId::Uniswap | ExchangeId::Gmx => {
Err(ExchangeError::UnsupportedOperation(
"Jupiter, Raydium, Uniswap, and GMX have been extracted to the dig2swap crate".into()
))
}
ExchangeId::Alpaca => {
let c = AlpacaConnector::crypto_only();
Ok(Arc::new(AnyConnector::Alpaca(Arc::new(c))))
}
ExchangeId::YahooFinance => {
let c = YahooFinanceConnector::new();
Ok(Arc::new(AnyConnector::YahooFinance(Arc::new(c))))
}
ExchangeId::DefiLlama => {
Err(ExchangeError::UnsupportedOperation(
"DefiLlama has moved to dig2feed crate".into()
))
}
ExchangeId::Polymarket => {
let c = PolymarketConnector::public();
Ok(Arc::new(AnyConnector::Polymarket(Arc::new(c))))
}
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(AnyConnector::Twelvedata(Arc::new(c))))
}
ExchangeId::AlphaVantage => {
Err(ExchangeError::Auth(
"AlphaVantage requires API key".into()
))
}
ExchangeId::CryptoCompare => {
let c = CryptoCompareConnector::public();
Ok(Arc::new(AnyConnector::CryptoCompare(Arc::new(c))))
}
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(AnyConnector::Dukascopy(Arc::new(c))))
}
ExchangeId::JQuants => {
Err(ExchangeError::Auth(
"JQuants requires credentials".into()
))
}
ExchangeId::Krx => {
#[allow(deprecated)]
let c = KrxConnector::new_public();
Ok(Arc::new(AnyConnector::Krx(Arc::new(c))))
}
ExchangeId::Moex => {
let c = MoexConnector::new_public();
Ok(Arc::new(AnyConnector::Moex(Arc::new(c))))
}
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 async fn create_authenticated(
id: ExchangeId,
credentials: Credentials,
) -> ExchangeResult<Arc<AnyConnector>> {
let testnet = credentials.testnet;
match id {
ExchangeId::Binance => {
let c = BinanceConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Binance(Arc::new(c))))
}
ExchangeId::Bybit => {
let c = BybitConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Bybit(Arc::new(c))))
}
ExchangeId::OKX => {
let c = OkxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::OKX(Arc::new(c))))
}
ExchangeId::KuCoin => {
let c = KuCoinConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::KuCoin(Arc::new(c))))
}
ExchangeId::Kraken => {
let c = KrakenConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Kraken(Arc::new(c))))
}
ExchangeId::GateIO => {
let c = GateioConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::GateIO(Arc::new(c))))
}
ExchangeId::Bitfinex => {
let c = BitfinexConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Bitfinex(Arc::new(c))))
}
ExchangeId::MEXC => {
let c = MexcConnector::new(Some(credentials)).await?;
Ok(Arc::new(AnyConnector::MEXC(Arc::new(c))))
}
ExchangeId::HTX => {
let c = HtxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::HTX(Arc::new(c))))
}
ExchangeId::BingX => {
let c = BingxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::BingX(Arc::new(c))))
}
ExchangeId::Phemex => {
let c = PhemexConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Phemex(Arc::new(c))))
}
ExchangeId::CryptoCom => {
let c = CryptoComConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::CryptoCom(Arc::new(c))))
}
ExchangeId::Upbit => {
let c = UpbitConnector::new(Some(credentials), "kr").await?;
Ok(Arc::new(AnyConnector::Upbit(Arc::new(c))))
}
ExchangeId::Deribit => {
let c = DeribitConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Deribit(Arc::new(c))))
}
ExchangeId::HyperLiquid => {
let c = HyperliquidConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::HyperLiquid(Arc::new(c))))
}
ExchangeId::Bitget => {
let c = BitgetConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Bitget(Arc::new(c))))
}
ExchangeId::Bitstamp => {
let c = BitstampConnector::new(Some(credentials)).await?;
Ok(Arc::new(AnyConnector::Bitstamp(Arc::new(c))))
}
ExchangeId::Coinbase => {
let c = CoinbaseConnector::new(Some(credentials)).await?;
Ok(Arc::new(AnyConnector::Coinbase(Arc::new(c))))
}
ExchangeId::Gemini => {
let c = GeminiConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Gemini(Arc::new(c))))
}
ExchangeId::Dydx => {
let c = DydxConnector::new(Some(credentials), testnet).await?;
Ok(Arc::new(AnyConnector::Dydx(Arc::new(c))))
}
ExchangeId::Paradex => {
let c = ParadexConnector::new(credentials, testnet).await?;
Ok(Arc::new(AnyConnector::Paradex(Arc::new(c))))
}
ExchangeId::Jupiter | ExchangeId::Raydium | ExchangeId::Uniswap | ExchangeId::Gmx => {
Err(ExchangeError::UnsupportedOperation(
"Jupiter, Raydium, Uniswap, and GMX have been extracted to the dig2swap crate".into()
))
}
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(AnyConnector::AlphaVantage(Arc::new(c))))
}
ExchangeId::CryptoCompare => {
let auth = crate::data_feeds::cryptocompare::CryptoCompareAuth::new(credentials.api_key);
let c = CryptoCompareConnector::new(auth);
Ok(Arc::new(AnyConnector::CryptoCompare(Arc::new(c))))
}
ExchangeId::Polygon => {
let c = PolygonConnector::new(credentials, testnet).await?;
Ok(Arc::new(AnyConnector::Polygon(Arc::new(c))))
}
ExchangeId::Finnhub => {
let c = FinnhubConnector::new(credentials).await?;
Ok(Arc::new(AnyConnector::Finnhub(Arc::new(c))))
}
ExchangeId::Tiingo => {
let c = TiingoConnector::new(credentials).await?;
Ok(Arc::new(AnyConnector::Tiingo(Arc::new(c))))
}
ExchangeId::Twelvedata => {
let c = TwelvedataConnector::new(credentials.api_key);
Ok(Arc::new(AnyConnector::Twelvedata(Arc::new(c))))
}
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(AnyConnector::Dhan(Arc::new(c))))
}
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::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(AnyConnector::Polymarket(Arc::new(c))))
}
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()
))
}
}
}
}
#[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.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.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(), 48, "Registry should have 48 connectors");
println!("\n=== Testing Factory Coverage for 48 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: {}/48", public_success + public_requires_auth);
assert_eq!(
public_success + public_requires_auth,
48,
"Factory should handle all 48 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.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.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.id(), ExchangeId::Binance);
assert_eq!(conn2.id(), ExchangeId::Bybit);
assert_eq!(conn3.id(), ExchangeId::OKX);
}
}