Skip to main content

schwab_api/endpoints/
orders.rs

1use crate::client::SchwabClient;
2use crate::error::{ApiError, Result};
3use crate::models::order::{Order, OrderRequest, PreviewOrder};
4use crate::query::{default_order_window, default_orders_all_window, resolve_time_range};
5
6pub struct OrdersApi<'a> {
7    client: &'a SchwabClient,
8}
9
10impl<'a> OrdersApi<'a> {
11    pub fn new(client: &'a SchwabClient) -> Self {
12        Self { client }
13    }
14
15    /// GET /accounts/{accountNumber}/orders
16    pub async fn list_for_account(
17        &self,
18        account_number: &str,
19        from_entered_time: Option<&str>,
20        to_entered_time: Option<&str>,
21        status: Option<&str>,
22        max_results: Option<&str>,
23    ) -> Result<Vec<Order>> {
24        let (from, to) =
25            resolve_time_range(from_entered_time, to_entered_time, default_order_window)
26                .map_err(ApiError::Other)?;
27        let path = format!("/accounts/{account_number}/orders");
28        let query = super::merge_queries(vec![
29            vec![
30                ("fromEnteredTime", from.as_str()),
31                ("toEnteredTime", to.as_str()),
32            ],
33            super::opt_query("status", status),
34            super::opt_query("maxResults", max_results),
35        ]);
36        self.client.get_json(&path, &query).await
37    }
38
39    /// GET /orders — all accounts (from date must be within 60 days).
40    pub async fn list_all(
41        &self,
42        from_entered_time: Option<&str>,
43        to_entered_time: Option<&str>,
44        status: Option<&str>,
45        max_results: Option<&str>,
46    ) -> Result<Vec<Order>> {
47        let (from, to) = resolve_time_range(
48            from_entered_time,
49            to_entered_time,
50            default_orders_all_window,
51        )
52        .map_err(ApiError::Other)?;
53        let query = super::merge_queries(vec![
54            vec![
55                ("fromEnteredTime", from.as_str()),
56                ("toEnteredTime", to.as_str()),
57            ],
58            super::opt_query("status", status),
59            super::opt_query("maxResults", max_results),
60        ]);
61        self.client.get_json("/orders", &query).await
62    }
63
64    /// GET /accounts/{accountNumber}/orders/{orderId}
65    pub async fn get(&self, account_number: &str, order_id: &str) -> Result<Order> {
66        let path = format!("/accounts/{account_number}/orders/{order_id}");
67        self.client.get_json(&path, &[]).await
68    }
69
70    /// POST /accounts/{accountNumber}/orders — 201 empty body + Location header.
71    pub async fn place(
72        &self,
73        account_number: &str,
74        order: &OrderRequest,
75    ) -> Result<crate::client::MutationResponse> {
76        let path = format!("/accounts/{account_number}/orders");
77        self.client.post_mutate(&path, order).await
78    }
79
80    /// POST /accounts/{accountNumber}/previewOrder
81    pub async fn preview(
82        &self,
83        account_number: &str,
84        order: &OrderRequest,
85    ) -> Result<PreviewOrder> {
86        let path = format!("/accounts/{account_number}/previewOrder");
87        self.client.post_json(&path, order).await
88    }
89
90    /// DELETE /accounts/{accountNumber}/orders/{orderId}
91    pub async fn cancel(
92        &self,
93        account_number: &str,
94        order_id: &str,
95    ) -> Result<crate::client::MutationResponse> {
96        let path = format!("/accounts/{account_number}/orders/{order_id}");
97        self.client.delete_mutate(&path).await
98    }
99
100    /// PUT /accounts/{accountNumber}/orders/{orderId}
101    pub async fn replace(
102        &self,
103        account_number: &str,
104        order_id: &str,
105        order: &OrderRequest,
106    ) -> Result<crate::client::MutationResponse> {
107        let path = format!("/accounts/{account_number}/orders/{order_id}");
108        self.client.put_mutate(&path, order).await
109    }
110}