cbat 0.0.18

The unofficial Rust crate for the Coinbase Advanced Trade API
Documentation
use crate::{
    client::Client, contract_expiry_type::ContractExpiryType,
    expiring_contract_status::ExpiringContractStatus, granularity::Granularity,
    market_trades::MarketTrades, product::Product, product_book::ProductBook,
    product_candles::ProductCandles, product_type::ProductType, products::Products,
    server_time::ServerTime,
};

pub struct ApiPublic;

impl ApiPublic {
    pub async fn get_public_market_trades(
        client: &Client<'_>,
        product_id: &str,
        limit: u32,
        start: Option<String>,
        end: Option<String>,
    ) -> Result<MarketTrades, reqwest::Error> {
        let start = match start {
            Some(start) => &format!("&start={}", start),
            None => "",
        };
        let end = match end {
            Some(end) => &format!("&end={}", end),
            None => "",
        };
        let url = &format!(
            "{}/{}/ticker?limit={}{}{}",
            PUBLIC_MARKET_TRADES_URL, product_id, limit, start, end
        );
        let response = client.get(url).await?;
        let market_trades: MarketTrades = response.json().await?;
        Ok(market_trades)
    }

    pub async fn get_public_product_book(
        client: &Client<'_>,
        product_id: &str,
        limit: Option<u32>,
        aggregation_price_increment: Option<&str>,
    ) -> Result<ProductBook, reqwest::Error> {
        let limit = match limit {
            Some(limit) => &format!("&limit={}", limit),
            None => "",
        };
        let aggregation_price_increment = match aggregation_price_increment {
            Some(aggregation_price_increment) => &format!(
                "&aggregation_price_increment={}",
                aggregation_price_increment
            ),
            None => "",
        };
        let url = &format!(
            "{}?product_id={}{}{}",
            PUBLIC_PRODUCT_BOOK_URL, product_id, limit, aggregation_price_increment
        );
        let response = client.get(url).await?;
        let product: ProductBook = response.json().await?;
        Ok(product)
    }

    pub async fn get_public_product_candles(
        client: &Client<'_>,
        product_id: &str,
        start: &str,
        end: &str,
        granularity: Granularity,
        limit: Option<u32>,
    ) -> Result<ProductCandles, reqwest::Error> {
        let limit = match limit {
            Some(limit) => &format!("&limit={}", limit),
            None => "",
        };

        let url = &format!(
            "{}/{}/candles?start={}&end={}&granularity={}{}",
            PUBLIC_PRODUCTS_URL, product_id, start, end, granularity, limit
        );
        let response = client.get(url).await?;
        let product_candles: ProductCandles = response.json().await?;
        Ok(product_candles)
    }

    pub async fn get_public_product(
        client: &Client<'_>,
        product_id: &str,
    ) -> Result<Product, reqwest::Error> {
        let url = &format!("{}/{}", PUBLIC_PRODUCTS_URL, product_id);
        let response = client.get(url).await?;
        let product: Product = response.json().await?;
        Ok(product)
    }
    pub async fn list_public_products(
        client: &Client<'_>,
        limit: Option<u32>,
        offset: Option<u32>,
        product_type: Option<ProductType>,
        product_ids: Option<Vec<&str>>,
        contract_expiry_type: Option<ContractExpiryType>,
        expiring_contract_status: Option<ExpiringContractStatus>,
        get_all_products: Option<bool>,
    ) -> Result<Products, reqwest::Error> {
        let mut query_params = Vec::new();

        if let Some(limit) = limit {
            query_params.push(format!("limit={}", limit));
        }

        if let Some(offset) = offset {
            query_params.push(format!("offset={}", offset));
        }

        if let Some(product_type) = product_type {
            query_params.push(format!("product_type={}", product_type));
        }

        if let Some(product_ids) = product_ids {
            for product_id in product_ids {
                query_params.push(format!("product_ids={}", product_id));
            }
        }

        if let Some(contract_expiry_type) = contract_expiry_type {
            query_params.push(format!("contract_expiry_type={}", contract_expiry_type));
        }

        if let Some(expiring_contract_status) = expiring_contract_status {
            query_params.push(format!(
                "expiring_contract_status={}",
                expiring_contract_status
            ));
        }

        if let Some(get_all_products) = get_all_products {
            query_params.push(format!("get_all_products={}", get_all_products));
        }

        let query_string = if query_params.is_empty() {
            String::new()
        } else {
            format!("?{}", query_params.join("&"))
        };

        let url = &format!("{}{}", PUBLIC_PRODUCTS_URL, query_string);
        let response = client.get(url).await?;
        let products: Products = response.json().await?;
        Ok(products)
    }

    pub async fn get_public_server_time(client: &Client<'_>) -> Result<ServerTime, reqwest::Error> {
        let response = client.get(PUBLIC_SERVER_TIME).await?;
        let server_time: ServerTime = response.json().await?;
        Ok(server_time)
    }
}

const PUBLIC_MARKET_TRADES_URL: &str = "https://api.coinbase.com/api/v3/brokerage/market/products";
const PUBLIC_PRODUCTS_URL: &str = "https://api.coinbase.com/api/v3/brokerage/market/products";
const PUBLIC_PRODUCT_BOOK_URL: &str =
    "https://api.coinbase.com/api/v3/brokerage/market/product_book";
const PUBLIC_SERVER_TIME: &str = "https://api.coinbase.com/api/v3/brokerage/time";