use crate::core::types::AccountType;
#[derive(Debug, Clone)]
pub struct BitfinexUrls {
pub public_rest: &'static str,
pub auth_rest: &'static str,
}
impl BitfinexUrls {
pub const MAINNET: Self = Self {
public_rest: "https://api-pub.bitfinex.com/v2",
auth_rest: "https://api.bitfinex.com/v2",
};
pub fn rest_url(&self, authenticated: bool) -> &str {
if authenticated {
self.auth_rest
} else {
self.public_rest
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BitfinexEndpoint {
PlatformStatus,
Ticker,
Tickers,
Orderbook,
Trades,
Candles,
Symbols,
SubmitOrder,
CancelOrder,
CancelMultipleOrders,
UpdateOrder,
OrderMulti,
Wallets,
ActiveOrders,
ActiveOrdersBySymbol,
OrderHistory,
OrderTrades,
TradeHistory,
TradeHistoryBySymbol,
Positions,
PositionHistory,
PositionSnapshot,
Transfer,
DepositAddress,
Withdraw,
Movements,
SubAccountList,
SubAccountTransfer,
LedgerHist,
}
impl BitfinexEndpoint {
pub fn path(&self) -> &'static str {
match self {
Self::PlatformStatus => "/platform/status",
Self::Ticker => "/ticker/{symbol}",
Self::Tickers => "/tickers",
Self::Orderbook => "/book/{symbol}/{precision}",
Self::Trades => "/trades/{symbol}/hist",
Self::Candles => "/candles/{candle}/hist",
Self::Symbols => "/conf/pub:list:pair:exchange",
Self::SubmitOrder => "/auth/w/order/submit",
Self::CancelOrder => "/auth/w/order/cancel",
Self::CancelMultipleOrders => "/auth/w/order/cancel/multi",
Self::UpdateOrder => "/auth/w/order/update",
Self::OrderMulti => "/auth/w/order/multi",
Self::Wallets => "/auth/r/wallets",
Self::ActiveOrders => "/auth/r/orders",
Self::ActiveOrdersBySymbol => "/auth/r/orders/{symbol}",
Self::OrderHistory => "/auth/r/orders/hist",
Self::OrderTrades => "/auth/r/order/{symbol}:{id}/trades",
Self::TradeHistory => "/auth/r/trades/hist",
Self::TradeHistoryBySymbol => "/auth/r/trades/{symbol}/hist",
Self::Positions => "/auth/r/positions",
Self::PositionHistory => "/auth/r/positions/hist",
Self::PositionSnapshot => "/auth/r/positions/snap",
Self::Transfer => "/auth/w/transfer",
Self::DepositAddress => "/auth/w/deposit/address",
Self::Withdraw => "/auth/w/withdraw",
Self::Movements => "/auth/r/movements/{symbol}/hist",
Self::SubAccountList => "/auth/r/sub_accounts/list",
Self::SubAccountTransfer => "/auth/w/sub_account/transfer",
Self::LedgerHist => "/auth/r/ledgers/{currency}/hist",
}
}
pub fn requires_auth(&self) -> bool {
match self {
Self::PlatformStatus
| Self::Ticker
| Self::Tickers
| Self::Orderbook
| Self::Trades
| Self::Candles
| Self::Symbols => false,
_ => true,
}
}
pub fn method(&self) -> &'static str {
match self {
Self::SubmitOrder
| Self::CancelOrder
| Self::CancelMultipleOrders
| Self::UpdateOrder
| Self::OrderMulti
| Self::Wallets
| Self::ActiveOrders
| Self::ActiveOrdersBySymbol
| Self::OrderHistory
| Self::OrderTrades
| Self::TradeHistory
| Self::TradeHistoryBySymbol
| Self::Positions
| Self::PositionHistory
| Self::PositionSnapshot
| Self::Transfer
| Self::DepositAddress
| Self::Withdraw
| Self::Movements
| Self::SubAccountList
| Self::SubAccountTransfer
| Self::LedgerHist => "POST",
_ => "GET",
}
}
}
pub fn format_symbol(base: &str, quote: &str, account_type: AccountType) -> String {
let base_up = base.to_uppercase();
let quote_up = quote.to_uppercase();
match account_type {
AccountType::Spot | AccountType::Margin => {
let quote_mapped = if quote_up == "USDT" { "USD" } else { "e_up };
format!("t{}{}", base_up, quote_mapped)
}
AccountType::FuturesCross | AccountType::FuturesIsolated => {
let quote_mapped = if quote_up == "USDT" { "UST" } else { "e_up };
format!("t{}F0:{}F0", base_up, quote_mapped)
}
_ => {
let quote_mapped = if quote_up == "USDT" { "USD" } else { "e_up };
format!("t{}{}", base_up, quote_mapped)
}
}
}
pub fn map_kline_interval(interval: &str) -> &'static str {
match interval {
"1m" => "1m",
"3m" => "3m",
"5m" => "5m",
"15m" => "15m",
"30m" => "30m",
"1h" => "1h",
"2h" => "2h",
"3h" => "3h",
"4h" => "4h",
"6h" => "6h",
"8h" => "8h",
"12h" => "12h",
"1d" => "1D",
"1w" => "1W",
"2w" => "14D",
"1M" => "1M",
_ => "1h", }
}
pub fn build_candle_key(symbol: &str, interval: &str) -> String {
format!("trade:{}:{}", map_kline_interval(interval), symbol)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_symbol_spot() {
let symbol = format_symbol("BTC", "USD", AccountType::Spot);
assert_eq!(symbol, "tBTCUSD");
let symbol = format_symbol("eth", "btc", AccountType::Spot);
assert_eq!(symbol, "tETHBTC");
}
#[test]
fn test_format_symbol_spot_usdt_mapped_to_usd() {
let symbol = format_symbol("BTC", "USDT", AccountType::Spot);
assert_eq!(symbol, "tBTCUSD");
let symbol = format_symbol("ETH", "USDT", AccountType::Margin);
assert_eq!(symbol, "tETHUSD");
}
#[test]
fn test_format_symbol_futures() {
let symbol = format_symbol("BTC", "UST", AccountType::FuturesCross);
assert_eq!(symbol, "tBTCF0:USTF0");
let symbol = format_symbol("ETH", "UST", AccountType::FuturesIsolated);
assert_eq!(symbol, "tETHF0:USTF0");
}
#[test]
fn test_format_symbol_futures_usdt_mapped_to_ust() {
let symbol = format_symbol("BTC", "USDT", AccountType::FuturesCross);
assert_eq!(symbol, "tBTCF0:USTF0");
}
#[test]
fn test_map_kline_interval() {
assert_eq!(map_kline_interval("1m"), "1m");
assert_eq!(map_kline_interval("1h"), "1h");
assert_eq!(map_kline_interval("1d"), "1D");
assert_eq!(map_kline_interval("1w"), "1W");
assert_eq!(map_kline_interval("1M"), "1M");
}
#[test]
fn test_build_candle_key() {
let key = build_candle_key("tBTCUSD", "1m");
assert_eq!(key, "trade:1m:tBTCUSD");
let key = build_candle_key("tETHUSD", "1d");
assert_eq!(key, "trade:1D:tETHUSD");
}
}