use ccxt_core::types::{EndpointType, Market, MarketType};
pub trait BinanceEndpointRouter {
fn rest_endpoint(&self, market: &Market, endpoint_type: EndpointType) -> String;
fn ws_endpoint(&self, market: &Market) -> String;
fn default_rest_endpoint(&self, endpoint_type: EndpointType) -> String;
fn default_ws_endpoint(&self) -> String;
fn sapi_endpoint(&self) -> String;
fn papi_endpoint(&self) -> String;
}
use super::Binance;
use ccxt_core::types::default_type::{DefaultSubType, DefaultType};
impl BinanceEndpointRouter for Binance {
fn rest_endpoint(&self, market: &Market, endpoint_type: EndpointType) -> String {
let urls = self.urls();
match market.market_type {
MarketType::Spot => match endpoint_type {
EndpointType::Public => urls.public.clone(),
EndpointType::Private => urls.private.clone(),
},
MarketType::Swap | MarketType::Futures => {
let is_linear = market.linear.unwrap_or(true);
if is_linear {
match endpoint_type {
EndpointType::Public => urls.fapi_public.clone(),
EndpointType::Private => urls.fapi_private.clone(),
}
} else {
match endpoint_type {
EndpointType::Public => urls.dapi_public.clone(),
EndpointType::Private => urls.dapi_private.clone(),
}
}
}
MarketType::Option => match endpoint_type {
EndpointType::Public => urls.eapi_public.clone(),
EndpointType::Private => urls.eapi_private.clone(),
},
}
}
fn ws_endpoint(&self, market: &Market) -> String {
let urls = self.urls();
match market.market_type {
MarketType::Spot => urls.ws.clone(),
MarketType::Swap | MarketType::Futures => {
let is_linear = market.linear.unwrap_or(true);
if is_linear {
urls.ws_fapi.clone()
} else {
urls.ws_dapi.clone()
}
}
MarketType::Option => urls.ws_eapi.clone(),
}
}
fn default_rest_endpoint(&self, endpoint_type: EndpointType) -> String {
let urls = self.urls();
let options = self.options();
match options.default_type {
DefaultType::Spot => match endpoint_type {
EndpointType::Public => urls.public.clone(),
EndpointType::Private => urls.private.clone(),
},
DefaultType::Margin => urls.sapi.clone(),
DefaultType::Swap | DefaultType::Futures => {
match options.default_sub_type {
Some(DefaultSubType::Inverse) => match endpoint_type {
EndpointType::Public => urls.dapi_public.clone(),
EndpointType::Private => urls.dapi_private.clone(),
},
_ => match endpoint_type {
EndpointType::Public => urls.fapi_public.clone(),
EndpointType::Private => urls.fapi_private.clone(),
},
}
}
DefaultType::Option => match endpoint_type {
EndpointType::Public => urls.eapi_public.clone(),
EndpointType::Private => urls.eapi_private.clone(),
},
}
}
fn default_ws_endpoint(&self) -> String {
let urls = self.urls();
let options = self.options();
match options.default_type {
DefaultType::Swap | DefaultType::Futures => {
match options.default_sub_type {
Some(DefaultSubType::Inverse) => urls.ws_dapi.clone(),
_ => urls.ws_fapi.clone(), }
}
DefaultType::Option => urls.ws_eapi.clone(),
_ => urls.ws.clone(), }
}
fn sapi_endpoint(&self) -> String {
self.urls().sapi.clone()
}
fn papi_endpoint(&self) -> String {
self.urls().papi.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ccxt_core::ExchangeConfig;
use rust_decimal_macros::dec;
fn create_test_binance() -> Binance {
Binance::new(ExchangeConfig::default()).unwrap()
}
fn create_sandbox_binance() -> Binance {
let config = ExchangeConfig {
sandbox: true,
..Default::default()
};
Binance::new(config).unwrap()
}
#[test]
fn test_rest_endpoint_spot_public() {
let binance = create_test_binance();
let market = Market::new_spot(
"BTCUSDT".to_string(),
"BTC/USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
);
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("api.binance.com"));
}
#[test]
fn test_rest_endpoint_spot_private() {
let binance = create_test_binance();
let market = Market::new_spot(
"BTCUSDT".to_string(),
"BTC/USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
);
let url = binance.rest_endpoint(&market, EndpointType::Private);
assert!(url.contains("api.binance.com"));
}
#[test]
fn test_rest_endpoint_linear_swap_public() {
let binance = create_test_binance();
let market = Market::new_swap(
"BTCUSDT".to_string(),
"BTC/USDT:USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
"USDT".to_string(),
dec!(1.0),
);
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("fapi.binance.com"));
}
#[test]
fn test_rest_endpoint_linear_swap_private() {
let binance = create_test_binance();
let market = Market::new_swap(
"BTCUSDT".to_string(),
"BTC/USDT:USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
"USDT".to_string(),
dec!(1.0),
);
let url = binance.rest_endpoint(&market, EndpointType::Private);
assert!(url.contains("fapi.binance.com"));
}
#[test]
fn test_rest_endpoint_inverse_swap_public() {
let binance = create_test_binance();
let mut market = Market::new_swap(
"BTCUSD_PERP".to_string(),
"BTC/USD:BTC".to_string(),
"BTC".to_string(),
"USD".to_string(),
"BTC".to_string(),
dec!(100.0),
);
market.linear = Some(false);
market.inverse = Some(true);
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("dapi.binance.com"));
}
#[test]
fn test_rest_endpoint_inverse_swap_private() {
let binance = create_test_binance();
let mut market = Market::new_swap(
"BTCUSD_PERP".to_string(),
"BTC/USD:BTC".to_string(),
"BTC".to_string(),
"USD".to_string(),
"BTC".to_string(),
dec!(100.0),
);
market.linear = Some(false);
market.inverse = Some(true);
let url = binance.rest_endpoint(&market, EndpointType::Private);
assert!(url.contains("dapi.binance.com"));
}
#[test]
fn test_rest_endpoint_option_public() {
let binance = create_test_binance();
let market = Market {
id: "BTC-250328-100000-C".to_string(),
symbol: "BTC/USDT:USDT-250328-100000-C".to_string(),
market_type: MarketType::Option,
..Default::default()
};
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("eapi.binance.com"));
}
#[test]
fn test_rest_endpoint_option_private() {
let binance = create_test_binance();
let market = Market {
id: "BTC-250328-100000-C".to_string(),
symbol: "BTC/USDT:USDT-250328-100000-C".to_string(),
market_type: MarketType::Option,
..Default::default()
};
let url = binance.rest_endpoint(&market, EndpointType::Private);
assert!(url.contains("eapi.binance.com"));
}
#[test]
fn test_ws_endpoint_spot() {
let binance = create_test_binance();
let market = Market::new_spot(
"BTCUSDT".to_string(),
"BTC/USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
);
let url = binance.ws_endpoint(&market);
assert!(url.contains("stream.binance.com"));
}
#[test]
fn test_ws_endpoint_linear_swap() {
let binance = create_test_binance();
let market = Market::new_swap(
"BTCUSDT".to_string(),
"BTC/USDT:USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
"USDT".to_string(),
dec!(1.0),
);
let url = binance.ws_endpoint(&market);
assert!(url.contains("fstream.binance.com"));
}
#[test]
fn test_ws_endpoint_inverse_swap() {
let binance = create_test_binance();
let mut market = Market::new_swap(
"BTCUSD_PERP".to_string(),
"BTC/USD:BTC".to_string(),
"BTC".to_string(),
"USD".to_string(),
"BTC".to_string(),
dec!(100.0),
);
market.linear = Some(false);
market.inverse = Some(true);
let url = binance.ws_endpoint(&market);
assert!(url.contains("dstream.binance.com"));
}
#[test]
fn test_ws_endpoint_option() {
let binance = create_test_binance();
let mut market = Market::default();
market.market_type = MarketType::Option;
let url = binance.ws_endpoint(&market);
assert!(url.contains("nbstream.binance.com"));
}
#[test]
fn test_default_rest_endpoint_spot() {
let binance = create_test_binance();
let url = binance.default_rest_endpoint(EndpointType::Public);
assert!(url.contains("api.binance.com"));
}
#[test]
fn test_default_ws_endpoint_spot() {
let binance = create_test_binance();
let url = binance.default_ws_endpoint();
assert!(url.contains("stream.binance.com"));
}
#[test]
fn test_sapi_endpoint() {
let binance = create_test_binance();
let url = binance.sapi_endpoint();
assert!(url.contains("sapi"));
assert!(url.contains("api.binance.com"));
}
#[test]
fn test_papi_endpoint() {
let binance = create_test_binance();
let url = binance.papi_endpoint();
assert!(url.contains("papi"));
}
#[test]
fn test_sandbox_rest_endpoint_spot() {
let binance = create_sandbox_binance();
let market = Market::new_spot(
"BTCUSDT".to_string(),
"BTC/USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
);
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("testnet"));
}
#[test]
fn test_sandbox_ws_endpoint_spot() {
let binance = create_sandbox_binance();
let market = Market::new_spot(
"BTCUSDT".to_string(),
"BTC/USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
);
let url = binance.ws_endpoint(&market);
assert!(url.contains("testnet"));
}
#[test]
fn test_sandbox_rest_endpoint_linear_swap() {
let binance = create_sandbox_binance();
let market = Market::new_swap(
"BTCUSDT".to_string(),
"BTC/USDT:USDT".to_string(),
"BTC".to_string(),
"USDT".to_string(),
"USDT".to_string(),
dec!(1.0),
);
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("testnet"));
}
#[test]
fn test_swap_defaults_to_linear_when_not_specified() {
let binance = create_test_binance();
let market = Market {
market_type: MarketType::Swap,
..Default::default()
};
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("fapi.binance.com"));
}
#[test]
fn test_futures_defaults_to_linear_when_not_specified() {
let binance = create_test_binance();
let market = Market {
market_type: MarketType::Futures,
..Default::default()
};
let url = binance.rest_endpoint(&market, EndpointType::Public);
assert!(url.contains("fapi.binance.com"));
}
}