use crate::core::types::AccountType;
#[derive(Debug, Clone)]
pub struct GeminiUrls {
pub rest: &'static str,
pub ws_market: &'static str,
pub ws_orders: &'static str,
}
impl GeminiUrls {
pub const MAINNET: Self = Self {
rest: "https://api.gemini.com",
ws_market: "wss://api.gemini.com/v2/marketdata",
ws_orders: "wss://api.gemini.com/v1/order/events",
};
pub const TESTNET: Self = Self {
rest: "https://api.sandbox.gemini.com",
ws_market: "wss://api.sandbox.gemini.com/v2/marketdata",
ws_orders: "wss://api.sandbox.gemini.com/v1/order/events",
};
pub fn rest_url(&self, _account_type: AccountType) -> &str {
self.rest
}
pub fn ws_market_url(&self) -> &str {
self.ws_market
}
pub fn ws_orders_url(&self) -> &str {
self.ws_orders
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GeminiEndpoint {
Symbols,
SymbolDetails,
Ticker,
TickerV2,
OrderBook,
Trades,
Candles,
DerivativeCandles,
PriceFeed,
NetworkInfo,
FundingAmount,
FeePromos,
RiskStats,
NewOrder,
CancelOrder,
CancelAllOrders,
CancelSessionOrders,
OrderStatus,
ActiveOrders,
PastTrades,
TradingVolume,
NotionalVolume,
WrapOrder,
Balances,
NotionalBalances,
StakingBalances,
DepositAddresses,
NewDepositAddress,
Withdraw,
WithdrawFeeEstimate,
Transfers,
AccountTransfer,
Transactions,
PaymentMethods,
AccountDetail,
Positions,
Margin,
MarginAccount,
MarginRates,
MarginOrderPreview,
FundingPayments,
FundingPaymentReport,
}
impl GeminiEndpoint {
pub fn path(&self) -> &'static str {
match self {
Self::Symbols => "/v1/symbols",
Self::SymbolDetails => "/v1/symbols/details/{symbol}",
Self::Ticker => "/v1/pubticker/{symbol}",
Self::TickerV2 => "/v2/ticker/{symbol}",
Self::OrderBook => "/v1/book/{symbol}",
Self::Trades => "/v1/trades/{symbol}",
Self::Candles => "/v2/candles/{symbol}/{time_frame}",
Self::DerivativeCandles => "/v2/derivatives/candles/{symbol}/{time_frame}",
Self::PriceFeed => "/v1/pricefeed",
Self::NetworkInfo => "/v1/network/{token}",
Self::FundingAmount => "/v1/fundingamount/{symbol}",
Self::FeePromos => "/v1/feepromos",
Self::RiskStats => "/v1/riskstats/{symbol}",
Self::NewOrder => "/v1/order/new",
Self::CancelOrder => "/v1/order/cancel",
Self::CancelAllOrders => "/v1/order/cancel/all",
Self::CancelSessionOrders => "/v1/order/cancel/session",
Self::OrderStatus => "/v1/order/status",
Self::ActiveOrders => "/v1/orders",
Self::PastTrades => "/v1/mytrades",
Self::TradingVolume => "/v1/tradevolume",
Self::NotionalVolume => "/v1/notionalvolume",
Self::WrapOrder => "/v1/wrap/{symbol}",
Self::Balances => "/v1/balances",
Self::NotionalBalances => "/v1/notionalbalances/{currency}",
Self::StakingBalances => "/v1/balances/staking",
Self::DepositAddresses => "/v1/addresses/{network}",
Self::NewDepositAddress => "/v1/deposit/{network}/newAddress",
Self::Withdraw => "/v1/withdraw/{currency}",
Self::WithdrawFeeEstimate => "/v1/withdraw/{currency}/feeEstimate",
Self::Transfers => "/v1/transfers",
Self::AccountTransfer => "/v1/account/transfer/{currency}",
Self::Transactions => "/v1/transactions",
Self::PaymentMethods => "/v1/payments/methods",
Self::AccountDetail => "/v1/account",
Self::Positions => "/v1/positions",
Self::Margin => "/v1/margin",
Self::MarginAccount => "/v1/margin/account",
Self::MarginRates => "/v1/margin/rates",
Self::MarginOrderPreview => "/v1/margin/order/preview",
Self::FundingPayments => "/v1/perpetuals/fundingPayment",
Self::FundingPaymentReport => "/v1/perpetuals/fundingpaymentreport/records.json",
}
}
pub fn requires_auth(&self) -> bool {
match self {
Self::Symbols
| Self::SymbolDetails
| Self::Ticker
| Self::TickerV2
| Self::OrderBook
| Self::Trades
| Self::Candles
| Self::DerivativeCandles
| Self::PriceFeed
| Self::NetworkInfo
| Self::FundingAmount
| Self::FeePromos
| Self::RiskStats => false,
_ => true,
}
}
pub fn method(&self) -> &'static str {
match self {
Self::NewOrder
| Self::CancelOrder
| Self::CancelAllOrders
| Self::CancelSessionOrders
| Self::OrderStatus
| Self::ActiveOrders
| Self::PastTrades
| Self::TradingVolume
| Self::NotionalVolume
| Self::WrapOrder
| Self::Balances
| Self::NotionalBalances
| Self::StakingBalances
| Self::DepositAddresses
| Self::NewDepositAddress
| Self::Withdraw
| Self::WithdrawFeeEstimate
| Self::Transfers
| Self::AccountTransfer
| Self::Transactions
| Self::PaymentMethods
| Self::AccountDetail
| Self::Positions
| Self::Margin
| Self::MarginAccount
| Self::MarginRates
| Self::MarginOrderPreview
| Self::FundingPayments
| Self::FundingPaymentReport => "POST",
_ => "GET",
}
}
}
pub fn format_symbol(base: &str, quote: &str, account_type: AccountType) -> String {
match account_type {
AccountType::Spot | AccountType::Margin => {
format!("{}{}", base.to_lowercase(), quote.to_lowercase())
}
AccountType::FuturesCross | AccountType::FuturesIsolated => {
format!("{}gusdperp", base.to_lowercase())
}
_ => {
format!("{}{}", base.to_lowercase(), quote.to_lowercase())
}
}
}
pub fn normalize_symbol(symbol: &str) -> String {
symbol.to_lowercase()
}
pub fn _is_perpetual(symbol: &str) -> bool {
symbol.to_lowercase().ends_with("perp")
}
pub fn map_kline_interval(interval: &str) -> &'static str {
match interval {
"1m" => "1m",
"5m" => "5m",
"15m" => "15m",
"30m" => "30m",
"1h" => "1hr",
"6h" => "6hr",
"1d" => "1day",
_ => "1hr", }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_symbol_spot() {
let symbol = format_symbol("BTC", "USD", AccountType::Spot);
assert_eq!(symbol, "btcusd");
let symbol = format_symbol("ETH", "USD", AccountType::Spot);
assert_eq!(symbol, "ethusd");
let symbol = format_symbol("ETH", "BTC", AccountType::Spot);
assert_eq!(symbol, "ethbtc");
}
#[test]
fn test_format_symbol_perpetual() {
let symbol = format_symbol("BTC", "USD", AccountType::FuturesCross);
assert_eq!(symbol, "btcgusdperp");
let symbol = format_symbol("ETH", "USD", AccountType::FuturesIsolated);
assert_eq!(symbol, "ethgusdperp");
}
#[test]
fn test_normalize_symbol() {
assert_eq!(normalize_symbol("BTCUSD"), "btcusd");
assert_eq!(normalize_symbol("btcusd"), "btcusd");
assert_eq!(normalize_symbol("BtCuSd"), "btcusd");
}
#[test]
fn test_is_perpetual() {
assert!(_is_perpetual("btcgusdperp"));
assert!(_is_perpetual("BTCGUSDPERP"));
assert!(!_is_perpetual("btcusd"));
assert!(!_is_perpetual("ethusd"));
}
#[test]
fn test_map_kline_interval() {
assert_eq!(map_kline_interval("1m"), "1m");
assert_eq!(map_kline_interval("5m"), "5m");
assert_eq!(map_kline_interval("1h"), "1hr");
assert_eq!(map_kline_interval("6h"), "6hr");
assert_eq!(map_kline_interval("1d"), "1day");
}
}