use rust_decimal::{Decimal, serde::str_option::deserialize as option_decimal};
use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_number_from_string as number;
use super::common::List;
use crate::{
CancelType, CreateType, OcoTriggerBy, OrderStatus, OrderType, PlaceType, PositionIdx,
RejectReason, Side, SmpType, TimeInForce, Timestamp, TpslMode, TriggerBy, TriggerDirection,
enums::{Category, StopOrderType},
serde::{empty_string_as_none, string_to_option_bool},
ws::OrderMsg,
};
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetOpenClosedOrdersParams {
pub category: Category,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_coin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub settle_coin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_link_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_only: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_filter: Option<OrderFilter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
impl GetOpenClosedOrdersParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
base_coin: None,
settle_coin: None,
order_id: None,
order_link_id: None,
open_only: None,
order_filter: None,
limit: None,
cursor: None,
}
}
pub fn with_symbol(mut self, v: String) -> Self {
self.symbol = Some(v);
self
}
pub fn with_base_coin(mut self, v: String) -> Self {
self.base_coin = Some(v);
self
}
pub fn with_settle_coin(mut self, v: String) -> Self {
self.settle_coin = Some(v);
self
}
pub fn with_order_id(mut self, v: String) -> Self {
self.order_id = Some(v);
self
}
pub fn with_order_link_id(mut self, v: String) -> Self {
self.order_link_id = Some(v);
self
}
pub fn with_open_only(mut self, v: i32) -> Self {
self.open_only = Some(v);
self
}
pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
self.order_filter = Some(v);
self
}
pub fn with_limit(mut self, v: i32) -> Self {
self.limit = Some(v);
self
}
pub fn with_cursor(mut self, v: String) -> Self {
self.cursor = Some(v);
self
}
}
#[derive(Debug, Clone, Serialize)]
pub enum OrderFilter {
Order,
StopOrder,
#[serde(rename = "camelCase")]
TpslOrder,
OcoOrder,
BidirectionalTpslOrder,
}
#[derive(Debug, Deserialize, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Order {
pub order_id: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub order_link_id: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub block_trade_id: Option<String>,
pub symbol: String,
pub price: Decimal,
pub qty: Decimal,
pub side: Side,
#[serde(default, deserialize_with = "string_to_option_bool")]
pub is_leverage: Option<bool>,
pub position_idx: PositionIdx,
pub order_status: OrderStatus,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub create_type: Option<CreateType>,
pub cancel_type: CancelType,
pub reject_reason: RejectReason,
#[serde(default, deserialize_with = "option_decimal")]
pub avg_price: Option<Decimal>,
pub leaves_qty: Decimal,
pub leaves_value: Decimal,
pub cum_exec_qty: Decimal,
pub cum_exec_value: Decimal,
pub cum_exec_fee: Decimal,
pub time_in_force: TimeInForce,
pub order_type: OrderType,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub stop_order_type: Option<StopOrderType>,
#[serde(default, deserialize_with = "option_decimal")]
pub order_iv: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub market_unit: Option<String>,
#[serde(default, deserialize_with = "option_decimal")]
pub trigger_price: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub take_profit: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub stop_loss: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub tpsl_mode: Option<TpslMode>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub oco_trigger_by: Option<OcoTriggerBy>,
#[serde(default, deserialize_with = "option_decimal")]
pub tp_limit_price: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub sl_limit_price: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub tp_trigger_by: Option<TriggerBy>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub sl_trigger_by: Option<TriggerBy>,
pub trigger_direction: TriggerDirection,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub trigger_by: Option<TriggerBy>,
#[serde(default, deserialize_with = "option_decimal")]
pub last_price_on_created: Option<Decimal>,
#[serde(default, deserialize_with = "option_decimal")]
pub base_price: Option<Decimal>,
pub reduce_only: bool,
pub close_on_trigger: bool,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub place_type: Option<PlaceType>,
pub smp_type: SmpType,
pub smp_group: i64,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub smp_order_id: Option<String>,
#[serde(deserialize_with = "number")]
pub created_time: Timestamp,
#[serde(deserialize_with = "number")]
pub updated_time: Timestamp,
}
impl Order {
pub fn is_open_status(&self) -> bool {
self.order_status.is_open()
}
pub fn is_closed_status(&self) -> bool {
self.order_status.is_closed()
}
pub fn update(&mut self, msg: OrderMsg) {
self.order_id = msg.order_id;
self.order_link_id = msg.order_link_id;
self.block_trade_id = msg.block_trade_id;
self.symbol = msg.symbol;
self.price = msg.price;
self.qty = msg.qty;
self.side = msg.side;
self.is_leverage = msg.is_leverage;
self.position_idx = msg.position_idx;
self.order_status = msg.order_status;
self.create_type = msg.create_type;
self.cancel_type = msg.cancel_type;
self.reject_reason = msg.reject_reason;
self.avg_price = msg.avg_price;
if let Some(leaves_qty) = msg.leaves_qty {
self.leaves_qty = leaves_qty;
}
if let Some(leaves_value) = msg.leaves_value {
self.leaves_value = leaves_value;
}
self.cum_exec_qty = msg.cum_exec_qty;
self.cum_exec_value = msg.cum_exec_value;
self.cum_exec_fee = msg.cum_exec_fee;
self.time_in_force = msg.time_in_force;
self.order_type = msg.order_type;
self.stop_order_type = msg.stop_order_type;
self.order_iv = msg.order_iv;
self.market_unit = msg.market_unit;
self.trigger_price = msg.trigger_price;
self.take_profit = msg.take_profit;
self.stop_loss = msg.stop_loss;
self.tpsl_mode = msg.tpsl_mode;
self.oco_trigger_by = msg.oco_trigger_by;
self.tp_limit_price = msg.tp_limit_price;
self.sl_limit_price = msg.sl_limit_price;
self.tp_trigger_by = msg.tp_trigger_by;
self.sl_trigger_by = msg.sl_trigger_by;
self.trigger_direction = msg.trigger_direction;
self.trigger_by = msg.trigger_by;
self.last_price_on_created = msg.last_price_on_created;
self.reduce_only = msg.reduce_only;
self.close_on_trigger = msg.close_on_trigger;
self.place_type = msg.place_type;
self.smp_type = msg.smp_type;
self.smp_group = msg.smp_group;
self.smp_order_id = msg.smp_order_id;
self.created_time = msg.created_time;
self.updated_time = msg.updated_time;
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PlaceOrderRequest {
pub category: Category,
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_leverage: Option<i64>,
pub side: Side,
pub order_type: OrderType,
pub qty: Decimal,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_unit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slippage_tolerance_type: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slippage_tolerance: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_direction: Option<TriggerDirection>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_filter: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_price: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_by: Option<TriggerBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_iv: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_in_force: Option<TimeInForce>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position_idx: Option<PositionIdx>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_link_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub take_profit: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_loss: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tp_trigger_by: Option<TriggerBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sl_trigger_by: Option<TriggerBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reduce_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub close_on_trigger: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub smp_type: Option<SmpType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mmp: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tpsl_mode: Option<TpslMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tp_limit_price: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sl_limit_price: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tp_order_type: Option<OrderType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sl_order_type: Option<OrderType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bbo_side_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bbo_level: Option<String>,
}
impl PlaceOrderRequest {
pub fn new(
category: Category,
symbol: String,
side: Side,
order_type: OrderType,
qty: Decimal,
) -> Self {
Self {
category,
symbol,
is_leverage: None,
side,
order_type,
qty,
market_unit: None,
slippage_tolerance_type: None,
slippage_tolerance: None,
price: None,
trigger_direction: None,
order_filter: None,
trigger_price: None,
trigger_by: None,
order_iv: None,
time_in_force: None,
position_idx: None,
order_link_id: None,
take_profit: None,
stop_loss: None,
tp_trigger_by: None,
sl_trigger_by: None,
reduce_only: None,
close_on_trigger: None,
smp_type: None,
mmp: None,
tpsl_mode: None,
tp_limit_price: None,
sl_limit_price: None,
tp_order_type: None,
sl_order_type: None,
bbo_side_type: None,
bbo_level: None,
}
}
pub fn with_is_leverage(mut self, v: i64) -> Self {
self.is_leverage = Some(v);
self
}
pub fn with_market_unit(mut self, v: String) -> Self {
self.market_unit = Some(v);
self
}
pub fn with_slippage_tolerance_type(mut self, v: Decimal) -> Self {
self.slippage_tolerance_type = Some(v);
self
}
pub fn with_slippage_tolerance(mut self, v: Decimal) -> Self {
self.slippage_tolerance = Some(v);
self
}
pub fn with_price(mut self, v: Decimal) -> Self {
self.price = Some(v);
self
}
pub fn with_trigger_direction(mut self, v: TriggerDirection) -> Self {
self.trigger_direction = Some(v);
self
}
pub fn with_order_filter(mut self, v: String) -> Self {
self.order_filter = Some(v);
self
}
pub fn with_trigger_price(mut self, v: Decimal) -> Self {
self.trigger_price = Some(v);
self
}
pub fn with_trigger_by(mut self, v: TriggerBy) -> Self {
self.trigger_by = Some(v);
self
}
pub fn with_order_iv(mut self, v: Decimal) -> Self {
self.order_iv = Some(v);
self
}
pub fn with_time_in_force(mut self, v: TimeInForce) -> Self {
self.time_in_force = Some(v);
self
}
pub fn with_position_idx(mut self, v: PositionIdx) -> Self {
self.position_idx = Some(v);
self
}
pub fn with_order_link_id(mut self, v: String) -> Self {
self.order_link_id = Some(v);
self
}
pub fn with_take_profit(mut self, v: Decimal) -> Self {
self.take_profit = Some(v);
self
}
pub fn with_stop_loss(mut self, v: Decimal) -> Self {
self.stop_loss = Some(v);
self
}
pub fn with_tp_trigger_by(mut self, v: TriggerBy) -> Self {
self.tp_trigger_by = Some(v);
self
}
pub fn with_sl_trigger_by(mut self, v: TriggerBy) -> Self {
self.sl_trigger_by = Some(v);
self
}
pub fn with_reduce_only(mut self, v: bool) -> Self {
self.reduce_only = Some(v);
self
}
pub fn with_close_on_trigger(mut self, v: bool) -> Self {
self.close_on_trigger = Some(v);
self
}
pub fn with_smp_type(mut self, v: SmpType) -> Self {
self.smp_type = Some(v);
self
}
pub fn with_mmp(mut self, v: bool) -> Self {
self.mmp = Some(v);
self
}
pub fn with_tpsl_mode(mut self, v: TpslMode) -> Self {
self.tpsl_mode = Some(v);
self
}
pub fn with_tp_limit_price(mut self, v: Decimal) -> Self {
self.tp_limit_price = Some(v);
self
}
pub fn with_sl_limit_price(mut self, v: Decimal) -> Self {
self.sl_limit_price = Some(v);
self
}
pub fn with_tp_order_type(mut self, v: OrderType) -> Self {
self.tp_order_type = Some(v);
self
}
pub fn with_sl_order_type(mut self, v: OrderType) -> Self {
self.sl_order_type = Some(v);
self
}
pub fn with_bbo_side_type(mut self, v: String) -> Self {
self.bbo_side_type = Some(v);
self
}
pub fn with_bbo_level(mut self, v: String) -> Self {
self.bbo_level = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PlaceOrderResponse {
pub order_id: String,
pub order_link_id: String,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AmendOrderRequest {
pub category: Category,
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_link_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub qty: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price: Option<Decimal>,
}
impl AmendOrderRequest {
pub fn new(category: Category, symbol: String) -> Self {
Self {
category,
symbol,
order_id: None,
order_link_id: None,
qty: None,
price: None,
}
}
pub fn with_order_id(mut self, v: String) -> Self {
self.order_id = Some(v);
self
}
pub fn with_order_link_id(mut self, v: String) -> Self {
self.order_link_id = Some(v);
self
}
pub fn with_qty(mut self, v: Decimal) -> Self {
self.qty = Some(v);
self
}
pub fn with_price(mut self, v: Decimal) -> Self {
self.price = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AmendOrderResponse {
pub order_id: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub order_link_id: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelOrderRequest {
pub category: Category,
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_link_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_filter: Option<OrderFilter>,
}
impl CancelOrderRequest {
pub fn new(category: Category, symbol: String) -> Self {
Self {
category,
symbol,
order_id: None,
order_link_id: None,
order_filter: None,
}
}
pub fn with_order_id(mut self, v: String) -> Self {
self.order_id = Some(v);
self
}
pub fn with_order_link_id(mut self, v: String) -> Self {
self.order_link_id = Some(v);
self
}
pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
self.order_filter = Some(v);
self
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CancelOrderResponse {
pub order_id: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub order_link_id: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelAllOrdersRequest {
pub category: Category,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_coin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub settle_coin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_filter: Option<OrderFilter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_order_type: Option<StopOrderType>,
}
impl CancelAllOrdersRequest {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
base_coin: None,
settle_coin: None,
order_filter: None,
stop_order_type: None,
}
}
pub fn with_symbol(mut self, v: String) -> Self {
self.symbol = Some(v);
self
}
pub fn with_base_coin(mut self, v: String) -> Self {
self.base_coin = Some(v);
self
}
pub fn with_settle_coin(mut self, v: String) -> Self {
self.settle_coin = Some(v);
self
}
pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
self.order_filter = Some(v);
self
}
pub fn with_stop_order_type(mut self, v: StopOrderType) -> Self {
self.stop_order_type = Some(v);
self
}
}
pub type CancelAllOrdersResponse = List<CancelOrderResponse>;
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetOrderHistoryParams {
pub category: Category,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_coin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub settle_coin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_link_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_filter: Option<OrderFilter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_status: Option<OrderStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_time: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_time: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
impl GetOrderHistoryParams {
pub fn new(category: Category) -> Self {
Self {
category,
symbol: None,
base_coin: None,
settle_coin: None,
order_id: None,
order_link_id: None,
order_filter: None,
order_status: None,
start_time: None,
end_time: None,
limit: None,
cursor: None,
}
}
pub fn with_symbol(mut self, v: String) -> Self {
self.symbol = Some(v);
self
}
pub fn with_base_coin(mut self, v: String) -> Self {
self.base_coin = Some(v);
self
}
pub fn with_settle_coin(mut self, v: String) -> Self {
self.settle_coin = Some(v);
self
}
pub fn with_order_id(mut self, v: String) -> Self {
self.order_id = Some(v);
self
}
pub fn with_order_link_id(mut self, v: String) -> Self {
self.order_link_id = Some(v);
self
}
pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
self.order_filter = Some(v);
self
}
pub fn with_order_status(mut self, v: OrderStatus) -> Self {
self.order_status = Some(v);
self
}
pub fn with_start_time(mut self, v: Timestamp) -> Self {
self.start_time = Some(v);
self
}
pub fn with_end_time(mut self, v: Timestamp) -> Self {
self.end_time = Some(v);
self
}
pub fn with_limit(mut self, v: i32) -> Self {
self.limit = Some(v);
self
}
pub fn with_cursor(mut self, v: String) -> Self {
self.cursor = Some(v);
self
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PlaceOrderBatchRequest {
pub category: Category,
pub request: Vec<PlaceOrderRequest>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AmendOrderBatchRequest {
pub category: Category,
pub request: Vec<AmendOrderRequest>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelOrderBatchRequest {
pub category: Category,
pub request: Vec<CancelOrderRequest>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PlaceOrderBatchResult {
pub category: Category,
pub symbol: String,
pub order_id: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub order_link_id: Option<String>,
#[serde(rename = "createAt", default)]
pub create_at: Option<Timestamp>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AmendOrderBatchResult {
pub category: Category,
pub symbol: String,
pub order_id: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub order_link_id: Option<String>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CancelOrderBatchResult {
pub category: Category,
pub symbol: String,
pub order_id: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub order_link_id: Option<String>,
}