kiteconnect_async_wasm/connect/orders.rs
1//! # Orders Module
2//!
3//! This module provides comprehensive order management capabilities for the KiteConnect API v1.0.3,
4//! offering complete control over trade execution with both simple and advanced order types.
5//!
6//! ## Overview
7//!
8//! The orders module is the core component for executing trades on the KiteConnect platform.
9//! It provides access to order placement, modification, cancellation, and monitoring with
10//! both legacy JSON-based and modern strongly-typed APIs for enhanced developer experience.
11//!
12//! ## Key Features
13//!
14//! ### 🔄 **Dual API Support**
15//! - **Legacy API**: Returns `JsonValue` for backward compatibility
16//! - **Typed API**: Returns structured types with compile-time safety (methods ending in `_typed`)
17//!
18//! ### 📊 **Complete Order Management**
19//! - **Order Placement**: Market, limit, stop-loss, and bracket orders
20//! - **Order Modification**: Update price, quantity, and order parameters
21//! - **Order Cancellation**: Cancel pending orders and exit positions
22//! - **Order Monitoring**: Real-time status updates and fill information
23//!
24//! ### 💡 **Advanced Order Types**
25//! - **Regular Orders**: Basic buy/sell orders
26//! - **Bracket Orders**: Auto stop-loss and profit booking
27//! - **Cover Orders**: Built-in stop-loss protection
28//! - **Iceberg Orders**: Large order execution in smaller chunks
29//! - **GTT Orders**: Good Till Triggered conditional orders
30//!
31//! ### 🛠️ **Builder Patterns**
32//! - **OrderBuilder**: Fluent API for constructing orders
33//! - **BracketOrderBuilder**: Specialized builder for bracket orders
34//! - **Type Safety**: Compile-time validation of order parameters
35//!
36//! ## Available Methods
37//!
38//! ### Order Placement
39//! - [`place_order()`](KiteConnect::place_order) / [`place_order_typed()`](KiteConnect::place_order_typed) - Place new orders
40//! - [`modify_order()`](KiteConnect::modify_order) - Modify existing orders
41//! - [`cancel_order()`](KiteConnect::cancel_order) - Cancel pending orders
42//!
43//! ### Order Information
44//! - [`orders()`](KiteConnect::orders) / [`orders_typed()`](KiteConnect::orders_typed) - Get all orders
45//! - [`order_history()`](KiteConnect::order_history) - Get order execution history
46//! - [`trades()`](KiteConnect::trades) / [`trades_typed()`](KiteConnect::trades_typed) - Get trade book
47//!
48//! ### Position Management
49//! - [`convert_position()`](KiteConnect::convert_position) - Convert product types
50//! - Position tracking and P&L monitoring
51//!
52//! ## Usage Examples
53//!
54//! ### Basic Order Placement
55//! ```rust,no_run
56//! use kiteconnect_async_wasm::connect::KiteConnect;
57//! use kiteconnect_async_wasm::models::orders::OrderParams;
58//! use kiteconnect_async_wasm::models::common::{Exchange, TransactionType, OrderType, Product, Validity};
59//!
60//! # #[tokio::main]
61//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
62//! let client = KiteConnect::new("api_key", "access_token");
63//!
64//! // Method 1: Direct order parameters
65//! let order_params = OrderParams {
66//! exchange: Exchange::NSE,
67//! trading_symbol: "RELIANCE".to_string(),
68//! transaction_type: TransactionType::BUY,
69//! quantity: 10,
70//! order_type: OrderType::LIMIT,
71//! product: Product::CNC,
72//! price: Some(2500.0),
73//! validity: Some(Validity::DAY),
74//! disclosed_quantity: None,
75//! trigger_price: None,
76//! tag: Some("MyOrder".to_string()),
77//! squareoff: None,
78//! stoploss: None,
79//! trailing_stoploss: None,
80//! market_protection: None,
81//! iceberg_legs: None,
82//! iceberg_quantity: None,
83//! auction_number: None,
84//! };
85//!
86//! let response = client.place_order_typed("regular", &order_params).await?;
87//! println!("🎯 Order placed successfully!");
88//! println!(" Order ID: {}", response.order_id);
89//! # Ok(())
90//! # }
91//! ```
92//!
93//! ### Using Order Builder (Recommended)
94//! ```rust,no_run
95//! use kiteconnect_async_wasm::connect::KiteConnect;
96//! use kiteconnect_async_wasm::models::orders::OrderBuilder;
97//! use kiteconnect_async_wasm::models::common::{Exchange, TransactionType, OrderType, Product, Validity};
98//!
99//! # #[tokio::main]
100//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
101//! let client = KiteConnect::new("api_key", "access_token");
102//!
103//! // Fluent builder pattern for better ergonomics
104//! let order = OrderBuilder::new()
105//! .trading_symbol("TCS")
106//! .exchange(Exchange::NSE)
107//! .transaction_type(TransactionType::BUY)
108//! .quantity(5)
109//! .order_type(OrderType::MARKET)
110//! .product(Product::MIS)
111//! .validity(Validity::DAY)
112//! .tag("QuickBuy")
113//! .build()?;
114//!
115//! let response = client.place_order_typed("regular", &order).await?;
116//! println!("🚀 Market order executed!");
117//! println!(" Order ID: {}", response.order_id);
118//! # Ok(())
119//! # }
120//! ```
121//!
122//! ### Advanced Order Types
123//! ```rust,no_run
124//! use kiteconnect_async_wasm::connect::KiteConnect;
125//! use kiteconnect_async_wasm::models::orders::{OrderBuilder, BracketOrderBuilder};
126//! use kiteconnect_async_wasm::models::common::{Exchange, TransactionType, OrderType, Product};
127//!
128//! # #[tokio::main]
129//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
130//! let client = KiteConnect::new("api_key", "access_token");
131//!
132//! // Stop-Loss Order
133//! let sl_order = OrderBuilder::new()
134//! .trading_symbol("INFY")
135//! .exchange(Exchange::NSE)
136//! .transaction_type(TransactionType::SELL)
137//! .quantity(20)
138//! .order_type(OrderType::SL)
139//! .product(Product::MIS)
140//! .price(1450.0)
141//! .trigger_price(1440.0)
142//! .build()?;
143//!
144//! let sl_response = client.place_order_typed("regular", &sl_order).await?;
145//! println!("🛡️ Stop-loss order placed: {}", sl_response.order_id);
146//!
147//! // Bracket Order (Buy with auto profit booking and stop-loss)
148//! let bracket_order = BracketOrderBuilder::new()
149//! .trading_symbol("HDFC")
150//! .exchange(Exchange::NSE)
151//! .transaction_type(TransactionType::BUY)
152//! .quantity(10)
153//! .price(1600.0)
154//! .squareoff(1650.0) // Take profit at +50
155//! .stoploss(1580.0) // Stop loss at -20
156//! .trailing_stoploss(5.0) // Trail by 5 points
157//! .build()?;
158//!
159//! let bo_response = client.place_order_typed("bo", &bracket_order.order_params).await?;
160//! println!("🎯 Bracket order placed: {}", bo_response.order_id);
161//!
162//! // Iceberg Order (Large order in small chunks)
163//! let iceberg_order = OrderBuilder::new()
164//! .trading_symbol("SBIN")
165//! .exchange(Exchange::NSE)
166//! .transaction_type(TransactionType::BUY)
167//! .quantity(1000) // Total quantity
168//! .order_type(OrderType::LIMIT)
169//! .product(Product::CNC)
170//! .price(250.0)
171//! .iceberg(10, 100) // 10 legs of 100 shares each
172//! .build()?;
173//!
174//! let iceberg_response = client.place_order_typed("iceberg", &iceberg_order).await?;
175//! println!("🧊 Iceberg order placed: {}", iceberg_response.order_id);
176//! # Ok(())
177//! # }
178//! ```
179//!
180//! ### Order Monitoring and Management
181//! ```rust,no_run
182//! use kiteconnect_async_wasm::connect::KiteConnect;
183//! use kiteconnect_async_wasm::models::orders::OrderStatus;
184//!
185//! # #[tokio::main]
186//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
187//! let client = KiteConnect::new("api_key", "access_token");
188//!
189//! // Get all orders for the day
190//! let orders = client.orders_typed().await?;
191//!
192//! println!("📋 Today's Orders ({}):", orders.len());
193//! println!("================================");
194//!
195//! for order in &orders {
196//! let status_icon = match order.status {
197//! OrderStatus::Complete => "✅",
198//! OrderStatus::Open | OrderStatus::TriggerPending => "⏳",
199//! OrderStatus::Cancelled => "❌",
200//! OrderStatus::Rejected => "🚫",
201//! _ => "❓",
202//! };
203//!
204//! println!("{} {} ({:?})", status_icon, order.order_id, order.status);
205//! println!(" 📊 {:?} {} {} @ ₹{:.2}",
206//! order.transaction_type,
207//! order.quantity,
208//! order.trading_symbol,
209//! order.price);
210//!
211//! if order.is_partially_filled() {
212//! println!(" 📈 Partial fill: {}/{} ({:.1}%)",
213//! order.filled_quantity,
214//! order.quantity,
215//! order.fill_percentage());
216//! }
217//!
218//! if order.is_complete() && order.filled_quantity > 0 {
219//! println!(" 💰 Avg fill price: ₹{:.2}",
220//! order.average_price);
221//! }
222//! println!();
223//! }
224//! # Ok(())
225//! # }
226//! ```
227//!
228//! ### Order Modification and Cancellation
229//! ```rust,no_run
230//! use kiteconnect_async_wasm::connect::KiteConnect;
231//!
232//! # #[tokio::main]
233//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
234//! let client = KiteConnect::new("api_key", "access_token");
235//!
236//! let order_id = "210429000000001"; // Replace with actual order ID
237//!
238//! // Modify order price
239//! let modify_result = client.modify_order(
240//! order_id,
241//! "regular", // variety
242//! None, // quantity (unchanged)
243//! Some("2550.0"), // new price
244//! None, // order_type (unchanged)
245//! None, // validity (unchanged)
246//! None, // disclosed_quantity
247//! None, // trigger_price
248//! None, // parent_order_id
249//! ).await;
250//!
251//! match modify_result {
252//! Ok(_) => println!("✅ Order {} modified successfully", order_id),
253//! Err(e) => println!("❌ Failed to modify order: {}", e),
254//! }
255//!
256//! // Cancel order if modification fails or no longer needed
257//! let cancel_result = client.cancel_order(order_id, "regular", None).await;
258//! match cancel_result {
259//! Ok(_) => println!("🗑️ Order {} cancelled successfully", order_id),
260//! Err(e) => println!("❌ Failed to cancel order: {}", e),
261//! }
262//! # Ok(())
263//! # }
264//! ```
265//!
266//! ### Trade Book Analysis
267//! ```rust,no_run
268//! use kiteconnect_async_wasm::connect::KiteConnect;
269//!
270//! # #[tokio::main]
271//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
272//! let client = KiteConnect::new("api_key", "access_token");
273//!
274//! // Get all trades for the day
275//! let trades = client.trades_typed().await?;
276//!
277//! println!("💼 Trade Book Analysis ({} trades):", trades.len());
278//! println!("=====================================");
279//!
280//! let mut total_turnover = 0.0;
281//! let mut buy_trades = 0;
282//! let mut sell_trades = 0;
283//!
284//! for trade in &trades {
285//! let trade_value = trade.total_value();
286//! total_turnover += trade_value;
287//!
288//! if trade.is_buy() {
289//! buy_trades += 1;
290//! } else {
291//! sell_trades += 1;
292//! }
293//!
294//! println!("🔄 {} {}: {} @ ₹{:.2} (₹{:.2})",
295//! trade.fill_timestamp.format("%H:%M:%S"),
296//! trade.trading_symbol,
297//! if trade.is_buy() { "BUY" } else { "SELL" },
298//! trade.average_price,
299//! trade_value);
300//! }
301//!
302//! println!();
303//! println!("📊 Summary:");
304//! println!(" Total trades: {} (Buy: {}, Sell: {})", trades.len(), buy_trades, sell_trades);
305//! println!(" Total turnover: ₹{:.2}", total_turnover);
306//! println!(" Average trade size: ₹{:.2}",
307//! if !trades.is_empty() { total_turnover / trades.len() as f64 } else { 0.0 });
308//! # Ok(())
309//! # }
310//! ```
311//!
312//! ## Data Models
313//!
314//! ### Order Types
315//! The [`Order`] struct represents order information with comprehensive status tracking:
316//! - **Order Status**: Open, complete, cancelled, rejected states
317//! - **Fill Information**: Partial and complete fill tracking
318//! - **Execution Details**: Average price, timestamps, and exchange data
319//! - **Order Analysis**: Helper methods for status checking and calculations
320//!
321//! ### Order Parameters
322//! The [`OrderParams`] struct defines order placement requirements:
323//! - **Required Fields**: Symbol, exchange, transaction type, quantity
324//! - **Optional Fields**: Price, validity, disclosed quantity, tags
325//! - **Advanced Features**: Stop-loss, iceberg, bracket order parameters
326//! - **Validation**: Built-in parameter validation and error checking
327//!
328//! ### Trade Information
329//! The [`Trade`] struct represents executed trades:
330//! - **Execution Data**: Fill price, quantity, and timestamp
331//! - **Order Linkage**: Connection to parent order information
332//! - **Value Calculations**: Trade value and commission tracking
333//! - **Direction Analysis**: Buy/sell identification and analysis
334//!
335//! ## Error Handling
336//!
337//! All methods return `Result<T>` with comprehensive error information:
338//!
339//! ```rust,no_run
340//! use kiteconnect_async_wasm::models::common::KiteError;
341//!
342//! # #[tokio::main]
343//! # async fn main() {
344//! # let client = kiteconnect_async_wasm::connect::KiteConnect::new("", "");
345//! # let order_params = kiteconnect_async_wasm::models::orders::OrderParams {
346//! # exchange: kiteconnect_async_wasm::models::common::Exchange::NSE,
347//! # trading_symbol: "TEST".to_string(),
348//! # transaction_type: kiteconnect_async_wasm::models::common::TransactionType::BUY,
349//! # quantity: 1,
350//! # order_type: kiteconnect_async_wasm::models::common::OrderType::MARKET,
351//! # product: kiteconnect_async_wasm::models::common::Product::MIS,
352//! # price: None, validity: None, disclosed_quantity: None, trigger_price: None,
353//! # tag: None, squareoff: None, stoploss: None, trailing_stoploss: None,
354//! # market_protection: None, iceberg_legs: None, iceberg_quantity: None,
355//! # auction_number: None,
356//! # };
357//! match client.place_order_typed("regular", &order_params).await {
358//! Ok(response) => {
359//! println!("✅ Order placed: {}", response.order_id);
360//! }
361//! Err(KiteError::Authentication(msg)) => {
362//! eprintln!("🔐 Authentication failed: {}", msg);
363//! // Handle re-authentication
364//! }
365//! Err(KiteError::Api { status, message, .. }) => {
366//! eprintln!("🚫 Order rejected: {} - {}", status, message);
367//! if status == "429" {
368//! eprintln!("⏱️ Rate limited - please wait before retrying");
369//! }
370//! // Handle order rejection (insufficient margin, invalid params, etc.)
371//! }
372//! Err(e) => eprintln!("❌ Other error: {}", e),
373//! }
374//! # }
375//! ```
376//!
377//! ## Order Validation
378//!
379//! The module provides built-in validation for order parameters:
380//!
381//! ```rust,no_run
382//! use kiteconnect_async_wasm::models::orders::OrderBuilder;
383//! use kiteconnect_async_wasm::models::common::{Exchange, TransactionType, OrderType, Product};
384//!
385//! # fn example() -> Result<(), String> {
386//! // This will fail validation - no price for LIMIT order
387//! let invalid_order = OrderBuilder::new()
388//! .trading_symbol("RELIANCE")
389//! .exchange(Exchange::NSE)
390//! .transaction_type(TransactionType::BUY)
391//! .quantity(10)
392//! .order_type(OrderType::LIMIT) // LIMIT order requires price
393//! .product(Product::CNC)
394//! .build(); // This returns Err("Price is required for LIMIT orders")
395//!
396//! match invalid_order {
397//! Ok(_) => println!("Order validated successfully"),
398//! Err(e) => println!("Validation error: {}", e),
399//! }
400//!
401//! // Correct version with price
402//! let valid_order = OrderBuilder::new()
403//! .trading_symbol("RELIANCE")
404//! .exchange(Exchange::NSE)
405//! .transaction_type(TransactionType::BUY)
406//! .quantity(10)
407//! .order_type(OrderType::LIMIT)
408//! .product(Product::CNC)
409//! .price(2500.0) // Price provided for LIMIT order
410//! .build()?;
411//!
412//! println!("✅ Order validated and ready for placement");
413//! # Ok(())
414//! # }
415//! ```
416//!
417//! ## Performance Considerations
418//!
419//! ### Efficient Order Management
420//! - **Batch Operations**: Use `tokio::join!` for concurrent order operations
421//! - **Typed APIs**: Use `*_typed()` methods for better performance and type safety
422//! - **Builder Patterns**: Use builders for complex orders to avoid parameter errors
423//!
424//! ### Memory Usage
425//! - **Structured Data**: Typed APIs use less memory than JSON parsing
426//! - **Efficient Calculations**: Built-in helper methods reduce computation overhead
427//! - **Order Filtering**: Filter orders client-side to reduce data processing
428//!
429//! ## Rate Limiting
430//!
431//! The module automatically handles rate limiting according to KiteConnect API guidelines:
432//! - **Order APIs**: 10 requests per second for order placement and modification
433//! - **Query APIs**: 3 requests per second for order and trade queries
434//! - **Automatic Retry**: Built-in retry mechanism with exponential backoff
435//! - **Connection Pooling**: HTTP connections are reused for better performance
436//!
437//! ## Thread Safety
438//!
439//! All methods are thread-safe and can be called concurrently:
440//! ```rust,no_run
441//! # use kiteconnect_async_wasm::connect::KiteConnect;
442//! # #[tokio::main]
443//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
444//! # let client = KiteConnect::new("", "");
445//! // Concurrent order and trade data retrieval
446//! let (orders, trades) = tokio::try_join!(
447//! client.orders_typed(),
448//! client.trades_typed()
449//! )?;
450//!
451//! // Process both datasets concurrently
452//! println!("Orders: {}, Trades: {}", orders.len(), trades.len());
453//! # Ok(())
454//! # }
455//! ```
456//!
457//! ## Migration from v1.0.2
458//!
459//! All existing methods continue to work. New typed methods provide enhanced features:
460//! - Replace `place_order()` with `place_order_typed()` for structured parameters
461//! - Use `orders_typed()` and `trades_typed()` for type safety
462//! - Leverage `OrderBuilder` and `BracketOrderBuilder` for complex orders
463//! - Legacy methods remain available for backward compatibility
464
465use crate::connect::endpoints::KiteEndpoint;
466use anyhow::Result;
467use serde_json::Value as JsonValue;
468use std::collections::HashMap;
469
470// Import typed models for dual API support
471use crate::models::common::KiteResult;
472use crate::models::orders::{Order, OrderParams, OrderResponse, Trade};
473
474use crate::connect::KiteConnect;
475
476impl KiteConnect {
477 // === LEGACY API METHODS (JSON responses) ===
478
479 /// Place an order
480 #[allow(clippy::too_many_arguments)]
481 pub async fn place_order(
482 &self,
483 variety: &str,
484 exchange: &str,
485 tradingsymbol: &str,
486 transaction_type: &str,
487 quantity: &str,
488 product: Option<&str>,
489 order_type: Option<&str>,
490 price: Option<&str>,
491 validity: Option<&str>,
492 disclosed_quantity: Option<&str>,
493 trigger_price: Option<&str>,
494 squareoff: Option<&str>,
495 stoploss: Option<&str>,
496 trailing_stoploss: Option<&str>,
497 tag: Option<&str>,
498 ) -> Result<JsonValue> {
499 let mut params = HashMap::new();
500 params.insert("variety", variety);
501 params.insert("exchange", exchange);
502 params.insert("tradingsymbol", tradingsymbol);
503 params.insert("transaction_type", transaction_type);
504 params.insert("quantity", quantity);
505
506 if let Some(product) = product {
507 params.insert("product", product);
508 }
509 if let Some(order_type) = order_type {
510 params.insert("order_type", order_type);
511 }
512 if let Some(price) = price {
513 params.insert("price", price);
514 }
515 if let Some(validity) = validity {
516 params.insert("validity", validity);
517 }
518 if let Some(disclosed_quantity) = disclosed_quantity {
519 params.insert("disclosed_quantity", disclosed_quantity);
520 }
521 if let Some(trigger_price) = trigger_price {
522 params.insert("trigger_price", trigger_price);
523 }
524 if let Some(squareoff) = squareoff {
525 params.insert("squareoff", squareoff);
526 }
527 if let Some(stoploss) = stoploss {
528 params.insert("stoploss", stoploss);
529 }
530 if let Some(trailing_stoploss) = trailing_stoploss {
531 params.insert("trailing_stoploss", trailing_stoploss);
532 }
533 if let Some(tag) = tag {
534 params.insert("tag", tag);
535 }
536
537 let resp = self
538 .send_request_with_rate_limiting_and_retry(
539 KiteEndpoint::PlaceOrder,
540 &[variety],
541 None,
542 Some(params),
543 )
544 .await
545 .map_err(|e| anyhow::anyhow!("Place order failed: {:?}", e))?;
546 self.raise_or_return_json(resp).await
547 }
548
549 /// Modify an open order
550 #[allow(clippy::too_many_arguments)]
551 pub async fn modify_order(
552 &self,
553 order_id: &str,
554 variety: &str,
555 quantity: Option<&str>,
556 price: Option<&str>,
557 order_type: Option<&str>,
558 validity: Option<&str>,
559 disclosed_quantity: Option<&str>,
560 trigger_price: Option<&str>,
561 parent_order_id: Option<&str>,
562 ) -> Result<JsonValue> {
563 let mut params = HashMap::new();
564 params.insert("order_id", order_id);
565 params.insert("variety", variety);
566
567 if let Some(quantity) = quantity {
568 params.insert("quantity", quantity);
569 }
570 if let Some(price) = price {
571 params.insert("price", price);
572 }
573 if let Some(order_type) = order_type {
574 params.insert("order_type", order_type);
575 }
576 if let Some(validity) = validity {
577 params.insert("validity", validity);
578 }
579 if let Some(disclosed_quantity) = disclosed_quantity {
580 params.insert("disclosed_quantity", disclosed_quantity);
581 }
582 if let Some(trigger_price) = trigger_price {
583 params.insert("trigger_price", trigger_price);
584 }
585 if let Some(parent_order_id) = parent_order_id {
586 params.insert("parent_order_id", parent_order_id);
587 }
588
589 let resp = self
590 .send_request_with_rate_limiting_and_retry(
591 KiteEndpoint::ModifyOrder,
592 &[variety, order_id],
593 None,
594 Some(params),
595 )
596 .await
597 .map_err(|e| anyhow::anyhow!("Modify order failed: {:?}", e))?;
598 self.raise_or_return_json(resp).await
599 }
600
601 /// Cancel an order
602 pub async fn cancel_order(
603 &self,
604 order_id: &str,
605 variety: &str,
606 parent_order_id: Option<&str>,
607 ) -> Result<JsonValue> {
608 let mut params = HashMap::new();
609 params.insert("order_id", order_id);
610 params.insert("variety", variety);
611 if let Some(parent_order_id) = parent_order_id {
612 params.insert("parent_order_id", parent_order_id);
613 }
614
615 let resp = self
616 .send_request_with_rate_limiting_and_retry(
617 KiteEndpoint::CancelOrder,
618 &[variety, order_id],
619 None,
620 Some(params),
621 )
622 .await
623 .map_err(|e| anyhow::anyhow!("Cancel order failed: {:?}", e))?;
624 self.raise_or_return_json(resp).await
625 }
626
627 /// Exit a BO/CO order
628 pub async fn exit_order(
629 &self,
630 order_id: &str,
631 variety: &str,
632 parent_order_id: Option<&str>,
633 ) -> Result<JsonValue> {
634 self.cancel_order(order_id, variety, parent_order_id).await
635 }
636
637 /// Retrieves a list of all orders for the current trading day
638 ///
639 /// Returns all orders placed by the user for the current trading day,
640 /// including pending, completed, rejected, and cancelled orders.
641 ///
642 /// # Returns
643 ///
644 /// A `Result<JsonValue>` containing orders data with fields like:
645 /// - `order_id` - Unique order identifier
646 /// - `tradingsymbol` - Trading symbol
647 /// - `quantity` - Order quantity
648 /// - `price` - Order price
649 /// - `status` - Order status (OPEN, COMPLETE, CANCELLED, REJECTED)
650 /// - `order_type` - Order type (MARKET, LIMIT, SL, SL-M)
651 /// - `product` - Product type (MIS, CNC, NRML)
652 ///
653 /// # Errors
654 ///
655 /// Returns an error if the API request fails or the user is not authenticated.
656 ///
657 /// # Example
658 ///
659 /// ```rust,no_run
660 /// use kiteconnect_async_wasm::connect::KiteConnect;
661 ///
662 /// # #[tokio::main]
663 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
664 /// let client = KiteConnect::new("api_key", "access_token");
665 ///
666 /// let orders = client.orders().await?;
667 /// println!("Orders: {:?}", orders);
668 ///
669 /// // Check order statuses
670 /// if let Some(data) = orders["data"].as_array() {
671 /// for order in data {
672 /// println!("Order {}: {} - {}",
673 /// order["order_id"],
674 /// order["tradingsymbol"],
675 /// order["status"]);
676 /// }
677 /// }
678 /// # Ok(())
679 /// # }
680 /// ```
681 pub async fn orders(&self) -> Result<JsonValue> {
682 let resp = self
683 .send_request_with_rate_limiting_and_retry(KiteEndpoint::Orders, &[], None, None)
684 .await
685 .map_err(|e| anyhow::anyhow!("Get orders failed: {:?}", e))?;
686 self.raise_or_return_json(resp).await
687 }
688
689 /// Get the list of order history
690 pub async fn order_history(&self, order_id: &str) -> Result<JsonValue> {
691 // REST spec: GET /orders/{order_id}
692 let resp = self
693 .send_request_with_rate_limiting_and_retry(
694 KiteEndpoint::OrderHistory,
695 &[order_id],
696 None,
697 None,
698 )
699 .await
700 .map_err(|e| anyhow::anyhow!("Get order history failed: {:?}", e))?;
701 self.raise_or_return_json(resp).await
702 }
703
704 /// Get all trades
705 pub async fn trades(&self) -> Result<JsonValue> {
706 let resp = self
707 .send_request_with_rate_limiting_and_retry(KiteEndpoint::Trades, &[], None, None)
708 .await
709 .map_err(|e| anyhow::anyhow!("Get trades failed: {:?}", e))?;
710 self.raise_or_return_json(resp).await
711 }
712
713 /// Get all trades for a specific order
714 pub async fn order_trades(&self, order_id: &str) -> Result<JsonValue> {
715 // REST spec: GET /orders/{order_id}/trades
716 let resp = self
717 .send_request_with_rate_limiting_and_retry(
718 KiteEndpoint::OrderTrades,
719 &[order_id, "trades"],
720 None,
721 None,
722 )
723 .await
724 .map_err(|e| anyhow::anyhow!("Get order trades failed: {:?}", e))?;
725 self.raise_or_return_json(resp).await
726 }
727
728 /// Modify an open position product type
729 #[allow(clippy::too_many_arguments)]
730 pub async fn convert_position(
731 &self,
732 exchange: &str,
733 tradingsymbol: &str,
734 transaction_type: &str,
735 position_type: &str,
736 quantity: &str,
737 old_product: &str,
738 new_product: &str,
739 ) -> Result<JsonValue> {
740 let mut params = HashMap::new();
741 params.insert("exchange", exchange);
742 params.insert("tradingsymbol", tradingsymbol);
743 params.insert("transaction_type", transaction_type);
744 params.insert("position_type", position_type);
745 params.insert("quantity", quantity);
746 params.insert("old_product", old_product);
747 params.insert("new_product", new_product);
748
749 let resp = self
750 .send_request_with_rate_limiting_and_retry(
751 KiteEndpoint::ConvertPosition,
752 &[],
753 None,
754 Some(params),
755 )
756 .await
757 .map_err(|e| anyhow::anyhow!("Convert position failed: {:?}", e))?;
758 self.raise_or_return_json(resp).await
759 }
760
761 // === TYPED API METHODS (v1.0.0) ===
762
763 /// Place an order with typed response
764 ///
765 /// Returns strongly typed order response instead of JsonValue.
766 /// This is the preferred method for new applications.
767 ///
768 /// # Arguments
769 ///
770 /// * `variety` - Order variety ("regular", "bo", "co", etc.)
771 /// * `order_params` - Typed order parameters struct
772 ///
773 /// # Returns
774 ///
775 /// A `KiteResult<OrderResponse>` containing the order ID
776 ///
777 /// # Example
778 ///
779 /// ```rust,no_run
780 /// use kiteconnect_async_wasm::connect::KiteConnect;
781 /// use kiteconnect_async_wasm::models::orders::OrderParams;
782 /// use kiteconnect_async_wasm::models::common::{Exchange, Product, OrderType, TransactionType, Validity};
783 ///
784 /// # #[tokio::main]
785 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
786 /// let client = KiteConnect::new("api_key", "access_token");
787 ///
788 /// let params = OrderParams {
789 /// trading_symbol: "INFY".to_string(),
790 /// exchange: Exchange::NSE,
791 /// transaction_type: TransactionType::BUY,
792 /// quantity: 1,
793 /// order_type: OrderType::LIMIT,
794 /// product: Product::CNC,
795 /// price: Some(1500.0),
796 /// validity: Some(Validity::DAY),
797 /// disclosed_quantity: None,
798 /// trigger_price: None,
799 /// squareoff: None,
800 /// stoploss: None,
801 /// trailing_stoploss: None,
802 /// market_protection: None,
803 /// iceberg_legs: None,
804 /// iceberg_quantity: None,
805 /// auction_number: None,
806 /// tag: None,
807 /// };
808 ///
809 /// let order_response = client.place_order_typed("regular", ¶ms).await?;
810 /// println!("Order ID: {}", order_response.order_id);
811 /// # Ok(())
812 /// # }
813 /// ```
814 pub async fn place_order_typed(
815 &self,
816 variety: &str,
817 order_params: &OrderParams,
818 ) -> KiteResult<OrderResponse> {
819 // Create all string conversions upfront to avoid lifetime issues
820 let exchange_str = order_params.exchange.to_string();
821 let transaction_type_str = order_params.transaction_type.to_string();
822 let quantity_str = order_params.quantity.to_string();
823 let product_str = order_params.product.to_string();
824 let order_type_str = order_params.order_type.to_string();
825
826 let price_str = order_params.price.map(|p| p.to_string());
827 let validity_str = order_params.validity.as_ref().map(|v| v.to_string());
828 let disclosed_str = order_params.disclosed_quantity.map(|d| d.to_string());
829 let trigger_str = order_params.trigger_price.map(|t| t.to_string());
830
831 let mut params = HashMap::new();
832 params.insert("variety", variety);
833 params.insert("exchange", exchange_str.as_str());
834 params.insert("tradingsymbol", order_params.trading_symbol.as_str());
835 params.insert("transaction_type", transaction_type_str.as_str());
836 params.insert("quantity", quantity_str.as_str());
837 params.insert("product", product_str.as_str());
838 params.insert("order_type", order_type_str.as_str());
839
840 if let Some(ref price) = price_str {
841 params.insert("price", price.as_str());
842 }
843 if let Some(ref validity) = validity_str {
844 params.insert("validity", validity.as_str());
845 }
846 if let Some(ref disclosed) = disclosed_str {
847 params.insert("disclosed_quantity", disclosed.as_str());
848 }
849 if let Some(ref trigger) = trigger_str {
850 params.insert("trigger_price", trigger.as_str());
851 }
852 if let Some(ref tag) = order_params.tag {
853 params.insert("tag", tag.as_str());
854 }
855
856 let resp = self
857 .send_request_with_rate_limiting_and_retry(
858 KiteEndpoint::PlaceOrder,
859 &[variety],
860 None,
861 Some(params),
862 )
863 .await?;
864 let json_response = self.raise_or_return_json_typed(resp).await?;
865
866 // Extract the data field from response
867 let data = json_response["data"].clone();
868 self.parse_response(data)
869 }
870
871 /// Get all orders with typed response
872 ///
873 /// Returns strongly typed list of orders instead of JsonValue.
874 ///
875 /// # Returns
876 ///
877 /// A `KiteResult<Vec<Order>>` containing typed order information
878 ///
879 /// # Example
880 ///
881 /// ```rust,no_run
882 /// use kiteconnect_async_wasm::connect::KiteConnect;
883 ///
884 /// # #[tokio::main]
885 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
886 /// let client = KiteConnect::new("api_key", "access_token");
887 ///
888 /// let orders = client.orders_typed().await?;
889 /// for order in orders {
890 /// println!("Order {}: {} - {:?}",
891 /// order.order_id,
892 /// order.trading_symbol,
893 /// order.status);
894 /// }
895 /// # Ok(())
896 /// # }
897 /// ```
898 pub async fn orders_typed(&self) -> KiteResult<Vec<Order>> {
899 let resp = self
900 .send_request_with_rate_limiting_and_retry(KiteEndpoint::Orders, &[], None, None)
901 .await?;
902 let json_response = self.raise_or_return_json_typed(resp).await?;
903
904 // Extract the data field from response
905 let data = json_response["data"].clone();
906 self.parse_response(data)
907 }
908
909 /// Get all trades with typed response
910 ///
911 /// Returns strongly typed list of trades instead of JsonValue.
912 ///
913 /// # Returns
914 ///
915 /// A `KiteResult<Vec<Trade>>` containing typed trade information
916 ///
917 /// # Example
918 ///
919 /// ```rust,no_run
920 /// use kiteconnect_async_wasm::connect::KiteConnect;
921 ///
922 /// # #[tokio::main]
923 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
924 /// let client = KiteConnect::new("api_key", "access_token");
925 ///
926 /// let trades = client.trades_typed().await?;
927 /// for trade in trades {
928 /// println!("Trade {}: {} @ {}",
929 /// trade.trade_id,
930 /// trade.quantity,
931 /// trade.average_price);
932 /// }
933 /// # Ok(())
934 /// # }
935 /// ```
936 pub async fn trades_typed(&self) -> KiteResult<Vec<Trade>> {
937 let resp = self
938 .send_request_with_rate_limiting_and_retry(KiteEndpoint::Trades, &[], None, None)
939 .await?;
940 let json_response = self.raise_or_return_json_typed(resp).await?;
941
942 // Extract the data field from response
943 let data = json_response["data"].clone();
944 self.parse_response(data)
945 }
946
947 /// Get trades for specific order with typed response
948 ///
949 /// Returns strongly typed list of trades for a specific order instead of JsonValue.
950 ///
951 /// # Arguments
952 ///
953 /// * `order_id` - The order ID to get trades for
954 ///
955 /// # Returns
956 ///
957 /// A `KiteResult<Vec<Trade>>` containing typed trade information
958 ///
959 /// # Example
960 ///
961 /// ```rust,no_run
962 /// use kiteconnect_async_wasm::connect::KiteConnect;
963 ///
964 /// # #[tokio::main]
965 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
966 /// let client = KiteConnect::new("api_key", "access_token");
967 ///
968 /// let trades = client.order_trades_typed("order_id").await?;
969 /// for trade in trades {
970 /// println!("Trade executed: {} @ {}", trade.quantity, trade.average_price);
971 /// }
972 /// # Ok(())
973 /// # }
974 /// ```
975 pub async fn order_trades_typed(&self, order_id: &str) -> KiteResult<Vec<Trade>> {
976 let resp = self
977 .send_request_with_rate_limiting_and_retry(
978 KiteEndpoint::OrderTrades,
979 &[order_id, "trades"],
980 None,
981 None,
982 )
983 .await?;
984 let json_response = self.raise_or_return_json_typed(resp).await?;
985
986 // Extract the data field from response
987 let data = json_response["data"].clone();
988 self.parse_response(data)
989 }
990}