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 },
AllTickers,
AllMarkPrices,
AllBookTickers,
AllForceOrders,
}
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 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
}
}
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::AllTickers => write!(f, "!ticker@arr"),
Topic::AllMarkPrices => write!(f, "!markPrice@arr"),
Topic::AllBookTickers => write!(f, "!bookTicker@arr"),
Topic::AllForceOrders => write!(f, "!forceOrder@arr"),
}
}
}
impl From<Topic> for String {
fn from(topic: Topic) -> Self {
topic.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agg_trade() {
assert_eq!(Topic::agg_trade("BTC-USD").to_string(), "BTC-USD@aggTrade");
}
#[test]
fn test_depth() {
assert_eq!(Topic::depth("BTC-USD", OrderbookDepth::D5).to_string(), "BTC-USD@depth5");
assert_eq!(Topic::depth("BTC-USD", OrderbookDepth::D10).to_string(), "BTC-USD@depth10");
assert_eq!(Topic::depth("BTC-USD", OrderbookDepth::D20).to_string(), "BTC-USD@depth20");
}
#[test]
fn test_book_ticker() {
assert_eq!(Topic::book_ticker("ETH-USD").to_string(), "ETH-USD@bookTicker");
}
#[test]
fn test_mark_price() {
assert_eq!(Topic::mark_price("SOL-USD").to_string(), "SOL-USD@markPrice");
}
#[test]
fn test_kline() {
assert_eq!(Topic::kline("BTC-USD", KlineInterval::M1).to_string(), "BTC-USD@kline_1m");
assert_eq!(Topic::kline("BTC-USD", KlineInterval::H4).to_string(), "BTC-USD@kline_4h");
assert_eq!(Topic::kline("BTC-USD", KlineInterval::D1).to_string(), "BTC-USD@kline_1d");
}
#[test]
fn test_force_order() {
assert_eq!(Topic::force_order("BTC-USD").to_string(), "BTC-USD@forceOrder");
}
#[test]
fn test_all_streams() {
assert_eq!(Topic::all_tickers().to_string(), "!ticker@arr");
assert_eq!(Topic::all_mark_prices().to_string(), "!markPrice@arr");
assert_eq!(Topic::all_book_tickers().to_string(), "!bookTicker@arr");
assert_eq!(Topic::all_force_orders().to_string(), "!forceOrder@arr");
}
#[test]
fn test_into_string() {
let topic = Topic::agg_trade("BTC-USD");
let s: String = topic.into();
assert_eq!(s, "BTC-USD@aggTrade");
}
}