Skip to main content

finance_query/adapters/alphavantage/
mod.rs

1//! Alpha Vantage API client for financial data.
2//!
3//! Requires the **`alphavantage`** feature flag and a free API key from
4//! <https://www.alphavantage.co/support/#api-key>.
5//!
6//! Call [`init`] once at startup before using any query functions.
7//!
8//! # Quick Start
9//!
10//! ```no_run
11//! use finance_query::{Providers, Provider, Capability, Interval, TimeRange};
12//! use finance_query::format::Raw;
13//!
14//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
15//! // Route capabilities to Alpha Vantage with Yahoo as fallback
16//! let providers = Providers::builder()
17//!     .route(Capability::QUOTE, [Provider::AlphaVantage, Provider::Yahoo])
18//!     .route(Capability::CHART, [Provider::AlphaVantage, Provider::Yahoo])
19//!     .route(Capability::ECONOMIC, [Provider::AlphaVantage])
20//!     .build().await?;
21//!
22//! let ticker = providers.ticker("AAPL").build().await?;
23//! let quote = ticker.quote::<Raw>().await?;
24//! let chart = ticker.chart(Interval::OneDay, TimeRange::OneMonth).await?;
25//!
26//! let gdp = providers.economic("REAL_GDP").series().await?;
27//! # Ok(())
28//! # }
29//! ```
30
31mod client;
32pub(crate) mod models;
33
34pub(crate) mod commodities;
35pub(crate) mod corporate;
36pub(crate) mod crypto;
37pub(crate) mod discovery;
38pub(crate) mod economic;
39pub(crate) mod forex;
40pub(crate) mod fundamentals;
41pub(crate) mod options;
42pub(crate) mod quote;
43
44use crate::adapters::singleton::{provider_build_client, provider_singleton_state};
45use crate::error::Result;
46use std::time::Duration;
47
48// Re-export public query functions (used by alphavantage provider)
49pub use commodities::*;
50pub use corporate::*;
51pub use crypto::*;
52pub use economic::*;
53pub use forex::*;
54pub use fundamentals::*;
55pub use options::*;
56pub use quote::*;
57
58/// Alpha Vantage free-tier rate limit: 25 requests/day.
59/// Premium: 75 req/min = 1.25 req/sec.
60/// Default to a conservative 1.0 req/sec.
61const AV_RATE_PER_SEC: f64 = 1.0;
62
63provider_singleton_state!(
64    name = AlphaVantageSingleton,
65    static_name = AV_SINGLETON,
66    rate_const = AV_RATE_PER_SEC,
67    provider_key = "alphavantage",
68    already_init_reason = "Alpha Vantage client already initialized",
69);
70
71provider_build_client!(
72    name = AlphaVantageSingleton,
73    static_name = AV_SINGLETON,
74    rate_const = AV_RATE_PER_SEC,
75    provider_key = "alphavantage",
76    env_var = "ALPHAVANTAGE_API_KEY",
77    env_missing_reason = "ALPHAVANTAGE_API_KEY not set. Call alphavantage::init(key) or set ALPHAVANTAGE_API_KEY env var.",
78    builder = client::AlphaVantageClientBuilder,
79    client_ty = client::AlphaVantageClient,
80);
81
82/// Initialize the global Alpha Vantage client with an API key.
83///
84/// Must be called once before using any query functions. Subsequent calls return an error.
85///
86/// # Arguments
87///
88/// * `api_key` - Your Alpha Vantage API key (free at <https://www.alphavantage.co/support/#api-key>)
89///
90/// # Errors
91///
92/// Returns [`crate::FinanceError::InvalidParameter`] if already initialized.
93#[allow(dead_code)] // public init path for direct adapter use; ProviderSet initialises via env var
94pub fn init(api_key: impl Into<String>) -> Result<()> {
95    init_with_timeout(api_key, Duration::from_secs(30))
96}
97
98/// Initialize the Alpha Vantage client with a custom timeout.
99#[allow(dead_code)] // public init path for direct adapter use; ProviderSet initialises via env var
100pub fn init_with_timeout(api_key: impl Into<String>, timeout: Duration) -> Result<()> {
101    set_singleton(api_key, timeout)
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use crate::error::FinanceError;
108
109    #[test]
110    fn test_init_errors_on_double_init() {
111        let _ = init("test-key-1");
112        let result = init("test-key-2");
113        assert!(matches!(result, Err(FinanceError::InvalidParameter { .. })));
114    }
115}