Skip to main content

binance_sdk/stocks/rest_api/apis/
trade_api.rs

1/*
2 * Stocks Trading REST API
3 *
4 * REST APIs for Binance Stocks Trading. All endpoints under `/sapi/v1/equity/_*`.
5 *
6 * The version of the OpenAPI document: 1.0.0
7 *
8 *
9 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
10 * https://openapi-generator.tech
11 * Do not edit the class manually.
12 */
13
14#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24    config::ConfigurationRestApi,
25    models::{ParamBuildError, RestApiResponse},
26    utils::send_request,
27};
28use crate::stocks::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait TradeApi: Send + Sync {
34    async fn cancel_all_equity_orders(
35        &self,
36        params: CancelAllEquityOrdersParams,
37    ) -> anyhow::Result<RestApiResponse<models::CancelAllEquityOrdersResponse>>;
38    async fn cancel_equity_order(
39        &self,
40        params: CancelEquityOrderParams,
41    ) -> anyhow::Result<RestApiResponse<models::CancelEquityOrderResponse>>;
42    async fn current_open_orders(
43        &self,
44        params: CurrentOpenOrdersParams,
45    ) -> anyhow::Result<RestApiResponse<Vec<models::CurrentOpenOrdersResponseInner>>>;
46    async fn equity_order_detail(
47        &self,
48        params: EquityOrderDetailParams,
49    ) -> anyhow::Result<RestApiResponse<models::EquityOrderDetailResponse>>;
50    async fn equity_order_history(
51        &self,
52        params: EquityOrderHistoryParams,
53    ) -> anyhow::Result<RestApiResponse<models::EquityOrderHistoryResponse>>;
54    async fn equity_trade_history(
55        &self,
56        params: EquityTradeHistoryParams,
57    ) -> anyhow::Result<RestApiResponse<models::EquityTradeHistoryResponse>>;
58    async fn place_equity_order(
59        &self,
60        params: PlaceEquityOrderParams,
61    ) -> anyhow::Result<RestApiResponse<models::PlaceEquityOrderResponse>>;
62}
63
64#[derive(Debug, Clone)]
65pub struct TradeApiClient {
66    configuration: ConfigurationRestApi,
67}
68
69impl TradeApiClient {
70    pub fn new(configuration: ConfigurationRestApi) -> Self {
71        Self { configuration }
72    }
73}
74
75#[allow(non_camel_case_types)]
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub enum EquityOrderHistoryOrderTypeEnum {
78    #[serde(rename = "MARKET")]
79    Market,
80    #[serde(rename = "LIMIT")]
81    Limit,
82}
83
84impl EquityOrderHistoryOrderTypeEnum {
85    #[must_use]
86    pub fn as_str(&self) -> &'static str {
87        match self {
88            Self::Market => "MARKET",
89            Self::Limit => "LIMIT",
90        }
91    }
92}
93
94impl std::str::FromStr for EquityOrderHistoryOrderTypeEnum {
95    type Err = Box<dyn std::error::Error + Send + Sync>;
96
97    fn from_str(s: &str) -> Result<Self, Self::Err> {
98        match s {
99            "MARKET" => Ok(Self::Market),
100            "LIMIT" => Ok(Self::Limit),
101            other => Err(format!("invalid EquityOrderHistoryOrderTypeEnum: {}", other).into()),
102        }
103    }
104}
105
106#[allow(non_camel_case_types)]
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub enum EquityOrderHistorySideEnum {
109    #[serde(rename = "BUY")]
110    Buy,
111    #[serde(rename = "SELL")]
112    Sell,
113}
114
115impl EquityOrderHistorySideEnum {
116    #[must_use]
117    pub fn as_str(&self) -> &'static str {
118        match self {
119            Self::Buy => "BUY",
120            Self::Sell => "SELL",
121        }
122    }
123}
124
125impl std::str::FromStr for EquityOrderHistorySideEnum {
126    type Err = Box<dyn std::error::Error + Send + Sync>;
127
128    fn from_str(s: &str) -> Result<Self, Self::Err> {
129        match s {
130            "BUY" => Ok(Self::Buy),
131            "SELL" => Ok(Self::Sell),
132            other => Err(format!("invalid EquityOrderHistorySideEnum: {}", other).into()),
133        }
134    }
135}
136
137#[allow(non_camel_case_types)]
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub enum EquityTradeHistorySideEnum {
140    #[serde(rename = "BUY")]
141    Buy,
142    #[serde(rename = "SELL")]
143    Sell,
144}
145
146impl EquityTradeHistorySideEnum {
147    #[must_use]
148    pub fn as_str(&self) -> &'static str {
149        match self {
150            Self::Buy => "BUY",
151            Self::Sell => "SELL",
152        }
153    }
154}
155
156impl std::str::FromStr for EquityTradeHistorySideEnum {
157    type Err = Box<dyn std::error::Error + Send + Sync>;
158
159    fn from_str(s: &str) -> Result<Self, Self::Err> {
160        match s {
161            "BUY" => Ok(Self::Buy),
162            "SELL" => Ok(Self::Sell),
163            other => Err(format!("invalid EquityTradeHistorySideEnum: {}", other).into()),
164        }
165    }
166}
167
168#[allow(non_camel_case_types)]
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub enum PlaceEquityOrderSideEnum {
171    #[serde(rename = "BUY")]
172    Buy,
173    #[serde(rename = "SELL")]
174    Sell,
175}
176
177impl PlaceEquityOrderSideEnum {
178    #[must_use]
179    pub fn as_str(&self) -> &'static str {
180        match self {
181            Self::Buy => "BUY",
182            Self::Sell => "SELL",
183        }
184    }
185}
186
187impl std::str::FromStr for PlaceEquityOrderSideEnum {
188    type Err = Box<dyn std::error::Error + Send + Sync>;
189
190    fn from_str(s: &str) -> Result<Self, Self::Err> {
191        match s {
192            "BUY" => Ok(Self::Buy),
193            "SELL" => Ok(Self::Sell),
194            other => Err(format!("invalid PlaceEquityOrderSideEnum: {}", other).into()),
195        }
196    }
197}
198
199#[allow(non_camel_case_types)]
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub enum PlaceEquityOrderOrderTypeEnum {
202    #[serde(rename = "MARKET")]
203    Market,
204    #[serde(rename = "LIMIT")]
205    Limit,
206}
207
208impl PlaceEquityOrderOrderTypeEnum {
209    #[must_use]
210    pub fn as_str(&self) -> &'static str {
211        match self {
212            Self::Market => "MARKET",
213            Self::Limit => "LIMIT",
214        }
215    }
216}
217
218impl std::str::FromStr for PlaceEquityOrderOrderTypeEnum {
219    type Err = Box<dyn std::error::Error + Send + Sync>;
220
221    fn from_str(s: &str) -> Result<Self, Self::Err> {
222        match s {
223            "MARKET" => Ok(Self::Market),
224            "LIMIT" => Ok(Self::Limit),
225            other => Err(format!("invalid PlaceEquityOrderOrderTypeEnum: {}", other).into()),
226        }
227    }
228}
229
230#[allow(non_camel_case_types)]
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub enum PlaceEquityOrderTimeInForceEnum {
233    #[serde(rename = "DAY")]
234    Day,
235    #[serde(rename = "GTC")]
236    Gtc,
237}
238
239impl PlaceEquityOrderTimeInForceEnum {
240    #[must_use]
241    pub fn as_str(&self) -> &'static str {
242        match self {
243            Self::Day => "DAY",
244            Self::Gtc => "GTC",
245        }
246    }
247}
248
249impl std::str::FromStr for PlaceEquityOrderTimeInForceEnum {
250    type Err = Box<dyn std::error::Error + Send + Sync>;
251
252    fn from_str(s: &str) -> Result<Self, Self::Err> {
253        match s {
254            "DAY" => Ok(Self::Day),
255            "GTC" => Ok(Self::Gtc),
256            other => Err(format!("invalid PlaceEquityOrderTimeInForceEnum: {}", other).into()),
257        }
258    }
259}
260
261#[allow(non_camel_case_types)]
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub enum PlaceEquityOrderTradingSessionEnum {
264    #[serde(rename = "RTH")]
265    Rth,
266    #[serde(rename = "EXTENDED")]
267    Extended,
268    #[serde(rename = "24H")]
269    TradingSession24H,
270}
271
272impl PlaceEquityOrderTradingSessionEnum {
273    #[must_use]
274    pub fn as_str(&self) -> &'static str {
275        match self {
276            Self::Rth => "RTH",
277            Self::Extended => "EXTENDED",
278            Self::TradingSession24H => "24H",
279        }
280    }
281}
282
283impl std::str::FromStr for PlaceEquityOrderTradingSessionEnum {
284    type Err = Box<dyn std::error::Error + Send + Sync>;
285
286    fn from_str(s: &str) -> Result<Self, Self::Err> {
287        match s {
288            "RTH" => Ok(Self::Rth),
289            "EXTENDED" => Ok(Self::Extended),
290            "24H" => Ok(Self::TradingSession24H),
291            other => Err(format!("invalid PlaceEquityOrderTradingSessionEnum: {}", other).into()),
292        }
293    }
294}
295
296#[allow(non_camel_case_types)]
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub enum PlaceEquityOrderWalletTypeEnum {
299    #[serde(rename = "CARD")]
300    Card,
301    #[serde(rename = "MAIN")]
302    Main,
303}
304
305impl PlaceEquityOrderWalletTypeEnum {
306    #[must_use]
307    pub fn as_str(&self) -> &'static str {
308        match self {
309            Self::Card => "CARD",
310            Self::Main => "MAIN",
311        }
312    }
313}
314
315impl std::str::FromStr for PlaceEquityOrderWalletTypeEnum {
316    type Err = Box<dyn std::error::Error + Send + Sync>;
317
318    fn from_str(s: &str) -> Result<Self, Self::Err> {
319        match s {
320            "CARD" => Ok(Self::Card),
321            "MAIN" => Ok(Self::Main),
322            other => Err(format!("invalid PlaceEquityOrderWalletTypeEnum: {}", other).into()),
323        }
324    }
325}
326
327/// Request parameters for the [`cancel_all_equity_orders`] operation.
328///
329/// This struct holds all of the inputs you can pass when calling
330/// [`cancel_all_equity_orders`](#method.cancel_all_equity_orders).
331#[derive(Clone, Debug, Builder, Deserialize, Default)]
332#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
333pub struct CancelAllEquityOrdersParams {
334    /// The value cannot be greater than `60000`.
335    ///
336    /// This field is **optional.
337    #[builder(setter(into), default)]
338    #[serde(rename = "recvWindow", default)]
339    pub recv_window: Option<i64>,
340}
341
342impl CancelAllEquityOrdersParams {
343    /// Create a builder for [`cancel_all_equity_orders`].
344    ///
345    #[must_use]
346    pub fn builder() -> CancelAllEquityOrdersParamsBuilder {
347        CancelAllEquityOrdersParamsBuilder::default()
348    }
349}
350/// Request parameters for the [`cancel_equity_order`] operation.
351///
352/// This struct holds all of the inputs you can pass when calling
353/// [`cancel_equity_order`](#method.cancel_equity_order).
354#[derive(Clone, Debug, Builder, Deserialize)]
355#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
356pub struct CancelEquityOrderParams {
357    /// Equity order id returned by `/order/place` or a query endpoint.
358    ///
359    /// This field is **required.
360    #[builder(setter(into))]
361    #[serde(rename = "orderId")]
362    pub order_id: String,
363    /// The value cannot be greater than `60000`.
364    ///
365    /// This field is **optional.
366    #[builder(setter(into), default)]
367    #[serde(rename = "recvWindow", default)]
368    pub recv_window: Option<i64>,
369}
370
371impl CancelEquityOrderParams {
372    /// Create a builder for [`cancel_equity_order`].
373    ///
374    /// Required parameters:
375    ///
376    /// * `order_id` — Equity order id returned by `/order/place` or a query endpoint.
377    ///
378    #[must_use]
379    pub fn builder(order_id: String) -> CancelEquityOrderParamsBuilder {
380        CancelEquityOrderParamsBuilder::default().order_id(order_id)
381    }
382}
383/// Request parameters for the [`current_open_orders`] operation.
384///
385/// This struct holds all of the inputs you can pass when calling
386/// [`current_open_orders`](#method.current_open_orders).
387#[derive(Clone, Debug, Builder, Deserialize, Default)]
388#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
389pub struct CurrentOpenOrdersParams {
390    /// The value cannot be greater than `60000`.
391    ///
392    /// This field is **optional.
393    #[builder(setter(into), default)]
394    #[serde(rename = "recvWindow", default)]
395    pub recv_window: Option<i64>,
396}
397
398impl CurrentOpenOrdersParams {
399    /// Create a builder for [`current_open_orders`].
400    ///
401    #[must_use]
402    pub fn builder() -> CurrentOpenOrdersParamsBuilder {
403        CurrentOpenOrdersParamsBuilder::default()
404    }
405}
406/// Request parameters for the [`equity_order_detail`] operation.
407///
408/// This struct holds all of the inputs you can pass when calling
409/// [`equity_order_detail`](#method.equity_order_detail).
410#[derive(Clone, Debug, Builder, Deserialize, Default)]
411#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
412pub struct EquityOrderDetailParams {
413    /// Equity order id. Either `orderId` or `clientOrderId` must be provided.
414    ///
415    /// This field is **optional.
416    #[builder(setter(into), default)]
417    #[serde(rename = "orderId", default)]
418    pub order_id: Option<String>,
419    /// Client-supplied order id. Either `orderId` or `clientOrderId` must be provided.
420    ///
421    /// This field is **optional.
422    #[builder(setter(into), default)]
423    #[serde(rename = "clientOrderId", default)]
424    pub client_order_id: Option<String>,
425    /// The value cannot be greater than `60000`.
426    ///
427    /// This field is **optional.
428    #[builder(setter(into), default)]
429    #[serde(rename = "recvWindow", default)]
430    pub recv_window: Option<i64>,
431}
432
433impl EquityOrderDetailParams {
434    /// Create a builder for [`equity_order_detail`].
435    ///
436    #[must_use]
437    pub fn builder() -> EquityOrderDetailParamsBuilder {
438        EquityOrderDetailParamsBuilder::default()
439    }
440}
441/// Request parameters for the [`equity_order_history`] operation.
442///
443/// This struct holds all of the inputs you can pass when calling
444/// [`equity_order_history`](#method.equity_order_history).
445#[derive(Clone, Debug, Builder, Deserialize)]
446#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
447pub struct EquityOrderHistoryParams {
448    /// Start time (ms epoch).
449    ///
450    /// This field is **required.
451    #[builder(setter(into))]
452    #[serde(rename = "startTime")]
453    pub start_time: i64,
454    /// End time (ms epoch).
455    ///
456    /// This field is **required.
457    #[builder(setter(into))]
458    #[serde(rename = "endTime")]
459    pub end_time: i64,
460    /// US-equity ticker filter, e.g. `NVDA`.
461    ///
462    /// This field is **optional.
463    #[builder(setter(into), default)]
464    #[serde(rename = "symbol", default)]
465    pub symbol: Option<String>,
466    /// Order type filter: `MARKET` / `LIMIT`.
467    ///
468    /// This field is **optional.
469    #[builder(setter(into), default)]
470    #[serde(rename = "orderType", default)]
471    pub order_type: Option<EquityOrderHistoryOrderTypeEnum>,
472    /// Side filter: `BUY` / `SELL`.
473    ///
474    /// This field is **optional.
475    #[builder(setter(into), default)]
476    #[serde(rename = "side", default)]
477    pub side: Option<EquityOrderHistorySideEnum>,
478    /// Comma-separated status filter. Allowed values: `FILLED`, `PARTIALLY_FILLED`, `CANCELED`, `EXPIRED`, `REJECTED`.
479    ///
480    /// This field is **optional.
481    #[builder(setter(into), default)]
482    #[serde(rename = "orderStatus", default)]
483    pub order_status: Option<String>,
484    /// Page number, 1-based. Default `1`.
485    ///
486    /// This field is **optional.
487    #[builder(setter(into), default)]
488    #[serde(rename = "current", default)]
489    pub current: Option<i32>,
490    /// Page size. Default `20`, max `100`.
491    ///
492    /// This field is **optional.
493    #[builder(setter(into), default)]
494    #[serde(rename = "size", default)]
495    pub size: Option<i32>,
496    /// The value cannot be greater than `60000`.
497    ///
498    /// This field is **optional.
499    #[builder(setter(into), default)]
500    #[serde(rename = "recvWindow", default)]
501    pub recv_window: Option<i64>,
502}
503
504impl EquityOrderHistoryParams {
505    /// Create a builder for [`equity_order_history`].
506    ///
507    /// Required parameters:
508    ///
509    /// * `start_time` — Start time (ms epoch).
510    /// * `end_time` — End time (ms epoch).
511    ///
512    #[must_use]
513    pub fn builder(start_time: i64, end_time: i64) -> EquityOrderHistoryParamsBuilder {
514        EquityOrderHistoryParamsBuilder::default()
515            .start_time(start_time)
516            .end_time(end_time)
517    }
518}
519/// Request parameters for the [`equity_trade_history`] operation.
520///
521/// This struct holds all of the inputs you can pass when calling
522/// [`equity_trade_history`](#method.equity_trade_history).
523#[derive(Clone, Debug, Builder, Deserialize)]
524#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
525pub struct EquityTradeHistoryParams {
526    /// Start time (ms epoch).
527    ///
528    /// This field is **required.
529    #[builder(setter(into))]
530    #[serde(rename = "startTime")]
531    pub start_time: i64,
532    /// End time (ms epoch).
533    ///
534    /// This field is **required.
535    #[builder(setter(into))]
536    #[serde(rename = "endTime")]
537    pub end_time: i64,
538    /// US-equity ticker filter, e.g. `NVDA`.
539    ///
540    /// This field is **optional.
541    #[builder(setter(into), default)]
542    #[serde(rename = "symbol", default)]
543    pub symbol: Option<String>,
544    /// Side filter: `BUY` / `SELL`.
545    ///
546    /// This field is **optional.
547    #[builder(setter(into), default)]
548    #[serde(rename = "side", default)]
549    pub side: Option<EquityTradeHistorySideEnum>,
550    /// Narrow the result to executions of a single order.
551    ///
552    /// This field is **optional.
553    #[builder(setter(into), default)]
554    #[serde(rename = "orderId", default)]
555    pub order_id: Option<String>,
556    /// Page number, 1-based. Default `1`.
557    ///
558    /// This field is **optional.
559    #[builder(setter(into), default)]
560    #[serde(rename = "current", default)]
561    pub current: Option<i32>,
562    /// Page size. Default `20`, max `100`.
563    ///
564    /// This field is **optional.
565    #[builder(setter(into), default)]
566    #[serde(rename = "size", default)]
567    pub size: Option<i32>,
568    /// The value cannot be greater than `60000`.
569    ///
570    /// This field is **optional.
571    #[builder(setter(into), default)]
572    #[serde(rename = "recvWindow", default)]
573    pub recv_window: Option<i64>,
574}
575
576impl EquityTradeHistoryParams {
577    /// Create a builder for [`equity_trade_history`].
578    ///
579    /// Required parameters:
580    ///
581    /// * `start_time` — Start time (ms epoch).
582    /// * `end_time` — End time (ms epoch).
583    ///
584    #[must_use]
585    pub fn builder(start_time: i64, end_time: i64) -> EquityTradeHistoryParamsBuilder {
586        EquityTradeHistoryParamsBuilder::default()
587            .start_time(start_time)
588            .end_time(end_time)
589    }
590}
591/// Request parameters for the [`place_equity_order`] operation.
592///
593/// This struct holds all of the inputs you can pass when calling
594/// [`place_equity_order`](#method.place_equity_order).
595#[derive(Clone, Debug, Builder, Deserialize)]
596#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
597pub struct PlaceEquityOrderParams {
598    /// US stock ticker, e.g. `AAPL`, `TSLA`. Must be a symbol with tokenization enabled — check via `/market/tokenized-assets`.
599    ///
600    /// This field is **required.
601    #[builder(setter(into))]
602    #[serde(rename = "symbol")]
603    pub symbol: String,
604    /// `BUY` / `SELL`.
605    ///
606    /// This field is **required.
607    #[builder(setter(into))]
608    #[serde(rename = "side")]
609    pub side: PlaceEquityOrderSideEnum,
610    /// `MARKET` / `LIMIT`.
611    ///
612    /// This field is **required.
613    #[builder(setter(into))]
614    #[serde(rename = "orderType")]
615    pub order_type: PlaceEquityOrderOrderTypeEnum,
616    /// Quote asset. Defaults to `USDC`; must be within the server's allowed set.
617    ///
618    /// This field is **optional.
619    #[builder(setter(into), default)]
620    #[serde(rename = "quoteAsset", default)]
621    pub quote_asset: Option<String>,
622    /// **Required** for `LIMIT`; **forbidden** for `MARKET`. Maximum 2 decimal places.
623    ///
624    /// This field is **optional.
625    #[builder(setter(into), default)]
626    #[serde(rename = "price", default)]
627    pub price: Option<String>,
628    /// **Required** for `LIMIT` (both sides) and `SELL MARKET`; **forbidden** for `BUY MARKET`.
629    ///
630    /// This field is **optional.
631    #[builder(setter(into), default)]
632    #[serde(rename = "quantity", default)]
633    pub quantity: Option<String>,
634    /// **Required** for `BUY MARKET`; **forbidden** for `LIMIT` and `SELL MARKET`.
635    ///
636    /// This field is **optional.
637    #[builder(setter(into), default)]
638    #[serde(rename = "notional", default)]
639    pub notional: Option<String>,
640    /// `DAY` (default) / `GTC`. `GTC` is only supported for `LIMIT` orders; a fractional-share `GTC` order must be paired with `tradingSession = EXTENDED` or `24H`.
641    ///
642    /// This field is **optional.
643    #[builder(setter(into), default)]
644    #[serde(rename = "timeInForce", default)]
645    pub time_in_force: Option<PlaceEquityOrderTimeInForceEnum>,
646    /// `RTH` / `EXTENDED` / `24H`. **Required** for `LIMIT`; **forbidden** for `MARKET`.
647    ///
648    /// This field is **optional.
649    #[builder(setter(into), default)]
650    #[serde(rename = "tradingSession", default)]
651    pub trading_session: Option<PlaceEquityOrderTradingSessionEnum>,
652    /// Payment wallet for `BUY` orders: `CARD` (default) / `MAIN`. `SELL` orders always settle to `CARD`.
653    ///
654    /// This field is **optional.
655    #[builder(setter(into), default)]
656    #[serde(rename = "walletType", default)]
657    pub wallet_type: Option<PlaceEquityOrderWalletTypeEnum>,
658    /// Client-supplied order id. Format `^[a-zA-Z0-9-_]{32,36}$`. Auto-generated when omitted.
659    ///
660    /// This field is **optional.
661    #[builder(setter(into), default)]
662    #[serde(rename = "clientOrderId", default)]
663    pub client_order_id: Option<String>,
664    /// Whether to tokenize the purchased stock asset upon settlement. Default `true`. Set to `false` to receive the underlying equity directly instead of a tokenized asset.
665    ///
666    /// This field is **optional.
667    #[builder(setter(into), default)]
668    #[serde(rename = "tokenize", default)]
669    pub tokenize: Option<bool>,
670    /// The value cannot be greater than `60000`.
671    ///
672    /// This field is **optional.
673    #[builder(setter(into), default)]
674    #[serde(rename = "recvWindow", default)]
675    pub recv_window: Option<i64>,
676}
677
678impl PlaceEquityOrderParams {
679    /// Create a builder for [`place_equity_order`].
680    ///
681    /// Required parameters:
682    ///
683    /// * `symbol` — US stock ticker, e.g. `AAPL`, `TSLA`. Must be a symbol with tokenization enabled — check via `/market/tokenized-assets`.
684    /// * `side` — `BUY` / `SELL`.
685    /// * `order_type` — `MARKET` / `LIMIT`.
686    ///
687    #[must_use]
688    pub fn builder(
689        symbol: String,
690        side: PlaceEquityOrderSideEnum,
691        order_type: PlaceEquityOrderOrderTypeEnum,
692    ) -> PlaceEquityOrderParamsBuilder {
693        PlaceEquityOrderParamsBuilder::default()
694            .symbol(symbol)
695            .side(side)
696            .order_type(order_type)
697    }
698}
699
700#[async_trait]
701impl TradeApi for TradeApiClient {
702    async fn cancel_all_equity_orders(
703        &self,
704        params: CancelAllEquityOrdersParams,
705    ) -> anyhow::Result<RestApiResponse<models::CancelAllEquityOrdersResponse>> {
706        let CancelAllEquityOrdersParams { recv_window } = params;
707
708        let mut query_params = BTreeMap::new();
709        let body_params = BTreeMap::new();
710
711        if let Some(rw) = recv_window {
712            query_params.insert("recvWindow".to_string(), json!(rw));
713        }
714
715        send_request::<models::CancelAllEquityOrdersResponse>(
716            &self.configuration,
717            "/sapi/v1/equity/order/cancel-all",
718            reqwest::Method::POST,
719            query_params,
720            body_params,
721            if HAS_TIME_UNIT {
722                self.configuration.time_unit
723            } else {
724                None
725            },
726            true,
727        )
728        .await
729    }
730
731    async fn cancel_equity_order(
732        &self,
733        params: CancelEquityOrderParams,
734    ) -> anyhow::Result<RestApiResponse<models::CancelEquityOrderResponse>> {
735        let CancelEquityOrderParams {
736            order_id,
737            recv_window,
738        } = params;
739
740        let mut query_params = BTreeMap::new();
741        let body_params = BTreeMap::new();
742
743        query_params.insert("orderId".to_string(), json!(order_id));
744
745        if let Some(rw) = recv_window {
746            query_params.insert("recvWindow".to_string(), json!(rw));
747        }
748
749        send_request::<models::CancelEquityOrderResponse>(
750            &self.configuration,
751            "/sapi/v1/equity/order/cancel",
752            reqwest::Method::POST,
753            query_params,
754            body_params,
755            if HAS_TIME_UNIT {
756                self.configuration.time_unit
757            } else {
758                None
759            },
760            true,
761        )
762        .await
763    }
764
765    async fn current_open_orders(
766        &self,
767        params: CurrentOpenOrdersParams,
768    ) -> anyhow::Result<RestApiResponse<Vec<models::CurrentOpenOrdersResponseInner>>> {
769        let CurrentOpenOrdersParams { recv_window } = params;
770
771        let mut query_params = BTreeMap::new();
772        let body_params = BTreeMap::new();
773
774        if let Some(rw) = recv_window {
775            query_params.insert("recvWindow".to_string(), json!(rw));
776        }
777
778        send_request::<Vec<models::CurrentOpenOrdersResponseInner>>(
779            &self.configuration,
780            "/sapi/v1/equity/order/open-orders",
781            reqwest::Method::GET,
782            query_params,
783            body_params,
784            if HAS_TIME_UNIT {
785                self.configuration.time_unit
786            } else {
787                None
788            },
789            true,
790        )
791        .await
792    }
793
794    async fn equity_order_detail(
795        &self,
796        params: EquityOrderDetailParams,
797    ) -> anyhow::Result<RestApiResponse<models::EquityOrderDetailResponse>> {
798        let EquityOrderDetailParams {
799            order_id,
800            client_order_id,
801            recv_window,
802        } = params;
803
804        let mut query_params = BTreeMap::new();
805        let body_params = BTreeMap::new();
806
807        if let Some(rw) = order_id {
808            query_params.insert("orderId".to_string(), json!(rw));
809        }
810
811        if let Some(rw) = client_order_id {
812            query_params.insert("clientOrderId".to_string(), json!(rw));
813        }
814
815        if let Some(rw) = recv_window {
816            query_params.insert("recvWindow".to_string(), json!(rw));
817        }
818
819        send_request::<models::EquityOrderDetailResponse>(
820            &self.configuration,
821            "/sapi/v1/equity/order/detail",
822            reqwest::Method::GET,
823            query_params,
824            body_params,
825            if HAS_TIME_UNIT {
826                self.configuration.time_unit
827            } else {
828                None
829            },
830            true,
831        )
832        .await
833    }
834
835    async fn equity_order_history(
836        &self,
837        params: EquityOrderHistoryParams,
838    ) -> anyhow::Result<RestApiResponse<models::EquityOrderHistoryResponse>> {
839        let EquityOrderHistoryParams {
840            start_time,
841            end_time,
842            symbol,
843            order_type,
844            side,
845            order_status,
846            current,
847            size,
848            recv_window,
849        } = params;
850
851        let mut query_params = BTreeMap::new();
852        let body_params = BTreeMap::new();
853
854        if let Some(rw) = symbol {
855            query_params.insert("symbol".to_string(), json!(rw));
856        }
857
858        if let Some(rw) = order_type {
859            query_params.insert("orderType".to_string(), json!(rw));
860        }
861
862        if let Some(rw) = side {
863            query_params.insert("side".to_string(), json!(rw));
864        }
865
866        if let Some(rw) = order_status {
867            query_params.insert("orderStatus".to_string(), json!(rw));
868        }
869
870        query_params.insert("startTime".to_string(), json!(start_time));
871
872        query_params.insert("endTime".to_string(), json!(end_time));
873
874        if let Some(rw) = current {
875            query_params.insert("current".to_string(), json!(rw));
876        }
877
878        if let Some(rw) = size {
879            query_params.insert("size".to_string(), json!(rw));
880        }
881
882        if let Some(rw) = recv_window {
883            query_params.insert("recvWindow".to_string(), json!(rw));
884        }
885
886        send_request::<models::EquityOrderHistoryResponse>(
887            &self.configuration,
888            "/sapi/v1/equity/order/history",
889            reqwest::Method::GET,
890            query_params,
891            body_params,
892            if HAS_TIME_UNIT {
893                self.configuration.time_unit
894            } else {
895                None
896            },
897            true,
898        )
899        .await
900    }
901
902    async fn equity_trade_history(
903        &self,
904        params: EquityTradeHistoryParams,
905    ) -> anyhow::Result<RestApiResponse<models::EquityTradeHistoryResponse>> {
906        let EquityTradeHistoryParams {
907            start_time,
908            end_time,
909            symbol,
910            side,
911            order_id,
912            current,
913            size,
914            recv_window,
915        } = params;
916
917        let mut query_params = BTreeMap::new();
918        let body_params = BTreeMap::new();
919
920        if let Some(rw) = symbol {
921            query_params.insert("symbol".to_string(), json!(rw));
922        }
923
924        if let Some(rw) = side {
925            query_params.insert("side".to_string(), json!(rw));
926        }
927
928        if let Some(rw) = order_id {
929            query_params.insert("orderId".to_string(), json!(rw));
930        }
931
932        query_params.insert("startTime".to_string(), json!(start_time));
933
934        query_params.insert("endTime".to_string(), json!(end_time));
935
936        if let Some(rw) = current {
937            query_params.insert("current".to_string(), json!(rw));
938        }
939
940        if let Some(rw) = size {
941            query_params.insert("size".to_string(), json!(rw));
942        }
943
944        if let Some(rw) = recv_window {
945            query_params.insert("recvWindow".to_string(), json!(rw));
946        }
947
948        send_request::<models::EquityTradeHistoryResponse>(
949            &self.configuration,
950            "/sapi/v1/equity/trade/history",
951            reqwest::Method::GET,
952            query_params,
953            body_params,
954            if HAS_TIME_UNIT {
955                self.configuration.time_unit
956            } else {
957                None
958            },
959            true,
960        )
961        .await
962    }
963
964    async fn place_equity_order(
965        &self,
966        params: PlaceEquityOrderParams,
967    ) -> anyhow::Result<RestApiResponse<models::PlaceEquityOrderResponse>> {
968        let PlaceEquityOrderParams {
969            symbol,
970            side,
971            order_type,
972            quote_asset,
973            price,
974            quantity,
975            notional,
976            time_in_force,
977            trading_session,
978            wallet_type,
979            client_order_id,
980            tokenize,
981            recv_window,
982        } = params;
983
984        let mut query_params = BTreeMap::new();
985        let body_params = BTreeMap::new();
986
987        query_params.insert("symbol".to_string(), json!(symbol));
988
989        if let Some(rw) = quote_asset {
990            query_params.insert("quoteAsset".to_string(), json!(rw));
991        }
992
993        query_params.insert("side".to_string(), json!(side));
994
995        query_params.insert("orderType".to_string(), json!(order_type));
996
997        if let Some(rw) = price {
998            query_params.insert("price".to_string(), json!(rw));
999        }
1000
1001        if let Some(rw) = quantity {
1002            query_params.insert("quantity".to_string(), json!(rw));
1003        }
1004
1005        if let Some(rw) = notional {
1006            query_params.insert("notional".to_string(), json!(rw));
1007        }
1008
1009        if let Some(rw) = time_in_force {
1010            query_params.insert("timeInForce".to_string(), json!(rw));
1011        }
1012
1013        if let Some(rw) = trading_session {
1014            query_params.insert("tradingSession".to_string(), json!(rw));
1015        }
1016
1017        if let Some(rw) = wallet_type {
1018            query_params.insert("walletType".to_string(), json!(rw));
1019        }
1020
1021        if let Some(rw) = client_order_id {
1022            query_params.insert("clientOrderId".to_string(), json!(rw));
1023        }
1024
1025        if let Some(rw) = tokenize {
1026            query_params.insert("tokenize".to_string(), json!(rw));
1027        }
1028
1029        if let Some(rw) = recv_window {
1030            query_params.insert("recvWindow".to_string(), json!(rw));
1031        }
1032
1033        send_request::<models::PlaceEquityOrderResponse>(
1034            &self.configuration,
1035            "/sapi/v1/equity/order/place",
1036            reqwest::Method::POST,
1037            query_params,
1038            body_params,
1039            if HAS_TIME_UNIT {
1040                self.configuration.time_unit
1041            } else {
1042                None
1043            },
1044            true,
1045        )
1046        .await
1047    }
1048}
1049
1050#[cfg(all(test, feature = "stocks"))]
1051mod tests {
1052    use super::*;
1053    use crate::TOKIO_SHARED_RT;
1054    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
1055    use async_trait::async_trait;
1056    use std::collections::HashMap;
1057
1058    struct DummyRestApiResponse<T> {
1059        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
1060        status: u16,
1061        headers: HashMap<String, String>,
1062        rate_limits: Option<Vec<RestApiRateLimit>>,
1063    }
1064
1065    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
1066        fn from(dummy: DummyRestApiResponse<T>) -> Self {
1067            Self {
1068                data_fn: dummy.inner,
1069                status: dummy.status,
1070                headers: dummy.headers,
1071                rate_limits: dummy.rate_limits,
1072            }
1073        }
1074    }
1075
1076    struct MockTradeApiClient {
1077        force_error: bool,
1078    }
1079
1080    #[async_trait]
1081    impl TradeApi for MockTradeApiClient {
1082        async fn cancel_all_equity_orders(
1083            &self,
1084            _params: CancelAllEquityOrdersParams,
1085        ) -> anyhow::Result<RestApiResponse<models::CancelAllEquityOrdersResponse>> {
1086            if self.force_error {
1087                return Err(ConnectorError::ConnectorClientError {
1088                    msg: "ResponseError".to_string(),
1089                    code: None,
1090                }
1091                .into());
1092            }
1093
1094            let resp_json: Value = serde_json::from_str(r#"{"success":true}"#)
1095                .unwrap_or_else(|_| serde_json::json!({}));
1096            let dummy_response: models::CancelAllEquityOrdersResponse =
1097                serde_json::from_value(resp_json.clone())
1098                    .expect("should parse into models::CancelAllEquityOrdersResponse");
1099
1100            let dummy = DummyRestApiResponse {
1101                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1102                status: 200,
1103                headers: HashMap::new(),
1104                rate_limits: None,
1105            };
1106
1107            Ok(dummy.into())
1108        }
1109
1110        async fn cancel_equity_order(
1111            &self,
1112            _params: CancelEquityOrderParams,
1113        ) -> anyhow::Result<RestApiResponse<models::CancelEquityOrderResponse>> {
1114            if self.force_error {
1115                return Err(ConnectorError::ConnectorClientError {
1116                    msg: "ResponseError".to_string(),
1117                    code: None,
1118                }
1119                .into());
1120            }
1121
1122            let resp_json: Value = serde_json::from_str(
1123                r#"{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","status":"S"}"#,
1124            )
1125            .unwrap_or_else(|_| serde_json::json!({}));
1126            let dummy_response: models::CancelEquityOrderResponse =
1127                serde_json::from_value(resp_json.clone())
1128                    .expect("should parse into models::CancelEquityOrderResponse");
1129
1130            let dummy = DummyRestApiResponse {
1131                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1132                status: 200,
1133                headers: HashMap::new(),
1134                rate_limits: None,
1135            };
1136
1137            Ok(dummy.into())
1138        }
1139
1140        async fn current_open_orders(
1141            &self,
1142            _params: CurrentOpenOrdersParams,
1143        ) -> anyhow::Result<RestApiResponse<Vec<models::CurrentOpenOrdersResponseInner>>> {
1144            if self.force_error {
1145                return Err(ConnectorError::ConnectorClientError {
1146                    msg: "ResponseError".to_string(),
1147                    code: None,
1148                }
1149                .into());
1150            }
1151
1152            let resp_json: Value = serde_json::from_str(r#"[{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"avgFilledPrice","qty":"1","notional":"notional","filledQty":"0","filledTotal":"filledTotal","fee":"0","session":"RTH","status":"NEW","createdAt":1735900000000,"updatedAt":1735900000000}]"#).unwrap_or_else(|_| serde_json::json!({}));
1153            let dummy_response: Vec<models::CurrentOpenOrdersResponseInner> =
1154                serde_json::from_value(resp_json.clone())
1155                    .expect("should parse into Vec<models::CurrentOpenOrdersResponseInner>");
1156
1157            let dummy = DummyRestApiResponse {
1158                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1159                status: 200,
1160                headers: HashMap::new(),
1161                rate_limits: None,
1162            };
1163
1164            Ok(dummy.into())
1165        }
1166
1167        async fn equity_order_detail(
1168            &self,
1169            _params: EquityOrderDetailParams,
1170        ) -> anyhow::Result<RestApiResponse<models::EquityOrderDetailResponse>> {
1171            if self.force_error {
1172                return Err(ConnectorError::ConnectorClientError {
1173                    msg: "ResponseError".to_string(),
1174                    code: None,
1175                }
1176                .into());
1177            }
1178
1179            let resp_json: Value = serde_json::from_str(r#"{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","clientOrderId":"web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"180.48","qty":"1","notional":"notional","filledQty":"1","filledTotal":"filledTotal","fee":"0.10","session":"RTH","status":"FILLED","createdAt":1735900000000,"updatedAt":1735900120000,"trades":[{"executionId":"exec-20260504-0001","executionAt":1735900115000,"price":"180.48","qty":"1"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1180            let dummy_response: models::EquityOrderDetailResponse =
1181                serde_json::from_value(resp_json.clone())
1182                    .expect("should parse into models::EquityOrderDetailResponse");
1183
1184            let dummy = DummyRestApiResponse {
1185                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1186                status: 200,
1187                headers: HashMap::new(),
1188                rate_limits: None,
1189            };
1190
1191            Ok(dummy.into())
1192        }
1193
1194        async fn equity_order_history(
1195            &self,
1196            _params: EquityOrderHistoryParams,
1197        ) -> anyhow::Result<RestApiResponse<models::EquityOrderHistoryResponse>> {
1198            if self.force_error {
1199                return Err(ConnectorError::ConnectorClientError {
1200                    msg: "ResponseError".to_string(),
1201                    code: None,
1202                }
1203                .into());
1204            }
1205
1206            let resp_json: Value = serde_json::from_str(r#"{"total":2,"page":1,"size":20,"rows":[{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"180.48","qty":"1","notional":"notional","filledQty":"1","filledTotal":"filledTotal","fee":"0.10","session":"RTH","status":"FILLED","createdAt":1735900000000,"updatedAt":1735900120000}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1207            let dummy_response: models::EquityOrderHistoryResponse =
1208                serde_json::from_value(resp_json.clone())
1209                    .expect("should parse into models::EquityOrderHistoryResponse");
1210
1211            let dummy = DummyRestApiResponse {
1212                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1213                status: 200,
1214                headers: HashMap::new(),
1215                rate_limits: None,
1216            };
1217
1218            Ok(dummy.into())
1219        }
1220
1221        async fn equity_trade_history(
1222            &self,
1223            _params: EquityTradeHistoryParams,
1224        ) -> anyhow::Result<RestApiResponse<models::EquityTradeHistoryResponse>> {
1225            if self.force_error {
1226                return Err(ConnectorError::ConnectorClientError {
1227                    msg: "ResponseError".to_string(),
1228                    code: None,
1229                }
1230                .into());
1231            }
1232
1233            let resp_json: Value = serde_json::from_str(r#"{"total":1,"page":1,"size":20,"rows":[{"executionId":"exec-20260504-0001","orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","price":"180.50","qty":"1","total":"180.50","executionAt":1735900115000,"updatedAt":1735900115200}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1234            let dummy_response: models::EquityTradeHistoryResponse =
1235                serde_json::from_value(resp_json.clone())
1236                    .expect("should parse into models::EquityTradeHistoryResponse");
1237
1238            let dummy = DummyRestApiResponse {
1239                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1240                status: 200,
1241                headers: HashMap::new(),
1242                rate_limits: None,
1243            };
1244
1245            Ok(dummy.into())
1246        }
1247
1248        async fn place_equity_order(
1249            &self,
1250            _params: PlaceEquityOrderParams,
1251        ) -> anyhow::Result<RestApiResponse<models::PlaceEquityOrderResponse>> {
1252            if self.force_error {
1253                return Err(ConnectorError::ConnectorClientError {
1254                    msg: "ResponseError".to_string(),
1255                    code: None,
1256                }
1257                .into());
1258            }
1259
1260            let resp_json: Value = serde_json::from_str(r#"{"status":"S","orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","clientOrderId":"web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a"}"#).unwrap_or_else(|_| serde_json::json!({}));
1261            let dummy_response: models::PlaceEquityOrderResponse =
1262                serde_json::from_value(resp_json.clone())
1263                    .expect("should parse into models::PlaceEquityOrderResponse");
1264
1265            let dummy = DummyRestApiResponse {
1266                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1267                status: 200,
1268                headers: HashMap::new(),
1269                rate_limits: None,
1270            };
1271
1272            Ok(dummy.into())
1273        }
1274    }
1275
1276    #[test]
1277    fn cancel_all_equity_orders_required_params_success() {
1278        TOKIO_SHARED_RT.block_on(async {
1279            let client = MockTradeApiClient { force_error: false };
1280
1281            let params = CancelAllEquityOrdersParams::builder().build().unwrap();
1282
1283            let resp_json: Value = serde_json::from_str(r#"{"success":true}"#)
1284                .unwrap_or_else(|_| serde_json::json!({}));
1285            let expected_response: models::CancelAllEquityOrdersResponse =
1286                serde_json::from_value(resp_json.clone())
1287                    .expect("should parse into models::CancelAllEquityOrdersResponse");
1288
1289            let resp = client
1290                .cancel_all_equity_orders(params)
1291                .await
1292                .expect("Expected a response");
1293            let data_future = resp.data();
1294            let actual_response = data_future.await.unwrap();
1295            assert_eq!(actual_response, expected_response);
1296        });
1297    }
1298
1299    #[test]
1300    fn cancel_all_equity_orders_optional_params_success() {
1301        TOKIO_SHARED_RT.block_on(async {
1302            let client = MockTradeApiClient { force_error: false };
1303
1304            let params = CancelAllEquityOrdersParams::builder()
1305                .recv_window(5000)
1306                .build()
1307                .unwrap();
1308
1309            let resp_json: Value = serde_json::from_str(r#"{"success":true}"#)
1310                .unwrap_or_else(|_| serde_json::json!({}));
1311            let expected_response: models::CancelAllEquityOrdersResponse =
1312                serde_json::from_value(resp_json.clone())
1313                    .expect("should parse into models::CancelAllEquityOrdersResponse");
1314
1315            let resp = client
1316                .cancel_all_equity_orders(params)
1317                .await
1318                .expect("Expected a response");
1319            let data_future = resp.data();
1320            let actual_response = data_future.await.unwrap();
1321            assert_eq!(actual_response, expected_response);
1322        });
1323    }
1324
1325    #[test]
1326    fn cancel_all_equity_orders_response_error() {
1327        TOKIO_SHARED_RT.block_on(async {
1328            let client = MockTradeApiClient { force_error: true };
1329
1330            let params = CancelAllEquityOrdersParams::builder().build().unwrap();
1331
1332            match client.cancel_all_equity_orders(params).await {
1333                Ok(_) => panic!("Expected an error"),
1334                Err(err) => {
1335                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1336                }
1337            }
1338        });
1339    }
1340
1341    #[test]
1342    fn cancel_equity_order_required_params_success() {
1343        TOKIO_SHARED_RT.block_on(async {
1344            let client = MockTradeApiClient { force_error: false };
1345
1346            let params = CancelEquityOrderParams::builder(
1347                "c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71".to_string(),
1348            )
1349            .build()
1350            .unwrap();
1351
1352            let resp_json: Value = serde_json::from_str(
1353                r#"{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","status":"S"}"#,
1354            )
1355            .unwrap_or_else(|_| serde_json::json!({}));
1356            let expected_response: models::CancelEquityOrderResponse =
1357                serde_json::from_value(resp_json.clone())
1358                    .expect("should parse into models::CancelEquityOrderResponse");
1359
1360            let resp = client
1361                .cancel_equity_order(params)
1362                .await
1363                .expect("Expected a response");
1364            let data_future = resp.data();
1365            let actual_response = data_future.await.unwrap();
1366            assert_eq!(actual_response, expected_response);
1367        });
1368    }
1369
1370    #[test]
1371    fn cancel_equity_order_optional_params_success() {
1372        TOKIO_SHARED_RT.block_on(async {
1373            let client = MockTradeApiClient { force_error: false };
1374
1375            let params = CancelEquityOrderParams::builder(
1376                "c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71".to_string(),
1377            )
1378            .recv_window(5000)
1379            .build()
1380            .unwrap();
1381
1382            let resp_json: Value = serde_json::from_str(
1383                r#"{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","status":"S"}"#,
1384            )
1385            .unwrap_or_else(|_| serde_json::json!({}));
1386            let expected_response: models::CancelEquityOrderResponse =
1387                serde_json::from_value(resp_json.clone())
1388                    .expect("should parse into models::CancelEquityOrderResponse");
1389
1390            let resp = client
1391                .cancel_equity_order(params)
1392                .await
1393                .expect("Expected a response");
1394            let data_future = resp.data();
1395            let actual_response = data_future.await.unwrap();
1396            assert_eq!(actual_response, expected_response);
1397        });
1398    }
1399
1400    #[test]
1401    fn cancel_equity_order_response_error() {
1402        TOKIO_SHARED_RT.block_on(async {
1403            let client = MockTradeApiClient { force_error: true };
1404
1405            let params = CancelEquityOrderParams::builder(
1406                "c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71".to_string(),
1407            )
1408            .build()
1409            .unwrap();
1410
1411            match client.cancel_equity_order(params).await {
1412                Ok(_) => panic!("Expected an error"),
1413                Err(err) => {
1414                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1415                }
1416            }
1417        });
1418    }
1419
1420    #[test]
1421    fn current_open_orders_required_params_success() {
1422        TOKIO_SHARED_RT.block_on(async {
1423            let client = MockTradeApiClient { force_error: false };
1424
1425            let params = CurrentOpenOrdersParams::builder().build().unwrap();
1426
1427            let resp_json: Value = serde_json::from_str(r#"[{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"avgFilledPrice","qty":"1","notional":"notional","filledQty":"0","filledTotal":"filledTotal","fee":"0","session":"RTH","status":"NEW","createdAt":1735900000000,"updatedAt":1735900000000}]"#).unwrap_or_else(|_| serde_json::json!({}));
1428            let expected_response : Vec<models::CurrentOpenOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::CurrentOpenOrdersResponseInner>");
1429
1430            let resp = client.current_open_orders(params).await.expect("Expected a response");
1431            let data_future = resp.data();
1432            let actual_response = data_future.await.unwrap();
1433            assert_eq!(actual_response, expected_response);
1434        });
1435    }
1436
1437    #[test]
1438    fn current_open_orders_optional_params_success() {
1439        TOKIO_SHARED_RT.block_on(async {
1440            let client = MockTradeApiClient { force_error: false };
1441
1442            let params = CurrentOpenOrdersParams::builder().recv_window(5000).build().unwrap();
1443
1444            let resp_json: Value = serde_json::from_str(r#"[{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"avgFilledPrice","qty":"1","notional":"notional","filledQty":"0","filledTotal":"filledTotal","fee":"0","session":"RTH","status":"NEW","createdAt":1735900000000,"updatedAt":1735900000000}]"#).unwrap_or_else(|_| serde_json::json!({}));
1445            let expected_response : Vec<models::CurrentOpenOrdersResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::CurrentOpenOrdersResponseInner>");
1446
1447            let resp = client.current_open_orders(params).await.expect("Expected a response");
1448            let data_future = resp.data();
1449            let actual_response = data_future.await.unwrap();
1450            assert_eq!(actual_response, expected_response);
1451        });
1452    }
1453
1454    #[test]
1455    fn current_open_orders_response_error() {
1456        TOKIO_SHARED_RT.block_on(async {
1457            let client = MockTradeApiClient { force_error: true };
1458
1459            let params = CurrentOpenOrdersParams::builder().build().unwrap();
1460
1461            match client.current_open_orders(params).await {
1462                Ok(_) => panic!("Expected an error"),
1463                Err(err) => {
1464                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1465                }
1466            }
1467        });
1468    }
1469
1470    #[test]
1471    fn equity_order_detail_required_params_success() {
1472        TOKIO_SHARED_RT.block_on(async {
1473            let client = MockTradeApiClient { force_error: false };
1474
1475            let params = EquityOrderDetailParams::builder().build().unwrap();
1476
1477            let resp_json: Value = serde_json::from_str(r#"{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","clientOrderId":"web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"180.48","qty":"1","notional":"notional","filledQty":"1","filledTotal":"filledTotal","fee":"0.10","session":"RTH","status":"FILLED","createdAt":1735900000000,"updatedAt":1735900120000,"trades":[{"executionId":"exec-20260504-0001","executionAt":1735900115000,"price":"180.48","qty":"1"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1478            let expected_response : models::EquityOrderDetailResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EquityOrderDetailResponse");
1479
1480            let resp = client.equity_order_detail(params).await.expect("Expected a response");
1481            let data_future = resp.data();
1482            let actual_response = data_future.await.unwrap();
1483            assert_eq!(actual_response, expected_response);
1484        });
1485    }
1486
1487    #[test]
1488    fn equity_order_detail_optional_params_success() {
1489        TOKIO_SHARED_RT.block_on(async {
1490            let client = MockTradeApiClient { force_error: false };
1491
1492            let params = EquityOrderDetailParams::builder().order_id("c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71".to_string()).client_order_id("web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a".to_string()).recv_window(5000).build().unwrap();
1493
1494            let resp_json: Value = serde_json::from_str(r#"{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","clientOrderId":"web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"180.48","qty":"1","notional":"notional","filledQty":"1","filledTotal":"filledTotal","fee":"0.10","session":"RTH","status":"FILLED","createdAt":1735900000000,"updatedAt":1735900120000,"trades":[{"executionId":"exec-20260504-0001","executionAt":1735900115000,"price":"180.48","qty":"1"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1495            let expected_response : models::EquityOrderDetailResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EquityOrderDetailResponse");
1496
1497            let resp = client.equity_order_detail(params).await.expect("Expected a response");
1498            let data_future = resp.data();
1499            let actual_response = data_future.await.unwrap();
1500            assert_eq!(actual_response, expected_response);
1501        });
1502    }
1503
1504    #[test]
1505    fn equity_order_detail_response_error() {
1506        TOKIO_SHARED_RT.block_on(async {
1507            let client = MockTradeApiClient { force_error: true };
1508
1509            let params = EquityOrderDetailParams::builder().build().unwrap();
1510
1511            match client.equity_order_detail(params).await {
1512                Ok(_) => panic!("Expected an error"),
1513                Err(err) => {
1514                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1515                }
1516            }
1517        });
1518    }
1519
1520    #[test]
1521    fn equity_order_history_required_params_success() {
1522        TOKIO_SHARED_RT.block_on(async {
1523            let client = MockTradeApiClient { force_error: false };
1524
1525            let params = EquityOrderHistoryParams::builder(1735800000000,1735900000000,).build().unwrap();
1526
1527            let resp_json: Value = serde_json::from_str(r#"{"total":2,"page":1,"size":20,"rows":[{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"180.48","qty":"1","notional":"notional","filledQty":"1","filledTotal":"filledTotal","fee":"0.10","session":"RTH","status":"FILLED","createdAt":1735900000000,"updatedAt":1735900120000}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1528            let expected_response : models::EquityOrderHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EquityOrderHistoryResponse");
1529
1530            let resp = client.equity_order_history(params).await.expect("Expected a response");
1531            let data_future = resp.data();
1532            let actual_response = data_future.await.unwrap();
1533            assert_eq!(actual_response, expected_response);
1534        });
1535    }
1536
1537    #[test]
1538    fn equity_order_history_optional_params_success() {
1539        TOKIO_SHARED_RT.block_on(async {
1540            let client = MockTradeApiClient { force_error: false };
1541
1542            let params = EquityOrderHistoryParams::builder(1735800000000,1735900000000,).symbol("NVDA".to_string()).order_type(EquityOrderHistoryOrderTypeEnum::Market).side(EquityOrderHistorySideEnum::Buy).order_status("FILLED,CANCELED".to_string()).current(1).size(20).recv_window(5000).build().unwrap();
1543
1544            let resp_json: Value = serde_json::from_str(r#"{"total":2,"page":1,"size":20,"rows":[{"orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","limitPrice":"180.50","avgFilledPrice":"180.48","qty":"1","notional":"notional","filledQty":"1","filledTotal":"filledTotal","fee":"0.10","session":"RTH","status":"FILLED","createdAt":1735900000000,"updatedAt":1735900120000}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1545            let expected_response : models::EquityOrderHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EquityOrderHistoryResponse");
1546
1547            let resp = client.equity_order_history(params).await.expect("Expected a response");
1548            let data_future = resp.data();
1549            let actual_response = data_future.await.unwrap();
1550            assert_eq!(actual_response, expected_response);
1551        });
1552    }
1553
1554    #[test]
1555    fn equity_order_history_response_error() {
1556        TOKIO_SHARED_RT.block_on(async {
1557            let client = MockTradeApiClient { force_error: true };
1558
1559            let params = EquityOrderHistoryParams::builder(1735800000000, 1735900000000)
1560                .build()
1561                .unwrap();
1562
1563            match client.equity_order_history(params).await {
1564                Ok(_) => panic!("Expected an error"),
1565                Err(err) => {
1566                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1567                }
1568            }
1569        });
1570    }
1571
1572    #[test]
1573    fn equity_trade_history_required_params_success() {
1574        TOKIO_SHARED_RT.block_on(async {
1575            let client = MockTradeApiClient { force_error: false };
1576
1577            let params = EquityTradeHistoryParams::builder(1735800000000,1735900000000,).build().unwrap();
1578
1579            let resp_json: Value = serde_json::from_str(r#"{"total":1,"page":1,"size":20,"rows":[{"executionId":"exec-20260504-0001","orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","price":"180.50","qty":"1","total":"180.50","executionAt":1735900115000,"updatedAt":1735900115200}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1580            let expected_response : models::EquityTradeHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EquityTradeHistoryResponse");
1581
1582            let resp = client.equity_trade_history(params).await.expect("Expected a response");
1583            let data_future = resp.data();
1584            let actual_response = data_future.await.unwrap();
1585            assert_eq!(actual_response, expected_response);
1586        });
1587    }
1588
1589    #[test]
1590    fn equity_trade_history_optional_params_success() {
1591        TOKIO_SHARED_RT.block_on(async {
1592            let client = MockTradeApiClient { force_error: false };
1593
1594            let params = EquityTradeHistoryParams::builder(1735800000000,1735900000000,).symbol("NVDA".to_string()).side(EquityTradeHistorySideEnum::Buy).order_id("c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71".to_string()).current(1).size(20).recv_window(5000).build().unwrap();
1595
1596            let resp_json: Value = serde_json::from_str(r#"{"total":1,"page":1,"size":20,"rows":[{"executionId":"exec-20260504-0001","orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","symbol":"AAPL","quote":"USDC","side":"BUY","orderType":"LIMIT","price":"180.50","qty":"1","total":"180.50","executionAt":1735900115000,"updatedAt":1735900115200}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1597            let expected_response : models::EquityTradeHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EquityTradeHistoryResponse");
1598
1599            let resp = client.equity_trade_history(params).await.expect("Expected a response");
1600            let data_future = resp.data();
1601            let actual_response = data_future.await.unwrap();
1602            assert_eq!(actual_response, expected_response);
1603        });
1604    }
1605
1606    #[test]
1607    fn equity_trade_history_response_error() {
1608        TOKIO_SHARED_RT.block_on(async {
1609            let client = MockTradeApiClient { force_error: true };
1610
1611            let params = EquityTradeHistoryParams::builder(1735800000000, 1735900000000)
1612                .build()
1613                .unwrap();
1614
1615            match client.equity_trade_history(params).await {
1616                Ok(_) => panic!("Expected an error"),
1617                Err(err) => {
1618                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1619                }
1620            }
1621        });
1622    }
1623
1624    #[test]
1625    fn place_equity_order_required_params_success() {
1626        TOKIO_SHARED_RT.block_on(async {
1627            let client = MockTradeApiClient { force_error: false };
1628
1629            let params = PlaceEquityOrderParams::builder("AAPL".to_string(),PlaceEquityOrderSideEnum::Buy,PlaceEquityOrderOrderTypeEnum::Market,).build().unwrap();
1630
1631            let resp_json: Value = serde_json::from_str(r#"{"status":"S","orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","clientOrderId":"web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a"}"#).unwrap_or_else(|_| serde_json::json!({}));
1632            let expected_response : models::PlaceEquityOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::PlaceEquityOrderResponse");
1633
1634            let resp = client.place_equity_order(params).await.expect("Expected a response");
1635            let data_future = resp.data();
1636            let actual_response = data_future.await.unwrap();
1637            assert_eq!(actual_response, expected_response);
1638        });
1639    }
1640
1641    #[test]
1642    fn place_equity_order_optional_params_success() {
1643        TOKIO_SHARED_RT.block_on(async {
1644            let client = MockTradeApiClient { force_error: false };
1645
1646            let params = PlaceEquityOrderParams::builder("AAPL".to_string(),PlaceEquityOrderSideEnum::Buy,PlaceEquityOrderOrderTypeEnum::Market,).quote_asset("USDC".to_string()).price("180.50".to_string()).quantity("1".to_string()).notional("1000.00".to_string()).time_in_force(PlaceEquityOrderTimeInForceEnum::Day).trading_session(PlaceEquityOrderTradingSessionEnum::Rth).wallet_type(PlaceEquityOrderWalletTypeEnum::Card).client_order_id("web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a".to_string()).tokenize(true).recv_window(5000).build().unwrap();
1647
1648            let resp_json: Value = serde_json::from_str(r#"{"status":"S","orderId":"c3c58f49-7b0d-4b9e-a2db-1a2f9a3b8c71","clientOrderId":"web_2c9c92b74f1e4a7c8f3b9e1a2d3c4b5a"}"#).unwrap_or_else(|_| serde_json::json!({}));
1649            let expected_response : models::PlaceEquityOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::PlaceEquityOrderResponse");
1650
1651            let resp = client.place_equity_order(params).await.expect("Expected a response");
1652            let data_future = resp.data();
1653            let actual_response = data_future.await.unwrap();
1654            assert_eq!(actual_response, expected_response);
1655        });
1656    }
1657
1658    #[test]
1659    fn place_equity_order_response_error() {
1660        TOKIO_SHARED_RT.block_on(async {
1661            let client = MockTradeApiClient { force_error: true };
1662
1663            let params = PlaceEquityOrderParams::builder(
1664                "AAPL".to_string(),
1665                PlaceEquityOrderSideEnum::Buy,
1666                PlaceEquityOrderOrderTypeEnum::Market,
1667            )
1668            .build()
1669            .unwrap();
1670
1671            match client.place_equity_order(params).await {
1672                Ok(_) => panic!("Expected an error"),
1673                Err(err) => {
1674                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1675                }
1676            }
1677        });
1678    }
1679}