use crate::futures::models::account::{AdlQuantileResponse, CommissionRateResponse, MultiAssetsModeResponse, StatusMsgResponse};
use crate::futures::models::trading::{
AllOrdersParams, CountdownParams, CountdownResponse, EmptyResponse, GetOrderParams,
IncomeParams, IncomeRecord, OrderResponse, PositionMarginParams, PositionMarginResponse,
UserTrade, UserTradesParams,
};
use crate::rest::client::RestClient;
use crate::rest::error::AsterDexError;
use crate::rest::response::ApiResponse;
impl RestClient {
pub async fn get_order(
&self,
params: GetOrderParams,
) -> Result<ApiResponse<OrderResponse>, AsterDexError> {
let order_id_str = params.order_id.map(|id| id.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(3);
pairs.push(("symbol", ¶ms.symbol));
if let Some(ref s) = order_id_str {
pairs.push(("orderId", s));
}
if let Some(ref cid) = params.orig_client_order_id {
pairs.push(("origClientOrderId", cid));
}
self.signed_get("/fapi/v3/order", &pairs).await
}
pub async fn get_open_order(
&self,
params: GetOrderParams,
) -> Result<ApiResponse<OrderResponse>, AsterDexError> {
let order_id_str = params.order_id.map(|id| id.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(3);
pairs.push(("symbol", ¶ms.symbol));
if let Some(ref s) = order_id_str {
pairs.push(("orderId", s));
}
if let Some(ref cid) = params.orig_client_order_id {
pairs.push(("origClientOrderId", cid));
}
self.signed_get("/fapi/v3/openOrder", &pairs).await
}
pub async fn get_all_orders(
&self,
params: AllOrdersParams,
) -> Result<ApiResponse<Vec<OrderResponse>>, AsterDexError> {
let order_id_str = params.order_id.map(|id| id.to_string());
let start_time_str = params.start_time.map(|t| t.to_string());
let end_time_str = params.end_time.map(|t| t.to_string());
let limit_str = params.limit.map(|l| l.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
pairs.push(("symbol", ¶ms.symbol));
if let Some(ref s) = order_id_str {
pairs.push(("orderId", s));
}
if let Some(ref s) = start_time_str {
pairs.push(("startTime", s));
}
if let Some(ref s) = end_time_str {
pairs.push(("endTime", s));
}
if let Some(ref s) = limit_str {
pairs.push(("limit", s));
}
self.signed_get("/fapi/v3/allOrders", &pairs).await
}
pub async fn get_user_trades(
&self,
params: UserTradesParams,
) -> Result<ApiResponse<Vec<UserTrade>>, AsterDexError> {
let order_id_str = params.order_id.map(|id| id.to_string());
let start_time_str = params.start_time.map(|t| t.to_string());
let end_time_str = params.end_time.map(|t| t.to_string());
let from_id_str = params.from_id.map(|id| id.to_string());
let limit_str = params.limit.map(|l| l.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(6);
pairs.push(("symbol", ¶ms.symbol));
if let Some(ref s) = order_id_str {
pairs.push(("orderId", s));
}
if let Some(ref s) = start_time_str {
pairs.push(("startTime", s));
}
if let Some(ref s) = end_time_str {
pairs.push(("endTime", s));
}
if let Some(ref s) = from_id_str {
pairs.push(("fromId", s));
}
if let Some(ref s) = limit_str {
pairs.push(("limit", s));
}
self.signed_get("/fapi/v3/trades", &pairs).await
}
pub async fn get_income(
&self,
params: IncomeParams,
) -> Result<ApiResponse<Vec<IncomeRecord>>, AsterDexError> {
let start_time_str = params.start_time.map(|t| t.to_string());
let end_time_str = params.end_time.map(|t| t.to_string());
let limit_str = params.limit.map(|l| l.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
if let Some(ref s) = params.symbol {
pairs.push(("symbol", s));
}
if let Some(ref it) = params.income_type {
pairs.push(("incomeType", it));
}
if let Some(ref s) = start_time_str {
pairs.push(("startTime", s));
}
if let Some(ref s) = end_time_str {
pairs.push(("endTime", s));
}
if let Some(ref s) = limit_str {
pairs.push(("limit", s));
}
self.signed_get("/fapi/v3/income", &pairs).await
}
pub async fn set_position_margin(
&self,
params: PositionMarginParams,
) -> Result<ApiResponse<PositionMarginResponse>, AsterDexError> {
let margin_type_str = params.margin_type.to_string();
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(4);
pairs.push(("symbol", ¶ms.symbol));
pairs.push(("amount", ¶ms.amount));
pairs.push(("type", &margin_type_str));
if let Some(ref ps) = params.position_side {
pairs.push(("positionSide", ps));
}
self.signed_post("/fapi/v3/positionMargin", &pairs).await
}
pub async fn get_position_margin_history(
&self,
symbol: &str,
margin_type: Option<u8>,
start_time: Option<u64>,
end_time: Option<u64>,
limit: Option<u32>,
) -> Result<ApiResponse<Vec<serde_json::Value>>, AsterDexError> {
let margin_type_str = margin_type.map(|t| t.to_string());
let start_time_str = start_time.map(|t| t.to_string());
let end_time_str = end_time.map(|t| t.to_string());
let limit_str = limit.map(|l| l.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
pairs.push(("symbol", symbol));
if let Some(ref s) = margin_type_str {
pairs.push(("type", s));
}
if let Some(ref s) = start_time_str {
pairs.push(("startTime", s));
}
if let Some(ref s) = end_time_str {
pairs.push(("endTime", s));
}
if let Some(ref s) = limit_str {
pairs.push(("limit", s));
}
self.signed_get("/fapi/v3/positionMargin/history", &pairs).await
}
pub async fn get_adl_quantile(
&self,
symbol: Option<&str>,
) -> Result<ApiResponse<Vec<AdlQuantileResponse>>, AsterDexError> {
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(1);
if let Some(s) = symbol {
pairs.push(("symbol", s));
}
self.signed_get("/fapi/v3/adlQuantile", &pairs).await
}
pub async fn get_force_orders(
&self,
symbol: Option<&str>,
auto_close_type: Option<&str>,
start_time: Option<u64>,
end_time: Option<u64>,
limit: Option<u32>,
) -> Result<ApiResponse<Vec<serde_json::Value>>, AsterDexError> {
let start_time_str = start_time.map(|t| t.to_string());
let end_time_str = end_time.map(|t| t.to_string());
let limit_str = limit.map(|l| l.to_string());
let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
if let Some(s) = symbol {
pairs.push(("symbol", s));
}
if let Some(t) = auto_close_type {
pairs.push(("autoCloseType", t));
}
if let Some(ref s) = start_time_str {
pairs.push(("startTime", s));
}
if let Some(ref s) = end_time_str {
pairs.push(("endTime", s));
}
if let Some(ref s) = limit_str {
pairs.push(("limit", s));
}
self.signed_get("/fapi/v3/forceOrders", &pairs).await
}
pub async fn get_commission_rate(
&self,
symbol: &str,
) -> Result<ApiResponse<CommissionRateResponse>, AsterDexError> {
self.signed_get("/fapi/v3/commissionRate", &[("symbol", symbol)]).await
}
pub async fn get_multi_assets_margin(
&self,
) -> Result<ApiResponse<MultiAssetsModeResponse>, AsterDexError> {
self.signed_get("/fapi/v3/multiAssetsMargin", &[]).await
}
pub async fn set_multi_assets_margin(
&self,
enabled: bool,
) -> Result<ApiResponse<StatusMsgResponse>, AsterDexError> {
let val = if enabled { "true" } else { "false" };
self.signed_post("/fapi/v3/multiAssetsMargin", &[("multiAssetsMargin", val)]).await
}
pub async fn countdown_cancel_all(
&self,
params: CountdownParams,
) -> Result<ApiResponse<CountdownResponse>, AsterDexError> {
let ct_str = params.countdown_time.to_string();
self.signed_post(
"/fapi/v3/countdownCancelAll",
&[("symbol", params.symbol.as_str()), ("countdownTime", &ct_str)],
)
.await
}
pub async fn noop(&self) -> Result<ApiResponse<EmptyResponse>, AsterDexError> {
self.signed_post("/fapi/v3/noop", &[]).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::futures::models::trading::{AllOrdersParams, GetOrderParams};
use mockito::Server;
#[tokio::test]
async fn get_order_without_credentials_returns_config_error() {
let client = RestClient::new_public("https://fapi.asterdex.com").unwrap();
let params = GetOrderParams {
symbol: "BTCUSDT".to_string(),
order_id: Some(123456),
orig_client_order_id: None,
};
let result = client.get_order(params).await;
assert!(
matches!(result, Err(AsterDexError::ConfigError { .. })),
"expected ConfigError, got: {:?}",
result
);
}
#[tokio::test]
async fn get_all_orders_without_credentials_returns_config_error() {
let client = RestClient::new_public("https://fapi.asterdex.com").unwrap();
let params = AllOrdersParams {
symbol: "BTCUSDT".to_string(),
limit: Some(100),
..Default::default()
};
let result = client.get_all_orders(params).await;
assert!(
matches!(result, Err(AsterDexError::ConfigError { .. })),
"expected ConfigError, got: {:?}",
result
);
}
#[tokio::test]
async fn noop_without_credentials_returns_config_error() {
let client = RestClient::new_public("https://fapi.asterdex.com").unwrap();
let result = client.noop().await;
assert!(
matches!(result, Err(AsterDexError::ConfigError { .. })),
"expected ConfigError, got: {:?}",
result
);
}
#[tokio::test]
async fn get_all_orders_mock() {
let mut server = Server::new_async().await;
let mock = server
.mock("GET", mockito::Matcher::Regex(r"^/fapi/v3/allOrders".to_string()))
.with_status(200)
.with_header("content-type", "application/json")
.with_body("[]")
.create_async()
.await;
use crate::auth::Credentials;
let creds = Credentials::new(
"0x0000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000001",
714,
)
.unwrap();
let client = RestClient::new(&server.url(), creds).unwrap();
let params = AllOrdersParams {
symbol: "BTCUSDT".to_string(),
..Default::default()
};
let resp = client.get_all_orders(params).await.unwrap();
assert!(resp.data.is_empty(), "expected empty vec, got {:?}", resp.data);
mock.assert_async().await;
}
}