#![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 TradeApi: Send + Sync {
async fn account_trade_list(
&self,
params: AccountTradeListParams,
) -> anyhow::Result<RestApiResponse<Vec<models::AccountTradeListResponseInner>>>;
async fn cancel_all_option_orders_by_underlying(
&self,
params: CancelAllOptionOrdersByUnderlyingParams,
) -> anyhow::Result<RestApiResponse<models::CancelAllOptionOrdersByUnderlyingResponse>>;
async fn cancel_all_option_orders_on_specific_symbol(
&self,
params: CancelAllOptionOrdersOnSpecificSymbolParams,
) -> anyhow::Result<RestApiResponse<models::CancelAllOptionOrdersOnSpecificSymbolResponse>>;
async fn cancel_multiple_option_orders(
&self,
params: CancelMultipleOptionOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::CancelMultipleOptionOrdersResponseInner>>>;
async fn cancel_option_order(
&self,
params: CancelOptionOrderParams,
) -> anyhow::Result<RestApiResponse<models::CancelOptionOrderResponse>>;
async fn new_order(
&self,
params: NewOrderParams,
) -> anyhow::Result<RestApiResponse<models::NewOrderResponse>>;
async fn option_position_information(
&self,
params: OptionPositionInformationParams,
) -> anyhow::Result<RestApiResponse<Vec<models::OptionPositionInformationResponseInner>>>;
async fn place_multiple_orders(
&self,
params: PlaceMultipleOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::PlaceMultipleOrdersResponseInner>>>;
async fn query_current_open_option_orders(
&self,
params: QueryCurrentOpenOptionOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::QueryCurrentOpenOptionOrdersResponseInner>>>;
async fn query_option_order_history(
&self,
params: QueryOptionOrderHistoryParams,
) -> anyhow::Result<RestApiResponse<Vec<models::QueryOptionOrderHistoryResponseInner>>>;
async fn query_single_order(
&self,
params: QuerySingleOrderParams,
) -> anyhow::Result<RestApiResponse<models::QuerySingleOrderResponse>>;
async fn user_commission(
&self,
params: UserCommissionParams,
) -> anyhow::Result<RestApiResponse<models::UserCommissionResponse>>;
async fn user_exercise_record(
&self,
params: UserExerciseRecordParams,
) -> anyhow::Result<RestApiResponse<Vec<models::UserExerciseRecordResponseInner>>>;
}
#[derive(Debug, Clone)]
pub struct TradeApiClient {
configuration: ConfigurationRestApi,
}
impl TradeApiClient {
pub fn new(configuration: ConfigurationRestApi) -> Self {
Self { configuration }
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NewOrderSideEnum {
#[serde(rename = "BUY")]
Buy,
#[serde(rename = "SELL")]
Sell,
}
impl NewOrderSideEnum {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Buy => "BUY",
Self::Sell => "SELL",
}
}
}
impl std::str::FromStr for NewOrderSideEnum {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"BUY" => Ok(Self::Buy),
"SELL" => Ok(Self::Sell),
other => Err(format!("invalid NewOrderSideEnum: {}", other).into()),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NewOrderTypeEnum {
#[serde(rename = "LIMIT")]
Limit,
}
impl NewOrderTypeEnum {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Limit => "LIMIT",
}
}
}
impl std::str::FromStr for NewOrderTypeEnum {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"LIMIT" => Ok(Self::Limit),
other => Err(format!("invalid NewOrderTypeEnum: {}", other).into()),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NewOrderTimeInForceEnum {
#[serde(rename = "GTC")]
Gtc,
#[serde(rename = "IOC")]
Ioc,
#[serde(rename = "FOK")]
Fok,
#[serde(rename = "GTX")]
Gtx,
}
impl NewOrderTimeInForceEnum {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Gtc => "GTC",
Self::Ioc => "IOC",
Self::Fok => "FOK",
Self::Gtx => "GTX",
}
}
}
impl std::str::FromStr for NewOrderTimeInForceEnum {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"GTC" => Ok(Self::Gtc),
"IOC" => Ok(Self::Ioc),
"FOK" => Ok(Self::Fok),
"GTX" => Ok(Self::Gtx),
other => Err(format!("invalid NewOrderTimeInForceEnum: {}", other).into()),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NewOrderNewOrderRespTypeEnum {
#[serde(rename = "ACK")]
Ack,
#[serde(rename = "RESULT")]
Result,
}
impl NewOrderNewOrderRespTypeEnum {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Ack => "ACK",
Self::Result => "RESULT",
}
}
}
impl std::str::FromStr for NewOrderNewOrderRespTypeEnum {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ACK" => Ok(Self::Ack),
"RESULT" => Ok(Self::Result),
other => Err(format!("invalid NewOrderNewOrderRespTypeEnum: {}", other).into()),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NewOrderSelfTradePreventionModeEnum {
#[serde(rename = "EXPIRE_TAKER")]
ExpireTaker,
#[serde(rename = "EXPIRE_BOTH")]
ExpireBoth,
#[serde(rename = "EXPIRE_MAKER")]
ExpireMaker,
}
impl NewOrderSelfTradePreventionModeEnum {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::ExpireTaker => "EXPIRE_TAKER",
Self::ExpireBoth => "EXPIRE_BOTH",
Self::ExpireMaker => "EXPIRE_MAKER",
}
}
}
impl std::str::FromStr for NewOrderSelfTradePreventionModeEnum {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"EXPIRE_TAKER" => Ok(Self::ExpireTaker),
"EXPIRE_BOTH" => Ok(Self::ExpireBoth),
"EXPIRE_MAKER" => Ok(Self::ExpireMaker),
other => Err(format!("invalid NewOrderSelfTradePreventionModeEnum: {}", other).into()),
}
}
}
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AccountTradeListParams {
#[builder(setter(into), default)]
pub symbol: Option<String>,
#[builder(setter(into), default)]
pub from_id: Option<i64>,
#[builder(setter(into), default)]
pub start_time: Option<i64>,
#[builder(setter(into), default)]
pub end_time: Option<i64>,
#[builder(setter(into), default)]
pub limit: Option<i64>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl AccountTradeListParams {
#[must_use]
pub fn builder() -> AccountTradeListParamsBuilder {
AccountTradeListParamsBuilder::default()
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct CancelAllOptionOrdersByUnderlyingParams {
#[builder(setter(into))]
pub underlying: String,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl CancelAllOptionOrdersByUnderlyingParams {
#[must_use]
pub fn builder(underlying: String) -> CancelAllOptionOrdersByUnderlyingParamsBuilder {
CancelAllOptionOrdersByUnderlyingParamsBuilder::default().underlying(underlying)
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct CancelAllOptionOrdersOnSpecificSymbolParams {
#[builder(setter(into))]
pub symbol: String,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl CancelAllOptionOrdersOnSpecificSymbolParams {
#[must_use]
pub fn builder(symbol: String) -> CancelAllOptionOrdersOnSpecificSymbolParamsBuilder {
CancelAllOptionOrdersOnSpecificSymbolParamsBuilder::default().symbol(symbol)
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct CancelMultipleOptionOrdersParams {
#[builder(setter(into))]
pub symbol: String,
#[builder(setter(into), default)]
pub order_ids: Option<Vec<i64>>,
#[builder(setter(into), default)]
pub client_order_ids: Option<Vec<String>>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl CancelMultipleOptionOrdersParams {
#[must_use]
pub fn builder(symbol: String) -> CancelMultipleOptionOrdersParamsBuilder {
CancelMultipleOptionOrdersParamsBuilder::default().symbol(symbol)
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct CancelOptionOrderParams {
#[builder(setter(into))]
pub symbol: String,
#[builder(setter(into), default)]
pub order_id: Option<i64>,
#[builder(setter(into), default)]
pub client_order_id: Option<String>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl CancelOptionOrderParams {
#[must_use]
pub fn builder(symbol: String) -> CancelOptionOrderParamsBuilder {
CancelOptionOrderParamsBuilder::default().symbol(symbol)
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct NewOrderParams {
#[builder(setter(into))]
pub symbol: String,
#[builder(setter(into))]
pub side: NewOrderSideEnum,
#[builder(setter(into))]
pub r#type: NewOrderTypeEnum,
#[builder(setter(into))]
pub quantity: rust_decimal::Decimal,
#[builder(setter(into), default)]
pub price: Option<rust_decimal::Decimal>,
#[builder(setter(into), default)]
pub time_in_force: Option<NewOrderTimeInForceEnum>,
#[builder(setter(into), default)]
pub reduce_only: Option<bool>,
#[builder(setter(into), default)]
pub post_only: Option<bool>,
#[builder(setter(into), default)]
pub new_order_resp_type: Option<NewOrderNewOrderRespTypeEnum>,
#[builder(setter(into), default)]
pub client_order_id: Option<String>,
#[builder(setter(into), default)]
pub is_mmp: Option<bool>,
#[builder(setter(into), default)]
pub self_trade_prevention_mode: Option<NewOrderSelfTradePreventionModeEnum>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl NewOrderParams {
#[must_use]
pub fn builder(
symbol: String,
side: NewOrderSideEnum,
r#type: NewOrderTypeEnum,
quantity: rust_decimal::Decimal,
) -> NewOrderParamsBuilder {
NewOrderParamsBuilder::default()
.symbol(symbol)
.side(side)
.r#type(r#type)
.quantity(quantity)
}
}
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OptionPositionInformationParams {
#[builder(setter(into), default)]
pub symbol: Option<String>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl OptionPositionInformationParams {
#[must_use]
pub fn builder() -> OptionPositionInformationParamsBuilder {
OptionPositionInformationParamsBuilder::default()
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct PlaceMultipleOrdersParams {
#[builder(setter(into))]
pub orders: Vec<models::PlaceMultipleOrdersOrdersParameterInner>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl PlaceMultipleOrdersParams {
#[must_use]
pub fn builder(
orders: Vec<models::PlaceMultipleOrdersOrdersParameterInner>,
) -> PlaceMultipleOrdersParamsBuilder {
PlaceMultipleOrdersParamsBuilder::default().orders(orders)
}
}
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryCurrentOpenOptionOrdersParams {
#[builder(setter(into), default)]
pub symbol: Option<String>,
#[builder(setter(into), default)]
pub order_id: Option<i64>,
#[builder(setter(into), default)]
pub start_time: Option<i64>,
#[builder(setter(into), default)]
pub end_time: Option<i64>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl QueryCurrentOpenOptionOrdersParams {
#[must_use]
pub fn builder() -> QueryCurrentOpenOptionOrdersParamsBuilder {
QueryCurrentOpenOptionOrdersParamsBuilder::default()
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryOptionOrderHistoryParams {
#[builder(setter(into))]
pub symbol: String,
#[builder(setter(into), default)]
pub order_id: Option<i64>,
#[builder(setter(into), default)]
pub start_time: Option<i64>,
#[builder(setter(into), default)]
pub end_time: Option<i64>,
#[builder(setter(into), default)]
pub limit: Option<i64>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl QueryOptionOrderHistoryParams {
#[must_use]
pub fn builder(symbol: String) -> QueryOptionOrderHistoryParamsBuilder {
QueryOptionOrderHistoryParamsBuilder::default().symbol(symbol)
}
}
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QuerySingleOrderParams {
#[builder(setter(into))]
pub symbol: String,
#[builder(setter(into), default)]
pub order_id: Option<i64>,
#[builder(setter(into), default)]
pub client_order_id: Option<String>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl QuerySingleOrderParams {
#[must_use]
pub fn builder(symbol: String) -> QuerySingleOrderParamsBuilder {
QuerySingleOrderParamsBuilder::default().symbol(symbol)
}
}
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct UserCommissionParams {
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl UserCommissionParams {
#[must_use]
pub fn builder() -> UserCommissionParamsBuilder {
UserCommissionParamsBuilder::default()
}
}
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct UserExerciseRecordParams {
#[builder(setter(into), default)]
pub symbol: Option<String>,
#[builder(setter(into), default)]
pub start_time: Option<i64>,
#[builder(setter(into), default)]
pub end_time: Option<i64>,
#[builder(setter(into), default)]
pub limit: Option<i64>,
#[builder(setter(into), default)]
pub recv_window: Option<i64>,
}
impl UserExerciseRecordParams {
#[must_use]
pub fn builder() -> UserExerciseRecordParamsBuilder {
UserExerciseRecordParamsBuilder::default()
}
}
#[async_trait]
impl TradeApi for TradeApiClient {
async fn account_trade_list(
&self,
params: AccountTradeListParams,
) -> anyhow::Result<RestApiResponse<Vec<models::AccountTradeListResponseInner>>> {
let AccountTradeListParams {
symbol,
from_id,
start_time,
end_time,
limit,
recv_window,
} = 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) = from_id {
query_params.insert("fromId".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));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::AccountTradeListResponseInner>>(
&self.configuration,
"/eapi/v1/userTrades",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn cancel_all_option_orders_by_underlying(
&self,
params: CancelAllOptionOrdersByUnderlyingParams,
) -> anyhow::Result<RestApiResponse<models::CancelAllOptionOrdersByUnderlyingResponse>> {
let CancelAllOptionOrdersByUnderlyingParams {
underlying,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("underlying".to_string(), json!(underlying));
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<models::CancelAllOptionOrdersByUnderlyingResponse>(
&self.configuration,
"/eapi/v1/allOpenOrdersByUnderlying",
reqwest::Method::DELETE,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn cancel_all_option_orders_on_specific_symbol(
&self,
params: CancelAllOptionOrdersOnSpecificSymbolParams,
) -> anyhow::Result<RestApiResponse<models::CancelAllOptionOrdersOnSpecificSymbolResponse>>
{
let CancelAllOptionOrdersOnSpecificSymbolParams {
symbol,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("symbol".to_string(), json!(symbol));
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<models::CancelAllOptionOrdersOnSpecificSymbolResponse>(
&self.configuration,
"/eapi/v1/allOpenOrders",
reqwest::Method::DELETE,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn cancel_multiple_option_orders(
&self,
params: CancelMultipleOptionOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::CancelMultipleOptionOrdersResponseInner>>> {
let CancelMultipleOptionOrdersParams {
symbol,
order_ids,
client_order_ids,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("symbol".to_string(), json!(symbol));
if let Some(rw) = order_ids {
query_params.insert("orderIds".to_string(), json!(rw));
}
if let Some(rw) = client_order_ids {
query_params.insert("clientOrderIds".to_string(), json!(rw));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::CancelMultipleOptionOrdersResponseInner>>(
&self.configuration,
"/eapi/v1/batchOrders",
reqwest::Method::DELETE,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn cancel_option_order(
&self,
params: CancelOptionOrderParams,
) -> anyhow::Result<RestApiResponse<models::CancelOptionOrderResponse>> {
let CancelOptionOrderParams {
symbol,
order_id,
client_order_id,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("symbol".to_string(), json!(symbol));
if let Some(rw) = order_id {
query_params.insert("orderId".to_string(), json!(rw));
}
if let Some(rw) = client_order_id {
query_params.insert("clientOrderId".to_string(), json!(rw));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<models::CancelOptionOrderResponse>(
&self.configuration,
"/eapi/v1/order",
reqwest::Method::DELETE,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn new_order(
&self,
params: NewOrderParams,
) -> anyhow::Result<RestApiResponse<models::NewOrderResponse>> {
let NewOrderParams {
symbol,
side,
r#type,
quantity,
price,
time_in_force,
reduce_only,
post_only,
new_order_resp_type,
client_order_id,
is_mmp,
self_trade_prevention_mode,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("symbol".to_string(), json!(symbol));
query_params.insert("side".to_string(), json!(side));
query_params.insert("type".to_string(), json!(r#type));
query_params.insert("quantity".to_string(), json!(quantity));
if let Some(rw) = price {
query_params.insert("price".to_string(), json!(rw));
}
if let Some(rw) = time_in_force {
query_params.insert("timeInForce".to_string(), json!(rw));
}
if let Some(rw) = reduce_only {
query_params.insert("reduceOnly".to_string(), json!(rw));
}
if let Some(rw) = post_only {
query_params.insert("postOnly".to_string(), json!(rw));
}
if let Some(rw) = new_order_resp_type {
query_params.insert("newOrderRespType".to_string(), json!(rw));
}
if let Some(rw) = client_order_id {
query_params.insert("clientOrderId".to_string(), json!(rw));
}
if let Some(rw) = is_mmp {
query_params.insert("isMmp".to_string(), json!(rw));
}
if let Some(rw) = self_trade_prevention_mode {
query_params.insert("selfTradePreventionMode".to_string(), json!(rw));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<models::NewOrderResponse>(
&self.configuration,
"/eapi/v1/order",
reqwest::Method::POST,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn option_position_information(
&self,
params: OptionPositionInformationParams,
) -> anyhow::Result<RestApiResponse<Vec<models::OptionPositionInformationResponseInner>>> {
let OptionPositionInformationParams {
symbol,
recv_window,
} = 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) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::OptionPositionInformationResponseInner>>(
&self.configuration,
"/eapi/v1/position",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn place_multiple_orders(
&self,
params: PlaceMultipleOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::PlaceMultipleOrdersResponseInner>>> {
let PlaceMultipleOrdersParams {
orders,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("orders".to_string(), json!(orders));
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::PlaceMultipleOrdersResponseInner>>(
&self.configuration,
"/eapi/v1/batchOrders",
reqwest::Method::POST,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn query_current_open_option_orders(
&self,
params: QueryCurrentOpenOptionOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::QueryCurrentOpenOptionOrdersResponseInner>>>
{
let QueryCurrentOpenOptionOrdersParams {
symbol,
order_id,
start_time,
end_time,
recv_window,
} = 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) = order_id {
query_params.insert("orderId".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) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::QueryCurrentOpenOptionOrdersResponseInner>>(
&self.configuration,
"/eapi/v1/openOrders",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn query_option_order_history(
&self,
params: QueryOptionOrderHistoryParams,
) -> anyhow::Result<RestApiResponse<Vec<models::QueryOptionOrderHistoryResponseInner>>> {
let QueryOptionOrderHistoryParams {
symbol,
order_id,
start_time,
end_time,
limit,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("symbol".to_string(), json!(symbol));
if let Some(rw) = order_id {
query_params.insert("orderId".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));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::QueryOptionOrderHistoryResponseInner>>(
&self.configuration,
"/eapi/v1/historyOrders",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn query_single_order(
&self,
params: QuerySingleOrderParams,
) -> anyhow::Result<RestApiResponse<models::QuerySingleOrderResponse>> {
let QuerySingleOrderParams {
symbol,
order_id,
client_order_id,
recv_window,
} = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
query_params.insert("symbol".to_string(), json!(symbol));
if let Some(rw) = order_id {
query_params.insert("orderId".to_string(), json!(rw));
}
if let Some(rw) = client_order_id {
query_params.insert("clientOrderId".to_string(), json!(rw));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<models::QuerySingleOrderResponse>(
&self.configuration,
"/eapi/v1/order",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn user_commission(
&self,
params: UserCommissionParams,
) -> anyhow::Result<RestApiResponse<models::UserCommissionResponse>> {
let UserCommissionParams { recv_window } = params;
let mut query_params = BTreeMap::new();
let body_params = BTreeMap::new();
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<models::UserCommissionResponse>(
&self.configuration,
"/eapi/v1/commission",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.await
}
async fn user_exercise_record(
&self,
params: UserExerciseRecordParams,
) -> anyhow::Result<RestApiResponse<Vec<models::UserExerciseRecordResponseInner>>> {
let UserExerciseRecordParams {
symbol,
start_time,
end_time,
limit,
recv_window,
} = 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) = 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));
}
if let Some(rw) = recv_window {
query_params.insert("recvWindow".to_string(), json!(rw));
}
send_request::<Vec<models::UserExerciseRecordResponseInner>>(
&self.configuration,
"/eapi/v1/exerciseRecord",
reqwest::Method::GET,
query_params,
body_params,
if HAS_TIME_UNIT {
self.configuration.time_unit
} else {
None
},
true,
)
.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 MockTradeApiClient {
force_error: bool,
}
#[async_trait]
impl TradeApi for MockTradeApiClient {
async fn account_trade_list(
&self,
_params: AccountTradeListParams,
) -> anyhow::Result<RestApiResponse<Vec<models::AccountTradeListResponseInner>>> {
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":4611875134427365000,"tradeId":239,"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","fee":"0","realizedProfit":"0.00000000","side":"BUY","type":"LIMIT","liquidity":"TAKER","time":1592465880683,"priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT"}]"#).unwrap();
let dummy_response: Vec<models::AccountTradeListResponseInner> =
serde_json::from_value(resp_json.clone())
.expect("should parse into Vec<models::AccountTradeListResponseInner>");
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 cancel_all_option_orders_by_underlying(
&self,
_params: CancelAllOptionOrdersByUnderlyingParams,
) -> anyhow::Result<RestApiResponse<models::CancelAllOptionOrdersByUnderlyingResponse>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"success"}"#).unwrap();
let dummy_response: models::CancelAllOptionOrdersByUnderlyingResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::CancelAllOptionOrdersByUnderlyingResponse");
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 cancel_all_option_orders_on_specific_symbol(
&self,
_params: CancelAllOptionOrdersOnSpecificSymbolParams,
) -> anyhow::Result<RestApiResponse<models::CancelAllOptionOrdersOnSpecificSymbolResponse>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"{"code":"0","msg":"success"}"#).unwrap();
let dummy_response: models::CancelAllOptionOrdersOnSpecificSymbolResponse =
serde_json::from_value(resp_json.clone()).expect(
"should parse into models::CancelAllOptionOrdersOnSpecificSymbolResponse",
);
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 cancel_multiple_option_orders(
&self,
_params: CancelMultipleOptionOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::CancelMultipleOptionOrdersResponseInner>>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":3,"quantityScale":4,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let dummy_response: Vec<models::CancelMultipleOptionOrdersResponseInner> =
serde_json::from_value(resp_json.clone()).expect(
"should parse into Vec<models::CancelMultipleOptionOrdersResponseInner>",
);
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 cancel_option_order(
&self,
_params: CancelOptionOrderParams,
) -> anyhow::Result<RestApiResponse<models::CancelOptionOrderResponse>> {
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createDate":1592465880683,"updateTime":1566818724722,"status":"ACCEPTED","avgPrice":"0","source":"API","clientOrderId":"","priceScale":4,"quantityScale":4,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let dummy_response: models::CancelOptionOrderResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::CancelOptionOrderResponse");
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 new_order(
&self,
_params: NewOrderParams,
) -> anyhow::Result<RestApiResponse<models::NewOrderResponse>> {
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let dummy_response: models::NewOrderResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::NewOrderResponse");
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_position_information(
&self,
_params: OptionPositionInformationParams,
) -> anyhow::Result<RestApiResponse<Vec<models::OptionPositionInformationResponseInner>>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"[{"entryPrice":"1000","symbol":"BTC-200730-9000-C","side":"SHORT","quantity":"-0.1","markValue":"105.00138","unrealizedPNL":"-5.00138","markPrice":"1050.0138","strikePrice":"9000","expiryDate":1593511200000,"priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","time":1762872654561,"bidQuantity":"0.0000","askQuantity":"0.0000"}]"#).unwrap();
let dummy_response: Vec<models::OptionPositionInformationResponseInner> =
serde_json::from_value(resp_json.clone()).expect(
"should parse into Vec<models::OptionPositionInformationResponseInner>",
);
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 place_multiple_orders(
&self,
_params: PlaceMultipleOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::PlaceMultipleOrdersResponseInner>>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let dummy_response: Vec<models::PlaceMultipleOrdersResponseInner> =
serde_json::from_value(resp_json.clone())
.expect("should parse into Vec<models::PlaceMultipleOrdersResponseInner>");
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 query_current_open_option_orders(
&self,
_params: QueryCurrentOpenOptionOrdersParams,
) -> anyhow::Result<RestApiResponse<Vec<models::QueryCurrentOpenOptionOrdersResponseInner>>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1592465880683,"status":"NEW","avgPrice":"0","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let dummy_response: Vec<models::QueryCurrentOpenOptionOrdersResponseInner> =
serde_json::from_value(resp_json.clone()).expect(
"should parse into Vec<models::QueryCurrentOpenOptionOrdersResponseInner>",
);
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 query_option_order_history(
&self,
_params: QueryOptionOrderHistoryParams,
) -> anyhow::Result<RestApiResponse<Vec<models::QueryOptionOrderHistoryResponseInner>>>
{
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611922413427360000,"symbol":"BTC-220715-2000-C","price":"18000.00000000","quantity":"-0.50000000","executedQty":"-0.50000000","side":"SELL","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1657867694244,"updateTime":1657867888216,"status":"FILLED","avgPrice":"18000.00000000","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false}]"#).unwrap();
let dummy_response: Vec<models::QueryOptionOrderHistoryResponseInner> =
serde_json::from_value(resp_json.clone())
.expect("should parse into Vec<models::QueryOptionOrderHistoryResponseInner>");
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 query_single_order(
&self,
_params: QuerySingleOrderParams,
) -> anyhow::Result<RestApiResponse<models::QuerySingleOrderResponse>> {
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let dummy_response: models::QuerySingleOrderResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::QuerySingleOrderResponse");
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 user_commission(
&self,
_params: UserCommissionParams,
) -> anyhow::Result<RestApiResponse<models::UserCommissionResponse>> {
if self.force_error {
return Err(ConnectorError::ConnectorClientError {
msg: "ResponseError".to_string(),
code: None,
}
.into());
}
let resp_json: Value = serde_json::from_str(r#"{"commissions":[{"underlying":"BTCUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"ETHUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"BNBUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"SOLUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"XRPUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"DOGEUSDT","makerFee":"0.000240","takerFee":"0.000240"}]}"#).unwrap();
let dummy_response: models::UserCommissionResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::UserCommissionResponse");
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 user_exercise_record(
&self,
_params: UserExerciseRecordParams,
) -> anyhow::Result<RestApiResponse<Vec<models::UserExerciseRecordResponseInner>>> {
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":"1125899906842624042","currency":"USDT","symbol":"BTC-220721-25000-C","exercisePrice":"25000.00000000","quantity":"1.00000000","amount":"0.00000000","fee":"0.00000000","createDate":1658361600000,"priceScale":2,"quantityScale":2,"optionSide":"CALL","positionSide":"LONG","quoteAsset":"USDT"}]"#).unwrap();
let dummy_response: Vec<models::UserExerciseRecordResponseInner> =
serde_json::from_value(resp_json.clone())
.expect("should parse into Vec<models::UserExerciseRecordResponseInner>");
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 account_trade_list_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = AccountTradeListParams::builder().build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"id":4611875134427365000,"tradeId":239,"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","fee":"0","realizedProfit":"0.00000000","side":"BUY","type":"LIMIT","liquidity":"TAKER","time":1592465880683,"priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT"}]"#).unwrap();
let expected_response : Vec<models::AccountTradeListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::AccountTradeListResponseInner>");
let resp = client.account_trade_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 account_trade_list_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = AccountTradeListParams::builder().symbol("symbol_example".to_string()).from_id(1).start_time(1623319461670).end_time(1641782889000).limit(100).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"id":4611875134427365000,"tradeId":239,"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","fee":"0","realizedProfit":"0.00000000","side":"BUY","type":"LIMIT","liquidity":"TAKER","time":1592465880683,"priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT"}]"#).unwrap();
let expected_response : Vec<models::AccountTradeListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::AccountTradeListResponseInner>");
let resp = client.account_trade_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 account_trade_list_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = AccountTradeListParams::builder().build().unwrap();
match client.account_trade_list(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn cancel_all_option_orders_by_underlying_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params =
CancelAllOptionOrdersByUnderlyingParams::builder("underlying_example".to_string())
.build()
.unwrap();
let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"success"}"#).unwrap();
let expected_response: models::CancelAllOptionOrdersByUnderlyingResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::CancelAllOptionOrdersByUnderlyingResponse");
let resp = client
.cancel_all_option_orders_by_underlying(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 cancel_all_option_orders_by_underlying_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params =
CancelAllOptionOrdersByUnderlyingParams::builder("underlying_example".to_string())
.recv_window(5000)
.build()
.unwrap();
let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"success"}"#).unwrap();
let expected_response: models::CancelAllOptionOrdersByUnderlyingResponse =
serde_json::from_value(resp_json.clone())
.expect("should parse into models::CancelAllOptionOrdersByUnderlyingResponse");
let resp = client
.cancel_all_option_orders_by_underlying(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 cancel_all_option_orders_by_underlying_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params =
CancelAllOptionOrdersByUnderlyingParams::builder("underlying_example".to_string())
.build()
.unwrap();
match client.cancel_all_option_orders_by_underlying(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn cancel_all_option_orders_on_specific_symbol_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params =
CancelAllOptionOrdersOnSpecificSymbolParams::builder("symbol_example".to_string())
.build()
.unwrap();
let resp_json: Value = serde_json::from_str(r#"{"code":"0","msg":"success"}"#).unwrap();
let expected_response: models::CancelAllOptionOrdersOnSpecificSymbolResponse =
serde_json::from_value(resp_json.clone()).expect(
"should parse into models::CancelAllOptionOrdersOnSpecificSymbolResponse",
);
let resp = client
.cancel_all_option_orders_on_specific_symbol(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 cancel_all_option_orders_on_specific_symbol_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params =
CancelAllOptionOrdersOnSpecificSymbolParams::builder("symbol_example".to_string())
.recv_window(5000)
.build()
.unwrap();
let resp_json: Value = serde_json::from_str(r#"{"code":"0","msg":"success"}"#).unwrap();
let expected_response: models::CancelAllOptionOrdersOnSpecificSymbolResponse =
serde_json::from_value(resp_json.clone()).expect(
"should parse into models::CancelAllOptionOrdersOnSpecificSymbolResponse",
);
let resp = client
.cancel_all_option_orders_on_specific_symbol(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 cancel_all_option_orders_on_specific_symbol_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params =
CancelAllOptionOrdersOnSpecificSymbolParams::builder("symbol_example".to_string())
.build()
.unwrap();
match client
.cancel_all_option_orders_on_specific_symbol(params)
.await
{
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn cancel_multiple_option_orders_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = CancelMultipleOptionOrdersParams::builder("symbol_example".to_string(),).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":3,"quantityScale":4,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let expected_response : Vec<models::CancelMultipleOptionOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::CancelMultipleOptionOrdersResponseInner>");
let resp = client.cancel_multiple_option_orders(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 cancel_multiple_option_orders_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = CancelMultipleOptionOrdersParams::builder("symbol_example".to_string(),).order_ids([4611875134427365000,].to_vec()).client_order_ids(["my_id_1".to_string(),].to_vec()).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":3,"quantityScale":4,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let expected_response : Vec<models::CancelMultipleOptionOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::CancelMultipleOptionOrdersResponseInner>");
let resp = client.cancel_multiple_option_orders(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 cancel_multiple_option_orders_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = CancelMultipleOptionOrdersParams::builder("symbol_example".to_string())
.build()
.unwrap();
match client.cancel_multiple_option_orders(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn cancel_option_order_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = CancelOptionOrderParams::builder("symbol_example".to_string(),).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createDate":1592465880683,"updateTime":1566818724722,"status":"ACCEPTED","avgPrice":"0","source":"API","clientOrderId":"","priceScale":4,"quantityScale":4,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let expected_response : models::CancelOptionOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::CancelOptionOrderResponse");
let resp = client.cancel_option_order(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 cancel_option_order_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = CancelOptionOrderParams::builder("symbol_example".to_string(),).order_id(1).client_order_id("1".to_string()).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createDate":1592465880683,"updateTime":1566818724722,"status":"ACCEPTED","avgPrice":"0","source":"API","clientOrderId":"","priceScale":4,"quantityScale":4,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let expected_response : models::CancelOptionOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::CancelOptionOrderResponse");
let resp = client.cancel_option_order(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 cancel_option_order_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = CancelOptionOrderParams::builder("symbol_example".to_string())
.build()
.unwrap();
match client.cancel_option_order(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn new_order_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = NewOrderParams::builder("symbol_example".to_string(),NewOrderSideEnum::Buy,NewOrderTypeEnum::Limit,dec!(1.0),).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let expected_response : models::NewOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::NewOrderResponse");
let resp = client.new_order(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 new_order_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = NewOrderParams::builder("symbol_example".to_string(),NewOrderSideEnum::Buy,NewOrderTypeEnum::Limit,dec!(1.0),).price(dec!(1.0)).time_in_force(NewOrderTimeInForceEnum::Gtc).reduce_only(false).post_only(false).new_order_resp_type(NewOrderNewOrderRespTypeEnum::Ack).client_order_id("1".to_string()).is_mmp(true).self_trade_prevention_mode(NewOrderSelfTradePreventionModeEnum::ExpireTaker).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let expected_response : models::NewOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::NewOrderResponse");
let resp = client.new_order(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 new_order_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = NewOrderParams::builder(
"symbol_example".to_string(),
NewOrderSideEnum::Buy,
NewOrderTypeEnum::Limit,
dec!(1.0),
)
.build()
.unwrap();
match client.new_order(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn option_position_information_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = OptionPositionInformationParams::builder().build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"entryPrice":"1000","symbol":"BTC-200730-9000-C","side":"SHORT","quantity":"-0.1","markValue":"105.00138","unrealizedPNL":"-5.00138","markPrice":"1050.0138","strikePrice":"9000","expiryDate":1593511200000,"priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","time":1762872654561,"bidQuantity":"0.0000","askQuantity":"0.0000"}]"#).unwrap();
let expected_response : Vec<models::OptionPositionInformationResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::OptionPositionInformationResponseInner>");
let resp = client.option_position_information(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_position_information_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = OptionPositionInformationParams::builder().symbol("symbol_example".to_string()).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"entryPrice":"1000","symbol":"BTC-200730-9000-C","side":"SHORT","quantity":"-0.1","markValue":"105.00138","unrealizedPNL":"-5.00138","markPrice":"1050.0138","strikePrice":"9000","expiryDate":1593511200000,"priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","time":1762872654561,"bidQuantity":"0.0000","askQuantity":"0.0000"}]"#).unwrap();
let expected_response : Vec<models::OptionPositionInformationResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::OptionPositionInformationResponseInner>");
let resp = client.option_position_information(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_position_information_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = OptionPositionInformationParams::builder().build().unwrap();
match client.option_position_information(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn place_multiple_orders_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = PlaceMultipleOrdersParams::builder(vec![],).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let expected_response : Vec<models::PlaceMultipleOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::PlaceMultipleOrdersResponseInner>");
let resp = client.place_multiple_orders(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 place_multiple_orders_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = PlaceMultipleOrdersParams::builder(vec![],).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","source":"API","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let expected_response : Vec<models::PlaceMultipleOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::PlaceMultipleOrdersResponseInner>");
let resp = client.place_multiple_orders(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 place_multiple_orders_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = PlaceMultipleOrdersParams::builder(vec![]).build().unwrap();
match client.place_multiple_orders(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn query_current_open_option_orders_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = QueryCurrentOpenOptionOrdersParams::builder().build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1592465880683,"status":"NEW","avgPrice":"0","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let expected_response : Vec<models::QueryCurrentOpenOptionOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryCurrentOpenOptionOrdersResponseInner>");
let resp = client.query_current_open_option_orders(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 query_current_open_option_orders_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = QueryCurrentOpenOptionOrdersParams::builder().symbol("symbol_example".to_string()).order_id(1).start_time(1623319461670).end_time(1641782889000).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1592465880683,"status":"NEW","avgPrice":"0","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}]"#).unwrap();
let expected_response : Vec<models::QueryCurrentOpenOptionOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryCurrentOpenOptionOrdersResponseInner>");
let resp = client.query_current_open_option_orders(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 query_current_open_option_orders_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = QueryCurrentOpenOptionOrdersParams::builder()
.build()
.unwrap();
match client.query_current_open_option_orders(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn query_option_order_history_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = QueryOptionOrderHistoryParams::builder("symbol_example".to_string(),).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611922413427360000,"symbol":"BTC-220715-2000-C","price":"18000.00000000","quantity":"-0.50000000","executedQty":"-0.50000000","side":"SELL","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1657867694244,"updateTime":1657867888216,"status":"FILLED","avgPrice":"18000.00000000","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false}]"#).unwrap();
let expected_response : Vec<models::QueryOptionOrderHistoryResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryOptionOrderHistoryResponseInner>");
let resp = client.query_option_order_history(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 query_option_order_history_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = QueryOptionOrderHistoryParams::builder("symbol_example".to_string(),).order_id(1).start_time(1623319461670).end_time(1641782889000).limit(100).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"orderId":4611922413427360000,"symbol":"BTC-220715-2000-C","price":"18000.00000000","quantity":"-0.50000000","executedQty":"-0.50000000","side":"SELL","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1657867694244,"updateTime":1657867888216,"status":"FILLED","avgPrice":"18000.00000000","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false}]"#).unwrap();
let expected_response : Vec<models::QueryOptionOrderHistoryResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryOptionOrderHistoryResponseInner>");
let resp = client.query_option_order_history(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 query_option_order_history_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = QueryOptionOrderHistoryParams::builder("symbol_example".to_string())
.build()
.unwrap();
match client.query_option_order_history(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn query_single_order_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = QuerySingleOrderParams::builder("symbol_example".to_string(),).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let expected_response : models::QuerySingleOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QuerySingleOrderResponse");
let resp = client.query_single_order(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 query_single_order_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = QuerySingleOrderParams::builder("symbol_example".to_string(),).order_id(1).client_order_id("1".to_string()).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"orderId":4611875134427365000,"symbol":"BTC-200730-9000-C","price":"100","quantity":"1","executedQty":"0","side":"BUY","type":"LIMIT","timeInForce":"GTC","reduceOnly":false,"createTime":1592465880683,"updateTime":1566818724722,"status":"NEW","avgPrice":"0","clientOrderId":"","priceScale":2,"quantityScale":2,"optionSide":"CALL","quoteAsset":"USDT","mmp":false,"selfTradePreventionMode":"EXPIRE_MAKER"}"#).unwrap();
let expected_response : models::QuerySingleOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QuerySingleOrderResponse");
let resp = client.query_single_order(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 query_single_order_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = QuerySingleOrderParams::builder("symbol_example".to_string())
.build()
.unwrap();
match client.query_single_order(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn user_commission_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = UserCommissionParams::builder().build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"commissions":[{"underlying":"BTCUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"ETHUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"BNBUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"SOLUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"XRPUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"DOGEUSDT","makerFee":"0.000240","takerFee":"0.000240"}]}"#).unwrap();
let expected_response : models::UserCommissionResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::UserCommissionResponse");
let resp = client.user_commission(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 user_commission_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = UserCommissionParams::builder().recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"{"commissions":[{"underlying":"BTCUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"ETHUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"BNBUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"SOLUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"XRPUSDT","makerFee":"0.000240","takerFee":"0.000240"},{"underlying":"DOGEUSDT","makerFee":"0.000240","takerFee":"0.000240"}]}"#).unwrap();
let expected_response : models::UserCommissionResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::UserCommissionResponse");
let resp = client.user_commission(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 user_commission_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = UserCommissionParams::builder().build().unwrap();
match client.user_commission(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
#[test]
fn user_exercise_record_required_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = UserExerciseRecordParams::builder().build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"id":"1125899906842624042","currency":"USDT","symbol":"BTC-220721-25000-C","exercisePrice":"25000.00000000","quantity":"1.00000000","amount":"0.00000000","fee":"0.00000000","createDate":1658361600000,"priceScale":2,"quantityScale":2,"optionSide":"CALL","positionSide":"LONG","quoteAsset":"USDT"}]"#).unwrap();
let expected_response : Vec<models::UserExerciseRecordResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::UserExerciseRecordResponseInner>");
let resp = client.user_exercise_record(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 user_exercise_record_optional_params_success() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: false };
let params = UserExerciseRecordParams::builder().symbol("symbol_example".to_string()).start_time(1623319461670).end_time(1641782889000).limit(100).recv_window(5000).build().unwrap();
let resp_json: Value = serde_json::from_str(r#"[{"id":"1125899906842624042","currency":"USDT","symbol":"BTC-220721-25000-C","exercisePrice":"25000.00000000","quantity":"1.00000000","amount":"0.00000000","fee":"0.00000000","createDate":1658361600000,"priceScale":2,"quantityScale":2,"optionSide":"CALL","positionSide":"LONG","quoteAsset":"USDT"}]"#).unwrap();
let expected_response : Vec<models::UserExerciseRecordResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::UserExerciseRecordResponseInner>");
let resp = client.user_exercise_record(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 user_exercise_record_response_error() {
TOKIO_SHARED_RT.block_on(async {
let client = MockTradeApiClient { force_error: true };
let params = UserExerciseRecordParams::builder().build().unwrap();
match client.user_exercise_record(params).await {
Ok(_) => panic!("Expected an error"),
Err(err) => {
assert_eq!(err.to_string(), "Connector client error: ResponseError");
}
}
});
}
}