finance_query/adapters/fmp/
calendars.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 EarningsCalendarEntry {
17 pub date: Option<String>,
19 pub symbol: Option<String>,
21 pub eps: Option<f64>,
23 #[serde(rename = "epsEstimated")]
25 pub eps_estimated: Option<f64>,
26 pub time: Option<String>,
28 pub revenue: Option<f64>,
30 #[serde(rename = "revenueEstimated")]
32 pub revenue_estimated: Option<f64>,
33 #[serde(rename = "fiscalDateEnding")]
35 pub fiscal_date_ending: Option<String>,
36 #[serde(rename = "updatedFromDate")]
38 pub updated_from_date: Option<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43#[non_exhaustive]
44pub struct IpoCalendarEntry {
45 pub date: Option<String>,
47 pub company: Option<String>,
49 pub symbol: Option<String>,
51 pub exchange: Option<String>,
53 pub actions: Option<String>,
55 pub shares: Option<f64>,
57 #[serde(rename = "priceRange")]
59 pub price_range: Option<String>,
60 #[serde(rename = "marketCap")]
62 pub market_cap: Option<f64>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67#[non_exhaustive]
68pub struct StockSplitCalendarEntry {
69 pub date: Option<String>,
71 pub symbol: Option<String>,
73 pub numerator: Option<f64>,
75 pub denominator: Option<f64>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81#[non_exhaustive]
82pub struct DividendCalendarEntry {
83 pub date: Option<String>,
85 pub symbol: Option<String>,
87 pub dividend: Option<f64>,
89 #[serde(rename = "adjDividend")]
91 pub adj_dividend: Option<f64>,
92 #[serde(rename = "recordDate")]
94 pub record_date: Option<String>,
95 #[serde(rename = "paymentDate")]
97 pub payment_date: Option<String>,
98 #[serde(rename = "declarationDate")]
100 pub declaration_date: Option<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105#[non_exhaustive]
106pub struct EconomicCalendarEntry {
107 pub event: Option<String>,
109 pub date: Option<String>,
111 pub country: Option<String>,
113 pub actual: Option<f64>,
115 pub previous: Option<f64>,
117 pub change: Option<f64>,
119 #[serde(rename = "changePercentage")]
121 pub change_percentage: Option<f64>,
122 pub estimate: Option<f64>,
124 pub impact: Option<String>,
126}
127
128pub async fn earnings_calendar(from: &str, to: &str) -> Result<Vec<EarningsCalendarEntry>> {
137 let client = build_client()?;
138 client
139 .get("/api/v3/earning_calendar", &[("from", from), ("to", to)])
140 .await
141}
142
143pub async fn ipo_calendar(from: &str, to: &str) -> Result<Vec<IpoCalendarEntry>> {
145 let client = build_client()?;
146 client
147 .get("/api/v3/ipo_calendar", &[("from", from), ("to", to)])
148 .await
149}
150
151pub async fn stock_split_calendar(from: &str, to: &str) -> Result<Vec<StockSplitCalendarEntry>> {
153 let client = build_client()?;
154 client
155 .get(
156 "/api/v3/stock_split_calendar",
157 &[("from", from), ("to", to)],
158 )
159 .await
160}
161
162pub async fn dividend_calendar(from: &str, to: &str) -> Result<Vec<DividendCalendarEntry>> {
164 let client = build_client()?;
165 client
166 .get(
167 "/api/v3/stock_dividend_calendar",
168 &[("from", from), ("to", to)],
169 )
170 .await
171}
172
173pub async fn economic_calendar(from: &str, to: &str) -> Result<Vec<EconomicCalendarEntry>> {
175 let client = build_client()?;
176 client
177 .get("/api/v3/economic_calendar", &[("from", from), ("to", to)])
178 .await
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[tokio::test]
186 async fn test_earnings_calendar_mock() {
187 let mut server = mockito::Server::new_async().await;
188 let _mock = server
189 .mock("GET", "/api/v3/earning_calendar")
190 .match_query(mockito::Matcher::AllOf(vec![
191 mockito::Matcher::UrlEncoded("apikey".into(), "test-key".into()),
192 mockito::Matcher::UrlEncoded("from".into(), "2024-01-01".into()),
193 mockito::Matcher::UrlEncoded("to".into(), "2024-01-31".into()),
194 ]))
195 .with_status(200)
196 .with_body(
197 serde_json::json!([
198 {
199 "date": "2024-01-25",
200 "symbol": "MSFT",
201 "eps": 2.93,
202 "epsEstimated": 2.78,
203 "time": "amc",
204 "revenue": 62020000000.0,
205 "revenueEstimated": 61100000000.0
206 }
207 ])
208 .to_string(),
209 )
210 .create_async()
211 .await;
212
213 let client = super::super::build_test_client(&server.url()).unwrap();
214 let resp: Vec<EarningsCalendarEntry> = client
215 .get(
216 "/api/v3/earning_calendar",
217 &[("from", "2024-01-01"), ("to", "2024-01-31")],
218 )
219 .await
220 .unwrap();
221 assert_eq!(resp.len(), 1);
222 assert_eq!(resp[0].symbol.as_deref(), Some("MSFT"));
223 assert!((resp[0].eps.unwrap() - 2.93).abs() < 0.01);
224 }
225
226 #[tokio::test]
227 async fn test_economic_calendar_mock() {
228 let mut server = mockito::Server::new_async().await;
229 let _mock = server
230 .mock("GET", "/api/v3/economic_calendar")
231 .match_query(mockito::Matcher::AllOf(vec![
232 mockito::Matcher::UrlEncoded("apikey".into(), "test-key".into()),
233 mockito::Matcher::UrlEncoded("from".into(), "2024-01-01".into()),
234 mockito::Matcher::UrlEncoded("to".into(), "2024-01-31".into()),
235 ]))
236 .with_status(200)
237 .with_body(
238 serde_json::json!([
239 {
240 "event": "CPI",
241 "date": "2024-01-11",
242 "country": "US",
243 "actual": 3.4,
244 "previous": 3.1,
245 "estimate": 3.2,
246 "impact": "High"
247 }
248 ])
249 .to_string(),
250 )
251 .create_async()
252 .await;
253
254 let client = super::super::build_test_client(&server.url()).unwrap();
255 let resp: Vec<EconomicCalendarEntry> = client
256 .get(
257 "/api/v3/economic_calendar",
258 &[("from", "2024-01-01"), ("to", "2024-01-31")],
259 )
260 .await
261 .unwrap();
262 assert_eq!(resp.len(), 1);
263 assert_eq!(resp[0].event.as_deref(), Some("CPI"));
264 }
265}