finance_query/adapters/fmp/
market_performance.rs1use serde::{Deserialize, Serialize};
4
5use crate::error::Result;
6
7use super::build_client;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
15#[non_exhaustive]
16pub struct SectorPe {
17 pub date: Option<String>,
19 pub sector: Option<String>,
21 pub exchange: Option<String>,
23 pub pe: Option<f64>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29#[non_exhaustive]
30pub struct IndustryPe {
31 pub date: Option<String>,
33 pub industry: Option<String>,
35 pub exchange: Option<String>,
37 pub pe: Option<f64>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43#[non_exhaustive]
44pub struct SectorPerformance {
45 pub sector: Option<String>,
47 #[serde(rename = "changesPercentage")]
49 pub changes_percentage: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54#[non_exhaustive]
55pub struct HistoricalSectorPerformance {
56 pub date: Option<String>,
58 #[serde(rename = "utilitiesChangesPercentage")]
60 pub utilities_changes_percentage: Option<f64>,
61 #[serde(rename = "basicMaterialsChangesPercentage")]
63 pub basic_materials_changes_percentage: Option<f64>,
64 #[serde(rename = "communicationServicesChangesPercentage")]
66 pub communication_services_changes_percentage: Option<f64>,
67 #[serde(rename = "consumerCyclicalChangesPercentage")]
69 pub consumer_cyclical_changes_percentage: Option<f64>,
70 #[serde(rename = "consumerDefensiveChangesPercentage")]
72 pub consumer_defensive_changes_percentage: Option<f64>,
73 #[serde(rename = "energyChangesPercentage")]
75 pub energy_changes_percentage: Option<f64>,
76 #[serde(rename = "financialServicesChangesPercentage")]
78 pub financial_services_changes_percentage: Option<f64>,
79 #[serde(rename = "healthcareChangesPercentage")]
81 pub healthcare_changes_percentage: Option<f64>,
82 #[serde(rename = "industrialsChangesPercentage")]
84 pub industrials_changes_percentage: Option<f64>,
85 #[serde(rename = "realEstateChangesPercentage")]
87 pub real_estate_changes_percentage: Option<f64>,
88 #[serde(rename = "technologyChangesPercentage")]
90 pub technology_changes_percentage: Option<f64>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95#[non_exhaustive]
96pub struct MarketMover {
97 pub symbol: Option<String>,
99 pub name: Option<String>,
101 pub change: Option<f64>,
103 pub price: Option<f64>,
105 #[serde(rename = "changesPercentage")]
107 pub changes_percentage: Option<f64>,
108}
109
110pub async fn sectors_pe() -> Result<Vec<SectorPe>> {
116 let client = build_client()?;
117 client.get("/api/v4/sector_price_earning_ratio", &[]).await
118}
119
120pub async fn industries_pe() -> Result<Vec<IndustryPe>> {
122 let client = build_client()?;
123 client
124 .get("/api/v4/industry_price_earning_ratio", &[])
125 .await
126}
127
128pub async fn sector_performance() -> Result<Vec<SectorPerformance>> {
130 let client = build_client()?;
131 client.get("/api/v3/sector-performance", &[]).await
132}
133
134pub async fn historical_sector_performance(limit: u32) -> Result<Vec<HistoricalSectorPerformance>> {
136 let client = build_client()?;
137 let limit_str = limit.to_string();
138 client
139 .get(
140 "/api/v3/historical-sectors-performance",
141 &[("limit", &*limit_str)],
142 )
143 .await
144}
145
146pub async fn stock_market_gainers() -> Result<Vec<MarketMover>> {
148 let client = build_client()?;
149 client.get("/api/v3/stock_market/gainers", &[]).await
150}
151
152pub async fn stock_market_losers() -> Result<Vec<MarketMover>> {
154 let client = build_client()?;
155 client.get("/api/v3/stock_market/losers", &[]).await
156}
157
158pub async fn stock_market_most_active() -> Result<Vec<MarketMover>> {
160 let client = build_client()?;
161 client.get("/api/v3/stock_market/actives", &[]).await
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[tokio::test]
169 async fn test_sector_performance_mock() {
170 let mut server = mockito::Server::new_async().await;
171 let _mock = server
172 .mock("GET", "/api/v3/sector-performance")
173 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
174 "apikey".into(),
175 "test-key".into(),
176 )]))
177 .with_status(200)
178 .with_body(
179 serde_json::json!([
180 {
181 "sector": "Technology",
182 "changesPercentage": "1.25%"
183 },
184 {
185 "sector": "Healthcare",
186 "changesPercentage": "-0.45%"
187 }
188 ])
189 .to_string(),
190 )
191 .create_async()
192 .await;
193
194 let client = super::super::build_test_client(&server.url()).unwrap();
195 let resp: Vec<SectorPerformance> =
196 client.get("/api/v3/sector-performance", &[]).await.unwrap();
197 assert_eq!(resp.len(), 2);
198 assert_eq!(resp[0].sector.as_deref(), Some("Technology"));
199 assert_eq!(resp[0].changes_percentage.as_deref(), Some("1.25%"));
200 }
201
202 #[tokio::test]
203 async fn test_stock_market_gainers_mock() {
204 let mut server = mockito::Server::new_async().await;
205 let _mock = server
206 .mock("GET", "/api/v3/stock_market/gainers")
207 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
208 "apikey".into(),
209 "test-key".into(),
210 )]))
211 .with_status(200)
212 .with_body(
213 serde_json::json!([
214 {
215 "symbol": "XYZ",
216 "name": "XYZ Corp",
217 "change": 5.20,
218 "price": 42.50,
219 "changesPercentage": 13.93
220 }
221 ])
222 .to_string(),
223 )
224 .create_async()
225 .await;
226
227 let client = super::super::build_test_client(&server.url()).unwrap();
228 let resp: Vec<MarketMover> = client
229 .get("/api/v3/stock_market/gainers", &[])
230 .await
231 .unwrap();
232 assert_eq!(resp.len(), 1);
233 assert_eq!(resp[0].symbol.as_deref(), Some("XYZ"));
234 assert!((resp[0].change.unwrap() - 5.20).abs() < 0.01);
235 }
236}