Skip to main content

coinbase_advanced/models/
order.rs

1//! Order-related types.
2
3use serde::{Deserialize, Serialize};
4
5/// Order side (buy or sell).
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
8pub enum OrderSide {
9    /// Buy order.
10    Buy,
11    /// Sell order.
12    Sell,
13}
14
15/// Order status.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
17#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
18pub enum OrderStatus {
19    /// Order is pending.
20    Pending,
21    /// Order is open.
22    Open,
23    /// Order has been filled.
24    Filled,
25    /// Order has been cancelled.
26    Cancelled,
27    /// Order has expired.
28    Expired,
29    /// Order failed.
30    Failed,
31    /// Unknown status.
32    #[serde(other)]
33    Unknown,
34}
35
36/// Stop direction for stop orders.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
39pub enum StopDirection {
40    /// Stop triggers when price goes up.
41    StopDirectionStopUp,
42    /// Stop triggers when price goes down.
43    StopDirectionStopDown,
44}
45
46/// Market IOC order configuration.
47#[derive(Debug, Clone, Serialize)]
48pub struct MarketIoc {
49    /// Size in quote currency (e.g., USD).
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub quote_size: Option<String>,
52    /// Size in base currency (e.g., BTC).
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub base_size: Option<String>,
55}
56
57/// Limit GTC order configuration.
58#[derive(Debug, Clone, Serialize)]
59pub struct LimitGtc {
60    /// Size in base currency.
61    pub base_size: String,
62    /// Limit price.
63    pub limit_price: String,
64    /// Whether to only add liquidity.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub post_only: Option<bool>,
67}
68
69/// Limit GTD order configuration.
70#[derive(Debug, Clone, Serialize)]
71pub struct LimitGtd {
72    /// Size in base currency.
73    pub base_size: String,
74    /// Limit price.
75    pub limit_price: String,
76    /// Expiration time (ISO 8601).
77    pub end_time: String,
78    /// Whether to only add liquidity.
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub post_only: Option<bool>,
81}
82
83/// Limit FOK order configuration.
84#[derive(Debug, Clone, Serialize)]
85pub struct LimitFok {
86    /// Size in base currency.
87    pub base_size: String,
88    /// Limit price.
89    pub limit_price: String,
90}
91
92/// Stop-limit GTC order configuration.
93#[derive(Debug, Clone, Serialize)]
94pub struct StopLimitGtc {
95    /// Size in base currency.
96    pub base_size: String,
97    /// Limit price.
98    pub limit_price: String,
99    /// Stop price.
100    pub stop_price: String,
101    /// Stop direction.
102    pub stop_direction: StopDirection,
103}
104
105/// Stop-limit GTD order configuration.
106#[derive(Debug, Clone, Serialize)]
107pub struct StopLimitGtd {
108    /// Size in base currency.
109    pub base_size: String,
110    /// Limit price.
111    pub limit_price: String,
112    /// Stop price.
113    pub stop_price: String,
114    /// Expiration time (ISO 8601).
115    pub end_time: String,
116    /// Stop direction.
117    pub stop_direction: StopDirection,
118}
119
120/// Order configuration.
121#[derive(Debug, Clone, Serialize)]
122#[serde(untagged)]
123pub enum OrderConfiguration {
124    /// Market order (immediate-or-cancel).
125    MarketIoc {
126        /// Market IOC configuration.
127        market_market_ioc: MarketIoc,
128    },
129    /// Limit order (good-til-cancelled).
130    LimitGtc {
131        /// Limit GTC configuration.
132        limit_limit_gtc: LimitGtc,
133    },
134    /// Limit order (good-til-date).
135    LimitGtd {
136        /// Limit GTD configuration.
137        limit_limit_gtd: LimitGtd,
138    },
139    /// Limit order (fill-or-kill).
140    LimitFok {
141        /// Limit FOK configuration.
142        limit_limit_fok: LimitFok,
143    },
144    /// Stop-limit order (good-til-cancelled).
145    StopLimitGtc {
146        /// Stop-limit GTC configuration.
147        stop_limit_stop_limit_gtc: StopLimitGtc,
148    },
149    /// Stop-limit order (good-til-date).
150    StopLimitGtd {
151        /// Stop-limit GTD configuration.
152        stop_limit_stop_limit_gtd: StopLimitGtd,
153    },
154}
155
156impl OrderConfiguration {
157    /// Create a market buy order by quote size (e.g., $100 of BTC).
158    pub fn market_buy_quote(quote_size: impl Into<String>) -> Self {
159        Self::MarketIoc {
160            market_market_ioc: MarketIoc {
161                quote_size: Some(quote_size.into()),
162                base_size: None,
163            },
164        }
165    }
166
167    /// Create a market buy order by base size (e.g., 0.001 BTC).
168    pub fn market_buy_base(base_size: impl Into<String>) -> Self {
169        Self::MarketIoc {
170            market_market_ioc: MarketIoc {
171                quote_size: None,
172                base_size: Some(base_size.into()),
173            },
174        }
175    }
176
177    /// Create a market sell order by base size.
178    pub fn market_sell(base_size: impl Into<String>) -> Self {
179        Self::MarketIoc {
180            market_market_ioc: MarketIoc {
181                quote_size: None,
182                base_size: Some(base_size.into()),
183            },
184        }
185    }
186
187    /// Create a limit GTC order.
188    pub fn limit_gtc(
189        base_size: impl Into<String>,
190        limit_price: impl Into<String>,
191        post_only: bool,
192    ) -> Self {
193        Self::LimitGtc {
194            limit_limit_gtc: LimitGtc {
195                base_size: base_size.into(),
196                limit_price: limit_price.into(),
197                post_only: Some(post_only),
198            },
199        }
200    }
201
202    /// Create a limit GTD order.
203    pub fn limit_gtd(
204        base_size: impl Into<String>,
205        limit_price: impl Into<String>,
206        end_time: impl Into<String>,
207        post_only: bool,
208    ) -> Self {
209        Self::LimitGtd {
210            limit_limit_gtd: LimitGtd {
211                base_size: base_size.into(),
212                limit_price: limit_price.into(),
213                end_time: end_time.into(),
214                post_only: Some(post_only),
215            },
216        }
217    }
218
219    /// Create a limit FOK order.
220    pub fn limit_fok(base_size: impl Into<String>, limit_price: impl Into<String>) -> Self {
221        Self::LimitFok {
222            limit_limit_fok: LimitFok {
223                base_size: base_size.into(),
224                limit_price: limit_price.into(),
225            },
226        }
227    }
228
229    /// Create a stop-limit GTC order.
230    pub fn stop_limit_gtc(
231        base_size: impl Into<String>,
232        limit_price: impl Into<String>,
233        stop_price: impl Into<String>,
234        stop_direction: StopDirection,
235    ) -> Self {
236        Self::StopLimitGtc {
237            stop_limit_stop_limit_gtc: StopLimitGtc {
238                base_size: base_size.into(),
239                limit_price: limit_price.into(),
240                stop_price: stop_price.into(),
241                stop_direction,
242            },
243        }
244    }
245
246    /// Create a stop-limit GTD order.
247    pub fn stop_limit_gtd(
248        base_size: impl Into<String>,
249        limit_price: impl Into<String>,
250        stop_price: impl Into<String>,
251        end_time: impl Into<String>,
252        stop_direction: StopDirection,
253    ) -> Self {
254        Self::StopLimitGtd {
255            stop_limit_stop_limit_gtd: StopLimitGtd {
256                base_size: base_size.into(),
257                limit_price: limit_price.into(),
258                stop_price: stop_price.into(),
259                end_time: end_time.into(),
260                stop_direction,
261            },
262        }
263    }
264}
265
266/// Request to create an order.
267#[derive(Debug, Clone, Serialize)]
268pub struct CreateOrderRequest {
269    /// Client-generated order ID (UUID recommended).
270    pub client_order_id: String,
271    /// Product ID (e.g., "BTC-USD").
272    pub product_id: String,
273    /// Order side (buy or sell).
274    pub side: OrderSide,
275    /// Order configuration.
276    pub order_configuration: OrderConfiguration,
277    /// Self-trade prevention ID (optional).
278    #[serde(skip_serializing_if = "Option::is_none")]
279    pub self_trade_prevention_id: Option<String>,
280    /// Leverage (for margin trading).
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub leverage: Option<String>,
283    /// Margin type (for margin trading).
284    #[serde(skip_serializing_if = "Option::is_none")]
285    pub margin_type: Option<String>,
286    /// Retail portfolio ID.
287    #[serde(skip_serializing_if = "Option::is_none")]
288    pub retail_portfolio_id: Option<String>,
289}
290
291impl CreateOrderRequest {
292    /// Create a new order request.
293    pub fn new(
294        client_order_id: impl Into<String>,
295        product_id: impl Into<String>,
296        side: OrderSide,
297        order_configuration: OrderConfiguration,
298    ) -> Self {
299        Self {
300            client_order_id: client_order_id.into(),
301            product_id: product_id.into(),
302            side,
303            order_configuration,
304            self_trade_prevention_id: None,
305            leverage: None,
306            margin_type: None,
307            retail_portfolio_id: None,
308        }
309    }
310}
311
312/// Success response when creating an order.
313#[derive(Debug, Clone, Deserialize)]
314pub struct OrderSuccessResponse {
315    /// The order ID.
316    pub order_id: String,
317    /// Product ID.
318    pub product_id: Option<String>,
319    /// Order side.
320    pub side: Option<String>,
321    /// Client order ID.
322    pub client_order_id: Option<String>,
323}
324
325/// Response from creating an order.
326#[derive(Debug, Clone, Deserialize)]
327pub struct CreateOrderResponse {
328    /// Whether the order was successful.
329    pub success: bool,
330    /// Failure reason (if failed).
331    pub failure_reason: Option<String>,
332    /// Order ID (if successful).
333    pub order_id: Option<String>,
334    /// Success response details.
335    pub success_response: Option<OrderSuccessResponse>,
336    /// Error response (if failed).
337    pub error_response: Option<serde_json::Value>,
338}
339
340/// Request to cancel orders.
341#[derive(Debug, Clone, Serialize)]
342pub struct CancelOrdersRequest {
343    /// Order IDs to cancel.
344    pub order_ids: Vec<String>,
345}
346
347impl CancelOrdersRequest {
348    /// Create a new cancel orders request.
349    pub fn new(order_ids: Vec<String>) -> Self {
350        Self { order_ids }
351    }
352
353    /// Create a cancel request for a single order.
354    pub fn single(order_id: impl Into<String>) -> Self {
355        Self {
356            order_ids: vec![order_id.into()],
357        }
358    }
359}
360
361/// Result of cancelling a single order.
362#[derive(Debug, Clone, Deserialize)]
363pub struct CancelOrderResult {
364    /// Whether the cancellation was successful.
365    pub success: bool,
366    /// Failure reason (if failed).
367    pub failure_reason: Option<String>,
368    /// The order ID.
369    pub order_id: String,
370}
371
372/// Response from cancelling orders.
373#[derive(Debug, Clone, Deserialize)]
374pub struct CancelOrdersResponse {
375    /// Results for each order.
376    pub results: Vec<CancelOrderResult>,
377}
378
379/// Request to edit an order.
380#[derive(Debug, Clone, Serialize)]
381pub struct EditOrderRequest {
382    /// The order ID to edit.
383    pub order_id: String,
384    /// New price.
385    #[serde(skip_serializing_if = "Option::is_none")]
386    pub price: Option<String>,
387    /// New size.
388    #[serde(skip_serializing_if = "Option::is_none")]
389    pub size: Option<String>,
390}
391
392impl EditOrderRequest {
393    /// Create a new edit order request.
394    pub fn new(order_id: impl Into<String>) -> Self {
395        Self {
396            order_id: order_id.into(),
397            price: None,
398            size: None,
399        }
400    }
401
402    /// Set the new price.
403    pub fn price(mut self, price: impl Into<String>) -> Self {
404        self.price = Some(price.into());
405        self
406    }
407
408    /// Set the new size.
409    pub fn size(mut self, size: impl Into<String>) -> Self {
410        self.size = Some(size.into());
411        self
412    }
413}
414
415/// Response from editing an order.
416#[derive(Debug, Clone, Deserialize)]
417pub struct EditOrderResponse {
418    /// Whether the edit was successful.
419    pub success: bool,
420    /// Errors (if any).
421    pub errors: Option<Vec<serde_json::Value>>,
422}
423
424/// An order.
425#[derive(Debug, Clone, Deserialize)]
426pub struct Order {
427    /// Order ID.
428    pub order_id: String,
429    /// Product ID.
430    pub product_id: String,
431    /// User ID.
432    pub user_id: Option<String>,
433    /// Order configuration.
434    pub order_configuration: Option<serde_json::Value>,
435    /// Order side.
436    pub side: String,
437    /// Client order ID.
438    pub client_order_id: String,
439    /// Order status.
440    pub status: String,
441    /// Time in force.
442    pub time_in_force: Option<String>,
443    /// Created time.
444    pub created_time: Option<String>,
445    /// Completion percentage.
446    pub completion_percentage: Option<String>,
447    /// Filled size.
448    pub filled_size: Option<String>,
449    /// Average filled price.
450    pub average_filled_price: Option<String>,
451    /// Fee amount.
452    pub fee: Option<String>,
453    /// Number of fills.
454    pub number_of_fills: Option<String>,
455    /// Filled value.
456    pub filled_value: Option<String>,
457    /// Whether the order is pending cancel.
458    pub pending_cancel: Option<bool>,
459    /// Whether the order size includes fees.
460    pub size_in_quote: Option<bool>,
461    /// Total fees.
462    pub total_fees: Option<String>,
463    /// Whether size includes fees.
464    pub size_inclusive_of_fees: Option<bool>,
465    /// Total value after fees.
466    pub total_value_after_fees: Option<String>,
467    /// Trigger status.
468    pub trigger_status: Option<String>,
469    /// Order type.
470    pub order_type: Option<String>,
471    /// Reject reason.
472    pub reject_reason: Option<String>,
473    /// Settled.
474    pub settled: Option<bool>,
475    /// Product type.
476    pub product_type: Option<String>,
477    /// Reject message.
478    pub reject_message: Option<String>,
479    /// Cancel message.
480    pub cancel_message: Option<String>,
481    /// Order placement source.
482    pub order_placement_source: Option<String>,
483    /// Outstanding hold amount.
484    pub outstanding_hold_amount: Option<String>,
485}
486
487/// Parameters for listing orders.
488#[derive(Debug, Clone, Default, Serialize)]
489pub struct ListOrdersParams {
490    /// Filter by product IDs.
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub product_ids: Option<String>,
493    /// Filter by order status.
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub order_status: Option<String>,
496    /// Maximum number of orders.
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub limit: Option<u32>,
499    /// Start date (ISO 8601).
500    #[serde(skip_serializing_if = "Option::is_none")]
501    pub start_date: Option<String>,
502    /// End date (ISO 8601).
503    #[serde(skip_serializing_if = "Option::is_none")]
504    pub end_date: Option<String>,
505    /// Order side.
506    #[serde(skip_serializing_if = "Option::is_none")]
507    pub order_side: Option<String>,
508    /// Cursor for pagination.
509    #[serde(skip_serializing_if = "Option::is_none")]
510    pub cursor: Option<String>,
511    /// Product type.
512    #[serde(skip_serializing_if = "Option::is_none")]
513    pub product_type: Option<String>,
514    /// Order type.
515    #[serde(skip_serializing_if = "Option::is_none")]
516    pub order_type: Option<String>,
517    /// Retail portfolio ID.
518    #[serde(skip_serializing_if = "Option::is_none")]
519    pub retail_portfolio_id: Option<String>,
520}
521
522impl ListOrdersParams {
523    /// Create new list orders parameters.
524    pub fn new() -> Self {
525        Self::default()
526    }
527
528    /// Filter by product ID.
529    pub fn product_id(mut self, product_id: impl Into<String>) -> Self {
530        self.product_ids = Some(product_id.into());
531        self
532    }
533
534    /// Filter by order status.
535    pub fn status(mut self, status: impl Into<String>) -> Self {
536        self.order_status = Some(status.into());
537        self
538    }
539
540    /// Set the limit.
541    pub fn limit(mut self, limit: u32) -> Self {
542        self.limit = Some(limit);
543        self
544    }
545
546    /// Set the cursor.
547    pub fn cursor(mut self, cursor: impl Into<String>) -> Self {
548        self.cursor = Some(cursor.into());
549        self
550    }
551}
552
553/// Response from listing orders.
554#[derive(Debug, Clone, Deserialize)]
555pub struct ListOrdersResponse {
556    /// The orders.
557    pub orders: Vec<Order>,
558    /// Sequence number.
559    pub sequence: Option<String>,
560    /// Whether there are more orders.
561    pub has_next: bool,
562    /// Cursor for the next page.
563    pub cursor: Option<String>,
564}
565
566/// An order fill (execution).
567#[derive(Debug, Clone, Deserialize)]
568pub struct Fill {
569    /// Entry ID.
570    pub entry_id: String,
571    /// Trade ID.
572    pub trade_id: String,
573    /// Order ID.
574    pub order_id: String,
575    /// Trade time.
576    pub trade_time: String,
577    /// Trade type.
578    pub trade_type: String,
579    /// Execution price.
580    pub price: String,
581    /// Execution size.
582    pub size: String,
583    /// Commission.
584    pub commission: String,
585    /// Product ID.
586    pub product_id: String,
587    /// Sequence timestamp.
588    pub sequence_timestamp: Option<String>,
589    /// Liquidity indicator.
590    pub liquidity_indicator: Option<String>,
591    /// Size in quote currency.
592    pub size_in_quote: Option<bool>,
593    /// User ID.
594    pub user_id: Option<String>,
595    /// Order side.
596    pub side: Option<String>,
597}
598
599/// Parameters for listing fills.
600#[derive(Debug, Clone, Default, Serialize)]
601pub struct ListFillsParams {
602    /// Filter by order ID.
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub order_id: Option<String>,
605    /// Filter by product ID.
606    #[serde(skip_serializing_if = "Option::is_none")]
607    pub product_id: Option<String>,
608    /// Start sequence timestamp.
609    #[serde(skip_serializing_if = "Option::is_none")]
610    pub start_sequence_timestamp: Option<String>,
611    /// End sequence timestamp.
612    #[serde(skip_serializing_if = "Option::is_none")]
613    pub end_sequence_timestamp: Option<String>,
614    /// Maximum number of fills.
615    #[serde(skip_serializing_if = "Option::is_none")]
616    pub limit: Option<u32>,
617    /// Cursor for pagination.
618    #[serde(skip_serializing_if = "Option::is_none")]
619    pub cursor: Option<String>,
620}
621
622impl ListFillsParams {
623    /// Create new list fills parameters.
624    pub fn new() -> Self {
625        Self::default()
626    }
627
628    /// Filter by order ID.
629    pub fn order_id(mut self, order_id: impl Into<String>) -> Self {
630        self.order_id = Some(order_id.into());
631        self
632    }
633
634    /// Filter by product ID.
635    pub fn product_id(mut self, product_id: impl Into<String>) -> Self {
636        self.product_id = Some(product_id.into());
637        self
638    }
639
640    /// Set the limit.
641    pub fn limit(mut self, limit: u32) -> Self {
642        self.limit = Some(limit);
643        self
644    }
645
646    /// Set the cursor.
647    pub fn cursor(mut self, cursor: impl Into<String>) -> Self {
648        self.cursor = Some(cursor.into());
649        self
650    }
651}
652
653/// Response from listing fills.
654#[derive(Debug, Clone, Deserialize)]
655pub struct ListFillsResponse {
656    /// The fills.
657    pub fills: Vec<Fill>,
658    /// Cursor for the next page.
659    pub cursor: Option<String>,
660}
661
662/// Request to close a position.
663#[derive(Debug, Clone, Serialize)]
664pub struct ClosePositionRequest {
665    /// Client order ID.
666    pub client_order_id: String,
667    /// Product ID.
668    pub product_id: String,
669    /// Size to close (optional, closes entire position if not specified).
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub size: Option<String>,
672}
673
674impl ClosePositionRequest {
675    /// Create a new close position request.
676    pub fn new(client_order_id: impl Into<String>, product_id: impl Into<String>) -> Self {
677        Self {
678            client_order_id: client_order_id.into(),
679            product_id: product_id.into(),
680            size: None,
681        }
682    }
683
684    /// Set the size to close.
685    pub fn size(mut self, size: impl Into<String>) -> Self {
686        self.size = Some(size.into());
687        self
688    }
689}