coinbase_client/private_client/
order.rs

1use serde::Serialize;
2
3/// A `OrderBuilder` should be used to create a `Order` with  custom configuration.
4#[derive(Serialize, Debug)]
5pub struct Order {
6    r#type: String,
7    size: Option<f64>,
8    price: Option<f64>,
9    side: OrderSide,
10    client_oid: Option<String>,
11    self_trade_prevention: Option<SelfTradePrevention>,
12    time_in_force: Option<TimeInForce>,
13    cancel_after: Option<CancelAfter>,
14    post_only: Option<bool>,
15    funds: Option<f64>,
16    product_id: String,
17    stp: Option<String>,
18    stop: Option<OrderStop>,
19    stop_price: Option<f64>,
20}
21
22/// A `OrderBuilder` should be used to create a `Order` with  custom configuration.
23impl Order {
24    /// returns a `OrderBuilder` with requiered market-order parameters, equivalent OrderBuilder::market
25    pub fn market_builder(
26        side: OrderSide,
27        product_id: &str,
28        size_or_funds: SizeOrFunds,
29    ) -> impl SharedOptions {
30        OrderBuilder {
31            r#type: "market".to_string(),
32            size: match size_or_funds {
33                SizeOrFunds::Size(n) => Some(n),
34                _ => None,
35            },
36            price: None,
37            side,
38            client_oid: None,
39            self_trade_prevention: None,
40            time_in_force: None,
41            cancel_after: None,
42            post_only: None,
43            funds: match size_or_funds {
44                SizeOrFunds::Funds(n) => Some(n),
45                _ => None,
46            },
47            product_id: product_id.to_string(),
48            stp: None,
49            stop: None,
50            stop_price: None,
51        }
52    }
53
54    /// returns a `OrderBuilder` with requiered limit-order parameters, equivalent OrderBuilder::limit
55    pub fn limit_builder(
56        side: OrderSide,
57        product_id: &str,
58        price: f64,
59        size: f64,
60    ) -> impl LimitOptions + SharedOptions {
61        OrderBuilder {
62            r#type: "limit".to_string(),
63            size: Some(size),
64            price: Some(price),
65            side: side,
66            client_oid: None,
67            self_trade_prevention: None,
68            time_in_force: None,
69            cancel_after: None,
70            post_only: None,
71            funds: None,
72            product_id: product_id.to_string(),
73            stp: None,
74            stop: None,
75            stop_price: None,
76        }
77    }
78
79    /// returns a `OrderBuilder` with requiered stop-order parameters, equivalent OrderBuilder::stop
80    pub fn stop_builder(
81        side: OrderSide,
82        product_id: &str,
83        price: f64,
84        size: f64,
85        stop_price: f64,
86        stop: OrderStop,
87    ) -> impl SharedOptions {
88        OrderBuilder {
89            r#type: "limit".to_string(),
90            size: Some(size),
91            price: Some(price),
92            side: side,
93            client_oid: None,
94            self_trade_prevention: None,
95            time_in_force: None,
96            cancel_after: None,
97            post_only: None,
98            funds: None,
99            product_id: product_id.to_string(),
100            stp: None,
101            stop: Some(stop),
102            stop_price: Some(stop_price),
103        }
104    }
105}
106
107/// A `OrderBuilder` can be used to create a `Order` with custom configuration.
108/// <br>
109/// Confiuguration parameters details can be found [here](https://docs.pro.coinbase.com/#orders)
110pub struct OrderBuilder {
111    r#type: String,
112    size: Option<f64>,
113    price: Option<f64>,
114    side: OrderSide,
115    client_oid: Option<String>,
116    self_trade_prevention: Option<SelfTradePrevention>,
117    time_in_force: Option<TimeInForce>,
118    cancel_after: Option<CancelAfter>,
119    post_only: Option<bool>,
120    funds: Option<f64>,
121    product_id: String,
122    stp: Option<String>,
123    stop: Option<OrderStop>,
124    stop_price: Option<f64>,
125}
126
127impl OrderBuilder {
128    /// returns a `OrderBuilder` with requiered market-order parameters.
129    pub fn market(
130        side: OrderSide,
131        product_id: &str,
132        size_or_funds: SizeOrFunds,
133    ) -> impl SharedOptions {
134        Self {
135            r#type: "market".to_string(),
136            size: match size_or_funds {
137                SizeOrFunds::Size(n) => Some(n),
138                _ => None,
139            },
140            price: None,
141            side,
142            client_oid: None,
143            self_trade_prevention: None,
144            time_in_force: None,
145            cancel_after: None,
146            post_only: None,
147            funds: match size_or_funds {
148                SizeOrFunds::Funds(n) => Some(n),
149                _ => None,
150            },
151            product_id: product_id.to_string(),
152            stp: None,
153            stop: None,
154            stop_price: None,
155        }
156    }
157
158    /// returns a `OrderBuilder` with requiered limit-order parameters.
159    pub fn limit(
160        side: OrderSide,
161        product_id: &str,
162        price: f64,
163        size: f64,
164    ) -> impl LimitOptions + SharedOptions {
165        Self {
166            r#type: "limit".to_string(),
167            size: Some(size),
168            price: Some(price),
169            side: side,
170            client_oid: None,
171            self_trade_prevention: None,
172            time_in_force: None,
173            cancel_after: None,
174            post_only: None,
175            funds: None,
176            product_id: product_id.to_string(),
177            stp: None,
178            stop: None,
179            stop_price: None,
180        }
181    }
182
183    /// returns a `OrderBuilder` with requiered stop-order parameters.
184    pub fn stop(
185        side: OrderSide,
186        product_id: &str,
187        price: f64,
188        size: f64,
189        stop_price: f64,
190        stop: OrderStop,
191    ) -> impl SharedOptions {
192        Self {
193            r#type: "limit".to_string(),
194            size: Some(size),
195            price: Some(price),
196            side: side,
197            client_oid: None,
198            self_trade_prevention: None,
199            time_in_force: None,
200            cancel_after: None,
201            post_only: None,
202            funds: None,
203            product_id: product_id.to_string(),
204            stp: None,
205            stop: Some(stop),
206            stop_price: Some(stop_price),
207        }
208    }
209}
210
211/// 'SharedOptions' options can be used with market, limit and stop order types
212pub trait SharedOptions {
213    fn self_trade_prevention(self, self_trade_prevention: SelfTradePrevention) -> Self;
214    fn client_oid(self, client_oid: String) -> Self;
215    fn build(self) -> Order;
216}
217
218impl SharedOptions for OrderBuilder {
219    /// Sets the Orders self-trade behavior
220    fn self_trade_prevention(mut self, self_trade_prevention: SelfTradePrevention) -> Self {
221        self.self_trade_prevention = Some(self_trade_prevention);
222        self
223    }
224
225    /// Sets the Order ID to identify your order
226    /// The client_oid is different than the server-assigned order id.
227    /// <br>
228    /// If you are consuming the public feed and see a received message with your client_oid,
229    /// <br>
230    /// you should record the server-assigned order_id as it will be used for future order status updates.
231    /// <br>
232    /// The client_oid will NOT be used after the received message is sent.
233    fn client_oid(mut self, client_oid: String) -> Self {
234        self.client_oid = Some(client_oid);
235        self
236    }
237
238    /// Builds `Order`
239    fn build(self) -> Order {
240        Order {
241            r#type: self.r#type,
242            size: self.size,
243            price: self.price,
244            side: self.side,
245            client_oid: self.client_oid,
246            self_trade_prevention: self.self_trade_prevention,
247            time_in_force: self.time_in_force,
248            cancel_after: self.cancel_after,
249            post_only: self.post_only,
250            funds: self.funds,
251            product_id: self.product_id,
252            stp: self.stp,
253            stop: self.stop,
254            stop_price: self.stop_price,
255        }
256    }
257}
258
259/// Builder options for Limit Orders
260pub trait LimitOptions {
261    fn time_in_force(self, time_in_force: TimeInForce) -> Self;
262}
263
264impl LimitOptions for OrderBuilder {
265    /// This option provides guarantees about the lifetime of an Order
266    fn time_in_force(mut self, time_in_force: TimeInForce) -> Self {
267        match time_in_force {
268            TimeInForce::GoodTillTime {
269                cancel_after,
270                post_only,
271            } => {
272                self.cancel_after = Some(cancel_after);
273                self.post_only = Some(post_only);
274            }
275            TimeInForce::GoodTillCancel { post_only } => self.post_only = Some(post_only),
276            _ => {}
277        }
278        self.time_in_force = Some(time_in_force);
279        self
280    }
281}
282
283/// Buy or Sell `Order`
284#[derive(Clone, Copy, Debug)]
285pub enum OrderSide {
286    Buy,
287    Sell,
288}
289/// Loss triggers when the last trade price changes to a value at or below the stop_price.
290/// <br>
291/// Entry triggers when the last trade price changes to a value at or above the stop_price.
292#[derive(Clone, Copy, Debug)]
293pub enum OrderStop {
294    Loss,
295    Entry,
296}
297
298/// Size or Funds of Currency
299#[derive(Clone, Copy, Debug)]
300pub enum SizeOrFunds {
301    Size(f64),
302    Funds(f64),
303}
304
305// Time in force policies provide guarantees about the lifetime of an `Order`
306#[derive(Clone, Copy, Debug)]
307pub enum TimeInForce {
308    GoodTillCancel {
309        post_only: bool,
310    },
311    GoodTillTime {
312        cancel_after: CancelAfter,
313        post_only: bool,
314    },
315    ImmediateOrCancel,
316    FillOrKill,
317}
318
319#[derive(Clone, Copy, Debug)]
320pub enum CancelAfter {
321    Minute,
322    Hour,
323    Day,
324}
325
326/// Used to change the self-trade behavior
327#[derive(Clone, Copy, Debug)]
328pub enum SelfTradePrevention {
329    DecreaseCancel,
330    CancelOldest,
331    CancelNewest,
332    CancelBoth,
333}
334
335impl serde::Serialize for SelfTradePrevention {
336    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
337    where
338        S: serde::ser::Serializer,
339    {
340        match self {
341            Self::CancelBoth => serializer.serialize_str("cb"),
342            Self::DecreaseCancel => serializer.serialize_str("dc"),
343            Self::CancelOldest => serializer.serialize_str("co"),
344            Self::CancelNewest => serializer.serialize_str("cn"),
345        }
346    }
347}
348
349impl serde::Serialize for OrderStop {
350    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
351    where
352        S: serde::ser::Serializer,
353    {
354        match self {
355            Self::Loss => serializer.serialize_str("loss"),
356            Self::Entry => serializer.serialize_str("entry"),
357        }
358    }
359}
360
361impl serde::Serialize for TimeInForce {
362    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
363    where
364        S: serde::ser::Serializer,
365    {
366        match self {
367            Self::GoodTillCancel { post_only: _ } => serializer.serialize_str("GTC"),
368            Self::GoodTillTime {
369                cancel_after: _,
370                post_only: _,
371            } => serializer.serialize_str("GTT"),
372            Self::ImmediateOrCancel => serializer.serialize_str("IOC"),
373            Self::FillOrKill => serializer.serialize_str("FOK"),
374        }
375    }
376}
377
378impl serde::Serialize for CancelAfter {
379    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
380    where
381        S: serde::ser::Serializer,
382    {
383        match self {
384            Self::Minute => serializer.serialize_str("min"),
385            Self::Hour => serializer.serialize_str("hour"),
386            Self::Day => serializer.serialize_str("day"),
387        }
388    }
389}
390
391impl serde::Serialize for OrderSide {
392    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
393    where
394        S: serde::ser::Serializer,
395    {
396        match self {
397            Self::Buy => serializer.serialize_str("buy"),
398            Self::Sell => serializer.serialize_str("sell"),
399        }
400    }
401}
402
403impl serde::Serialize for SizeOrFunds {
404    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
405    where
406        S: serde::ser::Serializer,
407    {
408        match *self {
409            Self::Size(size) => serializer.serialize_f64(size),
410            Self::Funds(funds) => serializer.serialize_f64(funds),
411        }
412    }
413}