crypto_contract_value/exchanges/
bitmex.rs

1use std::collections::{BTreeMap, HashMap};
2
3use super::utils::http_get;
4use crypto_market_type::MarketType;
5use once_cell::sync::Lazy;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9// key = market_type + pair
10static CONTRACT_VALUES: Lazy<HashMap<String, f64>> = Lazy::new(|| {
11    // offline data, in case the network is down
12    let mut m: HashMap<String, f64> = vec![
13        ("inverse_future.BTC/USD", 1.0),
14        ("inverse_future.ETH/USD", 1.0),
15        ("inverse_swap.BTC/EUR", 1.0),
16        ("inverse_swap.BTC/USD", 1.0),
17        ("inverse_swap.ETH/USD", 1.0),
18        ("inverse_swap.XBTETH/BTC", 0.01),
19        ("linear_future.ADA/BTC", 0.01),
20        ("linear_future.BTC/USDT", 0.000001),
21        ("linear_future.ETH/BTC", 0.00001),
22        ("linear_future.ETH/USDT", 0.00001),
23        ("linear_future.ETHPOW/USDT", 0.00001),
24        ("linear_future.XRP/BTC", 0.01),
25        ("linear_swap.ADA/USDT", 0.01),
26        ("linear_swap.APE/USDT", 0.001),
27        ("linear_swap.APT/USDT", 0.001),
28        ("linear_swap.ARB/USDT", 0.001),
29        ("linear_swap.AVAX/USDT", 0.0001),
30        ("linear_swap.BCH/USDT", 0.00001),
31        ("linear_swap.BIGTIME/USDT", 1.0),
32        ("linear_swap.BLUR/USDT", 0.001),
33        ("linear_swap.BMEX/USDT", 0.001),
34        ("linear_swap.BNB/USDT", 0.0001),
35        ("linear_swap.BTC/USDT", 0.000001),
36        ("linear_swap.CRO/USDT", 0.01),
37        ("linear_swap.CYBER/USDT", 1.0),
38        ("linear_swap.DEFIMEXT/USDT", 0.0001),
39        ("linear_swap.DOGE/USDT", 0.01),
40        ("linear_swap.DOT/USDT", 0.001),
41        ("linear_swap.EOS/USDT", 0.001),
42        ("linear_swap.ETH/USDT", 0.00001),
43        ("linear_swap.FIL/USDT", 1.0),
44        ("linear_swap.FTM/USDT", 0.01),
45        ("linear_swap.FTT/USDT", 0.0001),
46        ("linear_swap.GAL/USDT", 0.001),
47        ("linear_swap.GMT/USDT", 0.001),
48        ("linear_swap.KLAY/USDT", 0.01),
49        ("linear_swap.LINK/USDT", 0.001),
50        ("linear_swap.LTC/USDT", 0.0001),
51        ("linear_swap.LUNA/USDT", 0.001),
52        ("linear_swap.MANA/USDT", 0.001),
53        ("linear_swap.MATIC/USDT", 0.01),
54        ("linear_swap.MEME/USDT", 1.0),
55        ("linear_swap.NEAR/USDT", 0.001),
56        ("linear_swap.OP/USDT", 0.001),
57        ("linear_swap.ORBS/USDT", 1.0),
58        ("linear_swap.PEPE/USDT", 1.0),
59        ("linear_swap.PYTH/USDT", 1.0),
60        ("linear_swap.SAND/USDT", 0.001),
61        ("linear_swap.SEI/USDT", 1.0),
62        ("linear_swap.SHIB/USDT", 1.0),
63        ("linear_swap.SOL/USDT", 0.0001),
64        ("linear_swap.SUI/USDT", 0.001),
65        ("linear_swap.TIA/USDT", 1.0),
66        ("linear_swap.TRX/USDT", 0.1),
67        ("linear_swap.XRP/USDT", 0.01),
68        ("quanto_swap.EUR/USDT", 1.0),
69        ("quanto_swap.NZD/USDT", 1.0),
70    ]
71    .into_iter()
72    .map(|x| (x.0.to_string(), x.1))
73    .collect();
74
75    let from_online = fetch_contract_values();
76    for (pair, contract_value) in from_online {
77        m.insert(pair, contract_value);
78    }
79
80    m
81});
82
83#[derive(Clone, Serialize, Deserialize)]
84#[allow(non_snake_case)]
85struct Instrument {
86    symbol: String,
87    state: String,
88    typ: String,
89    quoteCurrency: String,
90    multiplier: f64,
91    isQuanto: bool,
92    isInverse: bool,
93    hasLiquidity: bool,
94    openInterest: i64,
95    volume: i64,
96    volume24h: i64,
97    turnover: i64,
98    turnover24h: i64,
99    underlyingToSettleMultiplier: Option<f64>,
100    underlyingToPositionMultiplier: Option<f64>,
101    quoteToSettleMultiplier: Option<f64>,
102    #[serde(flatten)]
103    extra: HashMap<String, Value>,
104}
105
106fn fetch_contract_values() -> BTreeMap<String, f64> {
107    let mut mapping: BTreeMap<String, f64> = BTreeMap::new();
108
109    if let Ok(text) = http_get("https://www.bitmex.com/api/v1/instrument/active") {
110        let instruments: Vec<Instrument> = serde_json::from_str::<Vec<Instrument>>(&text)
111            .unwrap()
112            .into_iter()
113            .filter(|x| x.state == "Open" && x.hasLiquidity && x.volume24h > 0 && x.turnover24h > 0)
114            .collect();
115
116        for instrument in instruments
117            .iter()
118            .filter(|instrument| !instrument.isQuanto && instrument.typ != "IFXXXP")
119        {
120            let market_type = crypto_pair::get_market_type(&instrument.symbol, "bitmex", None);
121            let pair = crypto_pair::normalize_pair(&instrument.symbol, "bitmex").unwrap();
122            mapping.insert(
123                market_type.to_string() + "." + pair.as_str(),
124                if let Some(x) = instrument.underlyingToSettleMultiplier {
125                    instrument.multiplier / x
126                } else {
127                    instrument.multiplier / instrument.quoteToSettleMultiplier.unwrap()
128                },
129            );
130        }
131    }
132
133    mapping
134}
135
136pub(crate) fn get_contract_value(market_type: MarketType, pair: &str) -> Option<f64> {
137    if market_type == MarketType::Unknown {
138        panic!("Must be a specific market type");
139    }
140    let key = market_type.to_string() + "." + pair;
141    if CONTRACT_VALUES.contains_key(key.as_str()) { Some(CONTRACT_VALUES[&key]) } else { Some(1.0) }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::fetch_contract_values;
147
148    #[ignore]
149    #[test]
150    fn test_fetch_contract_values() {
151        let mut mapping = fetch_contract_values();
152        for (key, value) in super::CONTRACT_VALUES.iter() {
153            if !mapping.contains_key(key) {
154                mapping.insert(key.to_string(), *value);
155            }
156        }
157        for (pair, contract_value) in &mapping {
158            println!("(\"{pair}\", {contract_value}),");
159        }
160    }
161}