Skip to main content

longbridge/trade/requests/
submit_order.rs

1use rust_decimal::Decimal;
2use serde::Serialize;
3use time::Date;
4
5use crate::{
6    serde_utils,
7    trade::{AttachedOrderType, OrderSide, OrderType, OutsideRTH, TimeInForceType},
8};
9
10/// Options for submit order request
11#[derive(Debug, Serialize, Clone)]
12pub struct SubmitOrderOptions {
13    symbol: String,
14    order_type: OrderType,
15    side: OrderSide,
16    submitted_quantity: Decimal,
17    time_in_force: TimeInForceType,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    submitted_price: Option<Decimal>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    trigger_price: Option<Decimal>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    limit_offset: Option<Decimal>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    trailing_amount: Option<Decimal>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    trailing_percent: Option<Decimal>,
28    #[serde(
29        with = "serde_utils::date_opt",
30        skip_serializing_if = "Option::is_none"
31    )]
32    expire_date: Option<Date>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    outside_rth: Option<OutsideRTH>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    limit_depth_level: Option<i32>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    trigger_count: Option<i32>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    monitor_price: Option<Decimal>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    remark: Option<String>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    client_request_id: Option<String>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    attached_params: Option<SubmitAttachedParams>,
47}
48
49impl SubmitOrderOptions {
50    /// Create a new `SubmitOrderOptions`
51    #[inline]
52    pub fn new(
53        symbol: impl Into<String>,
54        order_type: OrderType,
55        side: OrderSide,
56        submitted_quantity: Decimal,
57        time_in_force: TimeInForceType,
58    ) -> Self {
59        Self {
60            symbol: symbol.into(),
61            order_type,
62            side,
63            submitted_quantity,
64            time_in_force,
65            submitted_price: None,
66            trigger_price: None,
67            limit_offset: None,
68            trailing_amount: None,
69            trailing_percent: None,
70            expire_date: None,
71            outside_rth: None,
72            limit_depth_level: None,
73            trigger_count: None,
74            monitor_price: None,
75            remark: None,
76            client_request_id: None,
77            attached_params: None,
78        }
79    }
80
81    /// Set the submitted price
82    #[inline]
83    #[must_use]
84    pub fn submitted_price(self, submitted_price: Decimal) -> Self {
85        Self {
86            submitted_price: Some(submitted_price),
87            ..self
88        }
89    }
90
91    /// Set the trigger price
92    #[inline]
93    #[must_use]
94    pub fn trigger_price(self, trigger_price: Decimal) -> Self {
95        Self {
96            trigger_price: Some(trigger_price),
97            ..self
98        }
99    }
100
101    /// Set the limit offset
102    #[inline]
103    #[must_use]
104    pub fn limit_offset(self, limit_offset: Decimal) -> Self {
105        Self {
106            limit_offset: Some(limit_offset),
107            ..self
108        }
109    }
110
111    /// Set the trailing amount
112    #[inline]
113    #[must_use]
114    pub fn trailing_amount(self, trailing_amount: Decimal) -> Self {
115        Self {
116            trailing_amount: Some(trailing_amount),
117            ..self
118        }
119    }
120
121    /// Set the trailing percent
122    #[inline]
123    #[must_use]
124    pub fn trailing_percent(self, trailing_percent: Decimal) -> Self {
125        Self {
126            trailing_percent: Some(trailing_percent),
127            ..self
128        }
129    }
130
131    /// Set the expire date
132    #[inline]
133    #[must_use]
134    pub fn expire_date(self, expire_date: Date) -> Self {
135        Self {
136            expire_date: Some(expire_date),
137            ..self
138        }
139    }
140
141    /// Enable or disable outside regular trading hours
142    #[inline]
143    #[must_use]
144    pub fn outside_rth(self, outside_rth: OutsideRTH) -> Self {
145        Self {
146            outside_rth: Some(outside_rth),
147            ..self
148        }
149    }
150
151    /// Set the limit depth level
152    pub fn limit_depth_level(self, level: i32) -> Self {
153        Self {
154            limit_depth_level: Some(level),
155            ..self
156        }
157    }
158
159    /// Set the trigger count
160    pub fn trigger_count(self, count: i32) -> Self {
161        Self {
162            trigger_count: Some(count),
163            ..self
164        }
165    }
166
167    /// Set the monitor price
168    pub fn monitor_price(self, price: Decimal) -> Self {
169        Self {
170            monitor_price: Some(price),
171            ..self
172        }
173    }
174
175    /// Set the remark
176    #[inline]
177    #[must_use]
178    pub fn remark(self, remark: impl Into<String>) -> Self {
179        Self {
180            remark: Some(remark.into()),
181            ..self
182        }
183    }
184
185    /// Set the client request ID for idempotency control.
186    /// If not specified, idempotency control is skipped.
187    /// The server caches this ID for 10 minutes; requests with the same ID
188    /// within that period return the original response without creating a new
189    /// order.
190    pub fn client_request_id(self, id: impl Into<String>) -> Self {
191        Self {
192            client_request_id: Some(id.into()),
193            ..self
194        }
195    }
196
197    /// Set attached order parameters
198    pub fn attached_params(self, params: SubmitAttachedParams) -> Self {
199        Self {
200            attached_params: Some(params),
201            ..self
202        }
203    }
204}
205
206/// Attached order parameters for submit order
207#[derive(Debug, Serialize, Clone)]
208pub struct SubmitAttachedParams {
209    attached_order_type: AttachedOrderType,
210    #[serde(skip_serializing_if = "Option::is_none")]
211    profit_taker_price: Option<Decimal>,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    stop_loss_price: Option<Decimal>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    time_in_force: Option<TimeInForceType>,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    expire_time: Option<i64>,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    activate_order_type: Option<OrderType>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    profit_taker_submit_price: Option<Decimal>,
222    #[serde(skip_serializing_if = "Option::is_none")]
223    stop_loss_submit_price: Option<Decimal>,
224    #[serde(skip_serializing_if = "Option::is_none")]
225    activate_rth: Option<OutsideRTH>,
226}
227
228impl SubmitAttachedParams {
229    /// Create new SubmitAttachedParams
230    pub fn new(attached_order_type: AttachedOrderType) -> Self {
231        Self {
232            attached_order_type,
233            profit_taker_price: None,
234            stop_loss_price: None,
235            time_in_force: None,
236            expire_time: None,
237            activate_order_type: None,
238            profit_taker_submit_price: None,
239            stop_loss_submit_price: None,
240            activate_rth: None,
241        }
242    }
243    /// Set the take-profit trigger price
244    pub fn profit_taker_price(self, v: Decimal) -> Self {
245        Self {
246            profit_taker_price: Some(v),
247            ..self
248        }
249    }
250    /// Set the stop-loss trigger price
251    pub fn stop_loss_price(self, v: Decimal) -> Self {
252        Self {
253            stop_loss_price: Some(v),
254            ..self
255        }
256    }
257    /// Set the time in force type
258    pub fn time_in_force(self, v: TimeInForceType) -> Self {
259        Self {
260            time_in_force: Some(v),
261            ..self
262        }
263    }
264    /// Set the expiry time (unix timestamp seconds)
265    pub fn expire_time(self, v: i64) -> Self {
266        Self {
267            expire_time: Some(v),
268            ..self
269        }
270    }
271    /// Set the order type to submit after trigger
272    pub fn activate_order_type(self, v: OrderType) -> Self {
273        Self {
274            activate_order_type: Some(v),
275            ..self
276        }
277    }
278    /// Set the take-profit limit price
279    pub fn profit_taker_submit_price(self, v: Decimal) -> Self {
280        Self {
281            profit_taker_submit_price: Some(v),
282            ..self
283        }
284    }
285    /// Set the stop-loss limit price
286    pub fn stop_loss_submit_price(self, v: Decimal) -> Self {
287        Self {
288            stop_loss_submit_price: Some(v),
289            ..self
290        }
291    }
292    /// Set the RTH setting for the activated order
293    pub fn activate_rth(self, v: OutsideRTH) -> Self {
294        Self {
295            activate_rth: Some(v),
296            ..self
297        }
298    }
299}