exc_binance/http/request/trading/
european_options.rs

1use exc_core::types;
2use rust_decimal::Decimal;
3use serde::Serialize;
4
5use crate::{
6    http::{
7        error::RestError,
8        request::{Payload, Rest, RestEndpoint},
9    },
10    types::trading::{OrderSide, TimeInForce},
11};
12
13use super::RespType;
14
15/// Supported order types for european options.
16#[derive(Debug, Clone, Copy, Serialize)]
17#[serde(rename_all = "UPPERCASE")]
18pub enum OrderType {
19    /// Limit.
20    Limit,
21    /// Market.
22    Market,
23}
24
25/// Place order.
26#[derive(Debug, Clone, Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct PlaceOrder {
29    /// Symbol.
30    pub symbol: String,
31    /// Side.
32    pub side: OrderSide,
33    /// Order type.
34    #[serde(rename = "type")]
35    pub order_type: OrderType,
36    /// Reduce only.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub reduce_only: Option<bool>,
39    /// Post only.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub post_only: Option<bool>,
42    /// Quantity.
43    pub quantity: Decimal,
44    /// Price.
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub price: Option<Decimal>,
47    /// Client id.
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub client_order_id: Option<String>,
50    /// Time-In-Force.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub time_in_force: Option<TimeInForce>,
53    /// New order response type.
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub new_order_resp_type: Option<RespType>,
56    /// Is the order a MMP order.
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub is_mmp: Option<bool>,
59}
60
61impl<'a> TryFrom<&'a types::PlaceOrder> for PlaceOrder {
62    type Error = RestError;
63
64    fn try_from(req: &'a exc_core::types::PlaceOrder) -> Result<Self, Self::Error> {
65        let place = req.place;
66        let side = if place.size.is_zero() {
67            return Err(RestError::PlaceZeroSize);
68        } else if place.size.is_sign_positive() {
69            OrderSide::Buy
70        } else {
71            OrderSide::Sell
72        };
73        let (order_type, price, tif, post_only) = match place.kind {
74            types::OrderKind::Market => (OrderType::Market, None, None, None),
75            types::OrderKind::Limit(price, tif) => {
76                let tif = match tif {
77                    types::TimeInForce::GoodTilCancelled => Some(TimeInForce::Gtc),
78                    types::TimeInForce::FillOrKill => Some(TimeInForce::Fok),
79                    types::TimeInForce::ImmediateOrCancel => Some(TimeInForce::Ioc),
80                };
81                (OrderType::Limit, Some(price), tif, None)
82            }
83            types::OrderKind::PostOnly(price) => (
84                OrderType::Limit,
85                Some(price),
86                Some(TimeInForce::Gtc),
87                Some(true),
88            ),
89        };
90        Ok(Self {
91            symbol: req.opts.instrument().to_uppercase(),
92            side,
93            order_type,
94            reduce_only: None,
95            quantity: place.size.abs(),
96            price,
97            client_order_id: req.opts.client_id().map(|s| s.to_string()),
98            time_in_force: tif,
99            new_order_resp_type: None,
100            post_only,
101            is_mmp: None,
102        })
103    }
104}
105
106impl Rest for PlaceOrder {
107    fn method(&self, _endpoint: &RestEndpoint) -> Result<http::Method, RestError> {
108        Ok(http::Method::POST)
109    }
110
111    fn to_path(&self, endpoint: &RestEndpoint) -> Result<String, RestError> {
112        match endpoint {
113            RestEndpoint::EuropeanOptions => Ok("/eapi/v1/order".to_string()),
114            _ => Err(RestError::UnsupportedEndpoint(anyhow::anyhow!(
115                "only support european options"
116            ))),
117        }
118    }
119
120    fn need_apikey(&self) -> bool {
121        true
122    }
123
124    fn need_sign(&self) -> bool {
125        true
126    }
127
128    fn serialize(&self, _endpoint: &RestEndpoint) -> Result<serde_json::Value, RestError> {
129        Ok(serde_json::to_value(self)?)
130    }
131
132    fn to_payload(&self) -> Payload {
133        Payload::new(self.clone())
134    }
135}