kiteconnect_async_wasm/models/orders/
order_params.rs

1use crate::models::common::{Exchange, OrderType, Product, TransactionType, Validity};
2use serde::{Deserialize, Serialize};
3
4/// Order placement parameters
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct OrderParams {
7    /// Trading symbol
8    #[serde(rename = "tradingsymbol")]
9    pub trading_symbol: String,
10
11    /// Exchange
12    pub exchange: Exchange,
13
14    /// Transaction type (BUY/SELL)
15    #[serde(rename = "transaction_type")]
16    pub transaction_type: TransactionType,
17
18    /// Order type
19    #[serde(rename = "order_type")]
20    pub order_type: OrderType,
21
22    /// Quantity
23    pub quantity: u32,
24
25    /// Product type
26    pub product: Product,
27
28    /// Price (required for LIMIT orders)
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub price: Option<f64>,
31
32    /// Trigger price (required for SL and SL-M orders)
33    #[serde(rename = "trigger_price", skip_serializing_if = "Option::is_none")]
34    pub trigger_price: Option<f64>,
35
36    /// Validity
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub validity: Option<Validity>,
39
40    /// Disclosed quantity for iceberg orders
41    #[serde(rename = "disclosed_quantity", skip_serializing_if = "Option::is_none")]
42    pub disclosed_quantity: Option<u32>,
43
44    /// Tag for the order
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub tag: Option<String>,
47
48    /// Square off value for bracket orders
49    #[serde(rename = "squareoff", skip_serializing_if = "Option::is_none")]
50    pub squareoff: Option<f64>,
51
52    /// Stoploss value for bracket orders
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub stoploss: Option<f64>,
55
56    /// Trailing stoploss value for bracket orders
57    #[serde(rename = "trailing_stoploss", skip_serializing_if = "Option::is_none")]
58    pub trailing_stoploss: Option<f64>,
59
60    /// Market protection percentage
61    #[serde(rename = "market_protection", skip_serializing_if = "Option::is_none")]
62    pub market_protection: Option<f64>,
63
64    /// ICEBERG legs for iceberg orders
65    #[serde(rename = "iceberg_legs", skip_serializing_if = "Option::is_none")]
66    pub iceberg_legs: Option<u32>,
67
68    /// ICEBERG quantity for iceberg orders
69    #[serde(rename = "iceberg_quantity", skip_serializing_if = "Option::is_none")]
70    pub iceberg_quantity: Option<u32>,
71
72    /// Auction number
73    #[serde(rename = "auction_number", skip_serializing_if = "Option::is_none")]
74    pub auction_number: Option<String>,
75}
76
77/// Bracket order parameters
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct BracketOrderParams {
80    /// Base order parameters
81    #[serde(flatten)]
82    pub order_params: OrderParams,
83
84    /// Square off value (mandatory for bracket orders)
85    pub squareoff: f64,
86
87    /// Stoploss value (mandatory for bracket orders)
88    pub stoploss: f64,
89
90    /// Trailing stoploss (optional)
91    #[serde(rename = "trailing_stoploss", skip_serializing_if = "Option::is_none")]
92    pub trailing_stoploss: Option<f64>,
93}
94
95/// Cover order parameters
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct CoverOrderParams {
98    /// Base order parameters
99    #[serde(flatten)]
100    pub order_params: OrderParams,
101
102    /// Trigger price (mandatory for cover orders)
103    #[serde(rename = "trigger_price")]
104    pub trigger_price: f64,
105}
106
107/// Order modification parameters
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct OrderModifyParams {
110    /// Order ID to modify
111    #[serde(skip_serializing)]
112    pub order_id: String,
113
114    /// New quantity
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub quantity: Option<u32>,
117
118    /// New price
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub price: Option<f64>,
121
122    /// New trigger price
123    #[serde(rename = "trigger_price", skip_serializing_if = "Option::is_none")]
124    pub trigger_price: Option<f64>,
125
126    /// New order type
127    #[serde(rename = "order_type", skip_serializing_if = "Option::is_none")]
128    pub order_type: Option<OrderType>,
129
130    /// New validity
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub validity: Option<Validity>,
133
134    /// New disclosed quantity
135    #[serde(rename = "disclosed_quantity", skip_serializing_if = "Option::is_none")]
136    pub disclosed_quantity: Option<u32>,
137
138    /// Parent order ID for bracket/cover orders
139    #[serde(rename = "parent_order_id", skip_serializing_if = "Option::is_none")]
140    pub parent_order_id: Option<String>,
141}
142
143/// Builder for order parameters
144#[derive(Debug, Clone)]
145pub struct OrderBuilder {
146    params: OrderParams,
147}
148
149impl OrderBuilder {
150    /// Create a new order builder
151    pub fn new() -> Self {
152        Self {
153            params: OrderParams {
154                trading_symbol: String::new(),
155                exchange: Exchange::NSE,
156                transaction_type: TransactionType::BUY,
157                order_type: OrderType::LIMIT,
158                quantity: 0,
159                product: Product::CNC,
160                price: None,
161                trigger_price: None,
162                validity: Some(Validity::DAY),
163                disclosed_quantity: None,
164                tag: None,
165                squareoff: None,
166                stoploss: None,
167                trailing_stoploss: None,
168                market_protection: None,
169                iceberg_legs: None,
170                iceberg_quantity: None,
171                auction_number: None,
172            },
173        }
174    }
175
176    /// Set trading symbol
177    pub fn trading_symbol<S: Into<String>>(mut self, symbol: S) -> Self {
178        self.params.trading_symbol = symbol.into();
179        self
180    }
181
182    /// Set exchange
183    pub fn exchange(mut self, exchange: Exchange) -> Self {
184        self.params.exchange = exchange;
185        self
186    }
187
188    /// Set transaction type
189    pub fn transaction_type(mut self, transaction_type: TransactionType) -> Self {
190        self.params.transaction_type = transaction_type;
191        self
192    }
193
194    /// Set order type
195    pub fn order_type(mut self, order_type: OrderType) -> Self {
196        self.params.order_type = order_type;
197        self
198    }
199
200    /// Set quantity
201    pub fn quantity(mut self, quantity: u32) -> Self {
202        self.params.quantity = quantity;
203        self
204    }
205
206    /// Set product
207    pub fn product(mut self, product: Product) -> Self {
208        self.params.product = product;
209        self
210    }
211
212    /// Set price (for limit orders)
213    pub fn price(mut self, price: f64) -> Self {
214        self.params.price = Some(price);
215        self
216    }
217
218    /// Set trigger price (for SL orders)
219    pub fn trigger_price(mut self, trigger_price: f64) -> Self {
220        self.params.trigger_price = Some(trigger_price);
221        self
222    }
223
224    /// Set validity
225    pub fn validity(mut self, validity: Validity) -> Self {
226        self.params.validity = Some(validity);
227        self
228    }
229
230    /// Set disclosed quantity
231    pub fn disclosed_quantity(mut self, disclosed_quantity: u32) -> Self {
232        self.params.disclosed_quantity = Some(disclosed_quantity);
233        self
234    }
235
236    /// Set tag
237    pub fn tag<S: Into<String>>(mut self, tag: S) -> Self {
238        self.params.tag = Some(tag.into());
239        self
240    }
241
242    /// Set market protection
243    pub fn market_protection(mut self, market_protection: f64) -> Self {
244        self.params.market_protection = Some(market_protection);
245        self
246    }
247
248    /// Configure for iceberg order
249    pub fn iceberg(mut self, legs: u32, quantity: u32) -> Self {
250        self.params.iceberg_legs = Some(legs);
251        self.params.iceberg_quantity = Some(quantity);
252        self
253    }
254
255    /// Build the order parameters
256    pub fn build(self) -> Result<OrderParams, String> {
257        // Validate required fields
258        if self.params.trading_symbol.is_empty() {
259            return Err("Trading symbol is required".to_string());
260        }
261
262        if self.params.quantity == 0 {
263            return Err("Quantity must be greater than 0".to_string());
264        }
265
266        // Validate price for limit orders
267        if self.params.order_type == OrderType::LIMIT && self.params.price.is_none() {
268            return Err("Price is required for LIMIT orders".to_string());
269        }
270
271        // Validate trigger price for SL orders
272        if matches!(self.params.order_type, OrderType::SL | OrderType::SLM)
273            && self.params.trigger_price.is_none()
274        {
275            return Err("Trigger price is required for SL/SL-M orders".to_string());
276        }
277
278        Ok(self.params)
279    }
280}
281
282impl Default for OrderBuilder {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288/// Builder for bracket order parameters
289pub struct BracketOrderBuilder {
290    params: OrderParams,
291    squareoff: Option<f64>,
292    stoploss: Option<f64>,
293    trailing_stoploss: Option<f64>,
294}
295
296impl BracketOrderBuilder {
297    /// Create a new bracket order builder
298    pub fn new() -> Self {
299        Self {
300            params: OrderParams {
301                trading_symbol: String::new(),
302                exchange: Exchange::NSE,
303                transaction_type: TransactionType::BUY,
304                order_type: OrderType::LIMIT,
305                quantity: 0,
306                product: Product::MIS,
307                price: None,
308                trigger_price: None,
309                validity: Some(Validity::DAY),
310                disclosed_quantity: None,
311                tag: None,
312                squareoff: None,
313                stoploss: None,
314                trailing_stoploss: None,
315                market_protection: None,
316                iceberg_legs: None,
317                iceberg_quantity: None,
318                auction_number: None,
319            },
320            squareoff: None,
321            stoploss: None,
322            trailing_stoploss: None,
323        }
324    }
325
326    /// Set trading symbol
327    pub fn trading_symbol<S: Into<String>>(mut self, symbol: S) -> Self {
328        self.params.trading_symbol = symbol.into();
329        self
330    }
331
332    /// Set exchange
333    pub fn exchange(mut self, exchange: Exchange) -> Self {
334        self.params.exchange = exchange;
335        self
336    }
337
338    /// Set transaction type
339    pub fn transaction_type(mut self, transaction_type: TransactionType) -> Self {
340        self.params.transaction_type = transaction_type;
341        self
342    }
343
344    /// Set quantity
345    pub fn quantity(mut self, quantity: u32) -> Self {
346        self.params.quantity = quantity;
347        self
348    }
349
350    /// Set price
351    pub fn price(mut self, price: f64) -> Self {
352        self.params.price = Some(price);
353        self
354    }
355
356    /// Set square off value
357    pub fn squareoff(mut self, squareoff: f64) -> Self {
358        self.squareoff = Some(squareoff);
359        self
360    }
361
362    /// Set stoploss value
363    pub fn stoploss(mut self, stoploss: f64) -> Self {
364        self.stoploss = Some(stoploss);
365        self
366    }
367
368    /// Set trailing stoploss
369    pub fn trailing_stoploss(mut self, trailing_stoploss: f64) -> Self {
370        self.trailing_stoploss = Some(trailing_stoploss);
371        self
372    }
373
374    /// Build the bracket order parameters
375    pub fn build(self) -> Result<BracketOrderParams, String> {
376        // Validate required fields
377        if self.params.trading_symbol.is_empty() {
378            return Err("Trading symbol is required".to_string());
379        }
380
381        if self.params.quantity == 0 {
382            return Err("Quantity must be greater than 0".to_string());
383        }
384
385        if self.params.price.is_none() {
386            return Err("Price is required for bracket orders".to_string());
387        }
388
389        let squareoff = self
390            .squareoff
391            .ok_or("Square off value is required for bracket orders")?;
392        let stoploss = self
393            .stoploss
394            .ok_or("Stoploss value is required for bracket orders")?;
395
396        let mut order_params = self.params;
397        order_params.squareoff = Some(squareoff);
398        order_params.stoploss = Some(stoploss);
399        order_params.trailing_stoploss = self.trailing_stoploss;
400
401        Ok(BracketOrderParams {
402            order_params,
403            squareoff,
404            stoploss,
405            trailing_stoploss: self.trailing_stoploss,
406        })
407    }
408}
409
410impl Default for BracketOrderBuilder {
411    fn default() -> Self {
412        Self::new()
413    }
414}