use crate::dto::common::{MarginMode, OrderType, PositionSide, Side};
use crate::dto::EnumToStrTrait;
use serde::{Deserialize, Serialize};
pub enum TdModeEnum {
ISOLATED,
CROSS,
CASH,
}
impl EnumToStrTrait for TdModeEnum {
fn as_str(&self) -> &'static str {
match self {
TdModeEnum::ISOLATED => "isolated",
TdModeEnum::CROSS => "cross",
TdModeEnum::CASH => "cash",
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AttachAlgoOrdReqDto {
pub attach_algo_cl_ord_id: Option<String>,
pub tp_trigger_px: Option<String>,
pub tp_ord_px: Option<String>,
pub tp_ord_kind: Option<String>,
pub tp_trigger_px_type: Option<String>,
pub sl_trigger_px: Option<String>,
pub sl_ord_px: Option<String>,
pub sl_trigger_px_type: Option<String>,
pub sz: Option<String>,
pub amend_px_on_trigger_type: Option<i32>,
}
impl AttachAlgoOrdReqDto {
pub fn new(
tp_trigger_px: Option<String>,
tp_ord_px: Option<String>,
sl_trigger_px: Option<String>,
sl_ord_px: Option<String>,
sz: String,
) -> Self {
Self {
attach_algo_cl_ord_id: None,
tp_trigger_px: tp_trigger_px,
tp_ord_px: tp_ord_px,
tp_ord_kind: Some(TpOrdKindEnum::CONDITION.as_str().to_owned()),
sl_trigger_px: sl_trigger_px,
sl_ord_px: sl_ord_px,
tp_trigger_px_type: Some("last".to_string()),
sl_trigger_px_type: Some("last".to_string()),
sz: Some(sz),
amend_px_on_trigger_type: Some(0),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderReqDto {
pub inst_id: String,
pub td_mode: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub ccy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cl_ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
pub side: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub pos_side: Option<String>,
pub ord_type: String,
pub sz: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub px_usd: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub px_vol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reduce_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tgt_ccy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ban_amend: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quick_mgn_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stp_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stp_mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trade_quote_ccy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attach_algo_ords: Option<Vec<AttachAlgoOrdReqDto>>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CancelOrderReqDto {
pub inst_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cl_ord_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkedAlgoOrd {
#[serde(rename = "algoId")]
pub algo_id: String,
}
#[derive(Serialize, Clone, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct AttachAlgoOrds {
pub attach_algo_id: String,
pub attach_algo_cl_ord_id: Option<String>,
pub tp_trigger_px: Option<String>,
pub tp_ord_px: Option<String>,
pub tp_ord_kind: Option<String>,
pub tp_trigger_px_type: Option<String>,
pub sl_trigger_px: Option<String>,
pub sl_ord_px: Option<String>,
pub sl_trigger_px_type: Option<String>,
pub sz: Option<String>,
pub amend_px_on_trigger_type: Option<String>,
pub fail_code: Option<String>,
pub fail_reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderDetailRespDto {
pub inst_type: String,
pub inst_id: String,
pub tgt_ccy: String,
pub ccy: String,
pub ord_id: String,
pub cl_ord_id: String,
pub tag: String,
pub px: String,
pub px_usd: String,
pub px_vol: String,
pub px_type: String,
pub sz: String,
pub pnl: String,
pub ord_type: String,
pub side: String,
pub pos_side: String,
pub td_mode: String,
pub acc_fill_sz: String,
pub fill_px: String,
pub trade_id: String,
pub fill_sz: String,
pub fill_time: String,
pub avg_px: String,
pub state: String,
pub lever: String,
pub attach_algo_cl_ord_id: String,
pub tp_trigger_px: String,
pub tp_trigger_px_type: String,
pub tp_ord_px: String,
pub sl_trigger_px: String,
pub sl_trigger_px_type: String,
pub sl_ord_px: String,
pub attach_algo_ords: Vec<AttachAlgoOrds>,
pub linked_algo_ord: LinkedAlgoOrd,
pub stp_id: String,
pub stp_mode: String,
pub fee_ccy: String,
pub fee: String,
pub rebate_ccy: String,
pub source: String,
pub rebate: String,
pub category: String,
pub reduce_only: String,
pub cancel_source: String,
pub cancel_source_reason: String,
pub quick_mgn_type: String,
pub algo_cl_ord_id: String,
pub algo_id: String,
pub is_tp_limit: String,
pub u_time: String,
pub c_time: String,
pub trade_quote_ccy: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderPendingRespDto {
#[serde(rename = "instType")]
pub inst_type: String,
#[serde(rename = "instId")]
pub inst_id: String,
#[serde(rename = "lever")]
pub leverage: String,
pub px: String,
pub sz: String,
#[serde(rename = "ordId")]
pub order_id: String,
#[serde(rename = "clOrdId", skip_serializing_if = "Option::is_none")]
pub client_order_id: Option<String>,
#[serde(rename = "fillSz", skip_serializing_if = "Option::is_none")]
pub filled_size: Option<String>,
#[serde(rename = "fillPx", skip_serializing_if = "Option::is_none")]
pub filled_price: Option<String>,
#[serde(rename = "fillTime", skip_serializing_if = "Option::is_none")]
pub filled_time: Option<String>,
#[serde(rename = "ordType")]
pub order_type: OrderType,
pub side: Side,
#[serde(rename = "posSide", skip_serializing_if = "Option::is_none")]
pub position_side: Option<PositionSide>,
pub state: String,
#[serde(rename = "cTime")]
pub creation_time: String,
#[serde(rename = "uTime", skip_serializing_if = "Option::is_none")]
pub update_time: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionRespDto {
#[serde(rename = "instType")]
pub inst_type: String,
#[serde(rename = "instId")]
pub inst_id: String,
#[serde(rename = "lever")]
pub leverage: String,
pub pos: String,
#[serde(rename = "posSide")]
pub position_side: PositionSide,
#[serde(rename = "avgPx")]
pub average_price: String,
pub upl: String,
#[serde(rename = "margin")]
pub margin: String,
#[serde(rename = "mgnMode")]
pub margin_mode: MarginMode,
#[serde(rename = "liqPx", skip_serializing_if = "Option::is_none")]
pub liquidation_price: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetPositionReqDto {
#[serde(rename = "instType", skip_serializing_if = "Option::is_none")]
pub inst_type: Option<String>,
#[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
pub inst_id: Option<String>,
#[serde(rename = "posId", skip_serializing_if = "Option::is_none")]
pub pos_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetLeverageReqDto {
#[serde(rename = "instId")]
pub inst_id: String,
#[serde(rename = "lever")]
pub leverage: String,
#[serde(rename = "mgnMode")]
pub margin_mode: String,
#[serde(rename = "posSide", skip_serializing_if = "Option::is_none")]
pub position_side: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetLeverageRespDto {
#[serde(rename = "lever")]
pub leverage: String,
#[serde(rename = "mgnMode")]
pub margin_mode: String,
#[serde(rename = "instId")]
pub inst_id: String,
#[serde(rename = "posSide")]
pub position_side: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeeRate {
#[serde(rename = "instType")]
pub inst_type: String,
#[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
pub inst_id: Option<String>,
#[serde(rename = "uly", skip_serializing_if = "Option::is_none")]
pub underlying: Option<String>,
#[serde(rename = "ccy", skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
#[serde(rename = "makerFeeRate")]
pub maker_fee_rate: String,
#[serde(rename = "takerFeeRate")]
pub taker_fee_rate: String,
}
pub enum TpOrdKindEnum {
CONDITION,
LIMIT,
}
impl EnumToStrTrait for TpOrdKindEnum {
fn as_str(&self) -> &'static str {
match self {
TpOrdKindEnum::CONDITION => "condition",
TpOrdKindEnum::LIMIT => "limit",
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderResDto {
pub ord_id: String,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
pub ts: String,
pub s_code: String,
pub s_msg: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CloseOrderReqDto {
pub inst_id: String,
pub pos_side: Option<String>,
pub mgn_mode: String,
pub ccy: Option<String>,
pub auto_cxl: Option<bool>,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CloseOrderResDto {
pub inst_id: String,
pub pos_side: Option<String>,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_order() -> OrderReqDto {
OrderReqDto {
inst_id: "BTC-USDT".to_string(),
td_mode: "cash".to_string(),
ccy: None,
cl_ord_id: None,
tag: None,
side: "buy".to_string(),
pos_side: None,
ord_type: "limit".to_string(),
sz: "0.001".to_string(),
px: Some("20000".to_string()),
px_usd: None,
px_vol: None,
reduce_only: None,
tgt_ccy: None,
ban_amend: None,
quick_mgn_type: None,
stp_id: None,
stp_mode: None,
trade_quote_ccy: Some("USDC".to_string()),
attach_algo_ords: None,
}
}
#[test]
fn serializes_order_request_without_null_optional_fields() {
let value = serde_json::to_value(sample_order()).unwrap();
assert_eq!(value["instId"], "BTC-USDT");
assert_eq!(value["px"], "20000");
assert_eq!(value["tradeQuoteCcy"], "USDC");
assert!(value.get("ccy").is_none());
assert!(value.get("clOrdId").is_none());
assert!(value.get("attachAlgoOrds").is_none());
}
#[test]
fn serializes_cancel_order_request_without_null_optional_fields() {
let value = serde_json::to_value(CancelOrderReqDto {
inst_id: "BTC-USDT".to_string(),
ord_id: Some("1".to_string()),
cl_ord_id: None,
})
.unwrap();
assert_eq!(value["instId"], "BTC-USDT");
assert_eq!(value["ordId"], "1");
assert!(value.get("clOrdId").is_none());
}
}
pub enum OrdTypeEnum {
LIMIT,
MARKET,
PostOnly,
FOK,
Ioc,
OptimalLimitIoc,
}
impl EnumToStrTrait for OrdTypeEnum {
fn as_str(&self) -> &'static str {
match self {
OrdTypeEnum::LIMIT => "limit",
OrdTypeEnum::MARKET => "market",
OrdTypeEnum::PostOnly => "post_only",
OrdTypeEnum::FOK => "fok",
OrdTypeEnum::Ioc => "ioc",
OrdTypeEnum::OptimalLimitIoc => "optimal_limit_ioc",
}
}
}
pub struct OrdListReqDto {
pub inst_type: String,
pub inst_id: Option<String>,
pub ord_type: Option<String>,
pub state: Option<String>,
pub after: Option<String>,
pub before: Option<String>,
pub limit: Option<u32>,
}