mod client;
pub mod models;
mod reference;
mod stocks;
mod crypto;
mod forex;
mod futures;
mod indices;
mod options;
mod alternative;
mod benzinga;
mod corporate_events;
mod economy;
mod etf;
pub mod websocket;
use crate::error::{FinanceError, Result};
use crate::rate_limiter::RateLimiter;
use client::PolygonClientBuilder;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
pub use alternative::*;
pub use benzinga::*;
pub use corporate_events::*;
pub use economy::*;
pub use etf::*;
pub use models::*;
pub use reference::*;
pub use crypto::{
CryptoDailyOpenClose, CryptoLastTrade, CryptoLastTradeResponse, CryptoOpenCloseTrade,
crypto_aggregates, crypto_daily_open_close, crypto_ema, crypto_grouped_daily,
crypto_last_trade, crypto_macd, crypto_previous_close, crypto_rsi, crypto_sma, crypto_snapshot,
crypto_snapshots_all, crypto_top_movers, crypto_trades,
};
pub use forex::{
ConversionLast, CurrencyConversion, ForexLastQuote, ForexQuoteResponse, currency_conversion,
forex_aggregates, forex_ema, forex_grouped_daily, forex_last_quote, forex_macd,
forex_previous_close, forex_quotes, forex_rsi, forex_sma, forex_snapshot, forex_snapshots_all,
forex_top_movers,
};
pub use futures::{
FuturesContract, FuturesProduct, FuturesSchedule, FuturesSession, FuturesSnapshot,
FuturesSnapshotResponse, futures_aggregates, futures_contracts, futures_products,
futures_quotes, futures_schedules, futures_snapshot, futures_trades,
};
pub use indices::{
IndexSession, IndexSnapshot, IndexSnapshotResponse, index_aggregates, index_daily_open_close,
index_ema, index_macd, index_previous_close, index_rsi, index_sma, index_snapshot,
};
pub use options::{
AdditionalUnderlying, OptionsContract, OptionsContractResponse,
OptionsContractSnapshotResponse, OptionsGreeks, OptionsSnapshot, OptionsSnapshotDetails,
OptionsSnapshotQuote, OptionsSnapshotTrade, OptionsUnderlyingAsset, options_aggregates,
options_chain_snapshot, options_contract_details, options_contract_snapshot, options_contracts,
options_daily_open_close, options_ema, options_last_trade, options_macd,
options_previous_close, options_quotes, options_rsi, options_sma, options_trades,
};
pub use stocks::*;
const PG_RATE_PER_SEC: f64 = 5.0;
struct PolygonSingleton {
api_key: String,
timeout: Duration,
limiter: Arc<RateLimiter>,
}
static PG_SINGLETON: OnceLock<PolygonSingleton> = OnceLock::new();
pub fn init(api_key: impl Into<String>) -> Result<()> {
init_with_timeout(api_key, Duration::from_secs(30))
}
pub fn init_with_timeout(api_key: impl Into<String>, timeout: Duration) -> Result<()> {
PG_SINGLETON
.set(PolygonSingleton {
api_key: api_key.into(),
timeout,
limiter: Arc::new(RateLimiter::new(PG_RATE_PER_SEC)),
})
.map_err(|_| FinanceError::InvalidParameter {
param: "polygon".to_string(),
reason: "Polygon client already initialized".to_string(),
})
}
pub(crate) fn build_client() -> Result<client::PolygonClient> {
let s = PG_SINGLETON
.get()
.ok_or_else(|| FinanceError::InvalidParameter {
param: "polygon".to_string(),
reason: "Polygon not initialized. Call polygon::init(api_key) first.".to_string(),
})?;
PolygonClientBuilder::new(&s.api_key)
.timeout(s.timeout)
.build_with_limiter(Arc::clone(&s.limiter))
}
#[cfg(test)]
pub(crate) fn build_test_client(base_url: &str) -> Result<client::PolygonClient> {
PolygonClientBuilder::new("test-key")
.timeout(Duration::from_secs(5))
.base_url(base_url)
.build_with_limiter(Arc::new(RateLimiter::new(100.0)))
}
pub(crate) fn api_key() -> Result<String> {
PG_SINGLETON
.get()
.map(|s| s.api_key.clone())
.ok_or_else(|| FinanceError::InvalidParameter {
param: "polygon".to_string(),
reason: "Polygon not initialized. Call polygon::init(api_key) first.".to_string(),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_init_errors_on_double_init() {
let _ = init("test-key-1");
let result = init("test-key-2");
assert!(matches!(result, Err(FinanceError::InvalidParameter { .. })));
}
}