use crate::core::types::AccountType;
#[derive(Debug, Clone)]
pub struct UpbitUrls {
pub rest: &'static str,
pub ws: &'static str,
}
impl UpbitUrls {
pub const SINGAPORE: Self = Self {
rest: "https://sg-api.upbit.com",
ws: "wss://sg-api.upbit.com/websocket/v1",
};
pub const INDONESIA: Self = Self {
rest: "https://id-api.upbit.com",
ws: "wss://id-api.upbit.com/websocket/v1",
};
pub const THAILAND: Self = Self {
rest: "https://th-api.upbit.com",
ws: "wss://th-api.upbit.com/websocket/v1",
};
pub const KOREA: Self = Self {
rest: "https://api.upbit.com",
ws: "wss://api.upbit.com/websocket/v1",
};
pub const DEFAULT: Self = Self::KOREA;
pub fn rest_url(&self, _account_type: AccountType) -> &str {
self.rest
}
pub fn ws_url(&self) -> &str {
self.ws
}
pub fn ws_private_url(&self) -> String {
format!("{}/private", self.ws)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UpbitEndpoint {
TradingPairs,
CandlesMinutes, CandlesDays,
CandlesWeeks,
CandlesMonths,
CandlesYears,
CandlesSeconds,
Tickers,
TickersQuote,
Orderbook,
OrderbookInstruments,
RecentTrades,
OrderInfo,
CreateOrder,
TestOrder,
GetOrder,
ListOrders,
CancelOrder,
BatchCancelOrders,
ReplaceOrder,
Balances,
DepositInfo,
ListDepositAddresses,
CreateDepositAddress,
ListDeposits,
WithdrawalInfo,
ListWithdrawalAddresses,
InitiateWithdrawal,
ListWithdrawals,
ClosedOrders,
OrderChance,
OpenOrders,
WalletStatus,
WithdrawKrw,
CancelWithdraw,
}
impl UpbitEndpoint {
pub fn path(&self) -> &'static str {
match self {
Self::TradingPairs => "/v1/market/all",
Self::CandlesMinutes => "/v1/candles/minutes", Self::CandlesDays => "/v1/candles/days",
Self::CandlesWeeks => "/v1/candles/weeks",
Self::CandlesMonths => "/v1/candles/months",
Self::CandlesYears => "/v1/candles/years",
Self::CandlesSeconds => "/v1/candles/seconds",
Self::Tickers => "/v1/ticker",
Self::TickersQuote => "/v1/ticker",
Self::Orderbook => "/v1/orderbook",
Self::OrderbookInstruments => "/v1/orderbook",
Self::RecentTrades => "/v1/trades/ticks",
Self::OrderInfo => "/v1/order",
Self::CreateOrder => "/v1/orders",
Self::TestOrder => "/v1/orders",
Self::GetOrder => "/v1/order",
Self::ListOrders => "/v1/orders",
Self::CancelOrder => "/v1/order",
Self::BatchCancelOrders => "/v1/orders",
Self::ReplaceOrder => "/v1/orders/cancel_and_new",
Self::Balances => "/v1/accounts",
Self::DepositInfo => "/v1/deposit",
Self::ListDepositAddresses => "/v1/deposits/coin_addresses",
Self::CreateDepositAddress => "/v1/deposits/generate_coin_address",
Self::ListDeposits => "/v1/deposits",
Self::WithdrawalInfo => "/v1/withdrawal",
Self::ListWithdrawalAddresses => "/v1/withdraws/coin_addresses",
Self::InitiateWithdrawal => "/v1/withdraws/coin",
Self::ListWithdrawals => "/v1/withdraws",
Self::ClosedOrders => "/v1/orders/closed",
Self::OrderChance => "/v1/orders/chance",
Self::OpenOrders => "/v1/orders/open",
Self::WalletStatus => "/v1/status/wallet",
Self::WithdrawKrw => "/v1/withdraws/krw",
Self::CancelWithdraw => "/v1/withdraws/uuid",
}
}
pub fn requires_auth(&self) -> bool {
match self {
Self::TradingPairs
| Self::CandlesMinutes
| Self::CandlesDays
| Self::CandlesWeeks
| Self::CandlesMonths
| Self::CandlesYears
| Self::CandlesSeconds
| Self::Tickers
| Self::TickersQuote
| Self::Orderbook
| Self::OrderbookInstruments
| Self::RecentTrades => false,
_ => true,
}
}
pub fn method(&self) -> &'static str {
match self {
Self::CreateOrder
| Self::TestOrder
| Self::ReplaceOrder
| Self::CreateDepositAddress
| Self::InitiateWithdrawal
| Self::WithdrawKrw => "POST",
Self::CancelOrder
| Self::BatchCancelOrders
| Self::CancelWithdraw => "DELETE",
_ => "GET",
}
}
}
#[allow(dead_code)]
pub fn parse_symbol(symbol: &str) -> Option<(String, String)> {
let parts: Vec<&str> = symbol.split('-').collect();
if parts.len() != 2 {
return None;
}
let quote = parts[0].to_string();
let base = parts[1].to_string();
Some((base, quote))
}
pub fn map_kline_interval(interval: &str) -> (UpbitEndpoint, Option<u32>) {
match interval {
"1m" => (UpbitEndpoint::CandlesMinutes, Some(1)),
"3m" => (UpbitEndpoint::CandlesMinutes, Some(3)),
"5m" => (UpbitEndpoint::CandlesMinutes, Some(5)),
"10m" => (UpbitEndpoint::CandlesMinutes, Some(10)),
"15m" => (UpbitEndpoint::CandlesMinutes, Some(15)),
"30m" => (UpbitEndpoint::CandlesMinutes, Some(30)),
"1h" => (UpbitEndpoint::CandlesMinutes, Some(60)),
"4h" => (UpbitEndpoint::CandlesMinutes, Some(240)),
"1d" => (UpbitEndpoint::CandlesDays, None),
"1w" => (UpbitEndpoint::CandlesWeeks, None),
"1M" => (UpbitEndpoint::CandlesMonths, None),
_ => (UpbitEndpoint::CandlesMinutes, Some(60)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_symbol() {
let (base, quote) = parse_symbol("SGD-BTC").unwrap();
assert_eq!(base, "BTC");
assert_eq!(quote, "SGD");
let (base, quote) = parse_symbol("KRW-ETH").unwrap();
assert_eq!(base, "ETH");
assert_eq!(quote, "KRW");
assert!(parse_symbol("INVALID").is_none());
assert!(parse_symbol("TOO-MANY-PARTS").is_none());
}
#[test]
fn test_map_kline_interval() {
let (endpoint, unit) = map_kline_interval("1m");
assert_eq!(endpoint, UpbitEndpoint::CandlesMinutes);
assert_eq!(unit, Some(1));
let (endpoint, unit) = map_kline_interval("1h");
assert_eq!(endpoint, UpbitEndpoint::CandlesMinutes);
assert_eq!(unit, Some(60));
let (endpoint, unit) = map_kline_interval("1d");
assert_eq!(endpoint, UpbitEndpoint::CandlesDays);
assert_eq!(unit, None);
}
}