Skip to main content

rust_okx/api/trade/
requests.rs

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