crypto_markets/exchanges/
bithumb.rs

1use std::collections::HashMap;
2
3use super::utils::http_get;
4use crate::{
5    error::{Error, Result},
6    Fees, Market, MarketType, Precision,
7};
8
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12pub(crate) fn fetch_symbols(market_type: MarketType) -> Result<Vec<String>> {
13    match market_type {
14        MarketType::Spot => fetch_spot_symbols(),
15        _ => panic!("Unsupported market_type: {market_type}"),
16    }
17}
18
19pub(crate) fn fetch_markets(market_type: MarketType) -> Result<Vec<Market>> {
20    match market_type {
21        MarketType::Spot => fetch_spot_markets(),
22        _ => panic!("Unsupported market_type: {market_type}"),
23    }
24}
25
26#[derive(Serialize, Deserialize)]
27#[allow(non_snake_case)]
28struct CoinConfig {
29    makerFeeRate: serde_json::Value, // String or i64
30    minTxAmt: Option<String>,
31    name: String,
32    depositStatus: String,
33    fullName: String,
34    takerFeeRate: serde_json::Value, // String or i64
35    minWithdraw: String,
36    withdrawFee: String,
37    withdrawStatus: String,
38    #[serde(flatten)]
39    extra: HashMap<String, Value>,
40}
41
42#[derive(Serialize, Deserialize)]
43#[allow(non_snake_case)]
44struct PercentPrice {
45    multiplierDown: String,
46    multiplierUp: String,
47}
48
49#[derive(Serialize, Deserialize)]
50#[allow(non_snake_case)]
51struct SpotConfig {
52    symbol: String,
53    percentPrice: PercentPrice,
54    accuracy: Vec<String>,
55    openPrice: String,
56    openTime: i64,
57    #[serde(flatten)]
58    extra: HashMap<String, Value>,
59}
60
61#[derive(Serialize, Deserialize)]
62#[allow(non_snake_case)]
63struct Data {
64    coinConfig: Vec<CoinConfig>,
65    spotConfig: Vec<SpotConfig>,
66}
67
68#[derive(Serialize, Deserialize)]
69struct Response {
70    data: Data,
71    code: String,
72    msg: String,
73    timestamp: i64,
74}
75
76// see https://github.com/bithumb-pro/bithumb.pro-official-api-docs/blob/master/rest-api.md#2-config-detail
77fn fetch_spot_coing() -> Result<Data> {
78    let txt = http_get("https://global-openapi.bithumb.pro/openapi/v1/spot/config", None)?;
79    let resp = serde_json::from_str::<Response>(&txt)?;
80    if resp.code != "0" { Err(Error(txt)) } else { Ok(resp.data) }
81}
82
83fn fetch_spot_symbols() -> Result<Vec<String>> {
84    let symbols =
85        fetch_spot_coing()?.spotConfig.into_iter().map(|m| m.symbol).collect::<Vec<String>>();
86    Ok(symbols)
87}
88
89fn fetch_spot_markets() -> Result<Vec<Market>> {
90    let markets = fetch_spot_coing()?
91        .spotConfig
92        .into_iter()
93        .map(|m| {
94            let info = serde_json::to_value(&m).unwrap().as_object().unwrap().clone();
95            let pair = crypto_pair::normalize_pair(&m.symbol, "bithumb").unwrap();
96            let (base, quote) = {
97                let v: Vec<&str> = pair.split('/').collect();
98                (v[0].to_string(), v[1].to_string())
99            };
100            let (base_id, quote_id) = {
101                let v: Vec<&str> = m.symbol.split('-').collect();
102                (v[0].to_string(), v[1].to_string())
103            };
104            Market {
105                exchange: "bithumb".to_string(),
106                market_type: MarketType::Spot,
107                symbol: m.symbol,
108                base_id,
109                quote_id,
110                settle_id: None,
111                base,
112                quote,
113                settle: None,
114                active: true,
115                margin: false,
116                // see https://www.bitglobal.com/en-us/fee
117                fees: Fees { maker: 0.001, taker: 0.001 },
118                precision: Precision {
119                    tick_size: 1.0 / (10_i64.pow(m.accuracy[0].parse::<u32>().unwrap()) as f64),
120                    lot_size: 1.0 / (10_i64.pow(m.accuracy[1].parse::<u32>().unwrap()) as f64),
121                },
122                quantity_limit: None,
123                contract_value: None,
124                delivery_date: None,
125                info,
126            }
127        })
128        .collect::<Vec<Market>>();
129    Ok(markets)
130}