Skip to main content

rust_okx/api/trade/
requests.rs

1use serde::Serialize;
2
3use crate::model::{OrderSide, OrderState, OrderType, PositionSide, TradeMode};
4
5/// Request for [`get_order`](crate::api::trade::Trade::get_order).
6#[derive(Debug, Clone, Serialize)]
7pub struct GetOrderRequest<'a> {
8    /// Instrument ID, e.g. `"BTC-USDT"`.
9    #[serde(rename = "instId")]
10    pub inst_id: &'a str,
11    /// OKX order ID.
12    #[serde(rename = "ordId")]
13    pub ord_id: &'a str,
14}
15
16mod advanced;
17mod algo;
18
19pub use advanced::*;
20pub use algo::*;
21
22/// A request to place an order.
23///
24/// Construct with [`PlaceOrderRequest::new`] (required fields) and chain setters
25/// for optional fields. Optional fields are omitted from the request body when
26/// unset.
27#[derive(Debug, Clone, Serialize)]
28pub struct PlaceOrderRequest {
29    #[serde(rename = "instId")]
30    inst_id: String,
31    #[serde(rename = "tdMode")]
32    td_mode: TradeMode,
33    side: OrderSide,
34    #[serde(rename = "ordType")]
35    ord_type: OrderType,
36    sz: String,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    ccy: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    tag: Option<String>,
41    #[serde(rename = "px", skip_serializing_if = "Option::is_none")]
42    px: Option<String>,
43    #[serde(rename = "posSide", skip_serializing_if = "Option::is_none")]
44    pos_side: Option<PositionSide>,
45    #[serde(rename = "clOrdId", skip_serializing_if = "Option::is_none")]
46    cl_ord_id: Option<String>,
47    #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
48    reduce_only: Option<bool>,
49    #[serde(rename = "tgtCcy", skip_serializing_if = "Option::is_none")]
50    tgt_ccy: Option<String>,
51}
52
53impl PlaceOrderRequest {
54    /// Create a new order request with the required fields.
55    pub fn new(
56        inst_id: impl Into<String>,
57        td_mode: TradeMode,
58        side: OrderSide,
59        ord_type: OrderType,
60        sz: impl Into<String>,
61    ) -> Self {
62        Self {
63            inst_id: inst_id.into(),
64            td_mode,
65            side,
66            ord_type,
67            sz: sz.into(),
68            ccy: None,
69            tag: None,
70            px: None,
71            pos_side: None,
72            cl_ord_id: None,
73            reduce_only: None,
74            tgt_ccy: None,
75        }
76    }
77
78    /// Set the order price (required for `limit`-style orders).
79    pub fn price(mut self, px: impl Into<String>) -> Self {
80        self.px = Some(px.into());
81        self
82    }
83
84    /// Set the position side (`long`/`short`/`net`).
85    pub fn position_side(mut self, pos_side: PositionSide) -> Self {
86        self.pos_side = Some(pos_side);
87        self
88    }
89
90    /// Set a client-supplied order ID.
91    pub fn client_order_id(mut self, cl_ord_id: impl Into<String>) -> Self {
92        self.cl_ord_id = Some(cl_ord_id.into());
93        self
94    }
95
96    /// Mark the order as reduce-only.
97    pub fn reduce_only(mut self, reduce_only: bool) -> Self {
98        self.reduce_only = Some(reduce_only);
99        self
100    }
101
102    /// Set the quantity unit for spot market orders (`base_ccy`/`quote_ccy`).
103    pub fn target_ccy(mut self, tgt_ccy: impl Into<String>) -> Self {
104        self.tgt_ccy = Some(tgt_ccy.into());
105        self
106    }
107
108    /// Set the margin currency.
109    pub fn currency(mut self, ccy: impl Into<String>) -> Self {
110        self.ccy = Some(ccy.into());
111        self
112    }
113
114    /// Set an order tag.
115    pub fn tag(mut self, tag: impl Into<String>) -> Self {
116        self.tag = Some(tag.into());
117        self
118    }
119}
120
121/// A request to cancel an order.
122#[derive(Debug, Clone, Serialize)]
123pub struct CancelOrderRequest {
124    #[serde(rename = "instId")]
125    inst_id: String,
126    #[serde(rename = "ordId", skip_serializing_if = "Option::is_none")]
127    ord_id: Option<String>,
128    #[serde(rename = "clOrdId", skip_serializing_if = "Option::is_none")]
129    cl_ord_id: Option<String>,
130}
131
132impl CancelOrderRequest {
133    /// Cancel by OKX order ID.
134    pub fn by_order_id(inst_id: impl Into<String>, ord_id: impl Into<String>) -> Self {
135        Self {
136            inst_id: inst_id.into(),
137            ord_id: Some(ord_id.into()),
138            cl_ord_id: None,
139        }
140    }
141
142    /// Cancel by client order ID.
143    pub fn by_client_order_id(inst_id: impl Into<String>, cl_ord_id: impl Into<String>) -> Self {
144        Self {
145            inst_id: inst_id.into(),
146            ord_id: None,
147            cl_ord_id: Some(cl_ord_id.into()),
148        }
149    }
150}
151
152/// A request to amend an order.
153#[derive(Debug, Clone, Serialize)]
154pub struct AmendOrderRequest {
155    #[serde(rename = "instId")]
156    inst_id: String,
157    #[serde(rename = "ordId", skip_serializing_if = "Option::is_none")]
158    ord_id: Option<String>,
159    #[serde(rename = "clOrdId", skip_serializing_if = "Option::is_none")]
160    cl_ord_id: Option<String>,
161    #[serde(rename = "reqId", skip_serializing_if = "Option::is_none")]
162    req_id: Option<String>,
163    #[serde(rename = "cxlOnFail", skip_serializing_if = "Option::is_none")]
164    cxl_on_fail: Option<bool>,
165    #[serde(rename = "newSz", skip_serializing_if = "Option::is_none")]
166    new_sz: Option<String>,
167    #[serde(rename = "newPx", skip_serializing_if = "Option::is_none")]
168    new_px: Option<String>,
169}
170
171impl AmendOrderRequest {
172    /// Create an amend-order request for an instrument.
173    pub fn new(inst_id: impl Into<String>) -> Self {
174        Self {
175            inst_id: inst_id.into(),
176            ord_id: None,
177            cl_ord_id: None,
178            req_id: None,
179            cxl_on_fail: None,
180            new_sz: None,
181            new_px: None,
182        }
183    }
184
185    /// Set the OKX order ID.
186    pub fn order_id(mut self, ord_id: impl Into<String>) -> Self {
187        self.ord_id = Some(ord_id.into());
188        self
189    }
190
191    /// Set the client order ID.
192    pub fn client_order_id(mut self, cl_ord_id: impl Into<String>) -> Self {
193        self.cl_ord_id = Some(cl_ord_id.into());
194        self
195    }
196
197    /// Set a request ID.
198    pub fn request_id(mut self, req_id: impl Into<String>) -> Self {
199        self.req_id = Some(req_id.into());
200        self
201    }
202
203    /// Set whether OKX should cancel the order if amendment fails.
204    pub fn cancel_on_fail(mut self, cxl_on_fail: bool) -> Self {
205        self.cxl_on_fail = Some(cxl_on_fail);
206        self
207    }
208
209    /// Set the new order size.
210    pub fn new_size(mut self, new_sz: impl Into<String>) -> Self {
211        self.new_sz = Some(new_sz.into());
212        self
213    }
214
215    /// Set the new order price.
216    pub fn new_price(mut self, new_px: impl Into<String>) -> Self {
217        self.new_px = Some(new_px.into());
218        self
219    }
220}
221
222/// A request to close positions.
223#[derive(Debug, Clone, Serialize)]
224pub struct ClosePositionRequest {
225    #[serde(rename = "instId")]
226    inst_id: String,
227    #[serde(rename = "mgnMode")]
228    mgn_mode: TradeMode,
229    #[serde(rename = "posSide", skip_serializing_if = "Option::is_none")]
230    pos_side: Option<PositionSide>,
231    #[serde(skip_serializing_if = "Option::is_none")]
232    ccy: Option<String>,
233    #[serde(rename = "autoCxl", skip_serializing_if = "Option::is_none")]
234    auto_cancel: Option<bool>,
235    #[serde(rename = "clOrdId", skip_serializing_if = "Option::is_none")]
236    cl_ord_id: Option<String>,
237    #[serde(skip_serializing_if = "Option::is_none")]
238    tag: Option<String>,
239}
240
241impl ClosePositionRequest {
242    /// Create a close-position request.
243    pub fn new(inst_id: impl Into<String>, mgn_mode: TradeMode) -> Self {
244        Self {
245            inst_id: inst_id.into(),
246            mgn_mode,
247            pos_side: None,
248            ccy: None,
249            auto_cancel: None,
250            cl_ord_id: None,
251            tag: None,
252        }
253    }
254
255    /// Set the position side.
256    pub fn position_side(mut self, pos_side: PositionSide) -> Self {
257        self.pos_side = Some(pos_side);
258        self
259    }
260
261    /// Set the margin currency.
262    pub fn currency(mut self, ccy: impl Into<String>) -> Self {
263        self.ccy = Some(ccy.into());
264        self
265    }
266
267    /// Set whether pending orders should be canceled automatically.
268    pub fn auto_cancel(mut self, auto_cancel: bool) -> Self {
269        self.auto_cancel = Some(auto_cancel);
270        self
271    }
272
273    /// Set the client order ID.
274    pub fn client_order_id(mut self, cl_ord_id: impl Into<String>) -> Self {
275        self.cl_ord_id = Some(cl_ord_id.into());
276        self
277    }
278
279    /// Set an order tag.
280    pub fn tag(mut self, tag: impl Into<String>) -> Self {
281        self.tag = Some(tag.into());
282        self
283    }
284}
285
286/// Query parameters for pending order lists.
287#[derive(Debug, Clone, Default, Serialize)]
288pub struct OrderListRequest {
289    #[serde(rename = "instType", skip_serializing_if = "Option::is_none")]
290    inst_type: Option<crate::model::InstType>,
291    #[serde(rename = "uly", skip_serializing_if = "Option::is_none")]
292    underlying: Option<String>,
293    #[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
294    inst_id: Option<String>,
295    #[serde(rename = "ordType", skip_serializing_if = "Option::is_none")]
296    ord_type: Option<OrderType>,
297    #[serde(skip_serializing_if = "Option::is_none")]
298    state: Option<OrderState>,
299    #[serde(skip_serializing_if = "Option::is_none")]
300    after: Option<String>,
301    #[serde(skip_serializing_if = "Option::is_none")]
302    before: Option<String>,
303    #[serde(skip_serializing_if = "Option::is_none")]
304    limit: Option<u32>,
305    #[serde(rename = "instFamily", skip_serializing_if = "Option::is_none")]
306    inst_family: Option<String>,
307}
308
309impl OrderListRequest {
310    /// Create an empty order-list query.
311    pub fn new() -> Self {
312        Self::default()
313    }
314
315    /// Set the instrument type filter.
316    pub fn inst_type(mut self, inst_type: crate::model::InstType) -> Self {
317        self.inst_type = Some(inst_type);
318        self
319    }
320
321    /// Set the underlying filter.
322    pub fn underlying(mut self, underlying: impl Into<String>) -> Self {
323        self.underlying = Some(underlying.into());
324        self
325    }
326
327    /// Set the instrument ID filter.
328    pub fn inst_id(mut self, inst_id: impl Into<String>) -> Self {
329        self.inst_id = Some(inst_id.into());
330        self
331    }
332
333    /// Set the order type filter.
334    pub fn order_type(mut self, ord_type: OrderType) -> Self {
335        self.ord_type = Some(ord_type);
336        self
337    }
338
339    /// Set the order state filter.
340    pub fn state(mut self, state: OrderState) -> Self {
341        self.state = Some(state);
342        self
343    }
344
345    /// Return records after this pagination cursor.
346    pub fn after(mut self, after: impl Into<String>) -> Self {
347        self.after = Some(after.into());
348        self
349    }
350
351    /// Return records before this pagination cursor.
352    pub fn before(mut self, before: impl Into<String>) -> Self {
353        self.before = Some(before.into());
354        self
355    }
356
357    /// Set the maximum number of rows to return.
358    pub fn limit(mut self, limit: u32) -> Self {
359        self.limit = Some(limit);
360        self
361    }
362
363    /// Set the instrument family filter.
364    pub fn inst_family(mut self, inst_family: impl Into<String>) -> Self {
365        self.inst_family = Some(inst_family.into());
366        self
367    }
368}
369
370/// Query parameters for order history.
371#[derive(Debug, Clone, Serialize)]
372pub struct OrderHistoryRequest {
373    #[serde(flatten)]
374    base: OrderListRequest,
375    #[serde(skip_serializing_if = "Option::is_none")]
376    begin: Option<String>,
377    #[serde(skip_serializing_if = "Option::is_none")]
378    end: Option<String>,
379}
380
381impl OrderHistoryRequest {
382    /// Create an order-history query with the required instrument type.
383    pub fn new(inst_type: crate::model::InstType) -> Self {
384        Self {
385            base: OrderListRequest::new().inst_type(inst_type),
386            begin: None,
387            end: None,
388        }
389    }
390
391    /// Set the common order-list filters.
392    pub fn filters(mut self, base: OrderListRequest) -> Self {
393        self.base = base;
394        self
395    }
396
397    /// Set the begin timestamp.
398    pub fn begin(mut self, begin: impl Into<String>) -> Self {
399        self.begin = Some(begin.into());
400        self
401    }
402
403    /// Set the end timestamp.
404    pub fn end(mut self, end: impl Into<String>) -> Self {
405        self.end = Some(end.into());
406        self
407    }
408}
409
410/// Query parameters for fills.
411#[derive(Debug, Clone, Default, Serialize)]
412pub struct FillsRequest {
413    #[serde(rename = "instType", skip_serializing_if = "Option::is_none")]
414    inst_type: Option<crate::model::InstType>,
415    #[serde(rename = "uly", skip_serializing_if = "Option::is_none")]
416    underlying: Option<String>,
417    #[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
418    inst_id: Option<String>,
419    #[serde(rename = "ordId", skip_serializing_if = "Option::is_none")]
420    ord_id: Option<String>,
421    #[serde(skip_serializing_if = "Option::is_none")]
422    after: Option<String>,
423    #[serde(skip_serializing_if = "Option::is_none")]
424    before: Option<String>,
425    #[serde(skip_serializing_if = "Option::is_none")]
426    begin: Option<String>,
427    #[serde(skip_serializing_if = "Option::is_none")]
428    end: Option<String>,
429    #[serde(skip_serializing_if = "Option::is_none")]
430    limit: Option<u32>,
431    #[serde(rename = "instFamily", skip_serializing_if = "Option::is_none")]
432    inst_family: Option<String>,
433}
434
435impl FillsRequest {
436    /// Create an empty fills query.
437    pub fn new() -> Self {
438        Self::default()
439    }
440
441    /// Set the instrument type filter.
442    pub fn inst_type(mut self, inst_type: crate::model::InstType) -> Self {
443        self.inst_type = Some(inst_type);
444        self
445    }
446
447    /// Set the underlying filter.
448    pub fn underlying(mut self, underlying: impl Into<String>) -> Self {
449        self.underlying = Some(underlying.into());
450        self
451    }
452
453    /// Set the instrument ID filter.
454    pub fn inst_id(mut self, inst_id: impl Into<String>) -> Self {
455        self.inst_id = Some(inst_id.into());
456        self
457    }
458
459    /// Set the order ID filter.
460    pub fn order_id(mut self, ord_id: impl Into<String>) -> Self {
461        self.ord_id = Some(ord_id.into());
462        self
463    }
464
465    /// Return records after this pagination cursor.
466    pub fn after(mut self, after: impl Into<String>) -> Self {
467        self.after = Some(after.into());
468        self
469    }
470
471    /// Return records before this pagination cursor.
472    pub fn before(mut self, before: impl Into<String>) -> Self {
473        self.before = Some(before.into());
474        self
475    }
476
477    /// Set the begin timestamp.
478    pub fn begin(mut self, begin: impl Into<String>) -> Self {
479        self.begin = Some(begin.into());
480        self
481    }
482
483    /// Set the end timestamp.
484    pub fn end(mut self, end: impl Into<String>) -> Self {
485        self.end = Some(end.into());
486        self
487    }
488
489    /// Set the maximum number of rows to return.
490    pub fn limit(mut self, limit: u32) -> Self {
491        self.limit = Some(limit);
492        self
493    }
494
495    /// Set the instrument family filter.
496    pub fn inst_family(mut self, inst_family: impl Into<String>) -> Self {
497        self.inst_family = Some(inst_family.into());
498        self
499    }
500}
501
502/// Query parameters for historical fills.
503///
504/// This is intentionally separate from [`FillsRequest`] because OKX documents
505/// `instType` as required for `GET /api/v5/trade/fills-history`, while it is
506/// optional for `GET /api/v5/trade/fills`.
507#[derive(Debug, Clone, Serialize)]
508pub struct FillHistoryRequest {
509    #[serde(rename = "instType")]
510    inst_type: crate::model::InstType,
511    #[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
512    inst_id: Option<String>,
513    #[serde(rename = "ordId", skip_serializing_if = "Option::is_none")]
514    ord_id: Option<String>,
515    #[serde(skip_serializing_if = "Option::is_none")]
516    after: Option<String>,
517    #[serde(skip_serializing_if = "Option::is_none")]
518    before: Option<String>,
519    #[serde(skip_serializing_if = "Option::is_none")]
520    begin: Option<String>,
521    #[serde(skip_serializing_if = "Option::is_none")]
522    end: Option<String>,
523    #[serde(skip_serializing_if = "Option::is_none")]
524    limit: Option<u32>,
525}
526
527impl FillHistoryRequest {
528    /// Create a historical fills query with the required instrument type.
529    pub fn new(inst_type: crate::model::InstType) -> Self {
530        Self {
531            inst_type,
532            inst_id: None,
533            ord_id: None,
534            after: None,
535            before: None,
536            begin: None,
537            end: None,
538            limit: None,
539        }
540    }
541
542    /// Set the instrument ID filter.
543    pub fn inst_id(mut self, inst_id: impl Into<String>) -> Self {
544        self.inst_id = Some(inst_id.into());
545        self
546    }
547
548    /// Set the order ID filter.
549    pub fn order_id(mut self, ord_id: impl Into<String>) -> Self {
550        self.ord_id = Some(ord_id.into());
551        self
552    }
553
554    /// Return records after this pagination cursor.
555    pub fn after(mut self, after: impl Into<String>) -> Self {
556        self.after = Some(after.into());
557        self
558    }
559
560    /// Return records before this pagination cursor.
561    pub fn before(mut self, before: impl Into<String>) -> Self {
562        self.before = Some(before.into());
563        self
564    }
565
566    /// Set the begin timestamp.
567    pub fn begin(mut self, begin: impl Into<String>) -> Self {
568        self.begin = Some(begin.into());
569        self
570    }
571
572    /// Set the end timestamp.
573    pub fn end(mut self, end: impl Into<String>) -> Self {
574        self.end = Some(end.into());
575        self
576    }
577
578    /// Set the maximum number of rows to return.
579    pub fn limit(mut self, limit: u32) -> Self {
580        self.limit = Some(limit);
581        self
582    }
583}