use serde::Deserialize;
use crate::client::http::{ApiError, Call, Transport, read};
use crate::markets::listed;
use crate::utils::dec::{Dec, Round};
use crate::utils::figure::Figure;
use crate::utils::ids::whole;
pub const STAT_FIGURES: u32 = 12;
#[must_use]
pub fn stat_value(figure: &Figure) -> Option<Dec> {
let value = figure.to_dec()?;
Some(value.round_sig(STAT_FIGURES, Round::Nearest))
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct MarketStats {
pub symbol: String,
pub last_trade_price: Figure,
#[serde(deserialize_with = "whole")]
pub daily_trades_count: u64,
pub daily_base_token_volume: Figure,
pub daily_quote_token_volume: Figure,
pub daily_price_change: Figure,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct ExchangeStats {
#[serde(deserialize_with = "whole")]
pub total: u64,
#[serde(default, deserialize_with = "listed")]
pub order_book_stats: Vec<MarketStats>,
pub daily_usd_volume: Figure,
#[serde(deserialize_with = "whole")]
pub daily_trades_count: u64,
}
#[must_use]
pub fn exchange_stats_call() -> Call {
Call::get("exchangeStats")
}
pub async fn exchange_stats(transport: &impl Transport) -> Result<ExchangeStats, ApiError> {
read(transport, exchange_stats_call()).await
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Period {
Hour,
Day,
Week,
Month,
Quarter,
Year,
All,
}
impl Period {
#[must_use]
pub const fn wire(self) -> &'static str {
match self {
Period::Hour => "h",
Period::Day => "d",
Period::Week => "w",
Period::Month => "m",
Period::Quarter => "q",
Period::Year => "y",
Period::All => "all",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetricKind {
Volume,
MakerFee,
TakerFee,
LiquidationFee,
TradeCount,
LiquidationCount,
LiquidationVolume,
Inflow,
Outflow,
TransferFee,
WithdrawFee,
OpenInterest,
AccountCount,
ActiveAccountCount,
Tps,
Buyback,
BuybackUsdc,
}
impl MetricKind {
#[must_use]
pub const fn wire(self) -> &'static str {
match self {
MetricKind::Volume => "volume",
MetricKind::MakerFee => "maker_fee",
MetricKind::TakerFee => "taker_fee",
MetricKind::LiquidationFee => "liquidation_fee",
MetricKind::TradeCount => "trade_count",
MetricKind::LiquidationCount => "liquidation_count",
MetricKind::LiquidationVolume => "liquidation_volume",
MetricKind::Inflow => "inflow",
MetricKind::Outflow => "outflow",
MetricKind::TransferFee => "transfer_fee",
MetricKind::WithdrawFee => "withdraw_fee",
MetricKind::OpenInterest => "open_interest",
MetricKind::AccountCount => "account_count",
MetricKind::ActiveAccountCount => "active_account_count",
MetricKind::Tps => "tps",
MetricKind::Buyback => "buyback",
MetricKind::BuybackUsdc => "buyback_usdc",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Metric {
#[serde(deserialize_with = "whole")]
pub timestamp: u64,
pub data: Figure,
}
#[derive(Deserialize)]
struct Metrics {
#[serde(default, deserialize_with = "listed")]
metrics: Vec<Metric>,
}
#[must_use]
pub fn exchange_metrics_call(period: Period, kind: MetricKind, market: Option<&str>) -> Call {
let call = Call::get("exchangeMetrics").field("period", period.wire()).field("kind", kind.wire());
match market {
Some(symbol) => call.field("filter", "byMarket").field("value", symbol),
None => call,
}
}
pub async fn exchange_metrics(
transport: &impl Transport,
period: Period,
kind: MetricKind,
market: Option<&str>,
) -> Result<Vec<Metric>, ApiError> {
Ok(read::<Metrics>(transport, exchange_metrics_call(period, kind, market)).await?.metrics)
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Slippage {
pub exchange: String,
pub market: String,
#[serde(deserialize_with = "whole")]
pub size_usd: u64,
pub avg_slippage: Figure,
#[serde(deserialize_with = "whole")]
pub data_count: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct ExecuteStat {
#[serde(deserialize_with = "whole")]
pub timestamp: u64,
#[serde(default, deserialize_with = "listed")]
pub slippage: Vec<Slippage>,
}
#[derive(Deserialize)]
struct ExecuteStats {
#[serde(default, deserialize_with = "listed")]
result: Vec<ExecuteStat>,
}
#[must_use]
pub fn execute_stats_call(period: Period) -> Call {
Call::get("executeStats").field("period", period.wire())
}
pub async fn execute_stats(transport: &impl Transport, period: Period) -> Result<Vec<ExecuteStat>, ApiError> {
Ok(read::<ExecuteStats>(transport, execute_stats_call(period)).await?.result)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum FeeFilter {
#[default]
All,
Perps,
Spot,
BinaryOptions,
}
impl FeeFilter {
#[must_use]
pub const fn wire(self) -> &'static str {
match self {
FeeFilter::All => "all",
FeeFilter::Perps => "perps",
FeeFilter::Spot => "spot",
FeeFilter::BinaryOptions => "bo",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct UserFeeTier {
pub name: String,
pub level: u32,
pub maker_fee_ticks: u32,
pub taker_fee_ticks: u32,
#[serde(deserialize_with = "whole")]
pub required_lit_stakes: u64,
#[serde(deserialize_with = "whole")]
pub required_volume: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct BinaryFeeTier {
pub name: String,
#[serde(deserialize_with = "whole")]
pub required_volume: u64,
pub fee_rate: Figure,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct StakingFeeTier {
pub name: String,
#[serde(deserialize_with = "whole")]
pub required_lit_stakes: u64,
pub fee_rate: Figure,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
pub struct FeeTiers {
#[serde(default, deserialize_with = "listed")]
pub user_tiers: Vec<UserFeeTier>,
#[serde(default, deserialize_with = "listed")]
pub bo_tiers: Vec<BinaryFeeTier>,
#[serde(default, deserialize_with = "listed")]
pub staking_tiers: Vec<StakingFeeTier>,
}
#[must_use]
pub fn fee_tiers_call(filter: FeeFilter) -> Call {
Call::get("feeTiers").field("filter", filter.wire())
}
pub async fn fee_tiers(transport: &impl Transport, filter: FeeFilter) -> Result<FeeTiers, ApiError> {
read(transport, fee_tiers_call(filter)).await
}
#[cfg(test)]
#[path = "tests/stats.rs"]
mod tests;