Skip to main content

finance_query/models/market/
index_trend.rs

1use crate::models::quote::FormattedValue;
2use serde::{Deserialize, Serialize};
3
4/// Index trend data (growth estimates for the index)
5#[derive(Default, Debug, Clone, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7#[non_exhaustive]
8pub struct IndexTrend {
9    /// Maximum age of the data in seconds
10    #[serde(default)]
11    pub max_age: Option<i64>,
12
13    /// Index symbol
14    #[serde(default)]
15    pub symbol: Option<String>,
16
17    /// Growth estimates for different periods
18    #[serde(default)]
19    pub estimates: Option<Vec<TrendEstimate>>,
20
21    /// PE ratio (may be FormattedValue or plain number)
22    #[serde(default)]
23    pub pe_ratio: Option<FormattedValue<f64>>,
24
25    /// PEG ratio (may be FormattedValue or plain number)
26    #[serde(default)]
27    pub peg_ratio: Option<FormattedValue<f64>>,
28}
29
30/// Industry trend data
31#[derive(Default, Debug, Clone, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33#[non_exhaustive]
34pub struct IndustryTrend {
35    /// Maximum age of the data in seconds
36    #[serde(default)]
37    pub max_age: Option<i64>,
38
39    /// Industry symbol
40    #[serde(default)]
41    pub symbol: Option<String>,
42
43    /// Growth estimates for different periods
44    #[serde(default)]
45    pub estimates: Option<Vec<TrendEstimate>>,
46
47    /// PE ratio (may be FormattedValue or plain number)
48    #[serde(default)]
49    pub pe_ratio: Option<FormattedValue<f64>>,
50
51    /// PEG ratio (may be FormattedValue or plain number)
52    #[serde(default)]
53    pub peg_ratio: Option<FormattedValue<f64>>,
54}
55
56/// Sector trend data
57#[derive(Default, Debug, Clone, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59#[non_exhaustive]
60pub struct SectorTrend {
61    /// Maximum age of the data in seconds
62    #[serde(default)]
63    pub max_age: Option<i64>,
64
65    /// Sector symbol
66    #[serde(default)]
67    pub symbol: Option<String>,
68
69    /// Growth estimates for different periods
70    #[serde(default)]
71    pub estimates: Option<Vec<TrendEstimate>>,
72
73    /// PE ratio (may be FormattedValue or plain number)
74    #[serde(default)]
75    pub pe_ratio: Option<FormattedValue<f64>>,
76
77    /// PEG ratio (may be FormattedValue or plain number)
78    #[serde(default)]
79    pub peg_ratio: Option<FormattedValue<f64>>,
80}
81
82/// Growth estimate for a specific period
83#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct TrendEstimate {
86    /// Period (e.g., "0q", "+1q", "0y", "+1y", "LTG")
87    #[serde(default)]
88    pub period: Option<String>,
89
90    /// Growth rate (formatted value)
91    #[serde(default)]
92    pub growth: Option<FormattedValue<f64>>,
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use serde_json;
99
100    #[test]
101    fn test_index_trend_deserialize() {
102        let json = r#"{
103          "maxAge": 1,
104          "symbol": "SP5",
105          "estimates": [
106            {
107              "period": "0q",
108              "growth": {"raw": 0.1516, "fmt": "0.15"}
109            },
110            {
111              "period": "+1q",
112              "growth": {"raw": 0.067600004, "fmt": "0.07"}
113            }
114          ],
115          "peRatio": {},
116          "pegRatio": {}
117        }"#;
118
119        let result: Result<IndexTrend, _> = serde_json::from_str(json);
120        assert!(
121            result.is_ok(),
122            "IndexTrend should deserialize successfully: {:?}",
123            result.err()
124        );
125
126        let trend = result.unwrap();
127        assert_eq!(trend.max_age, Some(1));
128        assert_eq!(trend.symbol, Some("SP5".to_string()));
129        assert!(trend.estimates.is_some());
130        assert_eq!(trend.estimates.as_ref().unwrap().len(), 2);
131
132        // Verify empty peRatio and pegRatio deserialize as None
133        assert_eq!(
134            trend.pe_ratio,
135            Some(FormattedValue {
136                fmt: None,
137                long_fmt: None,
138                raw: None
139            })
140        );
141        assert_eq!(
142            trend.peg_ratio,
143            Some(FormattedValue {
144                fmt: None,
145                long_fmt: None,
146                raw: None
147            })
148        );
149    }
150
151    #[test]
152    fn test_index_trend_empty() {
153        let json = r#"{"maxAge": 1, "symbol": null, "estimates": []}"#;
154        let result: Result<IndexTrend, _> = serde_json::from_str(json);
155        assert!(result.is_ok());
156    }
157}