kiteconnect_async_wasm/connect/
gtt.rs

1//! # GTT (Good Till Triggered) Module
2//!
3//! This module provides comprehensive GTT (Good Till Triggered) order management functionality
4//! for the KiteConnect API. GTT orders are advanced conditional orders that remain active until
5//! triggered by specific market conditions or manually cancelled.
6//!
7//! ## Overview
8//!
9//! GTT orders allow you to:
10//! - Set stop-loss orders that trigger when price moves against your position
11//! - Set target orders that trigger when price reaches your profit target
12//! - Create bracket orders (OCO - One Cancels Other) with both stop-loss and target
13//! - Monitor and manage conditional orders without constant market watching
14//!
15//! ## GTT Types
16//!
17//! ### Single Trigger GTT
18//! A single trigger GTT executes one order when a specific price level is reached.
19//!
20//! ### Two-Leg GTT (OCO - One Cancels Other)
21//! A two-leg GTT contains two orders where execution of one automatically cancels the other.
22//! This is commonly used for bracket orders with both stop-loss and target prices.
23//!
24//! ## Basic Usage
25//!
26//! ### Creating a Simple Stop-Loss GTT
27//!
28//! ```rust,no_run
29//! use kiteconnect_async_wasm::connect::KiteConnect;
30//! use serde_json::json;
31//!
32//! # #[tokio::main]
33//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
34//! let client = KiteConnect::new("api_key", "access_token");
35//!
36//! // Place a stop-loss GTT - sell 10 shares of RELIANCE when price drops to ₹2000
37//! let stop_loss_gtt = client.place_gtt(
38//!     "single",           // Single trigger type
39//!     "RELIANCE",         // Trading symbol
40//!     "NSE",              // Exchange
41//!     &[2000.0],          // Trigger price
42//!     2100.0,             // Current market price
43//!     &[json!({
44//!         "transaction_type": "SELL",
45//!         "quantity": 10,
46//!         "order_type": "MARKET",
47//!         "product": "CNC"
48//!     })]
49//! ).await?;
50//!
51//! println!("Stop-loss GTT placed: {:?}", stop_loss_gtt);
52//! # Ok(())
53//! # }
54//! ```
55//!
56//! ### Creating a Target GTT
57//!
58//! ```rust,no_run
59//! use kiteconnect_async_wasm::connect::KiteConnect;
60//! use serde_json::json;
61//!
62//! # #[tokio::main]
63//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
64//! let client = KiteConnect::new("api_key", "access_token");
65//!
66//! // Place a target GTT - sell 10 shares of RELIANCE when price reaches ₹2200
67//! let target_gtt = client.place_gtt(
68//!     "single",           // Single trigger type
69//!     "RELIANCE",         // Trading symbol
70//!     "NSE",              // Exchange
71//!     &[2200.0],          // Target price
72//!     2100.0,             // Current market price
73//!     &[json!({
74//!         "transaction_type": "SELL",
75//!         "quantity": 10,
76//!         "order_type": "LIMIT",
77//!         "product": "CNC",
78//!         "price": 2200.0
79//!     })]
80//! ).await?;
81//!
82//! println!("Target GTT placed: {:?}", target_gtt);
83//! # Ok(())
84//! # }
85//! ```
86//!
87//! ### Creating a Bracket GTT (OCO - One Cancels Other)
88//!
89//! ```rust,no_run
90//! use kiteconnect_async_wasm::connect::KiteConnect;
91//! use serde_json::json;
92//!
93//! # #[tokio::main]
94//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
95//! let client = KiteConnect::new("api_key", "access_token");
96//!
97//! // Place a bracket GTT with both stop-loss and target
98//! let bracket_gtt = client.place_gtt(
99//!     "two-leg",          // Two-leg trigger type (OCO)
100//!     "RELIANCE",         // Trading symbol
101//!     "NSE",              // Exchange
102//!     &[2000.0, 2200.0],  // Stop-loss and target prices
103//!     2100.0,             // Current market price
104//!     &[
105//!         // Stop-loss order (market order)
106//!         json!({
107//!             "transaction_type": "SELL",
108//!             "quantity": 10,
109//!             "order_type": "MARKET",
110//!             "product": "CNC"
111//!         }),
112//!         // Target order (limit order)
113//!         json!({
114//!             "transaction_type": "SELL",
115//!             "quantity": 10,
116//!             "order_type": "LIMIT",
117//!             "product": "CNC",
118//!             "price": 2200.0
119//!         })
120//!     ]
121//! ).await?;
122//!
123//! println!("Bracket GTT placed: {:?}", bracket_gtt);
124//! # Ok(())
125//! # }
126//! ```
127//!
128//! ## Advanced Usage with Builder Patterns
129//!
130//! For more complex GTT orders, you can use the builder patterns provided in the models:
131//!
132//! ```rust,no_run
133//! use kiteconnect_async_wasm::models::gtt::{StopLossGTTBuilder, BracketGTTBuilder};
134//! use kiteconnect_async_wasm::models::common::{Exchange, TransactionType, Product};
135//!
136//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
137//! // Create a stop-loss GTT using the builder pattern
138//! let stop_loss_gtt = StopLossGTTBuilder::new()
139//!     .exchange(Exchange::NSE)
140//!     .trading_symbol("RELIANCE")
141//!     .transaction_type(TransactionType::SELL)
142//!     .product(Product::CNC)
143//!     .quantity(10)
144//!     .trigger_price(2000.0)
145//!     .current_price(2100.0)
146//!     .build_market()?;  // Creates market order on trigger
147//!
148//! // Create a bracket GTT using the builder pattern
149//! let bracket_gtt = BracketGTTBuilder::new()
150//!     .exchange(Exchange::NSE)
151//!     .trading_symbol("RELIANCE")
152//!     .transaction_type(TransactionType::SELL)
153//!     .product(Product::CNC)
154//!     .quantity(10)
155//!     .stop_loss_price(2000.0)
156//!     .target_price(2200.0)
157//!     .current_price(2100.0)
158//!     .build()?;
159//!
160//! println!("GTT parameters ready for placement");
161//! # Ok(())
162//! # }
163//! ```
164//!
165//! ## GTT Management Operations
166//!
167//! ### Retrieving GTT Orders
168//!
169//! ```rust,no_run
170//! use kiteconnect_async_wasm::connect::KiteConnect;
171//!
172//! # #[tokio::main]
173//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
174//! let client = KiteConnect::new("api_key", "access_token");
175//!
176//! // Get all GTT orders
177//! let all_gtts = client.get_gtts(None).await?;
178//! println!("All GTT orders: {:?}", all_gtts);
179//!
180//! // Get specific GTT order details
181//! let gtt_details = client.get_gtts(Some("123456")).await?;
182//! println!("GTT 123456 details: {:?}", gtt_details);
183//! # Ok(())
184//! # }
185//! ```
186//!
187//! ### Modifying GTT Orders
188//!
189//! ```rust,no_run
190//! use kiteconnect_async_wasm::connect::KiteConnect;
191//! use serde_json::json;
192//!
193//! # #[tokio::main]
194//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
195//! let client = KiteConnect::new("api_key", "access_token");
196//!
197//! // Modify an existing GTT order
198//! let modified_gtt = client.modify_gtt(
199//!     "123456",           // GTT ID to modify
200//!     "single",           // Trigger type
201//!     "RELIANCE",         // Trading symbol
202//!     "NSE",              // Exchange
203//!     &[1950.0],          // New trigger price (lowered from 2000)
204//!     2100.0,             // Current market price
205//!     &[json!({
206//!         "transaction_type": "SELL",
207//!         "quantity": 15,  // Increased quantity
208//!         "order_type": "MARKET",
209//!         "product": "CNC"
210//!     })]
211//! ).await?;
212//!
213//! println!("GTT modified: {:?}", modified_gtt);
214//! # Ok(())
215//! # }
216//! ```
217//!
218//! ### Cancelling GTT Orders
219//!
220//! ```rust,no_run
221//! use kiteconnect_async_wasm::connect::KiteConnect;
222//!
223//! # #[tokio::main]
224//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
225//! let client = KiteConnect::new("api_key", "access_token");
226//!
227//! // Cancel a GTT order
228//! let cancellation_result = client.delete_gtt("123456").await?;
229//! println!("GTT cancelled: {:?}", cancellation_result);
230//! # Ok(())
231//! # }
232//! ```
233//!
234//! ## Best Practices
235//!
236//! ### 1. Risk Management
237//!
238//! - **Always set stop-losses**: Use GTT orders to automatically limit losses
239//! - **Position sizing**: Never risk more than 1-2% of your capital per trade
240//! - **Multiple GTTs**: Use bracket orders to capture profits and limit losses simultaneously
241//!
242//! ### 2. Price Setting Guidelines
243//!
244//! - **Stop-loss placement**: Set 3-5% below entry price for swing trades
245//! - **Target placement**: Use risk-reward ratio of at least 1:2 (target = 2x stop-loss distance)
246//! - **Market conditions**: Adjust trigger levels based on volatility and support/resistance levels
247//!
248//! ### 3. Order Types
249//!
250//! - **Market orders**: Use for stop-losses when quick execution is priority
251//! - **Limit orders**: Use for targets to ensure specific price execution
252//! - **Slippage consideration**: Account for potential slippage in volatile markets
253//!
254//! ### 4. Monitoring and Maintenance
255//!
256//! ```rust,no_run
257//! use kiteconnect_async_wasm::connect::KiteConnect;
258//! use std::time::Duration;
259//!
260//! # #[tokio::main]
261//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
262//! let client = KiteConnect::new("api_key", "access_token");
263//!
264//! // Regular GTT monitoring loop
265//! loop {
266//!     let gtts = client.get_gtts(None).await?;
267//!     
268//!     // Process GTT status and take action if needed
269//!     // - Check for triggered GTTs
270//!     // - Update stop-losses based on favorable price movement
271//!     // - Cancel outdated GTTs
272//!     
273//!     println!("GTT status check completed");
274//!     
275//!     // Wait before next check (respect rate limits)
276//!     tokio::time::sleep(Duration::from_secs(300)).await; // Check every 5 minutes
277//! }
278//! # }
279//! ```
280//!
281//! ## Error Handling
282//!
283//! GTT operations can fail for various reasons. Always implement proper error handling:
284//!
285//! ```rust,no_run
286//! use kiteconnect_async_wasm::connect::KiteConnect;
287//! use serde_json::json;
288//!
289//! # #[tokio::main]
290//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
291//! let client = KiteConnect::new("api_key", "access_token");
292//!
293//! match client.place_gtt(
294//!     "single",
295//!     "RELIANCE",
296//!     "NSE",
297//!     &[2000.0],
298//!     2100.0,
299//!     &[json!({
300//!         "transaction_type": "SELL",
301//!         "quantity": 10,
302//!         "order_type": "MARKET",
303//!         "product": "CNC"
304//!     })]
305//! ).await {
306//!     Ok(response) => {
307//!         println!("✅ GTT placed successfully: {:?}", response);
308//!     },
309//!     Err(e) => {
310//!         eprintln!("❌ GTT placement failed: {}", e);
311//!         // Handle specific error cases:
312//!         // - Invalid price levels
313//!         // - Insufficient margin
314//!         // - Invalid instrument
315//!         // - Rate limiting
316//!     }
317//! }
318//! # Ok(())
319//! # }
320//! ```
321//!
322//! ## Platform Compatibility
323//!
324//! This GTT module works seamlessly across platforms:
325//! - **Native (Desktop/Server)**: Full functionality with optimal performance
326//! - **WASM (Browser)**: Complete GTT management in web applications
327//! - **Cross-platform**: Identical API surface and behavior
328//!
329//! ## Rate Limiting
330//!
331//! GTT operations are subject to API rate limits:
332//! - **Standard category**: 10 requests per second
333//! - **Automatic handling**: Built-in rate limiting with retry logic
334//! - **Best practice**: Batch multiple operations when possible
335//!
336//! For high-frequency GTT management, consider implementing local caching and
337//! batching strategies to minimize API calls.
338
339use crate::connect::endpoints::KiteEndpoint;
340use crate::connect::KiteConnect;
341use anyhow::Result;
342use serde_json::Value as JsonValue;
343use std::collections::HashMap;
344
345impl KiteConnect {
346    /// Get all GTT orders or details of a specific GTT
347    ///
348    /// Retrieves all Good Till Triggered (GTT) orders or details of a specific GTT order.
349    /// GTT orders are conditional orders that get executed when certain trigger conditions are met.
350    ///
351    /// # Arguments
352    ///
353    /// * `gtt_id` - Optional GTT ID. If None, returns all GTT orders
354    ///
355    /// # Returns
356    ///
357    /// A `Result<JsonValue>` containing GTT orders data
358    ///
359    /// # Errors
360    ///
361    /// Returns an error if the API request fails or the user is not authenticated.
362    ///
363    /// # Example
364    ///
365    /// ```rust,no_run
366    /// use kiteconnect_async_wasm::connect::KiteConnect;
367    ///
368    /// # #[tokio::main]
369    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
370    /// let client = KiteConnect::new("api_key", "access_token");
371    ///
372    /// // Get all GTT orders
373    /// let all_gtts = client.get_gtts(None).await?;
374    /// println!("All GTTs: {:?}", all_gtts);
375    ///
376    /// // Get specific GTT
377    /// let gtt_details = client.get_gtts(Some("123456")).await?;
378    /// println!("GTT details: {:?}", gtt_details);
379    /// # Ok(())
380    /// # }
381    /// ```
382    pub async fn get_gtts(&self, gtt_id: Option<&str>) -> Result<JsonValue> {
383        let resp = if let Some(id) = gtt_id {
384            self.send_request_with_rate_limiting_and_retry(KiteEndpoint::GTTInfo, &[id], None, None)
385                .await
386                .map_err(|e| anyhow::anyhow!("Failed to get GTT info: {}", e))?
387        } else {
388            self.send_request_with_rate_limiting_and_retry(KiteEndpoint::GTTs, &[], None, None)
389                .await
390                .map_err(|e| anyhow::anyhow!("Failed to get GTTs: {}", e))?
391        };
392
393        self.raise_or_return_json(resp).await
394    }
395
396    /// Place a GTT order
397    ///
398    /// Creates a new Good Till Triggered order that will be executed when
399    /// the specified trigger conditions are met.
400    ///
401    /// # Arguments
402    ///
403    /// * `trigger_type` - Type of trigger ("single" or "two-leg")
404    /// * `tradingsymbol` - Trading symbol of the instrument
405    /// * `exchange` - Exchange where the instrument is traded
406    /// * `trigger_values` - Trigger price values
407    /// * `last_price` - Current market price of the instrument
408    /// * `orders` - List of orders to be placed when triggered
409    ///
410    /// # Returns
411    ///
412    /// A `Result<JsonValue>` containing GTT order creation response
413    ///
414    /// # Errors
415    ///
416    /// Returns an error if the GTT placement fails or parameters are invalid
417    ///
418    /// # Example
419    ///
420    /// ```rust,no_run
421    /// use kiteconnect_async_wasm::connect::KiteConnect;
422    ///
423    /// # #[tokio::main]
424    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
425    /// let client = KiteConnect::new("api_key", "access_token");
426    ///
427    /// let gtt = client.place_gtt(
428    ///     "single",
429    ///     "RELIANCE",
430    ///     "NSE",
431    ///     &[2500.0], // Trigger when price hits 2500
432    ///     2450.0,    // Current price
433    ///     &[serde_json::json!({
434    ///         "transaction_type": "SELL",
435    ///         "quantity": 10,
436    ///         "order_type": "LIMIT",
437    ///         "product": "CNC",
438    ///         "price": 2500.0
439    ///     })]
440    /// ).await?;
441    ///
442    /// println!("GTT placed: {:?}", gtt);
443    /// # Ok(())
444    /// # }
445    /// ```
446    pub async fn place_gtt(
447        &self,
448        trigger_type: &str,
449        tradingsymbol: &str,
450        exchange: &str,
451        trigger_values: &[f64],
452        last_price: f64,
453        orders: &[JsonValue],
454    ) -> Result<JsonValue> {
455        let mut params = HashMap::new();
456        params.insert("type", trigger_type);
457        params.insert("tradingsymbol", tradingsymbol);
458        params.insert("exchange", exchange);
459
460        // Convert trigger values to comma-separated string
461        let trigger_values_str = trigger_values
462            .iter()
463            .map(|v| v.to_string())
464            .collect::<Vec<_>>()
465            .join(",");
466        params.insert("trigger_values", &trigger_values_str);
467
468        let last_price_str = last_price.to_string();
469        params.insert("last_price", &last_price_str);
470
471        // Convert orders to JSON string
472        let orders_json = serde_json::to_string(orders)?;
473        params.insert("orders", &orders_json);
474
475        let resp = self
476            .send_request_with_rate_limiting_and_retry(
477                KiteEndpoint::PlaceGTT,
478                &[],
479                None,
480                Some(params),
481            )
482            .await
483            .map_err(|e| anyhow::anyhow!("Failed to place GTT: {}", e))?;
484
485        self.raise_or_return_json(resp).await
486    }
487
488    /// Modify a GTT order
489    ///
490    /// Updates an existing GTT order with new trigger conditions or orders.
491    ///
492    /// # Arguments
493    ///
494    /// * `gtt_id` - GTT order ID to modify
495    /// * `trigger_type` - Type of trigger ("single" or "two-leg")
496    /// * `tradingsymbol` - Trading symbol of the instrument
497    /// * `exchange` - Exchange where the instrument is traded
498    /// * `trigger_values` - New trigger price values
499    /// * `last_price` - Current market price of the instrument
500    /// * `orders` - Updated list of orders to be placed when triggered
501    ///
502    /// # Returns
503    ///
504    /// A `Result<JsonValue>` containing GTT order modification response
505    ///
506    /// # Example
507    ///
508    /// ```rust,no_run
509    /// use kiteconnect_async_wasm::connect::KiteConnect;
510    ///
511    /// # #[tokio::main]
512    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
513    /// let client = KiteConnect::new("api_key", "access_token");
514    ///
515    /// let result = client.modify_gtt(
516    ///     "123456",  // GTT ID
517    ///     "single",
518    ///     "RELIANCE",
519    ///     "NSE",
520    ///     &[2600.0], // New trigger price
521    ///     2450.0,    // Current price
522    ///     &[serde_json::json!({
523    ///         "transaction_type": "SELL",
524    ///         "quantity": 20,  // Updated quantity
525    ///         "order_type": "LIMIT",
526    ///         "product": "CNC",
527    ///         "price": 2600.0
528    ///     })]
529    /// ).await?;
530    ///
531    /// println!("GTT modified: {:?}", result);
532    /// # Ok(())
533    /// # }
534    /// ```
535    #[allow(clippy::too_many_arguments)]
536    pub async fn modify_gtt(
537        &self,
538        gtt_id: &str,
539        trigger_type: &str,
540        tradingsymbol: &str,
541        exchange: &str,
542        trigger_values: &[f64],
543        last_price: f64,
544        orders: &[JsonValue],
545    ) -> Result<JsonValue> {
546        let mut params = HashMap::new();
547        params.insert("type", trigger_type);
548        params.insert("tradingsymbol", tradingsymbol);
549        params.insert("exchange", exchange);
550
551        // Convert trigger values to comma-separated string
552        let trigger_values_str = trigger_values
553            .iter()
554            .map(|v| v.to_string())
555            .collect::<Vec<_>>()
556            .join(",");
557        params.insert("trigger_values", &trigger_values_str);
558
559        let last_price_str = last_price.to_string();
560        params.insert("last_price", &last_price_str);
561
562        // Convert orders to JSON string
563        let orders_json = serde_json::to_string(orders)?;
564        params.insert("orders", &orders_json);
565
566        let resp = self
567            .send_request_with_rate_limiting_and_retry(
568                KiteEndpoint::ModifyGTT,
569                &[gtt_id],
570                None,
571                Some(params),
572            )
573            .await
574            .map_err(|e| anyhow::anyhow!("Failed to modify GTT: {}", e))?;
575
576        self.raise_or_return_json(resp).await
577    }
578
579    /// Delete a GTT order
580    ///
581    /// Cancels an existing GTT order. Once deleted, the GTT will no longer
582    /// monitor for trigger conditions.
583    ///
584    /// # Arguments
585    ///
586    /// * `gtt_id` - GTT order ID to delete
587    ///
588    /// # Returns
589    ///
590    /// A `Result<JsonValue>` containing deletion confirmation
591    ///
592    /// # Example
593    ///
594    /// ```rust,no_run
595    /// use kiteconnect_async_wasm::connect::KiteConnect;
596    ///
597    /// # #[tokio::main]
598    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
599    /// let client = KiteConnect::new("api_key", "access_token");
600    ///
601    /// let result = client.delete_gtt("123456").await?;
602    /// println!("GTT deleted: {:?}", result);
603    /// # Ok(())
604    /// # }
605    /// ```
606    pub async fn delete_gtt(&self, gtt_id: &str) -> Result<JsonValue> {
607        let resp = self
608            .send_request_with_rate_limiting_and_retry(
609                KiteEndpoint::CancelGTT,
610                &[gtt_id],
611                None,
612                None,
613            )
614            .await
615            .map_err(|e| anyhow::anyhow!("Failed to delete GTT: {}", e))?;
616
617        self.raise_or_return_json(resp).await
618    }
619}