use serde::{Deserialize, Serialize};
use crate::error::Result;
use super::build_client;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EarningsCalendarEntry {
pub date: Option<String>,
pub symbol: Option<String>,
pub eps: Option<f64>,
#[serde(rename = "epsEstimated")]
pub eps_estimated: Option<f64>,
pub time: Option<String>,
pub revenue: Option<f64>,
#[serde(rename = "revenueEstimated")]
pub revenue_estimated: Option<f64>,
#[serde(rename = "fiscalDateEnding")]
pub fiscal_date_ending: Option<String>,
#[serde(rename = "updatedFromDate")]
pub updated_from_date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct IpoCalendarEntry {
pub date: Option<String>,
pub company: Option<String>,
pub symbol: Option<String>,
pub exchange: Option<String>,
pub actions: Option<String>,
pub shares: Option<f64>,
#[serde(rename = "priceRange")]
pub price_range: Option<String>,
#[serde(rename = "marketCap")]
pub market_cap: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct StockSplitCalendarEntry {
pub date: Option<String>,
pub symbol: Option<String>,
pub numerator: Option<f64>,
pub denominator: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DividendCalendarEntry {
pub date: Option<String>,
pub symbol: Option<String>,
pub dividend: Option<f64>,
#[serde(rename = "adjDividend")]
pub adj_dividend: Option<f64>,
#[serde(rename = "recordDate")]
pub record_date: Option<String>,
#[serde(rename = "paymentDate")]
pub payment_date: Option<String>,
#[serde(rename = "declarationDate")]
pub declaration_date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EconomicCalendarEntry {
pub event: Option<String>,
pub date: Option<String>,
pub country: Option<String>,
pub actual: Option<f64>,
pub previous: Option<f64>,
pub change: Option<f64>,
#[serde(rename = "changePercentage")]
pub change_percentage: Option<f64>,
pub estimate: Option<f64>,
pub impact: Option<String>,
}
pub async fn earnings_calendar(from: &str, to: &str) -> Result<Vec<EarningsCalendarEntry>> {
let client = build_client()?;
client
.get("/api/v3/earning_calendar", &[("from", from), ("to", to)])
.await
}
pub async fn ipo_calendar(from: &str, to: &str) -> Result<Vec<IpoCalendarEntry>> {
let client = build_client()?;
client
.get("/api/v3/ipo_calendar", &[("from", from), ("to", to)])
.await
}
pub async fn stock_split_calendar(from: &str, to: &str) -> Result<Vec<StockSplitCalendarEntry>> {
let client = build_client()?;
client
.get(
"/api/v3/stock_split_calendar",
&[("from", from), ("to", to)],
)
.await
}
pub async fn dividend_calendar(from: &str, to: &str) -> Result<Vec<DividendCalendarEntry>> {
let client = build_client()?;
client
.get(
"/api/v3/stock_dividend_calendar",
&[("from", from), ("to", to)],
)
.await
}
pub async fn economic_calendar(from: &str, to: &str) -> Result<Vec<EconomicCalendarEntry>> {
let client = build_client()?;
client
.get("/api/v3/economic_calendar", &[("from", from), ("to", to)])
.await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_earnings_calendar_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/api/v3/earning_calendar")
.match_query(mockito::Matcher::AllOf(vec![
mockito::Matcher::UrlEncoded("apikey".into(), "test-key".into()),
mockito::Matcher::UrlEncoded("from".into(), "2024-01-01".into()),
mockito::Matcher::UrlEncoded("to".into(), "2024-01-31".into()),
]))
.with_status(200)
.with_body(
serde_json::json!([
{
"date": "2024-01-25",
"symbol": "MSFT",
"eps": 2.93,
"epsEstimated": 2.78,
"time": "amc",
"revenue": 62020000000.0,
"revenueEstimated": 61100000000.0
}
])
.to_string(),
)
.create_async()
.await;
let client = super::super::build_test_client(&server.url()).unwrap();
let resp: Vec<EarningsCalendarEntry> = client
.get(
"/api/v3/earning_calendar",
&[("from", "2024-01-01"), ("to", "2024-01-31")],
)
.await
.unwrap();
assert_eq!(resp.len(), 1);
assert_eq!(resp[0].symbol.as_deref(), Some("MSFT"));
assert!((resp[0].eps.unwrap() - 2.93).abs() < 0.01);
}
#[tokio::test]
async fn test_economic_calendar_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/api/v3/economic_calendar")
.match_query(mockito::Matcher::AllOf(vec![
mockito::Matcher::UrlEncoded("apikey".into(), "test-key".into()),
mockito::Matcher::UrlEncoded("from".into(), "2024-01-01".into()),
mockito::Matcher::UrlEncoded("to".into(), "2024-01-31".into()),
]))
.with_status(200)
.with_body(
serde_json::json!([
{
"event": "CPI",
"date": "2024-01-11",
"country": "US",
"actual": 3.4,
"previous": 3.1,
"estimate": 3.2,
"impact": "High"
}
])
.to_string(),
)
.create_async()
.await;
let client = super::super::build_test_client(&server.url()).unwrap();
let resp: Vec<EconomicCalendarEntry> = client
.get(
"/api/v3/economic_calendar",
&[("from", "2024-01-01"), ("to", "2024-01-31")],
)
.await
.unwrap();
assert_eq!(resp.len(), 1);
assert_eq!(resp[0].event.as_deref(), Some("CPI"));
}
}