crypto-pair 2.3.20

Parse exchange-specific symbols to unified format
Documentation
mod utils;

use crypto_pair::normalize_pair;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use utils::http_get;

const EXCHANGE_NAME: &str = "ftx";

#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct FtxMarket {
    name: String,
    baseCurrency: Option<String>,
    quoteCurrency: Option<String>,
    underlying: Option<String>,
    #[serde(rename = "type")]
    type_: String,
    #[serde(flatten)]
    extra: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize)]
struct Response {
    success: bool,
    result: Vec<FtxMarket>,
}

fn fetch_markets_raw() -> Vec<FtxMarket> {
    let txt = http_get("https://ftx.com/api/markets").unwrap();
    let resp = serde_json::from_str::<Response>(&txt).unwrap();
    assert!(resp.success);
    resp.result
}

#[ignore = "503 Service Temporarily Unavailable"]
#[test]
fn verify_all_symbols() {
    let markets = fetch_markets_raw();
    for market in markets.iter() {
        let pair = normalize_pair(&market.name, EXCHANGE_NAME).unwrap();
        let pair_expected = if market.type_ == "spot" {
            // spot
            market.name.clone()
        } else if market.type_ == "future" {
            if market.name.ends_with("-PERP") || market.name.contains("-MOVE-") {
                format!("{}/USD", market.underlying.clone().unwrap())
            } else if market.name.contains("BVOL/") {
                format!(
                    "{}/{}",
                    market.baseCurrency.clone().unwrap(),
                    market.quoteCurrency.clone().unwrap()
                )
            } else {
                // linear future
                if market.name.contains('-') {
                    format!("{}/USD", market.underlying.clone().unwrap())
                } else {
                    // prediction
                    format!("{}/USD", market.name)
                }
            }
        } else {
            panic!("Unknown symbol {}", market.name);
        };

        assert_eq!(pair.as_str(), pair_expected);
    }
}