exc_binance/http/request/trading/
european_options.rs1use 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#[derive(Debug, Clone, Copy, Serialize)]
17#[serde(rename_all = "UPPERCASE")]
18pub enum OrderType {
19 Limit,
21 Market,
23}
24
25#[derive(Debug, Clone, Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct PlaceOrder {
29 pub symbol: String,
31 pub side: OrderSide,
33 #[serde(rename = "type")]
35 pub order_type: OrderType,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub reduce_only: Option<bool>,
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub post_only: Option<bool>,
42 pub quantity: Decimal,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub price: Option<Decimal>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub client_order_id: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub time_in_force: Option<TimeInForce>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub new_order_resp_type: Option<RespType>,
56 #[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}