use rust_decimal::{Decimal, serde::str_option::deserialize as option_decimal};
use serde::{Deserialize, Serialize};
use serde_aux::prelude::{
deserialize_number_from_string as number,
deserialize_option_number_from_string as option_number,
};
use crate::{
ContractType, CopyTrading, CurAuctionPhase, OptionType, Side, Status, Timestamp,
enums::{Category, Interval, IntervalTime},
serde::{empty_string_as_none, int_to_bool, string_to_bool},
};
#[derive(Debug, Serialize, Clone)]
pub struct GetKLinesParams {
pub category: Category,
pub symbol: String,
pub interval: Interval,
pub start: Option<Timestamp>,
pub end: Option<Timestamp>,
pub limit: Option<u64>,
}
impl GetKLinesParams {
pub fn new(category: Category, symbol: impl Into<String>, interval: Interval) -> Self {
Self {
category,
symbol: symbol.into(),
interval,
start: None,
end: None,
limit: None,
}
}
pub fn with_start(mut self, v: Timestamp) -> Self {
self.start = Some(v);
self
}
pub fn with_end(mut self, v: Timestamp) -> Self {
self.end = Some(v);
self
}
pub fn with_limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(tag = "category")]
pub enum KLine {
#[serde(rename = "inverse")]
Inverse {
symbol: String,
list: Vec<KLineRow>,
},
#[serde(rename = "linear")]
Linear {
symbol: String,
list: Vec<KLineRow>,
},
#[serde(rename = "option")]
Option {
symbol: String,
list: Vec<KLineRow>,
},
#[serde(rename = "spot")]
Spot {
symbol: String,
list: Vec<KLineRow>,
},
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct PriceKLine {
pub category: Category,
pub symbol: String,
pub list: Vec<PriceKLineRow>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct PriceKLineRow {
#[serde(deserialize_with = "number")]
pub start_time: Timestamp,
pub open_price: Decimal,
pub high_price: Decimal,
pub low_price: Decimal,
pub close_price: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct KLineRow {
#[serde(rename = "startTime", deserialize_with = "number")]
pub start_time: Timestamp,
#[serde(rename = "openPrice")]
pub open_price: Decimal,
#[serde(rename = "highPrice")]
pub high_price: Decimal,
#[serde(rename = "lowPrice")]
pub low_price: Decimal,
#[serde(rename = "closePrice")]
pub close_price: Decimal,
#[serde(rename = "volume")]
pub volume: Decimal,
#[serde(rename = "turnover")]
pub turnover: Decimal,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetTickersParams {
pub category: Category,
pub symbol: Option<String>,
pub base_coin: Option<String>,
pub exp_date: Option<String>,
}
impl GetTickersParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
base_coin: None,
exp_date: None,
}
}
pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
self.symbol = Some(v.into());
self
}
pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
self.base_coin = Some(v.into());
self
}
pub fn with_exp_date(mut self, v: impl Into<String>) -> Self {
self.exp_date = Some(v.into());
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(tag = "category")]
pub enum Ticker {
#[serde(rename = "inverse")]
Inverse {
list: Vec<LinearInverseTicker>,
},
#[serde(rename = "linear")]
Linear {
list: Vec<LinearInverseTicker>,
},
#[serde(rename = "option")]
Option {
list: Vec<OptionTicker>,
},
#[serde(rename = "spot")]
Spot {
list: Vec<SpotTicker>,
},
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LinearInverseTicker {
pub symbol: String,
pub last_price: Decimal,
pub mark_price: Decimal,
pub index_price: Decimal,
pub prev_price24h: Decimal,
pub price24h_pcnt: Decimal,
pub high_price24h: Decimal,
pub low_price24h: Decimal,
pub prev_price1h: Decimal,
pub open_interest: Decimal,
pub open_interest_value: Decimal,
pub turnover24h: Decimal,
pub volume24h: Decimal,
#[serde(default, deserialize_with = "option_decimal")]
pub funding_rate: Option<Decimal>,
#[serde(deserialize_with = "number")]
pub next_funding_time: Timestamp,
#[serde(default, deserialize_with = "option_decimal")]
pub predicted_delivery_price: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub basis_rate: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub basis: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub delivery_fee_rate: Option<Decimal>,
#[serde(deserialize_with = "option_number")]
pub delivery_time: Option<Timestamp>,
pub bid1_price: Decimal,
pub bid1_size: Decimal,
pub ask1_price: Decimal,
pub ask1_size: Decimal,
#[serde(default, deserialize_with = "option_decimal")]
pub pre_open_price: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub pre_qty: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub cur_pre_listing_phase: Option<CurAuctionPhase>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OptionTicker {
pub symbol: String,
pub bid1_price: Decimal,
pub bid1_size: Decimal,
pub bid1_iv: Decimal,
pub ask1_price: Decimal,
pub ask1_size: Decimal,
pub ask1_iv: Decimal,
pub last_price: Decimal,
pub high_price24h: Decimal,
pub low_price24h: Decimal,
pub mark_price: Decimal,
pub index_price: Decimal,
pub mark_iv: Decimal,
pub underlying_price: Decimal,
pub open_interest: Decimal,
pub turnover24h: Decimal,
pub volume24h: Decimal,
pub total_volume: Decimal,
pub total_turnover: Decimal,
pub delta: Decimal,
pub gamma: Decimal,
pub vega: Decimal,
pub theta: Decimal,
pub predicted_delivery_price: Decimal,
pub change24h: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SpotTicker {
pub symbol: String,
pub bid1_price: Decimal,
pub bid1_size: Decimal,
pub ask1_price: Decimal,
pub ask1_size: Decimal,
pub last_price: Decimal,
pub prev_price24h: Decimal,
pub price24h_pcnt: Decimal,
pub high_price24h: Decimal,
pub low_price24h: Decimal,
pub turnover24h: Decimal,
pub volume24h: Decimal,
#[serde(default, deserialize_with = "option_decimal")]
pub usd_index_price: Option<Decimal>,
}
#[derive(Debug, Serialize, Clone)]
pub struct GetOrderbookParams {
pub category: Category,
pub symbol: String,
pub limit: Option<u64>,
}
impl GetOrderbookParams {
pub fn new(category: Category, symbol: impl Into<String>) -> Self {
Self {
category,
symbol: symbol.into(),
limit: None,
}
}
pub fn with_limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Orderbook {
#[serde(rename = "s")]
pub symbol: String,
#[serde(rename = "b")]
pub bids: Vec<OrderbookLevel>,
#[serde(rename = "a")]
pub asks: Vec<OrderbookLevel>,
pub ts: Timestamp,
#[serde(rename = "u")]
pub update_id: i64,
pub seq: i64,
#[serde(default)]
pub cts: Option<Timestamp>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct OrderbookLevel {
pub price: Decimal,
pub size: Decimal,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetTradesParams {
pub category: Category,
pub symbol: Option<String>,
pub base_coin: Option<String>,
pub option_type: Option<OptionType>,
pub limit: Option<u64>,
}
impl GetTradesParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
base_coin: None,
option_type: None,
limit: None,
}
}
pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
self.symbol = Some(v.into());
self
}
pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
self.base_coin = Some(v.into());
self
}
pub fn with_option_type(mut self, v: OptionType) -> Self {
self.option_type = Some(v);
self
}
pub fn with_limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(tag = "category")]
pub enum Trade {
#[serde(rename = "inverse")]
Inverse {
list: Vec<InverseLinearSpotTrade>,
},
#[serde(rename = "linear")]
Linear {
list: Vec<InverseLinearSpotTrade>,
},
#[serde(rename = "option")]
Option {
list: Vec<OptionTrade>,
},
#[serde(rename = "spot")]
Spot {
list: Vec<InverseLinearSpotTrade>,
},
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InverseLinearSpotTrade {
pub exec_id: String,
pub symbol: String,
pub price: Decimal,
pub size: Decimal,
pub side: Side,
#[serde(deserialize_with = "number")]
pub time: Timestamp,
pub is_block_trade: bool,
#[serde(rename = "isRPITrade")]
pub is_rpi_trade: bool,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OptionTrade {
pub exec_id: String,
pub symbol: String,
pub price: Decimal,
pub size: Decimal,
pub side: Side,
#[serde(deserialize_with = "number")]
pub time: Timestamp,
pub is_block_trade: bool,
#[serde(rename = "isRPITrade", default)]
pub is_rpi_trade: bool,
#[serde(rename = "mP")]
pub mark_price: Decimal,
#[serde(rename = "iP")]
pub index_price: Decimal,
#[serde(rename = "mIv")]
pub mark_iv: Decimal,
#[serde(rename = "iv")]
pub iv: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ServerTime {
#[serde(deserialize_with = "number")]
pub time_second: u64,
#[serde(deserialize_with = "number")]
pub time_nano: u64,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetInstrumentsInfoParams {
pub category: Category,
pub symbol: Option<String>,
pub status: Option<Status>,
pub base_coin: Option<String>,
pub limit: Option<i64>,
pub cursor: Option<String>,
}
impl GetInstrumentsInfoParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
status: None,
base_coin: None,
limit: None,
cursor: None,
}
}
pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
self.symbol = Some(v.into());
self
}
pub fn with_status(mut self, v: Status) -> Self {
self.status = Some(v);
self
}
pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
self.base_coin = Some(v.into());
self
}
pub fn with_limit(mut self, v: i64) -> Self {
self.limit = Some(v);
self
}
pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
self.cursor = Some(v.into());
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(tag = "category")]
pub enum InstrumentsInfo {
#[serde(rename = "inverse", rename_all = "camelCase")]
Inverse {
next_page_cursor: String,
list: Vec<InverseLinearInstrumentsInfo>,
},
#[serde(rename = "linear", rename_all = "camelCase")]
Linear {
next_page_cursor: String,
list: Vec<InverseLinearInstrumentsInfo>,
},
#[serde(rename = "option", rename_all = "camelCase")]
Option {
next_page_cursor: String,
list: Vec<OptionInstrumentsInfo>,
},
#[serde(rename = "spot", rename_all = "camelCase")]
Spot {
#[serde(default, deserialize_with = "empty_string_as_none")]
next_page_cursor: Option<String>,
list: Vec<SpotInstrumentsInfo>,
},
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InverseLinearInstrumentsInfo {
pub symbol: String,
pub contract_type: ContractType,
pub status: Status,
pub base_coin: String,
pub quote_coin: String,
#[serde(deserialize_with = "number")]
pub launch_time: Timestamp,
#[serde(deserialize_with = "number")]
pub delivery_time: Timestamp,
#[serde(deserialize_with = "option_decimal")]
pub delivery_fee_rate: Option<Decimal>,
#[serde(deserialize_with = "number")]
pub price_scale: i64,
pub leverage_filter: LeverageFilter,
pub price_filter: PriceFilter,
pub lot_size_filter: LotSizeFilter,
pub unified_margin_trade: bool,
pub funding_interval: i64,
pub settle_coin: String,
pub copy_trading: CopyTrading,
pub upper_funding_rate: Decimal,
pub lower_funding_rate: Decimal,
pub risk_parameters: RiskParameters,
pub is_pre_listing: bool,
pub pre_listing_info: Option<PreListingInfo>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OptionInstrumentsInfo {
pub symbol: String,
#[serde(default)]
pub display_name: Option<String>,
pub status: Status,
pub base_coin: String,
pub quote_coin: String,
pub settle_coin: String,
pub options_type: OptionType,
#[serde(deserialize_with = "number")]
pub launch_time: Timestamp,
#[serde(deserialize_with = "number")]
pub delivery_time: Timestamp,
#[serde(default, deserialize_with = "option_decimal")]
pub delivery_fee_rate: Option<Decimal>,
pub price_filter: PriceFilter,
pub lot_size_filter: OptionLotSizeFilter,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OptionLotSizeFilter {
pub max_order_qty: Decimal,
pub min_order_qty: Decimal,
pub qty_step: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SpotInstrumentsInfo {
pub symbol: String,
pub base_coin: String,
pub quote_coin: String,
#[serde(deserialize_with = "string_to_bool")]
pub innovation: bool,
pub status: Status,
pub margin_trading: String,
#[serde(deserialize_with = "string_to_bool")]
pub st_tag: bool,
pub lot_size_filter: SpotLotSizeFilter,
pub price_filter: SpotPriceFilter,
pub risk_parameters: RiskParameters,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LeverageFilter {
pub min_leverage: Decimal,
pub max_leverage: Decimal,
pub leverage_step: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PriceFilter {
pub min_price: Decimal,
pub max_price: Decimal,
pub tick_size: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SpotPriceFilter {
pub tick_size: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LotSizeFilter {
pub min_notional_value: Decimal,
pub max_order_qty: Decimal,
pub max_mkt_order_qty: Decimal,
pub min_order_qty: Decimal,
pub qty_step: Decimal,
pub post_only_max_order_qty: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SpotLotSizeFilter {
pub base_precision: Decimal,
pub quote_precision: Decimal,
pub min_order_qty: Decimal,
pub max_order_qty: Decimal,
pub min_order_amt: Decimal,
pub max_order_amt: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RiskParameters {
pub price_limit_ratio_x: Decimal,
pub price_limit_ratio_y: Decimal,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PreListingInfo {
pub cur_auction_phase: CurAuctionPhase,
pub phases: Vec<Phase>,
pub auction_fee_info: AuctionFeeInfo,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Phase {
pub phase: CurAuctionPhase,
#[serde(deserialize_with = "option_number")]
pub start_time: Option<Timestamp>,
#[serde(deserialize_with = "option_number")]
pub end_time: Option<Timestamp>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AuctionFeeInfo {
pub auction_fee_rate: Decimal,
pub taker_fee_rate: Decimal,
pub maker_fee_rate: Decimal,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetFundingRateHistoryParams {
pub category: Category,
pub symbol: String,
pub start_time: Option<Timestamp>,
pub end_time: Option<Timestamp>,
pub limit: Option<u64>,
}
impl GetFundingRateHistoryParams {
pub fn new(category: Category, symbol: impl Into<String>) -> Self {
Self {
category,
symbol: symbol.into(),
start_time: None,
end_time: None,
limit: None,
}
}
pub fn with_start_time(mut self, v: Timestamp) -> Self {
self.start_time = Some(v);
self
}
pub fn with_end_time(mut self, v: Timestamp) -> Self {
self.end_time = Some(v);
self
}
pub fn with_limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(tag = "category")]
pub enum FundingRateHistory {
#[serde(rename = "linear")]
Linear {
list: Vec<FundingRateEntry>,
},
#[serde(rename = "inverse")]
Inverse {
list: Vec<FundingRateEntry>,
},
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct FundingRateEntry {
pub symbol: String,
pub funding_rate: Decimal,
#[serde(deserialize_with = "number")]
pub funding_rate_timestamp: Timestamp,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetOpenInterestParams {
pub category: Category,
pub symbol: String,
pub interval_time: IntervalTime,
pub start_time: Option<Timestamp>,
pub end_time: Option<Timestamp>,
pub limit: Option<u64>,
pub cursor: Option<String>,
}
impl GetOpenInterestParams {
pub fn new(category: Category, symbol: impl Into<String>, interval_time: IntervalTime) -> Self {
Self {
category,
symbol: symbol.into(),
interval_time,
start_time: None,
end_time: None,
limit: None,
cursor: None,
}
}
pub fn with_start_time(mut self, v: Timestamp) -> Self {
self.start_time = Some(v);
self
}
pub fn with_end_time(mut self, v: Timestamp) -> Self {
self.end_time = Some(v);
self
}
pub fn with_limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
self.cursor = Some(v.into());
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OpenInterest {
pub symbol: String,
pub category: String,
pub list: Vec<OpenInterestEntry>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub next_page_cursor: Option<String>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OpenInterestEntry {
pub open_interest: Decimal,
#[serde(deserialize_with = "number")]
pub timestamp: Timestamp,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetHistoricalVolatilityParams {
pub category: Category,
pub base_coin: Option<String>,
pub period: Option<u16>,
pub start_time: Option<Timestamp>,
pub end_time: Option<Timestamp>,
}
impl GetHistoricalVolatilityParams {
pub fn new(category: Category) -> Self {
Self {
category,
base_coin: None,
period: None,
start_time: None,
end_time: None,
}
}
pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
self.base_coin = Some(v.into());
self
}
pub fn with_period(mut self, v: u16) -> Self {
self.period = Some(v);
self
}
pub fn with_start_time(mut self, v: Timestamp) -> Self {
self.start_time = Some(v);
self
}
pub fn with_end_time(mut self, v: Timestamp) -> Self {
self.end_time = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct HistoricalVolatilityEntry {
pub period: u16,
pub value: Decimal,
#[serde(deserialize_with = "number")]
pub time: Timestamp,
}
#[derive(Debug, Serialize, Clone, Default)]
pub struct GetInsuranceParams {
pub coin: Option<String>,
}
impl GetInsuranceParams {
pub fn new() -> Self {
Self { coin: None }
}
pub fn with_coin(mut self, v: impl Into<String>) -> Self {
self.coin = Some(v.into());
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Insurance {
#[serde(deserialize_with = "number")]
pub updated_time: Timestamp,
pub list: Vec<InsuranceEntry>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InsuranceEntry {
pub coin: String,
pub balance: Decimal,
pub value: Decimal,
}
#[derive(Debug, Serialize, Clone)]
pub struct GetRiskLimitParams {
pub category: Category,
pub symbol: Option<String>,
pub cursor: Option<String>,
}
impl GetRiskLimitParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
cursor: None,
}
}
pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
self.symbol = Some(v.into());
self
}
pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
self.cursor = Some(v.into());
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RiskLimit {
pub category: String,
pub list: Vec<RiskLimitEntry>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RiskLimitEntry {
pub id: u32,
pub symbol: String,
pub risk_limit_value: Decimal,
pub maintenance_margin: Decimal,
pub initial_margin: Decimal,
#[serde(deserialize_with = "int_to_bool")]
pub is_lowest_risk: bool,
pub max_leverage: Decimal,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetDeliveryPriceParams {
pub category: Category,
pub symbol: Option<String>,
pub base_coin: Option<String>,
pub limit: Option<u64>,
pub cursor: Option<String>,
}
impl GetDeliveryPriceParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
base_coin: None,
limit: None,
cursor: None,
}
}
pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
self.symbol = Some(v.into());
self
}
pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
self.base_coin = Some(v.into());
self
}
pub fn with_limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
self.cursor = Some(v.into());
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DeliveryPrice {
pub category: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub next_page_cursor: Option<String>,
pub list: Vec<DeliveryPriceEntry>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DeliveryPriceEntry {
pub symbol: String,
pub delivery_price: Decimal,
#[serde(deserialize_with = "number")]
pub delivery_time: Timestamp,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Category, serde::serialize_query};
#[test]
fn trades_params_serialize_option_type_as_call_or_put() {
let params = GetTradesParams {
category: Category::Option,
symbol: None,
base_coin: Some(String::from("BTC")),
option_type: Some(OptionType::Put),
limit: None,
};
let query = serialize_query(¶ms).unwrap();
assert_eq!(query, "category=option&baseCoin=BTC&optionType=Put");
}
#[test]
fn base_coin_and_exp_date_are_sent_in_camel_case() {
let tickers = GetTickersParams::new(Category::Option)
.with_base_coin("ETH")
.with_exp_date("25DEC26");
let instruments = GetInstrumentsInfoParams::new(Category::Option).with_base_coin("ETH");
assert_eq!(
serialize_query(&tickers).unwrap(),
"category=option&baseCoin=ETH&expDate=25DEC26"
);
assert_eq!(
serialize_query(&instruments).unwrap(),
"category=option&baseCoin=ETH"
);
}
#[test]
fn option_instruments_info_parses() {
let json = r#"{
"category": "option",
"nextPageCursor": "",
"list": [{
"symbolId": 379919,
"symbol": "ETH-25JUN27-7000-P-USDT",
"status": "Trading",
"baseCoin": "ETH",
"quoteCoin": "USDT",
"settleCoin": "USDT",
"optionsType": "Put",
"launchTime": "1787350800000",
"deliveryTime": "1813910400000",
"deliveryFeeRate": "0.00015",
"priceFilter": {"minPrice": "0.1", "maxPrice": "41000", "tickSize": "0.1"},
"lotSizeFilter": {"maxOrderQty": "5000", "minOrderQty": "0.1", "qtyStep": "0.1"},
"displayName": "ETHUSDT-25JUN27-7000-P"
}]
}"#;
let info: InstrumentsInfo = serde_json::from_str(json).unwrap();
let InstrumentsInfo::Option { list, .. } = info else {
panic!("not an option list");
};
assert_eq!(list[0].options_type, OptionType::Put);
assert_eq!(list[0].lot_size_filter.qty_step, rust_decimal::dec!(0.1));
assert_eq!(list[0].delivery_time, 1813910400000);
}
}