webull_rs/models/
order.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4/// Order information from Webull.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Order {
7    /// Order ID
8    pub id: String,
9
10    /// Symbol of the security
11    pub symbol: String,
12
13    /// Quantity of shares
14    pub quantity: f64,
15
16    /// Filled quantity of shares
17    pub filled_quantity: f64,
18
19    /// Price of the order (for limit orders)
20    pub price: Option<f64>,
21
22    /// Stop price (for stop orders)
23    pub stop_price: Option<f64>,
24
25    /// Order status
26    pub status: OrderStatus,
27
28    /// Order side (buy/sell)
29    pub side: OrderSide,
30
31    /// Order type
32    pub order_type: OrderType,
33
34    /// Time in force
35    pub time_in_force: TimeInForce,
36
37    /// Whether the order is for extended hours trading
38    pub extended_hours: bool,
39
40    /// When the order was created
41    pub created_at: DateTime<Utc>,
42
43    /// When the order was last updated
44    pub updated_at: DateTime<Utc>,
45
46    /// Commission charged for the order
47    pub commission: f64,
48
49    /// Rejected reason (if the order was rejected)
50    pub rejected_reason: Option<String>,
51
52    /// Average fill price
53    pub average_fill_price: Option<f64>,
54}
55
56/// Status of an order.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "UPPERCASE")]
59pub enum OrderStatus {
60    /// Order is new
61    New,
62
63    /// Order is partially filled
64    PartiallyFilled,
65
66    /// Order is filled
67    Filled,
68
69    /// Order is canceled
70    Canceled,
71
72    /// Order is rejected
73    Rejected,
74
75    /// Order is pending cancel
76    PendingCancel,
77
78    /// Order is pending new
79    PendingNew,
80
81    /// Order is pending replace
82    PendingReplace,
83
84    /// Order is replaced
85    Replaced,
86
87    /// Order is suspended
88    Suspended,
89
90    /// Order is expired
91    Expired,
92}
93
94/// Side of an order.
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
96#[serde(rename_all = "UPPERCASE")]
97pub enum OrderSide {
98    /// Buy order
99    Buy,
100
101    /// Sell order
102    Sell,
103
104    /// Sell short order
105    SellShort,
106
107    /// Buy to cover order
108    BuyToCover,
109}
110
111/// Type of an order.
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
113#[serde(rename_all = "UPPERCASE")]
114pub enum OrderType {
115    /// Market order
116    #[serde(rename = "MARKET")]
117    Market,
118
119    /// Limit order
120    #[serde(rename = "LIMIT")]
121    Limit,
122
123    /// Stop order
124    #[serde(rename = "STOP_LOSS")]
125    Stop,
126
127    /// Stop limit order
128    #[serde(rename = "STOP_LOSS_LIMIT")]
129    StopLimit,
130
131    /// Trailing stop order
132    #[serde(rename = "TRAILING_STOP")]
133    TrailingStop,
134
135    /// Trailing stop limit order
136    #[serde(rename = "TRAILING_STOP_LIMIT")]
137    TrailingStopLimit,
138
139    /// Enhanced limit order (for Hong Kong stocks)
140    #[serde(rename = "ENHANCED_LIMIT")]
141    EnhancedLimit,
142
143    /// At auction order (for Hong Kong stocks)
144    #[serde(rename = "AT_AUCTION")]
145    AtAuction,
146
147    /// At auction limit order (for Hong Kong stocks)
148    #[serde(rename = "AT_AUCTION_LIMIT")]
149    AtAuctionLimit,
150}
151
152/// Time in force for an order.
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
154#[serde(rename_all = "UPPERCASE")]
155pub enum TimeInForce {
156    /// Day order
157    #[serde(rename = "DAY")]
158    Day,
159
160    /// Good till canceled order
161    #[serde(rename = "GTC")]
162    Gtc,
163
164    /// Good till date order
165    #[serde(rename = "GTD")]
166    Gtd,
167
168    /// Immediate or cancel order
169    #[serde(rename = "IOC")]
170    Ioc,
171
172    /// Fill or kill order
173    #[serde(rename = "FOK")]
174    Fok,
175}
176
177/// Trailing stop type for an order.
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
179#[serde(rename_all = "UPPERCASE")]
180pub enum TrailingStopType {
181    /// Amount in currency
182    #[serde(rename = "AMOUNT")]
183    Amount,
184
185    /// Percentage
186    #[serde(rename = "PERCENT")]
187    Percent,
188}
189
190/// Request to place an order.
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct OrderRequest {
193    /// Symbol of the security
194    pub symbol: String,
195
196    /// Quantity of shares
197    pub quantity: f64,
198
199    /// Price of the order (for limit orders)
200    pub price: Option<f64>,
201
202    /// Stop price (for stop orders)
203    pub stop_price: Option<f64>,
204
205    /// Order side (buy/sell)
206    pub side: OrderSide,
207
208    /// Order type
209    pub order_type: OrderType,
210
211    /// Time in force
212    pub time_in_force: TimeInForce,
213
214    /// Whether the order is for extended hours trading
215    pub extended_hours: bool,
216
217    /// Trailing stop type (for trailing stop orders)
218    pub trailing_type: Option<TrailingStopType>,
219
220    /// Trailing stop step (for trailing stop orders)
221    pub trailing_stop_step: Option<f64>,
222
223    /// Client order ID (for tracking purposes)
224    pub client_order_id: Option<String>,
225
226    /// Instrument ID (alternative to symbol)
227    pub instrument_id: Option<String>,
228}
229
230impl OrderRequest {
231    /// Create a new order request.
232    pub fn new() -> Self {
233        Self {
234            symbol: String::new(),
235            quantity: 0.0,
236            price: None,
237            stop_price: None,
238            side: OrderSide::Buy,
239            order_type: OrderType::Market,
240            time_in_force: TimeInForce::Day,
241            extended_hours: false,
242            trailing_type: None,
243            trailing_stop_step: None,
244            client_order_id: None,
245            instrument_id: None,
246        }
247    }
248
249    /// Set the symbol.
250    pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
251        self.symbol = symbol.into();
252        self
253    }
254
255    /// Set the quantity.
256    pub fn quantity(mut self, quantity: f64) -> Self {
257        self.quantity = quantity;
258        self
259    }
260
261    /// Set the price.
262    pub fn price(mut self, price: f64) -> Self {
263        self.price = Some(price);
264        self
265    }
266
267    /// Set the stop price.
268    pub fn stop_price(mut self, stop_price: f64) -> Self {
269        self.stop_price = Some(stop_price);
270        self
271    }
272
273    /// Set the order side.
274    pub fn side(mut self, side: OrderSide) -> Self {
275        self.side = side;
276        self
277    }
278
279    /// Set the order type.
280    pub fn order_type(mut self, order_type: OrderType) -> Self {
281        self.order_type = order_type;
282        self
283    }
284
285    /// Set the time in force.
286    pub fn time_in_force(mut self, time_in_force: TimeInForce) -> Self {
287        self.time_in_force = time_in_force;
288        self
289    }
290
291    /// Set whether the order is for extended hours trading.
292    pub fn extended_hours(mut self, extended_hours: bool) -> Self {
293        self.extended_hours = extended_hours;
294        self
295    }
296
297    /// Set the trailing stop type.
298    pub fn trailing_type(mut self, trailing_type: TrailingStopType) -> Self {
299        self.trailing_type = Some(trailing_type);
300        self
301    }
302
303    /// Set the trailing stop step.
304    pub fn trailing_stop_step(mut self, trailing_stop_step: f64) -> Self {
305        self.trailing_stop_step = Some(trailing_stop_step);
306        self
307    }
308
309    /// Set the client order ID.
310    pub fn client_order_id(mut self, client_order_id: impl Into<String>) -> Self {
311        self.client_order_id = Some(client_order_id.into());
312        self
313    }
314
315    /// Set the instrument ID.
316    pub fn instrument_id(mut self, instrument_id: impl Into<String>) -> Self {
317        self.instrument_id = Some(instrument_id.into());
318        self
319    }
320
321    /// Create a market order.
322    pub fn market() -> Self {
323        Self::new().order_type(OrderType::Market)
324    }
325
326    /// Create a limit order.
327    pub fn limit() -> Self {
328        Self::new().order_type(OrderType::Limit)
329    }
330
331    /// Create a stop order.
332    pub fn stop() -> Self {
333        Self::new().order_type(OrderType::Stop)
334    }
335
336    /// Create a stop limit order.
337    pub fn stop_limit() -> Self {
338        Self::new().order_type(OrderType::StopLimit)
339    }
340
341    /// Create a trailing stop order.
342    pub fn trailing_stop() -> Self {
343        Self::new().order_type(OrderType::TrailingStop)
344    }
345
346    /// Create a trailing stop limit order.
347    pub fn trailing_stop_limit() -> Self {
348        Self::new().order_type(OrderType::TrailingStopLimit)
349    }
350}
351
352impl Default for OrderRequest {
353    fn default() -> Self {
354        Self::new()
355    }
356}
357
358/// Response from placing an order.
359#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct OrderResponse {
361    /// Order ID
362    pub id: String,
363
364    /// Order status
365    pub status: OrderStatus,
366
367    /// Symbol of the security
368    pub symbol: String,
369
370    /// Quantity of shares
371    pub quantity: f64,
372
373    /// Price of the order (for limit orders)
374    pub price: Option<f64>,
375
376    /// Stop price (for stop orders)
377    pub stop_price: Option<f64>,
378
379    /// Order side (buy/sell)
380    pub side: OrderSide,
381
382    /// Order type
383    pub order_type: OrderType,
384
385    /// Time in force
386    pub time_in_force: TimeInForce,
387
388    /// Whether the order is for extended hours trading
389    pub extended_hours: bool,
390
391    /// When the order was created
392    pub created_at: DateTime<Utc>,
393}
394
395/// Parameters for querying orders.
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct OrderQueryParams {
398    /// Status of orders to query
399    pub status: Option<OrderStatus>,
400
401    /// Symbol to filter by
402    pub symbol: Option<String>,
403
404    /// Start date for the query
405    pub start_date: Option<DateTime<Utc>>,
406
407    /// End date for the query
408    pub end_date: Option<DateTime<Utc>>,
409
410    /// Maximum number of orders to return
411    pub limit: Option<u32>,
412}
413
414impl OrderQueryParams {
415    /// Create new order query parameters.
416    pub fn new() -> Self {
417        Self {
418            status: None,
419            symbol: None,
420            start_date: None,
421            end_date: None,
422            limit: None,
423        }
424    }
425
426    /// Set the status filter.
427    pub fn status(mut self, status: OrderStatus) -> Self {
428        self.status = Some(status);
429        self
430    }
431
432    /// Set the symbol filter.
433    pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
434        self.symbol = Some(symbol.into());
435        self
436    }
437
438    /// Set the start date filter.
439    pub fn start_date(mut self, start_date: DateTime<Utc>) -> Self {
440        self.start_date = Some(start_date);
441        self
442    }
443
444    /// Set the end date filter.
445    pub fn end_date(mut self, end_date: DateTime<Utc>) -> Self {
446        self.end_date = Some(end_date);
447        self
448    }
449
450    /// Set the limit.
451    pub fn limit(mut self, limit: u32) -> Self {
452        self.limit = Some(limit);
453        self
454    }
455}
456
457impl Default for OrderQueryParams {
458    fn default() -> Self {
459        Self::new()
460    }
461}
462
463/// Option order request.
464#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct OptionOrderRequest {
466    /// Client order ID
467    #[serde(rename = "client_order_id")]
468    pub client_order_id: String,
469
470    /// Option contract ID
471    #[serde(rename = "contract_id")]
472    pub contract_id: String,
473
474    /// Quantity of contracts
475    #[serde(rename = "qty")]
476    pub quantity: f64,
477
478    /// Order side (buy/sell)
479    #[serde(rename = "side")]
480    pub side: OrderSide,
481
482    /// Order type
483    #[serde(rename = "order_type")]
484    pub order_type: OrderType,
485
486    /// Time in force
487    #[serde(rename = "tif")]
488    pub time_in_force: TimeInForce,
489
490    /// Whether the order is for extended hours trading
491    #[serde(rename = "extended_hours_trading")]
492    pub extended_hours: bool,
493
494    /// Limit price (for limit orders)
495    #[serde(rename = "limit_price", skip_serializing_if = "Option::is_none")]
496    pub price: Option<f64>,
497
498    /// Stop price (for stop orders)
499    #[serde(rename = "stop_price", skip_serializing_if = "Option::is_none")]
500    pub stop_price: Option<f64>,
501}
502
503impl OptionOrderRequest {
504    /// Create a new option order request.
505    pub fn new(
506        client_order_id: impl Into<String>,
507        contract_id: impl Into<String>,
508        quantity: f64,
509    ) -> Self {
510        Self {
511            client_order_id: client_order_id.into(),
512            contract_id: contract_id.into(),
513            quantity,
514            side: OrderSide::Buy,
515            order_type: OrderType::Limit,
516            time_in_force: TimeInForce::Day,
517            extended_hours: false,
518            price: None,
519            stop_price: None,
520        }
521    }
522
523    /// Set the order side.
524    pub fn side(mut self, side: OrderSide) -> Self {
525        self.side = side;
526        self
527    }
528
529    /// Set the order type.
530    pub fn order_type(mut self, order_type: OrderType) -> Self {
531        self.order_type = order_type;
532        self
533    }
534
535    /// Set the time in force.
536    pub fn time_in_force(mut self, time_in_force: TimeInForce) -> Self {
537        self.time_in_force = time_in_force;
538        self
539    }
540
541    /// Set whether the order is for extended hours trading.
542    pub fn extended_hours(mut self, extended_hours: bool) -> Self {
543        self.extended_hours = extended_hours;
544        self
545    }
546
547    /// Set the price.
548    pub fn price(mut self, price: f64) -> Self {
549        self.price = Some(price);
550        self
551    }
552
553    /// Set the stop price.
554    pub fn stop_price(mut self, stop_price: f64) -> Self {
555        self.stop_price = Some(stop_price);
556        self
557    }
558}
559
560/// Option order preview request.
561#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct OptionOrderPreviewRequest {
563    /// Account ID
564    #[serde(rename = "account_id")]
565    pub account_id: String,
566
567    /// New orders
568    #[serde(rename = "new_orders")]
569    pub new_orders: Vec<OptionOrderRequest>,
570}
571
572impl OptionOrderPreviewRequest {
573    /// Create a new option order preview request.
574    pub fn new(account_id: impl Into<String>) -> Self {
575        Self {
576            account_id: account_id.into(),
577            new_orders: Vec::new(),
578        }
579    }
580
581    /// Add a new order to the preview request.
582    pub fn add_order(mut self, order: OptionOrderRequest) -> Self {
583        self.new_orders.push(order);
584        self
585    }
586}
587
588/// Option order preview response.
589#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct OptionOrderPreviewResponse {
591    /// Order ID
592    pub id: String,
593
594    /// Commission
595    pub commission: f64,
596
597    /// Estimated cost
598    pub estimated_cost: f64,
599
600    /// Estimated proceeds
601    pub estimated_proceeds: f64,
602
603    /// Buying power effect
604    pub buying_power_effect: f64,
605
606    /// Margin requirement
607    pub margin_requirement: f64,
608
609    /// Error message (if any)
610    pub error_message: Option<String>,
611
612    /// Warning message (if any)
613    pub warning_message: Option<String>,
614}