1use serde::{Deserialize, Serialize};
4
5use crate::adapters::common::encode_path_segment;
6use crate::error::Result;
7
8use super::models::Period;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
16#[non_exhaustive]
17pub struct IncomeStatement {
18 pub date: Option<String>,
20 pub symbol: Option<String>,
22 #[serde(rename = "reportedCurrency")]
24 pub reported_currency: Option<String>,
25 pub cik: Option<String>,
27 #[serde(rename = "fillingDate")]
29 pub filling_date: Option<String>,
30 #[serde(rename = "acceptedDate")]
32 pub accepted_date: Option<String>,
33 #[serde(rename = "calendarYear")]
35 pub calendar_year: Option<String>,
36 pub period: Option<String>,
38 pub revenue: Option<f64>,
40 #[serde(rename = "costOfRevenue")]
42 pub cost_of_revenue: Option<f64>,
43 #[serde(rename = "grossProfit")]
45 pub gross_profit: Option<f64>,
46 #[serde(rename = "grossProfitRatio")]
48 pub gross_profit_ratio: Option<f64>,
49 #[serde(rename = "researchAndDevelopmentExpenses")]
51 pub research_and_development_expenses: Option<f64>,
52 #[serde(rename = "generalAndAdministrativeExpenses")]
54 pub general_and_administrative_expenses: Option<f64>,
55 #[serde(rename = "sellingAndMarketingExpenses")]
57 pub selling_and_marketing_expenses: Option<f64>,
58 #[serde(rename = "sellingGeneralAndAdministrativeExpenses")]
60 pub selling_general_and_administrative_expenses: Option<f64>,
61 #[serde(rename = "otherExpenses")]
63 pub other_expenses: Option<f64>,
64 #[serde(rename = "operatingExpenses")]
66 pub operating_expenses: Option<f64>,
67 #[serde(rename = "costAndExpenses")]
69 pub cost_and_expenses: Option<f64>,
70 #[serde(rename = "interestIncome")]
72 pub interest_income: Option<f64>,
73 #[serde(rename = "interestExpense")]
75 pub interest_expense: Option<f64>,
76 #[serde(rename = "depreciationAndAmortization")]
78 pub depreciation_and_amortization: Option<f64>,
79 pub ebitda: Option<f64>,
81 #[serde(rename = "ebitdaratio")]
83 pub ebitda_ratio: Option<f64>,
84 #[serde(rename = "operatingIncome")]
86 pub operating_income: Option<f64>,
87 #[serde(rename = "operatingIncomeRatio")]
89 pub operating_income_ratio: Option<f64>,
90 #[serde(rename = "totalOtherIncomeExpensesNet")]
92 pub total_other_income_expenses_net: Option<f64>,
93 #[serde(rename = "incomeBeforeTax")]
95 pub income_before_tax: Option<f64>,
96 #[serde(rename = "incomeBeforeTaxRatio")]
98 pub income_before_tax_ratio: Option<f64>,
99 #[serde(rename = "incomeTaxExpense")]
101 pub income_tax_expense: Option<f64>,
102 #[serde(rename = "netIncome")]
104 pub net_income: Option<f64>,
105 #[serde(rename = "netIncomeRatio")]
107 pub net_income_ratio: Option<f64>,
108 pub eps: Option<f64>,
110 #[serde(rename = "epsdiluted")]
112 pub eps_diluted: Option<f64>,
113 #[serde(rename = "weightedAverageShsOut")]
115 pub weighted_average_shs_out: Option<f64>,
116 #[serde(rename = "weightedAverageShsOutDil")]
118 pub weighted_average_shs_out_dil: Option<f64>,
119 pub link: Option<String>,
121 #[serde(rename = "finalLink")]
123 pub final_link: Option<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128#[non_exhaustive]
129pub struct BalanceSheet {
130 pub date: Option<String>,
132 pub symbol: Option<String>,
134 #[serde(rename = "reportedCurrency")]
136 pub reported_currency: Option<String>,
137 pub cik: Option<String>,
139 #[serde(rename = "fillingDate")]
141 pub filling_date: Option<String>,
142 #[serde(rename = "acceptedDate")]
144 pub accepted_date: Option<String>,
145 #[serde(rename = "calendarYear")]
147 pub calendar_year: Option<String>,
148 pub period: Option<String>,
150 #[serde(rename = "cashAndCashEquivalents")]
152 pub cash_and_cash_equivalents: Option<f64>,
153 #[serde(rename = "shortTermInvestments")]
155 pub short_term_investments: Option<f64>,
156 #[serde(rename = "cashAndShortTermInvestments")]
158 pub cash_and_short_term_investments: Option<f64>,
159 #[serde(rename = "netReceivables")]
161 pub net_receivables: Option<f64>,
162 pub inventory: Option<f64>,
164 #[serde(rename = "otherCurrentAssets")]
166 pub other_current_assets: Option<f64>,
167 #[serde(rename = "totalCurrentAssets")]
169 pub total_current_assets: Option<f64>,
170 #[serde(rename = "propertyPlantEquipmentNet")]
172 pub property_plant_equipment_net: Option<f64>,
173 pub goodwill: Option<f64>,
175 #[serde(rename = "intangibleAssets")]
177 pub intangible_assets: Option<f64>,
178 #[serde(rename = "goodwillAndIntangibleAssets")]
180 pub goodwill_and_intangible_assets: Option<f64>,
181 #[serde(rename = "longTermInvestments")]
183 pub long_term_investments: Option<f64>,
184 #[serde(rename = "taxAssets")]
186 pub tax_assets: Option<f64>,
187 #[serde(rename = "otherNonCurrentAssets")]
189 pub other_non_current_assets: Option<f64>,
190 #[serde(rename = "totalNonCurrentAssets")]
192 pub total_non_current_assets: Option<f64>,
193 #[serde(rename = "otherAssets")]
195 pub other_assets: Option<f64>,
196 #[serde(rename = "totalAssets")]
198 pub total_assets: Option<f64>,
199 #[serde(rename = "accountPayables")]
201 pub account_payables: Option<f64>,
202 #[serde(rename = "shortTermDebt")]
204 pub short_term_debt: Option<f64>,
205 #[serde(rename = "taxPayables")]
207 pub tax_payables: Option<f64>,
208 #[serde(rename = "deferredRevenue")]
210 pub deferred_revenue: Option<f64>,
211 #[serde(rename = "otherCurrentLiabilities")]
213 pub other_current_liabilities: Option<f64>,
214 #[serde(rename = "totalCurrentLiabilities")]
216 pub total_current_liabilities: Option<f64>,
217 #[serde(rename = "longTermDebt")]
219 pub long_term_debt: Option<f64>,
220 #[serde(rename = "deferredRevenueNonCurrent")]
222 pub deferred_revenue_non_current: Option<f64>,
223 #[serde(rename = "deferredTaxLiabilitiesNonCurrent")]
225 pub deferred_tax_liabilities_non_current: Option<f64>,
226 #[serde(rename = "otherNonCurrentLiabilities")]
228 pub other_non_current_liabilities: Option<f64>,
229 #[serde(rename = "totalNonCurrentLiabilities")]
231 pub total_non_current_liabilities: Option<f64>,
232 #[serde(rename = "otherLiabilities")]
234 pub other_liabilities: Option<f64>,
235 #[serde(rename = "capitalLeaseObligations")]
237 pub capital_lease_obligations: Option<f64>,
238 #[serde(rename = "totalLiabilities")]
240 pub total_liabilities: Option<f64>,
241 #[serde(rename = "preferredStock")]
243 pub preferred_stock: Option<f64>,
244 #[serde(rename = "commonStock")]
246 pub common_stock: Option<f64>,
247 #[serde(rename = "retainedEarnings")]
249 pub retained_earnings: Option<f64>,
250 #[serde(rename = "accumulatedOtherComprehensiveIncomeLoss")]
252 pub accumulated_other_comprehensive_income_loss: Option<f64>,
253 #[serde(rename = "othertotalStockholdersEquity")]
255 pub other_total_stockholders_equity: Option<f64>,
256 #[serde(rename = "totalStockholdersEquity")]
258 pub total_stockholders_equity: Option<f64>,
259 #[serde(rename = "totalEquity")]
261 pub total_equity: Option<f64>,
262 #[serde(rename = "totalLiabilitiesAndStockholdersEquity")]
264 pub total_liabilities_and_stockholders_equity: Option<f64>,
265 #[serde(rename = "minorityInterest")]
267 pub minority_interest: Option<f64>,
268 #[serde(rename = "totalLiabilitiesAndTotalEquity")]
270 pub total_liabilities_and_total_equity: Option<f64>,
271 #[serde(rename = "totalInvestments")]
273 pub total_investments: Option<f64>,
274 #[serde(rename = "totalDebt")]
276 pub total_debt: Option<f64>,
277 #[serde(rename = "netDebt")]
279 pub net_debt: Option<f64>,
280 pub link: Option<String>,
282 #[serde(rename = "finalLink")]
284 pub final_link: Option<String>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289#[non_exhaustive]
290pub struct CashFlow {
291 pub date: Option<String>,
293 pub symbol: Option<String>,
295 #[serde(rename = "reportedCurrency")]
297 pub reported_currency: Option<String>,
298 pub cik: Option<String>,
300 #[serde(rename = "fillingDate")]
302 pub filling_date: Option<String>,
303 #[serde(rename = "acceptedDate")]
305 pub accepted_date: Option<String>,
306 #[serde(rename = "calendarYear")]
308 pub calendar_year: Option<String>,
309 pub period: Option<String>,
311 #[serde(rename = "netIncome")]
313 pub net_income: Option<f64>,
314 #[serde(rename = "depreciationAndAmortization")]
316 pub depreciation_and_amortization: Option<f64>,
317 #[serde(rename = "deferredIncomeTax")]
319 pub deferred_income_tax: Option<f64>,
320 #[serde(rename = "stockBasedCompensation")]
322 pub stock_based_compensation: Option<f64>,
323 #[serde(rename = "changeInWorkingCapital")]
325 pub change_in_working_capital: Option<f64>,
326 #[serde(rename = "accountsReceivables")]
328 pub accounts_receivables: Option<f64>,
329 pub inventory: Option<f64>,
331 #[serde(rename = "accountsPayables")]
333 pub accounts_payables: Option<f64>,
334 #[serde(rename = "otherWorkingCapital")]
336 pub other_working_capital: Option<f64>,
337 #[serde(rename = "otherNonCashItems")]
339 pub other_non_cash_items: Option<f64>,
340 #[serde(rename = "netCashProvidedByOperatingActivities")]
342 pub net_cash_provided_by_operating_activities: Option<f64>,
343 #[serde(rename = "investmentsInPropertyPlantAndEquipment")]
345 pub investments_in_property_plant_and_equipment: Option<f64>,
346 #[serde(rename = "acquisitionsNet")]
348 pub acquisitions_net: Option<f64>,
349 #[serde(rename = "purchasesOfInvestments")]
351 pub purchases_of_investments: Option<f64>,
352 #[serde(rename = "salesMaturitiesOfInvestments")]
354 pub sales_maturities_of_investments: Option<f64>,
355 #[serde(rename = "otherInvestingActivites")]
357 pub other_investing_activities: Option<f64>,
358 #[serde(rename = "netCashUsedForInvestingActivites")]
360 pub net_cash_used_for_investing_activities: Option<f64>,
361 #[serde(rename = "debtRepayment")]
363 pub debt_repayment: Option<f64>,
364 #[serde(rename = "commonStockIssued")]
366 pub common_stock_issued: Option<f64>,
367 #[serde(rename = "commonStockRepurchased")]
369 pub common_stock_repurchased: Option<f64>,
370 #[serde(rename = "dividendsPaid")]
372 pub dividends_paid: Option<f64>,
373 #[serde(rename = "otherFinancingActivites")]
375 pub other_financing_activities: Option<f64>,
376 #[serde(rename = "netCashUsedProvidedByFinancingActivities")]
378 pub net_cash_used_provided_by_financing_activities: Option<f64>,
379 #[serde(rename = "effectOfForexChangesOnCash")]
381 pub effect_of_forex_changes_on_cash: Option<f64>,
382 #[serde(rename = "netChangeInCash")]
384 pub net_change_in_cash: Option<f64>,
385 #[serde(rename = "cashAtEndOfPeriod")]
387 pub cash_at_end_of_period: Option<f64>,
388 #[serde(rename = "cashAtBeginningOfPeriod")]
390 pub cash_at_beginning_of_period: Option<f64>,
391 #[serde(rename = "operatingCashFlow")]
393 pub operating_cash_flow: Option<f64>,
394 #[serde(rename = "capitalExpenditure")]
396 pub capital_expenditure: Option<f64>,
397 #[serde(rename = "freeCashFlow")]
399 pub free_cash_flow: Option<f64>,
400 pub link: Option<String>,
402 #[serde(rename = "finalLink")]
404 pub final_link: Option<String>,
405}
406
407pub async fn income_statement(
415 symbol: &str,
416 period: Period,
417 limit: Option<u32>,
418) -> Result<Vec<IncomeStatement>> {
419 let client = super::build_client()?;
420 let limit_str = limit.unwrap_or(4).to_string();
421 client
422 .get(
423 &format!("/api/v3/income-statement/{}", encode_path_segment(symbol)),
424 &[("period", period.as_str()), ("limit", &limit_str)],
425 )
426 .await
427}
428
429pub async fn balance_sheet(
431 symbol: &str,
432 period: Period,
433 limit: Option<u32>,
434) -> Result<Vec<BalanceSheet>> {
435 let client = super::build_client()?;
436 let limit_str = limit.unwrap_or(4).to_string();
437 client
438 .get(
439 &format!(
440 "/api/v3/balance-sheet-statement/{}",
441 encode_path_segment(symbol)
442 ),
443 &[("period", period.as_str()), ("limit", &limit_str)],
444 )
445 .await
446}
447
448pub async fn cash_flow(symbol: &str, period: Period, limit: Option<u32>) -> Result<Vec<CashFlow>> {
450 let client = super::build_client()?;
451 let limit_str = limit.unwrap_or(4).to_string();
452 client
453 .get(
454 &format!(
455 "/api/v3/cash-flow-statement/{}",
456 encode_path_segment(symbol)
457 ),
458 &[("period", period.as_str()), ("limit", &limit_str)],
459 )
460 .await
461}
462
463pub async fn income_statement_as_reported(
465 symbol: &str,
466 period: Period,
467 limit: Option<u32>,
468) -> Result<Vec<serde_json::Value>> {
469 let client = super::build_client()?;
470 let limit_str = limit.unwrap_or(4).to_string();
471 client
472 .get(
473 &format!(
474 "/api/v3/income-statement-as-reported/{}",
475 encode_path_segment(symbol)
476 ),
477 &[("period", period.as_str()), ("limit", &limit_str)],
478 )
479 .await
480}
481
482pub async fn balance_sheet_as_reported(
484 symbol: &str,
485 period: Period,
486 limit: Option<u32>,
487) -> Result<Vec<serde_json::Value>> {
488 let client = super::build_client()?;
489 let limit_str = limit.unwrap_or(4).to_string();
490 client
491 .get(
492 &format!(
493 "/api/v3/balance-sheet-statement-as-reported/{}",
494 encode_path_segment(symbol)
495 ),
496 &[("period", period.as_str()), ("limit", &limit_str)],
497 )
498 .await
499}
500
501pub async fn cash_flow_as_reported(
503 symbol: &str,
504 period: Period,
505 limit: Option<u32>,
506) -> Result<Vec<serde_json::Value>> {
507 let client = super::build_client()?;
508 let limit_str = limit.unwrap_or(4).to_string();
509 client
510 .get(
511 &format!(
512 "/api/v3/cash-flow-statement-as-reported/{}",
513 encode_path_segment(symbol)
514 ),
515 &[("period", period.as_str()), ("limit", &limit_str)],
516 )
517 .await
518}
519
520pub async fn full_financial_statement(
522 symbol: &str,
523 period: Period,
524) -> Result<Vec<serde_json::Value>> {
525 let client = super::build_client()?;
526 client
527 .get(
528 &format!(
529 "/api/v3/financial-statement-full-as-reported/{}",
530 encode_path_segment(symbol)
531 ),
532 &[("period", period.as_str())],
533 )
534 .await
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[tokio::test]
542 async fn test_income_statement_mock() {
543 let mut server = mockito::Server::new_async().await;
544 let _mock = server
545 .mock("GET", "/api/v3/income-statement/AAPL")
546 .match_query(mockito::Matcher::AllOf(vec![
547 mockito::Matcher::UrlEncoded("apikey".into(), "test-key".into()),
548 mockito::Matcher::UrlEncoded("period".into(), "quarter".into()),
549 mockito::Matcher::UrlEncoded("limit".into(), "2".into()),
550 ]))
551 .with_status(200)
552 .with_body(
553 serde_json::json!([{
554 "date": "2024-03-30",
555 "symbol": "AAPL",
556 "reportedCurrency": "USD",
557 "calendarYear": "2024",
558 "period": "Q2",
559 "revenue": 90753000000.0,
560 "costOfRevenue": 49141000000.0,
561 "grossProfit": 41612000000.0,
562 "netIncome": 23636000000.0,
563 "eps": 1.53,
564 "epsdiluted": 1.52
565 }])
566 .to_string(),
567 )
568 .create_async()
569 .await;
570
571 let client = super::super::build_test_client(&server.url()).unwrap();
572 let result: Vec<IncomeStatement> = client
573 .get(
574 "/api/v3/income-statement/AAPL",
575 &[("period", "quarter"), ("limit", "2")],
576 )
577 .await
578 .unwrap();
579
580 assert_eq!(result.len(), 1);
581 assert_eq!(result[0].symbol.as_deref(), Some("AAPL"));
582 assert_eq!(result[0].revenue, Some(90753000000.0));
583 assert_eq!(result[0].eps, Some(1.53));
584 }
585
586 #[tokio::test]
587 async fn test_balance_sheet_mock() {
588 let mut server = mockito::Server::new_async().await;
589 let _mock = server
590 .mock("GET", "/api/v3/balance-sheet-statement/AAPL")
591 .match_query(mockito::Matcher::AllOf(vec![
592 mockito::Matcher::UrlEncoded("apikey".into(), "test-key".into()),
593 mockito::Matcher::UrlEncoded("period".into(), "annual".into()),
594 mockito::Matcher::UrlEncoded("limit".into(), "1".into()),
595 ]))
596 .with_status(200)
597 .with_body(
598 serde_json::json!([{
599 "date": "2024-09-28",
600 "symbol": "AAPL",
601 "totalAssets": 364980000000.0,
602 "totalLiabilities": 308030000000.0,
603 "totalStockholdersEquity": 56950000000.0,
604 "cashAndCashEquivalents": 29943000000.0
605 }])
606 .to_string(),
607 )
608 .create_async()
609 .await;
610
611 let client = super::super::build_test_client(&server.url()).unwrap();
612 let result: Vec<BalanceSheet> = client
613 .get(
614 "/api/v3/balance-sheet-statement/AAPL",
615 &[("period", "annual"), ("limit", "1")],
616 )
617 .await
618 .unwrap();
619
620 assert_eq!(result.len(), 1);
621 assert_eq!(result[0].total_assets, Some(364980000000.0));
622 assert_eq!(result[0].total_stockholders_equity, Some(56950000000.0));
623 }
624}