Skip to main content

pragma_common/exchange/
mod.rs

1use crate::{
2    pair::{AssetSymbol, RawMarketName},
3    Pair,
4};
5
6pub mod margin_type;
7pub use margin_type::MarginType;
8
9#[derive(
10    Clone,
11    Debug,
12    Hash,
13    PartialEq,
14    PartialOrd,
15    Ord,
16    Eq,
17    Copy,
18    strum::EnumString,
19    strum::Display,
20    strum::EnumIter,
21)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(
24    feature = "borsh",
25    derive(borsh::BorshSerialize, borsh::BorshDeserialize)
26)]
27#[strum(ascii_case_insensitive, serialize_all = "UPPERCASE")]
28#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
29#[non_exhaustive]
30pub enum Exchange {
31    Hyperliquid,
32    Paradex,
33    Kraken,
34    Lmax,
35    Extended,
36}
37
38impl Exchange {
39    // TODO: add instrument type argument ?
40    /// Returns the market name for the market `pair`
41    /// Both base and quote assets are taken into account in the returned market name
42    pub fn market_name_from_pair(&self, pair: &Pair) -> RawMarketName {
43        match self {
44            Exchange::Hyperliquid => pair.base.to_string(),
45            Exchange::Paradex => format!("{}-{}-PERP", pair.base, pair.quote),
46            Exchange::Kraken => match pair.base.as_str() {
47                "BTC" => "PF_XBTUSD".to_string(),
48                other => format!("PF_{}{}", other, pair.quote),
49            },
50            Exchange::Lmax | Exchange::Extended => format!("{}-{}", pair.base, pair.quote),
51        }
52    }
53
54    /// Returns the market name for the market `asset_symbol` with the quote asset being USD
55    pub fn usd_market_name_from_asset_symbol(&self, asset_symbol: &AssetSymbol) -> RawMarketName {
56        match self {
57            Exchange::Hyperliquid => asset_symbol.to_string(),
58            Exchange::Paradex => format!("{asset_symbol}-USD-PERP"),
59            Exchange::Kraken => match asset_symbol.to_string().to_uppercase().as_str() {
60                "BTC" => "PF_XBTUSD".to_string(),
61                other => format!("PF_{other}USD"),
62            },
63            Exchange::Lmax | Exchange::Extended => format!("{asset_symbol}-USD"),
64        }
65    }
66
67    pub fn asset_symbol_from_raw_market_name(&self, market_name: &RawMarketName) -> AssetSymbol {
68        match self {
69            Exchange::Hyperliquid => AssetSymbol::from(market_name),
70            Exchange::Paradex | Exchange::Lmax | Exchange::Extended => {
71                market_name.split('-').next().unwrap().into()
72            }
73            Exchange::Kraken => {
74                if market_name.starts_with("PF_") && market_name.ends_with("USD") {
75                    let base_part = &market_name[3..market_name.len() - 3];
76                    match base_part {
77                        "XBT" => "BTC".into(),
78                        other => other.into(),
79                    }
80                } else {
81                    market_name.split('/').next().unwrap().into()
82                }
83            }
84        }
85    }
86
87    /// Returns the taker fees as a percentage
88    /// e.g 0.00045 = 0.045%
89    pub const fn taker_fees_rate(&self) -> f64 {
90        match self {
91            // TODO: make this configurable as they have tiers
92            Exchange::Hyperliquid => 0.00045, // 0.045% https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees
93            Exchange::Paradex => 0.0003, // 0.03% https://docs.paradex.trade/documentation/trading/trading-fees
94            Exchange::Kraken => 0.0002,  // 0.02% https://www.kraken.com/features/fee-schedule
95            Exchange::Extended => 0.00025, // 0.025% https://docs.extended.exchange/extended-resources/trading/trading-fees-and-rebates
96            _ => todo!(),
97        }
98    }
99
100    /// Whether the exchange has some kind of set leverage endpoint
101    pub const fn supports_leverage(&self) -> bool {
102        match self {
103            Exchange::Hyperliquid => true,
104            Exchange::Paradex => true,
105            Exchange::Kraken => false,
106            Exchange::Extended => true,
107            Exchange::Lmax => false,
108        }
109    }
110
111    pub const fn from_str_const(s: &str) -> Option<Self> {
112        match s.as_bytes() {
113            b"Lmax" | b"lmax" | b"LMAX" => Some(Exchange::Lmax),
114            b"Extended" | b"extended" | b"EXTENDED" => Some(Exchange::Extended),
115            b"Hyperliquid" | b"hyperliquid" | b"HYPERLIQUID" => Some(Exchange::Hyperliquid),
116            b"Paradex" | b"paradex" | b"PARADEX" => Some(Exchange::Paradex),
117            b"Kraken" | b"kraken" | b"KRAKEN" => Some(Exchange::Kraken),
118            _ => None,
119        }
120    }
121}