/*
 * Binance Derivatives Trading Options REST API
 *
 * OpenAPI Specification for the Binance Derivatives Trading Options REST API
 *
 * The version of the OpenAPI document: 1.0.0
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#![allow(unused_imports)]
use async_trait::async_trait;
use derive_builder::Builder;
use reqwest;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::collections::BTreeMap;

use crate::common::{
    config::ConfigurationRestApi,
    models::{ParamBuildError, RestApiResponse},
    utils::send_request,
};
use crate::derivatives_trading_options::rest_api::models;

const HAS_TIME_UNIT: bool = false;

#[async_trait]
pub trait MarketDataApi: Send + Sync {
    async fn check_server_time(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::CheckServerTimeResponse>>;
    async fn exchange_information(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::ExchangeInformationResponse>>;
    async fn historical_exercise_records(
        &self,
        params: HistoricalExerciseRecordsParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::HistoricalExerciseRecordsResponseInner>>>;
    async fn index_price(
        &self,
        params: IndexPriceParams,
    ) -> anyhow::Result<RestApiResponse<models::IndexPriceResponse>>;
    async fn kline_candlestick_data(
        &self,
        params: KlineCandlestickDataParams,
    ) -> anyhow::Result<RestApiResponse<Vec<Vec<models::KlineCandlestickDataResponseItemInner>>>>;
    async fn open_interest(
        &self,
        params: OpenInterestParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::OpenInterestResponseInner>>>;
    async fn option_mark_price(
        &self,
        params: OptionMarkPriceParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::OptionMarkPriceResponseInner>>>;
    async fn order_book(
        &self,
        params: OrderBookParams,
    ) -> anyhow::Result<RestApiResponse<models::OrderBookResponse>>;
    async fn recent_block_trades_list(
        &self,
        params: RecentBlockTradesListParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::RecentBlockTradesListResponseInner>>>;
    async fn recent_trades_list(
        &self,
        params: RecentTradesListParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::RecentTradesListResponseInner>>>;
    async fn test_connectivity(&self) -> anyhow::Result<RestApiResponse<Value>>;
    async fn ticker24hr_price_change_statistics(
        &self,
        params: Ticker24hrPriceChangeStatisticsParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>>>;
}

#[derive(Debug, Clone)]
pub struct MarketDataApiClient {
    configuration: ConfigurationRestApi,
}

impl MarketDataApiClient {
    pub fn new(configuration: ConfigurationRestApi) -> Self {
        Self { configuration }
    }
}

/// Request parameters for the [`historical_exercise_records`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`historical_exercise_records`](#method.historical_exercise_records).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct HistoricalExerciseRecordsParams {
    /// underlying, e.g BTCUSDT
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub underlying: Option<String>,
    /// Start Time, e.g 1593511200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    /// End Time, e.g 1593512200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Number of result sets returned Default:100 Max:1000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i64>,
}

impl HistoricalExerciseRecordsParams {
    /// Create a builder for [`historical_exercise_records`].
    ///
    #[must_use]
    pub fn builder() -> HistoricalExerciseRecordsParamsBuilder {
        HistoricalExerciseRecordsParamsBuilder::default()
    }
}
/// Request parameters for the [`index_price`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`index_price`](#method.index_price).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct IndexPriceParams {
    /// Option underlying, e.g BTCUSDT
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub underlying: String,
}

impl IndexPriceParams {
    /// Create a builder for [`index_price`].
    ///
    /// Required parameters:
    ///
    /// * `underlying` — Option underlying, e.g BTCUSDT
    ///
    #[must_use]
    pub fn builder(underlying: String) -> IndexPriceParamsBuilder {
        IndexPriceParamsBuilder::default().underlying(underlying)
    }
}
/// Request parameters for the [`kline_candlestick_data`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`kline_candlestick_data`](#method.kline_candlestick_data).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct KlineCandlestickDataParams {
    /// Option trading pair, e.g BTC-200730-9000-C
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Time interval
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub interval: String,
    /// Start Time, e.g 1593511200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    /// End Time, e.g 1593512200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Number of result sets returned Default:100 Max:1000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i64>,
}

impl KlineCandlestickDataParams {
    /// Create a builder for [`kline_candlestick_data`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — Option trading pair, e.g BTC-200730-9000-C
    /// * `interval` — Time interval
    ///
    #[must_use]
    pub fn builder(symbol: String, interval: String) -> KlineCandlestickDataParamsBuilder {
        KlineCandlestickDataParamsBuilder::default()
            .symbol(symbol)
            .interval(interval)
    }
}
/// Request parameters for the [`open_interest`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`open_interest`](#method.open_interest).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OpenInterestParams {
    /// underlying asset, e.g ETH/BTC
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub underlying_asset: String,
    /// expiration date, e.g 221225
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub expiration: String,
}

impl OpenInterestParams {
    /// Create a builder for [`open_interest`].
    ///
    /// Required parameters:
    ///
    /// * `underlying_asset` — underlying asset, e.g ETH/BTC
    /// * `expiration` — expiration date, e.g 221225
    ///
    #[must_use]
    pub fn builder(underlying_asset: String, expiration: String) -> OpenInterestParamsBuilder {
        OpenInterestParamsBuilder::default()
            .underlying_asset(underlying_asset)
            .expiration(expiration)
    }
}
/// Request parameters for the [`option_mark_price`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`option_mark_price`](#method.option_mark_price).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OptionMarkPriceParams {
    /// Option trading pair, e.g BTC-200730-9000-C
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub symbol: Option<String>,
}

impl OptionMarkPriceParams {
    /// Create a builder for [`option_mark_price`].
    ///
    #[must_use]
    pub fn builder() -> OptionMarkPriceParamsBuilder {
        OptionMarkPriceParamsBuilder::default()
    }
}
/// Request parameters for the [`order_book`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_book`](#method.order_book).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderBookParams {
    /// Option trading pair, e.g BTC-200730-9000-C
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Number of result sets returned Default:100 Max:1000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i64>,
}

impl OrderBookParams {
    /// Create a builder for [`order_book`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — Option trading pair, e.g BTC-200730-9000-C
    ///
    #[must_use]
    pub fn builder(symbol: String) -> OrderBookParamsBuilder {
        OrderBookParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`recent_block_trades_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`recent_block_trades_list`](#method.recent_block_trades_list).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct RecentBlockTradesListParams {
    /// Option trading pair, e.g BTC-200730-9000-C
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub symbol: Option<String>,
    /// Number of result sets returned Default:100 Max:1000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i64>,
}

impl RecentBlockTradesListParams {
    /// Create a builder for [`recent_block_trades_list`].
    ///
    #[must_use]
    pub fn builder() -> RecentBlockTradesListParamsBuilder {
        RecentBlockTradesListParamsBuilder::default()
    }
}
/// Request parameters for the [`recent_trades_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`recent_trades_list`](#method.recent_trades_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct RecentTradesListParams {
    /// Option trading pair, e.g BTC-200730-9000-C
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Number of result sets returned Default:100 Max:1000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i64>,
}

impl RecentTradesListParams {
    /// Create a builder for [`recent_trades_list`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — Option trading pair, e.g BTC-200730-9000-C
    ///
    #[must_use]
    pub fn builder(symbol: String) -> RecentTradesListParamsBuilder {
        RecentTradesListParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`ticker24hr_price_change_statistics`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`ticker24hr_price_change_statistics`](#method.ticker24hr_price_change_statistics).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct Ticker24hrPriceChangeStatisticsParams {
    /// Option trading pair, e.g BTC-200730-9000-C
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub symbol: Option<String>,
}

impl Ticker24hrPriceChangeStatisticsParams {
    /// Create a builder for [`ticker24hr_price_change_statistics`].
    ///
    #[must_use]
    pub fn builder() -> Ticker24hrPriceChangeStatisticsParamsBuilder {
        Ticker24hrPriceChangeStatisticsParamsBuilder::default()
    }
}

#[async_trait]
impl MarketDataApi for MarketDataApiClient {
    async fn check_server_time(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::CheckServerTimeResponse>> {
        let query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        send_request::<models::CheckServerTimeResponse>(
            &self.configuration,
            "/eapi/v1/time",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn exchange_information(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::ExchangeInformationResponse>> {
        let query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        send_request::<models::ExchangeInformationResponse>(
            &self.configuration,
            "/eapi/v1/exchangeInfo",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn historical_exercise_records(
        &self,
        params: HistoricalExerciseRecordsParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::HistoricalExerciseRecordsResponseInner>>> {
        let HistoricalExerciseRecordsParams {
            underlying,
            start_time,
            end_time,
            limit,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = underlying {
            query_params.insert("underlying".to_string(), json!(rw));
        }

        if let Some(rw) = start_time {
            query_params.insert("startTime".to_string(), json!(rw));
        }

        if let Some(rw) = end_time {
            query_params.insert("endTime".to_string(), json!(rw));
        }

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        send_request::<Vec<models::HistoricalExerciseRecordsResponseInner>>(
            &self.configuration,
            "/eapi/v1/exerciseHistory",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn index_price(
        &self,
        params: IndexPriceParams,
    ) -> anyhow::Result<RestApiResponse<models::IndexPriceResponse>> {
        let IndexPriceParams { underlying } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("underlying".to_string(), json!(underlying));

        send_request::<models::IndexPriceResponse>(
            &self.configuration,
            "/eapi/v1/index",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn kline_candlestick_data(
        &self,
        params: KlineCandlestickDataParams,
    ) -> anyhow::Result<RestApiResponse<Vec<Vec<models::KlineCandlestickDataResponseItemInner>>>>
    {
        let KlineCandlestickDataParams {
            symbol,
            interval,
            start_time,
            end_time,
            limit,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("symbol".to_string(), json!(symbol));

        query_params.insert("interval".to_string(), json!(interval));

        if let Some(rw) = start_time {
            query_params.insert("startTime".to_string(), json!(rw));
        }

        if let Some(rw) = end_time {
            query_params.insert("endTime".to_string(), json!(rw));
        }

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        send_request::<Vec<Vec<models::KlineCandlestickDataResponseItemInner>>>(
            &self.configuration,
            "/eapi/v1/klines",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn open_interest(
        &self,
        params: OpenInterestParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::OpenInterestResponseInner>>> {
        let OpenInterestParams {
            underlying_asset,
            expiration,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("underlyingAsset".to_string(), json!(underlying_asset));

        query_params.insert("expiration".to_string(), json!(expiration));

        send_request::<Vec<models::OpenInterestResponseInner>>(
            &self.configuration,
            "/eapi/v1/openInterest",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn option_mark_price(
        &self,
        params: OptionMarkPriceParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::OptionMarkPriceResponseInner>>> {
        let OptionMarkPriceParams { symbol } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = symbol {
            query_params.insert("symbol".to_string(), json!(rw));
        }

        send_request::<Vec<models::OptionMarkPriceResponseInner>>(
            &self.configuration,
            "/eapi/v1/mark",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn order_book(
        &self,
        params: OrderBookParams,
    ) -> anyhow::Result<RestApiResponse<models::OrderBookResponse>> {
        let OrderBookParams { symbol, limit } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("symbol".to_string(), json!(symbol));

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        send_request::<models::OrderBookResponse>(
            &self.configuration,
            "/eapi/v1/depth",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn recent_block_trades_list(
        &self,
        params: RecentBlockTradesListParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::RecentBlockTradesListResponseInner>>> {
        let RecentBlockTradesListParams { symbol, limit } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = symbol {
            query_params.insert("symbol".to_string(), json!(rw));
        }

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        send_request::<Vec<models::RecentBlockTradesListResponseInner>>(
            &self.configuration,
            "/eapi/v1/blockTrades",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn recent_trades_list(
        &self,
        params: RecentTradesListParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::RecentTradesListResponseInner>>> {
        let RecentTradesListParams { symbol, limit } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("symbol".to_string(), json!(symbol));

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        send_request::<Vec<models::RecentTradesListResponseInner>>(
            &self.configuration,
            "/eapi/v1/trades",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn test_connectivity(&self) -> anyhow::Result<RestApiResponse<Value>> {
        let query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        send_request::<Value>(
            &self.configuration,
            "/eapi/v1/ping",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn ticker24hr_price_change_statistics(
        &self,
        params: Ticker24hrPriceChangeStatisticsParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>>>
    {
        let Ticker24hrPriceChangeStatisticsParams { symbol } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = symbol {
            query_params.insert("symbol".to_string(), json!(rw));
        }

        send_request::<Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>>(
            &self.configuration,
            "/eapi/v1/ticker",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }
}

#[cfg(all(test, feature = "derivatives_trading_options"))]
mod tests {
    use super::*;
    use crate::TOKIO_SHARED_RT;
    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
    use async_trait::async_trait;
    use std::collections::HashMap;

    struct DummyRestApiResponse<T> {
        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
        status: u16,
        headers: HashMap<String, String>,
        rate_limits: Option<Vec<RestApiRateLimit>>,
    }

    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
        fn from(dummy: DummyRestApiResponse<T>) -> Self {
            Self {
                data_fn: dummy.inner,
                status: dummy.status,
                headers: dummy.headers,
                rate_limits: dummy.rate_limits,
            }
        }
    }

    struct MockMarketDataApiClient {
        force_error: bool,
    }

    #[async_trait]
    impl MarketDataApi for MockMarketDataApiClient {
        async fn check_server_time(
            &self,
        ) -> anyhow::Result<RestApiResponse<models::CheckServerTimeResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"serverTime":1499827319559}"#).unwrap();
            let dummy_response: models::CheckServerTimeResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::CheckServerTimeResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn exchange_information(
            &self,
        ) -> anyhow::Result<RestApiResponse<models::ExchangeInformationResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"timezone":"UTC","serverTime":1592387337630,"optionContracts":[{"baseAsset":"BTC","quoteAsset":"USDT","underlying":"BTCUSDT","settleAsset":"USDT"}],"optionAssets":[{"name":"USDT"}],"optionSymbols":[{"expiryDate":1660521600000,"filters":[{"filterType":"PRICE_FILTER","minPrice":"0.02","maxPrice":"80000.01","tickSize":"0.01"},{"filterType":"LOT_SIZE","minQty":"0.01","maxQty":"100","stepSize":"0.01"}],"symbol":"BTC-220815-50000-C","side":"CALL","strikePrice":"50000","underlying":"BTCUSDT","unit":1,"liquidationFeeRate":"0.0019000","minQty":"0.01","maxQty":"100","initialMargin":"0.15","maintenanceMargin":"0.075","minInitialMargin":"0.1","minMaintenanceMargin":"0.05","priceScale":2,"quantityScale":2,"quoteAsset":"USDT","status":"TRADING"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":2400},{"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200},{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":300}]}"#).unwrap();
            let dummy_response: models::ExchangeInformationResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::ExchangeInformationResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn historical_exercise_records(
            &self,
            _params: HistoricalExerciseRecordsParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::HistoricalExerciseRecordsResponseInner>>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-220121-60000-P","strikePrice":"60000","realStrikePrice":"38844.69652571","expiryDate":1642752000000,"strikeResult":"REALISTIC_VALUE_STRICKEN"}]"#).unwrap();
            let dummy_response: Vec<models::HistoricalExerciseRecordsResponseInner> =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into Vec<models::HistoricalExerciseRecordsResponseInner>",
                );

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn index_price(
            &self,
            _params: IndexPriceParams,
        ) -> anyhow::Result<RestApiResponse<models::IndexPriceResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value =
                serde_json::from_str(r#"{"time":1656647305000,"indexPrice":"105917.75"}"#).unwrap();
            let dummy_response: models::IndexPriceResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::IndexPriceResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn kline_candlestick_data(
            &self,
            _params: KlineCandlestickDataParams,
        ) -> anyhow::Result<RestApiResponse<Vec<Vec<models::KlineCandlestickDataResponseItemInner>>>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[[1762779600000,"1300.000","1300.000","1300.000","1300.000","0.1000",1762780499999,"130.0000000",1,"0.1000","130.0000000","0"]]"#).unwrap();
            let dummy_response: Vec<Vec<models::KlineCandlestickDataResponseItemInner>> =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into Vec<Vec<models::KlineCandlestickDataResponseItemInner>>",
                );

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn open_interest(
            &self,
            _params: OpenInterestParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::OpenInterestResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"ETH-221119-1175-P","sumOpenInterest":"4.01","sumOpenInterestUsd":"4880.2985615624","timestamp":"1668754020000"}]"#).unwrap();
            let dummy_response: Vec<models::OpenInterestResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::OpenInterestResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn option_mark_price(
            &self,
            _params: OptionMarkPriceParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::OptionMarkPriceResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-200730-9000-C","markPrice":"1343.2883","bidIV":"1.40000077","askIV":"1.50000153","markIV":"1.45000000","delta":"0.55937056","theta":"3739.82509871","gamma":"0.00010969","vega":"978.58874732","highPriceLimit":"1618.241","lowPriceLimit":"1068.3356","riskFreeInterest":"0.1"}]"#).unwrap();
            let dummy_response: Vec<models::OptionMarkPriceResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::OptionMarkPriceResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn order_book(
            &self,
            _params: OrderBookParams,
        ) -> anyhow::Result<RestApiResponse<models::OrderBookResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"bids":[["1000.000","0.1000"]],"asks":[["1900.000","0.1000"]],"T":1762780909676,"lastUpdateId":361}"#).unwrap();
            let dummy_response: models::OrderBookResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::OrderBookResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn recent_block_trades_list(
            &self,
            _params: RecentBlockTradesListParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::RecentBlockTradesListResponseInner>>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"id":1125899906901081100,"tradeId":389,"symbol":"ETH-250725-1200-P","price":"342.40","qty":"-2167.20","quoteQty":"-4.90","side":-1,"time":1733950676483},{"id":1125899906901081000,"tradeId":161,"symbol":"XRP-250904-0.086-P","price":"3.0","qty":"-6.0","quoteQty":"-2.02","side":-1,"time":1733950488444}]"#).unwrap();
            let dummy_response: Vec<models::RecentBlockTradesListResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::RecentBlockTradesListResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn recent_trades_list(
            &self,
            _params: RecentTradesListParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::RecentTradesListResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"id":2323857420768529000,"tradeId":1,"symbol":"BTC-251123-126000-C","price":"1300","qty":"0.1","quoteQty":"130","side":-1,"time":1762780453623}]"#).unwrap();
            let dummy_response: Vec<models::RecentTradesListResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::RecentTradesListResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn test_connectivity(&self) -> anyhow::Result<RestApiResponse<Value>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let dummy_response = Value::Null;

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn ticker24hr_price_change_statistics(
            &self,
            _params: Ticker24hrPriceChangeStatisticsParams,
        ) -> anyhow::Result<
            RestApiResponse<Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>>,
        > {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-200730-9000-C","priceChange":"-16.2038","priceChangePercent":"-0.0162","lastPrice":"1000","lastQty":"1000","open":"1016.2038","high":"1016.2038","low":"0","volume":"5","amount":"1","bidPrice":"999.34","askPrice":"1000.23","openTime":1592317127349,"closeTime":1592380593516,"firstTradeId":1,"tradeCount":5,"strikePrice":"9000","exercisePrice":"3000.3356"}]"#).unwrap();
            let dummy_response: Vec<models::Ticker24hrPriceChangeStatisticsResponseInner> =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>",
                );

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }
    }

    #[test]
    fn check_server_time_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let resp_json: Value = serde_json::from_str(r#"{"serverTime":1499827319559}"#).unwrap();
            let expected_response: models::CheckServerTimeResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::CheckServerTimeResponse");

            let resp = client
                .check_server_time()
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn check_server_time_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let resp_json: Value = serde_json::from_str(r#"{"serverTime":1499827319559}"#).unwrap();
            let expected_response: models::CheckServerTimeResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::CheckServerTimeResponse");

            let resp = client
                .check_server_time()
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn check_server_time_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            match client.check_server_time().await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn exchange_information_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"{"timezone":"UTC","serverTime":1592387337630,"optionContracts":[{"baseAsset":"BTC","quoteAsset":"USDT","underlying":"BTCUSDT","settleAsset":"USDT"}],"optionAssets":[{"name":"USDT"}],"optionSymbols":[{"expiryDate":1660521600000,"filters":[{"filterType":"PRICE_FILTER","minPrice":"0.02","maxPrice":"80000.01","tickSize":"0.01"},{"filterType":"LOT_SIZE","minQty":"0.01","maxQty":"100","stepSize":"0.01"}],"symbol":"BTC-220815-50000-C","side":"CALL","strikePrice":"50000","underlying":"BTCUSDT","unit":1,"liquidationFeeRate":"0.0019000","minQty":"0.01","maxQty":"100","initialMargin":"0.15","maintenanceMargin":"0.075","minInitialMargin":"0.1","minMaintenanceMargin":"0.05","priceScale":2,"quantityScale":2,"quoteAsset":"USDT","status":"TRADING"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":2400},{"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200},{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":300}]}"#).unwrap();
            let expected_response : models::ExchangeInformationResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ExchangeInformationResponse");

            let resp = client.exchange_information().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn exchange_information_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"{"timezone":"UTC","serverTime":1592387337630,"optionContracts":[{"baseAsset":"BTC","quoteAsset":"USDT","underlying":"BTCUSDT","settleAsset":"USDT"}],"optionAssets":[{"name":"USDT"}],"optionSymbols":[{"expiryDate":1660521600000,"filters":[{"filterType":"PRICE_FILTER","minPrice":"0.02","maxPrice":"80000.01","tickSize":"0.01"},{"filterType":"LOT_SIZE","minQty":"0.01","maxQty":"100","stepSize":"0.01"}],"symbol":"BTC-220815-50000-C","side":"CALL","strikePrice":"50000","underlying":"BTCUSDT","unit":1,"liquidationFeeRate":"0.0019000","minQty":"0.01","maxQty":"100","initialMargin":"0.15","maintenanceMargin":"0.075","minInitialMargin":"0.1","minMaintenanceMargin":"0.05","priceScale":2,"quantityScale":2,"quoteAsset":"USDT","status":"TRADING"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":2400},{"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200},{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":300}]}"#).unwrap();
            let expected_response : models::ExchangeInformationResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ExchangeInformationResponse");

            let resp = client.exchange_information().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn exchange_information_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            match client.exchange_information().await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn historical_exercise_records_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = HistoricalExerciseRecordsParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-220121-60000-P","strikePrice":"60000","realStrikePrice":"38844.69652571","expiryDate":1642752000000,"strikeResult":"REALISTIC_VALUE_STRICKEN"}]"#).unwrap();
            let expected_response : Vec<models::HistoricalExerciseRecordsResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::HistoricalExerciseRecordsResponseInner>");

            let resp = client.historical_exercise_records(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn historical_exercise_records_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = HistoricalExerciseRecordsParams::builder().underlying("underlying_example".to_string()).start_time(1623319461670).end_time(1641782889000).limit(100).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-220121-60000-P","strikePrice":"60000","realStrikePrice":"38844.69652571","expiryDate":1642752000000,"strikeResult":"REALISTIC_VALUE_STRICKEN"}]"#).unwrap();
            let expected_response : Vec<models::HistoricalExerciseRecordsResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::HistoricalExerciseRecordsResponseInner>");

            let resp = client.historical_exercise_records(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn historical_exercise_records_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = HistoricalExerciseRecordsParams::builder().build().unwrap();

            match client.historical_exercise_records(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn index_price_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = IndexPriceParams::builder("underlying_example".to_string())
                .build()
                .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"time":1656647305000,"indexPrice":"105917.75"}"#).unwrap();
            let expected_response: models::IndexPriceResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::IndexPriceResponse");

            let resp = client
                .index_price(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn index_price_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = IndexPriceParams::builder("underlying_example".to_string())
                .build()
                .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"time":1656647305000,"indexPrice":"105917.75"}"#).unwrap();
            let expected_response: models::IndexPriceResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::IndexPriceResponse");

            let resp = client
                .index_price(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn index_price_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = IndexPriceParams::builder("underlying_example".to_string())
                .build()
                .unwrap();

            match client.index_price(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn kline_candlestick_data_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = KlineCandlestickDataParams::builder("symbol_example".to_string(),"interval_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[[1762779600000,"1300.000","1300.000","1300.000","1300.000","0.1000",1762780499999,"130.0000000",1,"0.1000","130.0000000","0"]]"#).unwrap();
            let expected_response : Vec<Vec<models::KlineCandlestickDataResponseItemInner>> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<Vec<models::KlineCandlestickDataResponseItemInner>>");

            let resp = client.kline_candlestick_data(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn kline_candlestick_data_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = KlineCandlestickDataParams::builder("symbol_example".to_string(),"interval_example".to_string(),).start_time(1623319461670).end_time(1641782889000).limit(100).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[[1762779600000,"1300.000","1300.000","1300.000","1300.000","0.1000",1762780499999,"130.0000000",1,"0.1000","130.0000000","0"]]"#).unwrap();
            let expected_response : Vec<Vec<models::KlineCandlestickDataResponseItemInner>> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<Vec<models::KlineCandlestickDataResponseItemInner>>");

            let resp = client.kline_candlestick_data(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn kline_candlestick_data_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = KlineCandlestickDataParams::builder(
                "symbol_example".to_string(),
                "interval_example".to_string(),
            )
            .build()
            .unwrap();

            match client.kline_candlestick_data(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn open_interest_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = OpenInterestParams::builder("underlying_asset_example".to_string(),"expiration_example".to_string()).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"ETH-221119-1175-P","sumOpenInterest":"4.01","sumOpenInterestUsd":"4880.2985615624","timestamp":"1668754020000"}]"#).unwrap();
            let expected_response : Vec<models::OpenInterestResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::OpenInterestResponseInner>");

            let resp = client.open_interest(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn open_interest_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = OpenInterestParams::builder("underlying_asset_example".to_string(),"expiration_example".to_string()).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"ETH-221119-1175-P","sumOpenInterest":"4.01","sumOpenInterestUsd":"4880.2985615624","timestamp":"1668754020000"}]"#).unwrap();
            let expected_response : Vec<models::OpenInterestResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::OpenInterestResponseInner>");

            let resp = client.open_interest(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn open_interest_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = OpenInterestParams::builder(
                "underlying_asset_example".to_string(),
                "expiration_example".to_string(),
            )
            .build()
            .unwrap();

            match client.open_interest(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn option_mark_price_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = OptionMarkPriceParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-200730-9000-C","markPrice":"1343.2883","bidIV":"1.40000077","askIV":"1.50000153","markIV":"1.45000000","delta":"0.55937056","theta":"3739.82509871","gamma":"0.00010969","vega":"978.58874732","highPriceLimit":"1618.241","lowPriceLimit":"1068.3356","riskFreeInterest":"0.1"}]"#).unwrap();
            let expected_response : Vec<models::OptionMarkPriceResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::OptionMarkPriceResponseInner>");

            let resp = client.option_mark_price(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn option_mark_price_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = OptionMarkPriceParams::builder().symbol("symbol_example".to_string()).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-200730-9000-C","markPrice":"1343.2883","bidIV":"1.40000077","askIV":"1.50000153","markIV":"1.45000000","delta":"0.55937056","theta":"3739.82509871","gamma":"0.00010969","vega":"978.58874732","highPriceLimit":"1618.241","lowPriceLimit":"1068.3356","riskFreeInterest":"0.1"}]"#).unwrap();
            let expected_response : Vec<models::OptionMarkPriceResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::OptionMarkPriceResponseInner>");

            let resp = client.option_mark_price(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn option_mark_price_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = OptionMarkPriceParams::builder().build().unwrap();

            match client.option_mark_price(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn order_book_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = OrderBookParams::builder("symbol_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"bids":[["1000.000","0.1000"]],"asks":[["1900.000","0.1000"]],"T":1762780909676,"lastUpdateId":361}"#).unwrap();
            let expected_response : models::OrderBookResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::OrderBookResponse");

            let resp = client.order_book(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn order_book_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = OrderBookParams::builder("symbol_example".to_string(),).limit(100).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"bids":[["1000.000","0.1000"]],"asks":[["1900.000","0.1000"]],"T":1762780909676,"lastUpdateId":361}"#).unwrap();
            let expected_response : models::OrderBookResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::OrderBookResponse");

            let resp = client.order_book(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn order_book_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = OrderBookParams::builder("symbol_example".to_string())
                .build()
                .unwrap();

            match client.order_book(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn recent_block_trades_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = RecentBlockTradesListParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"id":1125899906901081100,"tradeId":389,"symbol":"ETH-250725-1200-P","price":"342.40","qty":"-2167.20","quoteQty":"-4.90","side":-1,"time":1733950676483},{"id":1125899906901081000,"tradeId":161,"symbol":"XRP-250904-0.086-P","price":"3.0","qty":"-6.0","quoteQty":"-2.02","side":-1,"time":1733950488444}]"#).unwrap();
            let expected_response : Vec<models::RecentBlockTradesListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::RecentBlockTradesListResponseInner>");

            let resp = client.recent_block_trades_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn recent_block_trades_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = RecentBlockTradesListParams::builder().symbol("symbol_example".to_string()).limit(100).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"id":1125899906901081100,"tradeId":389,"symbol":"ETH-250725-1200-P","price":"342.40","qty":"-2167.20","quoteQty":"-4.90","side":-1,"time":1733950676483},{"id":1125899906901081000,"tradeId":161,"symbol":"XRP-250904-0.086-P","price":"3.0","qty":"-6.0","quoteQty":"-2.02","side":-1,"time":1733950488444}]"#).unwrap();
            let expected_response : Vec<models::RecentBlockTradesListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::RecentBlockTradesListResponseInner>");

            let resp = client.recent_block_trades_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn recent_block_trades_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = RecentBlockTradesListParams::builder().build().unwrap();

            match client.recent_block_trades_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn recent_trades_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = RecentTradesListParams::builder("symbol_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"id":2323857420768529000,"tradeId":1,"symbol":"BTC-251123-126000-C","price":"1300","qty":"0.1","quoteQty":"130","side":-1,"time":1762780453623}]"#).unwrap();
            let expected_response : Vec<models::RecentTradesListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::RecentTradesListResponseInner>");

            let resp = client.recent_trades_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn recent_trades_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = RecentTradesListParams::builder("symbol_example".to_string(),).limit(100).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"id":2323857420768529000,"tradeId":1,"symbol":"BTC-251123-126000-C","price":"1300","qty":"0.1","quoteQty":"130","side":-1,"time":1762780453623}]"#).unwrap();
            let expected_response : Vec<models::RecentTradesListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::RecentTradesListResponseInner>");

            let resp = client.recent_trades_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn recent_trades_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = RecentTradesListParams::builder("symbol_example".to_string())
                .build()
                .unwrap();

            match client.recent_trades_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn test_connectivity_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let expected_response = Value::Null;

            let resp = client
                .test_connectivity()
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn test_connectivity_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let expected_response = Value::Null;

            let resp = client
                .test_connectivity()
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn test_connectivity_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            match client.test_connectivity().await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn ticker24hr_price_change_statistics_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = Ticker24hrPriceChangeStatisticsParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-200730-9000-C","priceChange":"-16.2038","priceChangePercent":"-0.0162","lastPrice":"1000","lastQty":"1000","open":"1016.2038","high":"1016.2038","low":"0","volume":"5","amount":"1","bidPrice":"999.34","askPrice":"1000.23","openTime":1592317127349,"closeTime":1592380593516,"firstTradeId":1,"tradeCount":5,"strikePrice":"9000","exercisePrice":"3000.3356"}]"#).unwrap();
            let expected_response : Vec<models::Ticker24hrPriceChangeStatisticsResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>");

            let resp = client.ticker24hr_price_change_statistics(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn ticker24hr_price_change_statistics_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: false };

            let params = Ticker24hrPriceChangeStatisticsParams::builder().symbol("symbol_example".to_string()).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"BTC-200730-9000-C","priceChange":"-16.2038","priceChangePercent":"-0.0162","lastPrice":"1000","lastQty":"1000","open":"1016.2038","high":"1016.2038","low":"0","volume":"5","amount":"1","bidPrice":"999.34","askPrice":"1000.23","openTime":1592317127349,"closeTime":1592380593516,"firstTradeId":1,"tradeCount":5,"strikePrice":"9000","exercisePrice":"3000.3356"}]"#).unwrap();
            let expected_response : Vec<models::Ticker24hrPriceChangeStatisticsResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::Ticker24hrPriceChangeStatisticsResponseInner>");

            let resp = client.ticker24hr_price_change_statistics(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn ticker24hr_price_change_statistics_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketDataApiClient { force_error: true };

            let params = Ticker24hrPriceChangeStatisticsParams::builder()
                .build()
                .unwrap();

            match client.ticker24hr_price_change_statistics(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }
}
