finance_query/adapters/polygon/
mod.rs1mod client;
30pub mod models;
31
32mod reference;
33mod stocks;
34
35mod crypto;
36mod forex;
37mod futures;
38mod indices;
39mod options;
40
41mod alternative;
42mod benzinga;
43mod corporate_events;
44mod economy;
45mod etf;
46pub mod websocket;
47
48use crate::error::{FinanceError, Result};
49use crate::rate_limiter::RateLimiter;
50use client::PolygonClientBuilder;
51use std::sync::{Arc, OnceLock};
52use std::time::Duration;
53
54pub use alternative::*;
55pub use benzinga::*;
56pub use corporate_events::*;
57pub use economy::*;
58pub use etf::*;
59pub use models::*;
60pub use reference::*;
61
62pub use crypto::{
65 CryptoDailyOpenClose, CryptoLastTrade, CryptoLastTradeResponse, CryptoOpenCloseTrade,
66 crypto_aggregates, crypto_daily_open_close, crypto_ema, crypto_grouped_daily,
67 crypto_last_trade, crypto_macd, crypto_previous_close, crypto_rsi, crypto_sma, crypto_snapshot,
68 crypto_snapshots_all, crypto_top_movers, crypto_trades,
69};
70pub use forex::{
71 ConversionLast, CurrencyConversion, ForexLastQuote, ForexQuoteResponse, currency_conversion,
72 forex_aggregates, forex_ema, forex_grouped_daily, forex_last_quote, forex_macd,
73 forex_previous_close, forex_quotes, forex_rsi, forex_sma, forex_snapshot, forex_snapshots_all,
74 forex_top_movers,
75};
76pub use futures::{
77 FuturesContract, FuturesProduct, FuturesSchedule, FuturesSession, FuturesSnapshot,
78 FuturesSnapshotResponse, futures_aggregates, futures_contracts, futures_products,
79 futures_quotes, futures_schedules, futures_snapshot, futures_trades,
80};
81pub use indices::{
82 IndexSession, IndexSnapshot, IndexSnapshotResponse, index_aggregates, index_daily_open_close,
83 index_ema, index_macd, index_previous_close, index_rsi, index_sma, index_snapshot,
84};
85pub use options::{
86 AdditionalUnderlying, OptionsContract, OptionsContractResponse,
87 OptionsContractSnapshotResponse, OptionsGreeks, OptionsSnapshot, OptionsSnapshotDetails,
88 OptionsSnapshotQuote, OptionsSnapshotTrade, OptionsUnderlyingAsset, options_aggregates,
89 options_chain_snapshot, options_contract_details, options_contract_snapshot, options_contracts,
90 options_daily_open_close, options_ema, options_last_trade, options_macd,
91 options_previous_close, options_quotes, options_rsi, options_sma, options_trades,
92};
93pub use stocks::*;
94
95const PG_RATE_PER_SEC: f64 = 5.0;
97
98struct PolygonSingleton {
99 api_key: String,
100 timeout: Duration,
101 limiter: Arc<RateLimiter>,
102}
103
104static PG_SINGLETON: OnceLock<PolygonSingleton> = OnceLock::new();
105
106pub fn init(api_key: impl Into<String>) -> Result<()> {
114 init_with_timeout(api_key, Duration::from_secs(30))
115}
116
117pub fn init_with_timeout(api_key: impl Into<String>, timeout: Duration) -> Result<()> {
119 PG_SINGLETON
120 .set(PolygonSingleton {
121 api_key: api_key.into(),
122 timeout,
123 limiter: Arc::new(RateLimiter::new(PG_RATE_PER_SEC)),
124 })
125 .map_err(|_| FinanceError::InvalidParameter {
126 param: "polygon".to_string(),
127 reason: "Polygon client already initialized".to_string(),
128 })
129}
130
131pub(crate) fn build_client() -> Result<client::PolygonClient> {
133 let s = PG_SINGLETON
134 .get()
135 .ok_or_else(|| FinanceError::InvalidParameter {
136 param: "polygon".to_string(),
137 reason: "Polygon not initialized. Call polygon::init(api_key) first.".to_string(),
138 })?;
139 PolygonClientBuilder::new(&s.api_key)
140 .timeout(s.timeout)
141 .build_with_limiter(Arc::clone(&s.limiter))
142}
143
144#[cfg(test)]
146pub(crate) fn build_test_client(base_url: &str) -> Result<client::PolygonClient> {
147 PolygonClientBuilder::new("test-key")
148 .timeout(Duration::from_secs(5))
149 .base_url(base_url)
150 .build_with_limiter(Arc::new(RateLimiter::new(100.0)))
151}
152
153pub(crate) fn api_key() -> Result<String> {
155 PG_SINGLETON
156 .get()
157 .map(|s| s.api_key.clone())
158 .ok_or_else(|| FinanceError::InvalidParameter {
159 param: "polygon".to_string(),
160 reason: "Polygon not initialized. Call polygon::init(api_key) first.".to_string(),
161 })
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_init_errors_on_double_init() {
170 let _ = init("test-key-1");
171 let result = init("test-key-2");
172 assert!(matches!(result, Err(FinanceError::InvalidParameter { .. })));
173 }
174}