use std::fmt;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum OrderbookDepth {
D5,
#[default]
D10,
D20,
}
impl OrderbookDepth {
fn as_str(&self) -> &'static str {
match self {
OrderbookDepth::D5 => "5",
OrderbookDepth::D10 => "10",
OrderbookDepth::D20 => "20",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum KlineInterval {
M1,
M5,
M15,
M30,
H1,
H4,
D1,
}
impl KlineInterval {
fn as_str(&self) -> &'static str {
match self {
KlineInterval::M1 => "1m",
KlineInterval::M5 => "5m",
KlineInterval::M15 => "15m",
KlineInterval::M30 => "30m",
KlineInterval::H1 => "1h",
KlineInterval::H4 => "4h",
KlineInterval::D1 => "1d",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Topic {
AggTrade { symbol: String },
Depth {
symbol: String,
depth: OrderbookDepth,
},
BookTicker { symbol: String },
MarkPrice { symbol: String },
Kline {
symbol: String,
interval: KlineInterval,
},
ForceOrder { symbol: String },
UserOrders { address: String },
AllTickers,
AllMarkPrices,
AllBookTickers,
AllForceOrders,
AllMarketStatuses,
}
impl Topic {
pub fn agg_trade(symbol: impl Into<String>) -> Self {
Self::AggTrade {
symbol: symbol.into(),
}
}
pub fn depth(symbol: impl Into<String>, depth: OrderbookDepth) -> Self {
Self::Depth {
symbol: symbol.into(),
depth,
}
}
pub fn book_ticker(symbol: impl Into<String>) -> Self {
Self::BookTicker {
symbol: symbol.into(),
}
}
pub fn mark_price(symbol: impl Into<String>) -> Self {
Self::MarkPrice {
symbol: symbol.into(),
}
}
pub fn kline(symbol: impl Into<String>, interval: KlineInterval) -> Self {
Self::Kline {
symbol: symbol.into(),
interval,
}
}
pub fn force_order(symbol: impl Into<String>) -> Self {
Self::ForceOrder {
symbol: symbol.into(),
}
}
pub fn user_orders(address: impl Into<String>) -> Self {
Self::UserOrders {
address: address.into(),
}
}
pub fn all_tickers() -> Self {
Self::AllTickers
}
pub fn all_mark_prices() -> Self {
Self::AllMarkPrices
}
pub fn all_book_tickers() -> Self {
Self::AllBookTickers
}
pub fn all_force_orders() -> Self {
Self::AllForceOrders
}
pub fn all_market_statuses() -> Self {
Self::AllMarketStatuses
}
}
impl fmt::Display for Topic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Topic::AggTrade { symbol } => write!(f, "{symbol}@aggTrade"),
Topic::Depth { symbol, depth } => write!(f, "{symbol}@depth{}", depth.as_str()),
Topic::BookTicker { symbol } => write!(f, "{symbol}@bookTicker"),
Topic::MarkPrice { symbol } => write!(f, "{symbol}@markPrice"),
Topic::Kline { symbol, interval } => write!(f, "{symbol}@kline_{}", interval.as_str()),
Topic::ForceOrder { symbol } => write!(f, "{symbol}@forceOrder"),
Topic::UserOrders { address } => write!(f, "{address}@user.orders"),
Topic::AllTickers => write!(f, "!ticker@arr"),
Topic::AllMarkPrices => write!(f, "!markPrice@arr"),
Topic::AllBookTickers => write!(f, "!bookTicker@arr"),
Topic::AllForceOrders => write!(f, "!forceOrder@arr"),
Topic::AllMarketStatuses => write!(f, "!marketStatus@arr"),
}
}
}
impl From<Topic> for String {
fn from(topic: Topic) -> Self {
topic.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn topic_wire_format() {
for (topic, want) in [
(Topic::agg_trade("BTC-USD"), "BTC-USD@aggTrade"),
(
Topic::depth("BTC-USD", OrderbookDepth::D5),
"BTC-USD@depth5",
),
(
Topic::depth("BTC-USD", OrderbookDepth::D10),
"BTC-USD@depth10",
),
(
Topic::depth("BTC-USD", OrderbookDepth::D20),
"BTC-USD@depth20",
),
(Topic::book_ticker("ETH-USD"), "ETH-USD@bookTicker"),
(Topic::mark_price("SOL-USD"), "SOL-USD@markPrice"),
(
Topic::kline("BTC-USD", KlineInterval::M1),
"BTC-USD@kline_1m",
),
(
Topic::kline("BTC-USD", KlineInterval::H4),
"BTC-USD@kline_4h",
),
(
Topic::kline("BTC-USD", KlineInterval::D1),
"BTC-USD@kline_1d",
),
(Topic::force_order("BTC-USD"), "BTC-USD@forceOrder"),
(Topic::user_orders("0xabc"), "0xabc@user.orders"),
(Topic::all_tickers(), "!ticker@arr"),
(Topic::all_mark_prices(), "!markPrice@arr"),
(Topic::all_market_statuses(), "!marketStatus@arr"),
(Topic::all_book_tickers(), "!bookTicker@arr"),
(Topic::all_force_orders(), "!forceOrder@arr"),
] {
assert_eq!(topic.to_string(), want);
assert_eq!(String::from(topic), want);
}
}
}