use serde::{Deserialize, Serialize};
use crate::adapters::common::encode_path_segment;
use crate::error::Result;
use super::super::build_client;
use super::super::models::PaginatedResponse;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FinancialResult {
pub tickers: Option<Vec<String>>,
pub company_name: Option<String>,
pub cik: Option<String>,
pub filing_date: Option<String>,
pub period_of_report_date: Option<String>,
pub fiscal_period: Option<String>,
pub fiscal_year: Option<String>,
pub source_filing_url: Option<String>,
pub financials: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ShortInterest {
pub settlement_date: Option<String>,
pub short_interest: Option<f64>,
pub avg_daily_volume: Option<f64>,
pub days_to_cover: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ShortVolume {
pub date: Option<String>,
pub short_volume: Option<f64>,
pub short_exempt_volume: Option<f64>,
pub total_volume: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FloatData {
pub ticker: Option<String>,
pub float_shares: Option<f64>,
pub outstanding_shares: Option<f64>,
pub date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FinancialRatios {
pub ticker: Option<String>,
pub period: Option<String>,
pub fiscal_year: Option<String>,
#[serde(flatten)]
pub ratios: std::collections::HashMap<String, serde_json::Value>,
}
pub async fn stock_financials(
ticker: &str,
params: &[(&str, &str)],
) -> Result<PaginatedResponse<FinancialResult>> {
let client = build_client()?;
let path = "/vX/reference/financials".to_string();
let mut query: Vec<(&str, &str)> = vec![("ticker", ticker)];
query.extend_from_slice(params);
client.get(&path, &query).await
}
pub async fn stock_short_interest(
ticker: &str,
params: &[(&str, &str)],
) -> Result<PaginatedResponse<ShortInterest>> {
let client = build_client()?;
let path = format!(
"/v3/reference/short-interest/{}",
encode_path_segment(ticker)
);
client.get(&path, params).await
}
pub async fn stock_short_volume(
ticker: &str,
params: &[(&str, &str)],
) -> Result<PaginatedResponse<ShortVolume>> {
let client = build_client()?;
let path = format!("/v3/reference/short-volume/{}", encode_path_segment(ticker));
client.get(&path, params).await
}
pub async fn stock_float(ticker: &str) -> Result<PaginatedResponse<FloatData>> {
let client = build_client()?;
let path = format!("/v3/reference/float/{}", encode_path_segment(ticker));
client.get(&path, &[]).await
}
pub async fn stock_ratios(
ticker: &str,
params: &[(&str, &str)],
) -> Result<PaginatedResponse<FinancialRatios>> {
let client = build_client()?;
let path = "/vX/reference/financials/ratios".to_string();
let mut query: Vec<(&str, &str)> = vec![("ticker", ticker)];
query.extend_from_slice(params);
client.get(&path, &query).await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_stock_financials_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/vX/reference/financials")
.match_query(mockito::Matcher::AllOf(vec![
mockito::Matcher::UrlEncoded("apiKey".into(), "test-key".into()),
mockito::Matcher::UrlEncoded("ticker".into(), "AAPL".into()),
]))
.with_status(200)
.with_body(
serde_json::json!({
"status": "OK",
"request_id": "abc",
"results": [{
"tickers": ["AAPL"],
"company_name": "Apple Inc",
"fiscal_period": "Q1",
"fiscal_year": "2024",
"filing_date": "2024-02-01"
}]
})
.to_string(),
)
.create_async()
.await;
let client = super::super::super::build_test_client(&server.url()).unwrap();
let resp: PaginatedResponse<FinancialResult> = client
.get("/vX/reference/financials", &[("ticker", "AAPL")])
.await
.unwrap();
let results = resp.results.unwrap();
assert_eq!(results[0].company_name.as_deref(), Some("Apple Inc"));
}
}