pub struct BitgetSymbolConverter;
impl BitgetSymbolConverter {
pub fn unified_to_exchange(symbol: &str) -> String {
let base_quote = if let Some(pos) = symbol.find(':') {
&symbol[..pos]
} else {
symbol
};
base_quote.replace('/', "")
}
pub fn product_type_from_symbol(symbol: &str) -> &'static str {
if let Some(pos) = symbol.find(':') {
let settle_part = &symbol[pos + 1..];
let settle = if let Some(dash_pos) = settle_part.find('-') {
&settle_part[..dash_pos]
} else {
settle_part
};
let base_quote = &symbol[..pos];
let quote = if let Some(slash_pos) = base_quote.find('/') {
&base_quote[slash_pos + 1..]
} else {
""
};
if settle == quote {
"USDT-FUTURES"
} else {
"COIN-FUTURES"
}
} else {
"spot"
}
}
pub fn is_contract(symbol: &str) -> bool {
symbol.contains(':')
}
pub fn is_spot(symbol: &str) -> bool {
!symbol.contains(':')
}
pub fn exchange_to_unified_hint(exchange_id: &str, product_type: &str) -> String {
let quote_currencies = ["USDT", "USDC", "USD", "BTC", "ETH"];
for quote in "e_currencies {
if exchange_id.ends_with(quote) && exchange_id.len() > quote.len() {
let base = &exchange_id[..exchange_id.len() - quote.len()];
return match product_type {
"USDT-FUTURES" | "usdt-futures" => {
format!("{}/{}:{}", base, quote, quote)
}
"COIN-FUTURES" | "coin-futures" => {
format!("{}/{}:{}", base, quote, base)
}
_ => {
format!("{}/{}", base, quote)
}
};
}
}
exchange_id.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spot_to_exchange() {
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("BTC/USDT"),
"BTCUSDT"
);
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("ETH/USDT"),
"ETHUSDT"
);
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("SOL/BTC"),
"SOLBTC"
);
}
#[test]
fn test_linear_swap_to_exchange() {
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("BTC/USDT:USDT"),
"BTCUSDT"
);
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("ETH/USDT:USDT"),
"ETHUSDT"
);
}
#[test]
fn test_inverse_swap_to_exchange() {
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("BTC/USD:BTC"),
"BTCUSD"
);
}
#[test]
fn test_futures_with_expiry_to_exchange() {
assert_eq!(
BitgetSymbolConverter::unified_to_exchange("BTC/USDT:USDT-241231"),
"BTCUSDT"
);
}
#[test]
fn test_product_type_spot() {
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("BTC/USDT"),
"spot"
);
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("ETH/BTC"),
"spot"
);
}
#[test]
fn test_product_type_linear() {
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("BTC/USDT:USDT"),
"USDT-FUTURES"
);
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("ETH/USDT:USDT"),
"USDT-FUTURES"
);
}
#[test]
fn test_product_type_inverse() {
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("BTC/USD:BTC"),
"COIN-FUTURES"
);
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("ETH/USD:ETH"),
"COIN-FUTURES"
);
}
#[test]
fn test_product_type_futures_with_expiry() {
assert_eq!(
BitgetSymbolConverter::product_type_from_symbol("BTC/USDT:USDT-241231"),
"USDT-FUTURES"
);
}
#[test]
fn test_is_contract() {
assert!(BitgetSymbolConverter::is_contract("BTC/USDT:USDT"));
assert!(BitgetSymbolConverter::is_contract("BTC/USD:BTC"));
assert!(!BitgetSymbolConverter::is_contract("BTC/USDT"));
}
#[test]
fn test_is_spot() {
assert!(BitgetSymbolConverter::is_spot("BTC/USDT"));
assert!(!BitgetSymbolConverter::is_spot("BTC/USDT:USDT"));
}
#[test]
fn test_exchange_to_unified_spot() {
assert_eq!(
BitgetSymbolConverter::exchange_to_unified_hint("BTCUSDT", "spot"),
"BTC/USDT"
);
}
#[test]
fn test_exchange_to_unified_linear() {
assert_eq!(
BitgetSymbolConverter::exchange_to_unified_hint("BTCUSDT", "USDT-FUTURES"),
"BTC/USDT:USDT"
);
}
#[test]
fn test_exchange_to_unified_inverse() {
assert_eq!(
BitgetSymbolConverter::exchange_to_unified_hint("BTCUSD", "COIN-FUTURES"),
"BTC/USD:BTC"
);
}
}