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}