use crate::core::types::AccountType;
#[derive(Debug, Clone)]
pub struct CryptoComUrls {
pub rest: &'static str,
pub ws_user: &'static str,
pub ws_market: &'static str,
}
impl CryptoComUrls {
pub const MAINNET: Self = Self {
rest: "https://api.crypto.com/exchange/v1",
ws_user: "wss://stream.crypto.com/exchange/v1/user",
ws_market: "wss://stream.crypto.com/exchange/v1/market",
};
pub const TESTNET: Self = Self {
rest: "https://uat-api.3ona.co/exchange/v1",
ws_user: "wss://uat-stream.3ona.co/exchange/v1/user",
ws_market: "wss://uat-stream.3ona.co/exchange/v1/market",
};
pub fn rest_url(&self) -> &str {
self.rest
}
pub fn ws_user_url(&self) -> &str {
self.ws_user
}
pub fn ws_market_url(&self) -> &str {
self.ws_market
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CryptoComEndpoint {
GetInstruments,
GetBook,
GetCandlestick,
GetTrades,
GetTickers,
GetValuations,
CreateOrder,
CreateOrderList,
CancelOrderList,
AmendOrder,
CancelOrder,
CancelAllOrders,
GetOpenOrders,
GetOrderDetail,
GetOrderHistory,
GetUserTrades,
AdvancedCreateOrder,
AdvancedCreateOco,
AdvancedCreateOto,
AdvancedCreateOtoco,
UserBalance,
GetAccounts,
GetFeeRate,
GetInstrumentFeeRate,
GetTransactions,
GetPositions,
ClosePosition,
ChangeAccountLeverage,
ChangeIsolatedMarginLeverage,
GetDepositAddress,
CreateWithdrawal,
GetDepositHistory,
GetWithdrawalHistory,
SubAccountCreate,
SubAccountList,
SubAccountTransfer,
SubAccountGetBalances,
WsAuth,
WsHeartbeat,
}
impl CryptoComEndpoint {
pub fn method(&self) -> &'static str {
match self {
Self::GetInstruments => "public/get-instruments",
Self::GetBook => "public/get-book",
Self::GetCandlestick => "public/get-candlestick",
Self::GetTrades => "public/get-trades",
Self::GetTickers => "public/get-tickers",
Self::GetValuations => "public/get-valuations",
Self::CreateOrder => "private/create-order",
Self::CreateOrderList => "private/create-order-list",
Self::CancelOrderList => "private/cancel-order-list",
Self::AmendOrder => "private/amend-order",
Self::CancelOrder => "private/cancel-order",
Self::CancelAllOrders => "private/cancel-all-orders",
Self::GetOpenOrders => "private/get-open-orders",
Self::GetOrderDetail => "private/get-order-detail",
Self::GetOrderHistory => "private/get-order-history",
Self::GetUserTrades => "private/get-trades",
Self::AdvancedCreateOrder => "private/advanced/create-order",
Self::AdvancedCreateOco => "private/advanced/create-oco",
Self::AdvancedCreateOto => "private/advanced/create-oto",
Self::AdvancedCreateOtoco => "private/advanced/create-otoco",
Self::UserBalance => "private/user-balance",
Self::GetAccounts => "private/get-accounts",
Self::GetFeeRate => "private/get-fee-rate",
Self::GetInstrumentFeeRate => "private/get-instrument-fee-rate",
Self::GetTransactions => "private/get-transactions",
Self::GetPositions => "private/get-positions",
Self::ClosePosition => "private/close-position",
Self::ChangeAccountLeverage => "private/change-account-leverage",
Self::ChangeIsolatedMarginLeverage => "private/change-isolated-margin-leverage",
Self::GetDepositAddress => "private/get-deposit-address",
Self::CreateWithdrawal => "private/create-withdrawal",
Self::GetDepositHistory => "private/get-deposit-history",
Self::GetWithdrawalHistory => "private/get-withdrawal-history",
Self::SubAccountCreate => "private/subaccount/create",
Self::SubAccountList => "private/subaccount/get-subaccounts",
Self::SubAccountTransfer => "private/subaccount/transfer",
Self::SubAccountGetBalances => "private/subaccount/get-balances",
Self::WsAuth => "public/auth",
Self::WsHeartbeat => "public/respond-heartbeat",
}
}
pub fn requires_auth(&self) -> bool {
self.method().starts_with("private/")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstrumentType {
Spot,
Perpetual,
Futures,
Index,
}
pub fn format_symbol(base: &str, quote: &str, instrument_type: InstrumentType) -> String {
let base_upper = base.to_uppercase();
let quote_upper = quote.to_uppercase();
match instrument_type {
InstrumentType::Spot => {
format!("{}_{}", base_upper, quote_upper)
}
InstrumentType::Perpetual => {
format!("{}{}-PERP", base_upper, quote_upper)
}
InstrumentType::Futures => {
unreachable!("Futures symbols require expiry date. Use format_futures_symbol() instead.")
}
InstrumentType::Index => {
format!("{}{}-INDEX", base_upper, quote_upper)
}
}
}
pub fn _format_futures_symbol(base: &str, quote: &str, expiry: &str) -> String {
format!("{}{}-{}", base.to_uppercase(), quote.to_uppercase(), expiry)
}
pub fn account_type_to_instrument(account_type: AccountType) -> InstrumentType {
match account_type {
AccountType::Spot | AccountType::Margin => InstrumentType::Spot,
AccountType::FuturesCross | AccountType::FuturesIsolated => InstrumentType::Perpetual,
AccountType::Earn | AccountType::Lending
| AccountType::Convert => InstrumentType::Spot,
AccountType::Options => InstrumentType::Perpetual,
}
}
pub fn map_kline_interval(interval: &str) -> &'static str {
match interval {
"1m" => "1m",
"5m" => "5m",
"15m" => "15m",
"30m" => "30m",
"1h" => "1h",
"2h" => "2h",
"4h" => "4h",
"12h" => "12h",
"1d" => "1D",
"1w" => "7D",
"1M" => "1M",
_ => "1h", }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spot_symbol_format() {
assert_eq!(
format_symbol("btc", "usdt", InstrumentType::Spot),
"BTC_USDT"
);
assert_eq!(
format_symbol("ETH", "USD", InstrumentType::Spot),
"ETH_USD"
);
}
#[test]
fn test_perpetual_symbol_format() {
assert_eq!(
format_symbol("btc", "usd", InstrumentType::Perpetual),
"BTCUSD-PERP"
);
assert_eq!(
format_symbol("ETH", "USDT", InstrumentType::Perpetual),
"ETHUSDT-PERP"
);
}
#[test]
fn test_index_symbol_format() {
assert_eq!(
format_symbol("btc", "usd", InstrumentType::Index),
"BTCUSD-INDEX"
);
}
#[test]
fn test_kline_interval_mapping() {
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"), "7D");
}
#[test]
fn test_method_names() {
assert_eq!(CryptoComEndpoint::GetInstruments.method(), "public/get-instruments");
assert_eq!(CryptoComEndpoint::CreateOrder.method(), "private/create-order");
assert_eq!(CryptoComEndpoint::UserBalance.method(), "private/user-balance");
}
#[test]
fn test_requires_auth() {
assert!(!CryptoComEndpoint::GetInstruments.requires_auth());
assert!(!CryptoComEndpoint::GetTickers.requires_auth());
assert!(CryptoComEndpoint::CreateOrder.requires_auth());
assert!(CryptoComEndpoint::UserBalance.requires_auth());
}
}