kiteconnect_async_wasm/models/common/enums/
interval.rs

1/*!
2Interval types for historical market data with dual string/integer serialization support.
3
4This module provides the `Interval` enum for specifying time intervals in historical data requests.
5The v1.0.3 enhancement adds dual serde support, accepting both string and integer formats during
6deserialization while always serializing as strings for API consistency.
7
8# Dual Serde Support (v1.0.3)
9
10The `Interval` enum now supports flexible input formats:
11- **Strings**: "minute", "day", "5minute", etc. (human-readable)
12- **Integers**: 1, 0, 3, etc. (compact, legacy support)
13
14It always serializes as strings for API compatibility and human readability.
15
16# Examples
17
18## Basic Usage
19
20```rust
21use kiteconnect_async_wasm::models::common::Interval;
22
23let interval = Interval::Day;
24assert_eq!(interval.to_string(), "day");
25```
26
27## Deserialization Flexibility
28
29```rust
30use kiteconnect_async_wasm::models::common::Interval;
31
32// From strings (human-readable)
33let from_string: Interval = serde_json::from_str("\"day\"").unwrap();
34let from_minute: Interval = serde_json::from_str("\"5minute\"").unwrap();
35
36// From integers (compact)
37let from_int: Interval = serde_json::from_str("0").unwrap();  // Day
38let from_five: Interval = serde_json::from_str("3").unwrap(); // FiveMinute
39
40assert_eq!(from_string, from_int);
41assert_eq!(from_minute, from_five);
42```
43
44## Serialization Consistency
45
46```rust
47use kiteconnect_async_wasm::models::common::Interval;
48
49// Always serializes as strings
50assert_eq!(serde_json::to_string(&Interval::Day).unwrap(), "\"day\"");
51assert_eq!(serde_json::to_string(&Interval::FiveMinute).unwrap(), "\"5minute\"");
52```
53*/
54
55/// Interval types for historical data with dual serialization support
56///
57/// This enum represents time intervals for historical market data requests.
58/// Each variant corresponds to a specific time period for OHLCV candles.
59///
60/// # Supported Intervals
61///
62/// - **Day**: Daily candles (most common for long-term analysis)
63/// - **Minute**: 1-minute candles (highest resolution intraday)
64/// - **Multi-minute**: 3, 5, 10, 15, 30, 60 minute candles (various intraday resolutions)
65///
66/// # Data Availability
67///
68/// - **Daily data**: Several years of history available
69/// - **Intraday data**: Limited to recent periods (varies by broker)
70/// - **Higher frequency**: More data points but faster rate limit consumption
71///
72/// # Rate Limiting Considerations
73///
74/// Higher frequency intervals generate more data points and may consume rate limits faster.
75/// Consider using appropriate intervals for your use case:
76/// - Backtesting: Daily or hourly data
77/// - Real-time monitoring: 1-5 minute data
78/// - Pattern analysis: 15-30 minute data
79///
80/// # Integer Mapping
81///
82/// For legacy compatibility, intervals map to integers:
83/// - Day = 0
84/// - Minute = 1  
85/// - ThreeMinute = 2
86/// - FiveMinute = 3
87/// - TenMinute = 4
88/// - FifteenMinute = 5
89/// - ThirtyMinute = 6
90/// - SixtyMinute = 7
91///
92/// # Examples
93///
94/// ```rust
95/// use kiteconnect_async_wasm::models::common::Interval;
96///
97/// // Different ways to create intervals
98/// let daily = Interval::Day;
99/// let intraday = Interval::FiveMinute;
100///
101/// // Convert to string for display
102/// println!("Daily interval: {}", daily);      // "day"
103/// println!("Intraday: {}", intraday);         // "5minute"
104///
105/// // Check if interval is intraday (less than a day)
106/// assert_ne!(daily.to_string(), "minute");
107/// assert_eq!(intraday.to_string(), "5minute");
108/// ```
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110#[repr(i8)]
111pub enum Interval {
112    /// Daily interval (1 day candles)
113    ///
114    /// Most commonly used for long-term analysis, backtesting, and trend identification.
115    /// Provides good data coverage with minimal API rate limit impact.
116    Day = 0,
117
118    /// 1-minute interval (highest resolution)
119    ///
120    /// Highest available resolution for intraday analysis. Useful for:
121    /// - Scalping strategies
122    /// - Real-time monitoring  
123    /// - High-frequency pattern analysis
124    ///
125    /// Note: Consumes rate limits quickly due to large data volumes.
126    Minute = 1,
127
128    /// 3-minute interval
129    ///
130    /// Good balance between resolution and data volume for short-term strategies.
131    ThreeMinute = 2,
132
133    /// 5-minute interval
134    ///
135    /// Popular choice for intraday trading strategies. Provides good detail
136    /// while maintaining manageable data volumes.
137    FiveMinute = 3,
138
139    /// 10-minute interval
140    ///
141    /// Suitable for medium-term intraday analysis with reduced noise.
142    TenMinute = 4,
143
144    /// 15-minute interval
145    ///
146    /// Common choice for swing trading and pattern recognition.
147    /// Good balance of detail and broader market perspective.
148    FifteenMinute = 5,
149
150    /// 30-minute interval
151    ///
152    /// Used for identifying medium-term trends within trading sessions.
153    /// Filters out short-term noise while maintaining intraday perspective.
154    ThirtyMinute = 6,
155
156    /// 60-minute (1-hour) interval
157    ///
158    /// Bridge between intraday and daily analysis. Useful for:
159    /// - Multi-session analysis
160    /// - Identifying major support/resistance levels
161    /// - Trend confirmation
162    SixtyMinute = 7,
163}
164
165impl std::fmt::Display for Interval {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        match self {
168            Interval::Day => write!(f, "day"),
169            Interval::Minute => write!(f, "minute"),
170            Interval::ThreeMinute => write!(f, "3minute"),
171            Interval::FiveMinute => write!(f, "5minute"),
172            Interval::TenMinute => write!(f, "10minute"),
173            Interval::FifteenMinute => write!(f, "15minute"),
174            Interval::ThirtyMinute => write!(f, "30minute"),
175            Interval::SixtyMinute => write!(f, "60minute"),
176        }
177    }
178}
179
180// Custom serde implementation that supports both string and integer formats
181impl serde::Serialize for Interval {
182    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
183    where
184        S: serde::Serializer,
185    {
186        // Try to serialize as string first (for compatibility)
187        serializer.serialize_str(&self.to_string())
188    }
189}
190
191impl<'de> serde::Deserialize<'de> for Interval {
192    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
193    where
194        D: serde::Deserializer<'de>,
195    {
196        struct IntervalVisitor;
197
198        impl<'de> serde::de::Visitor<'de> for IntervalVisitor {
199            type Value = Interval;
200
201            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
202                formatter.write_str("a string or integer representing an interval")
203            }
204
205            fn visit_str<E>(self, value: &str) -> Result<Interval, E>
206            where
207                E: serde::de::Error,
208            {
209                match value {
210                    "minute" => Ok(Interval::Minute),
211                    "3minute" => Ok(Interval::ThreeMinute),
212                    "5minute" => Ok(Interval::FiveMinute),
213                    "10minute" => Ok(Interval::TenMinute),
214                    "15minute" => Ok(Interval::FifteenMinute),
215                    "30minute" => Ok(Interval::ThirtyMinute),
216                    "60minute" => Ok(Interval::SixtyMinute),
217                    "day" => Ok(Interval::Day),
218                    _ => Err(serde::de::Error::unknown_variant(
219                        value,
220                        &[
221                            "minute", "3minute", "5minute", "10minute", "15minute", "30minute",
222                            "60minute", "day",
223                        ],
224                    )),
225                }
226            }
227
228            fn visit_i8<E>(self, value: i8) -> Result<Interval, E>
229            where
230                E: serde::de::Error,
231            {
232                match value {
233                    0 => Ok(Interval::Day),
234                    1 => Ok(Interval::Minute),
235                    2 => Ok(Interval::ThreeMinute),
236                    3 => Ok(Interval::FiveMinute),
237                    4 => Ok(Interval::TenMinute),
238                    5 => Ok(Interval::FifteenMinute),
239                    6 => Ok(Interval::ThirtyMinute),
240                    7 => Ok(Interval::SixtyMinute),
241                    _ => Err(serde::de::Error::invalid_value(
242                        serde::de::Unexpected::Signed(value as i64),
243                        &"an integer between 0 and 7",
244                    )),
245                }
246            }
247
248            fn visit_u8<E>(self, value: u8) -> Result<Interval, E>
249            where
250                E: serde::de::Error,
251            {
252                self.visit_i8(value as i8)
253            }
254
255            fn visit_i32<E>(self, value: i32) -> Result<Interval, E>
256            where
257                E: serde::de::Error,
258            {
259                if (0..=7).contains(&value) {
260                    self.visit_i8(value as i8)
261                } else {
262                    Err(serde::de::Error::invalid_value(
263                        serde::de::Unexpected::Signed(value as i64),
264                        &"an integer between 0 and 7",
265                    ))
266                }
267            }
268
269            fn visit_u32<E>(self, value: u32) -> Result<Interval, E>
270            where
271                E: serde::de::Error,
272            {
273                if value <= 7 {
274                    self.visit_i8(value as i8)
275                } else {
276                    Err(serde::de::Error::invalid_value(
277                        serde::de::Unexpected::Unsigned(value as u64),
278                        &"an integer between 0 and 7",
279                    ))
280                }
281            }
282
283            fn visit_i64<E>(self, value: i64) -> Result<Interval, E>
284            where
285                E: serde::de::Error,
286            {
287                if (0..=7).contains(&value) {
288                    self.visit_i8(value as i8)
289                } else {
290                    Err(serde::de::Error::invalid_value(
291                        serde::de::Unexpected::Signed(value),
292                        &"an integer between 0 and 7",
293                    ))
294                }
295            }
296
297            fn visit_u64<E>(self, value: u64) -> Result<Interval, E>
298            where
299                E: serde::de::Error,
300            {
301                if value <= 7 {
302                    self.visit_i8(value as i8)
303                } else {
304                    Err(serde::de::Error::invalid_value(
305                        serde::de::Unexpected::Unsigned(value),
306                        &"an integer between 0 and 7",
307                    ))
308                }
309            }
310        }
311
312        deserializer.deserialize_any(IntervalVisitor)
313    }
314}
315
316impl Interval {
317    /// Get the integer representation of the interval
318    pub fn as_i8(self) -> i8 {
319        self as i8
320    }
321
322    /// Create an interval from its integer representation
323    pub fn from_i8(value: i8) -> Option<Self> {
324        match value {
325            0 => Some(Interval::Day),
326            1 => Some(Interval::Minute),
327            2 => Some(Interval::ThreeMinute),
328            3 => Some(Interval::FiveMinute),
329            4 => Some(Interval::TenMinute),
330            5 => Some(Interval::FifteenMinute),
331            6 => Some(Interval::ThirtyMinute),
332            7 => Some(Interval::SixtyMinute),
333            _ => None,
334        }
335    }
336
337    /// All available intervals
338    pub fn all() -> Vec<Self> {
339        vec![
340            Interval::Minute,
341            Interval::ThreeMinute,
342            Interval::FiveMinute,
343            Interval::TenMinute,
344            Interval::FifteenMinute,
345            Interval::ThirtyMinute,
346            Interval::SixtyMinute,
347            Interval::Day,
348        ]
349    }
350}