dukascopy_fx/
lib.rs

1//! # dukascopy-fx
2//!
3//! A production-ready Rust library for fetching historical forex exchange rates,
4//! inspired by Python's yfinance library.
5//!
6//! ## Quick Start
7//!
8//! ```no_run
9//! use dukascopy_fx::{Ticker, datetime};
10//!
11//! # async fn example() -> dukascopy_fx::Result<()> {
12//! // Create a ticker and get data - yfinance style!
13//! let ticker = Ticker::new("EUR", "USD");
14//!
15//! // Get recent rate
16//! let rate = ticker.rate().await?;
17//! println!("EUR/USD: {}", rate.rate);
18//!
19//! // Get last week of data
20//! let history = ticker.history("1w").await?;
21//! for r in history {
22//!     println!("{}: {}", r.timestamp, r.rate);
23//! }
24//! # Ok(())
25//! # }
26//! ```
27//!
28//! ## Features
29//!
30//! - **yfinance-style API**: Familiar `Ticker` object with `history()` method
31//! - **Period strings**: Use `"1d"`, `"1w"`, `"1mo"`, `"1y"` for easy time ranges
32//! - **Built-in time utilities**: No need to add chrono separately
33//! - **Type-safe**: Strong types for currency pairs, rates, and errors
34//! - **Automatic handling**: JPY pairs, metals, weekends - all transparent
35//!
36//! ## Usage Patterns
37//!
38//! ### Ticker API (Recommended)
39//! ```no_run
40//! use dukascopy_fx::{Ticker, datetime};
41//!
42//! # async fn example() -> dukascopy_fx::Result<()> {
43//! let eur_usd = Ticker::new("EUR", "USD");
44//! let gold = Ticker::xau_usd();
45//!
46//! // Get rate at specific time
47//! let rate = eur_usd.rate_at(datetime!(2024-01-15 14:30 UTC)).await?;
48//!
49//! // Get historical data with period strings
50//! let weekly = eur_usd.history("1w").await?;
51//! # Ok(())
52//! # }
53//! ```
54//!
55//! ### Batch Download
56//! ```no_run
57//! use dukascopy_fx::{Ticker, download};
58//!
59//! # async fn example() -> dukascopy_fx::Result<()> {
60//! let tickers = vec![
61//!     Ticker::eur_usd(),
62//!     Ticker::gbp_usd(),
63//!     Ticker::usd_jpy(),
64//! ];
65//!
66//! let data = download(&tickers, "1w").await?;
67//! for (ticker, rates) in data {
68//!     println!("{}: {} records", ticker.symbol(), rates.len());
69//! }
70//! # Ok(())
71//! # }
72//! ```
73//!
74//! ### Simple Function API
75//! ```no_run
76//! use dukascopy_fx::{get_rate, datetime};
77//!
78//! # async fn example() -> dukascopy_fx::Result<()> {
79//! let rate = get_rate("EUR", "USD", datetime!(2024-01-15 14:30 UTC)).await?;
80//! println!("Rate: {}", rate.rate);
81//! # Ok(())
82//! # }
83//! ```
84
85// ============================================================================
86// Internal modules
87// ============================================================================
88
89mod api;
90pub(crate) mod core;
91
92// ============================================================================
93// Public modules
94// ============================================================================
95
96pub mod error;
97pub mod macros;
98pub mod market;
99pub mod models;
100pub mod time;
101
102// ============================================================================
103// Core exports
104// ============================================================================
105
106pub use api::{download, download_range, Ticker};
107pub use error::DukascopyError;
108pub use models::{CurrencyExchange, CurrencyPair};
109
110/// Convenient alias for [`DukascopyError`]
111pub type Error = DukascopyError;
112
113/// Convenient Result type for this crate
114pub type Result<T> = std::result::Result<T, Error>;
115
116// ============================================================================
117// Simple Function API
118// ============================================================================
119
120use chrono::{DateTime, Duration, Utc};
121
122/// Fetches the exchange rate for a currency pair at a specific timestamp.
123#[inline]
124pub async fn get_rate(from: &str, to: &str, timestamp: DateTime<Utc>) -> Result<CurrencyExchange> {
125    let pair = CurrencyPair::new(from, to);
126    core::client::DukascopyClient::get_exchange_rate(&pair, timestamp).await
127}
128
129/// Fetches the exchange rate using a [`CurrencyPair`].
130#[inline]
131pub async fn get_rate_for_pair(
132    pair: &CurrencyPair,
133    timestamp: DateTime<Utc>,
134) -> Result<CurrencyExchange> {
135    core::client::DukascopyClient::get_exchange_rate(pair, timestamp).await
136}
137
138/// Fetches exchange rates over a time range.
139#[inline]
140pub async fn get_rates_range(
141    from: &str,
142    to: &str,
143    start: DateTime<Utc>,
144    end: DateTime<Utc>,
145    interval: Duration,
146) -> Result<Vec<CurrencyExchange>> {
147    let pair = CurrencyPair::new(from, to);
148    core::client::DukascopyClient::get_exchange_rates_range(&pair, start, end, interval).await
149}
150
151/// Fetches exchange rates over a time range using a [`CurrencyPair`].
152#[inline]
153pub async fn get_rates_range_for_pair(
154    pair: &CurrencyPair,
155    start: DateTime<Utc>,
156    end: DateTime<Utc>,
157    interval: Duration,
158) -> Result<Vec<CurrencyExchange>> {
159    core::client::DukascopyClient::get_exchange_rates_range(pair, start, end, interval).await
160}
161
162// ============================================================================
163// Market hours API
164// ============================================================================
165
166pub use market::{get_market_status, is_market_open, is_weekend, MarketStatus};
167
168// ============================================================================
169// Advanced API module
170// ============================================================================
171
172/// Advanced API for power users who need fine-grained control.
173///
174/// This module exposes internal types for:
175/// - Custom client configuration (cache size, timeouts)
176/// - Custom instrument configurations (for new/exotic instruments)
177/// - Low-level parsing utilities
178///
179/// # Example
180/// ```
181/// use dukascopy_fx::advanced::{DukascopyClientBuilder, InstrumentConfig};
182///
183/// let client = DukascopyClientBuilder::new()
184///     .cache_size(500)
185///     .timeout_secs(60)
186///     .with_instrument_config("BTC", "USD", InstrumentConfig::new(100.0, 2))
187///     .build();
188/// ```
189pub mod advanced {
190    pub use crate::core::client::{
191        ClientConfig, ConfiguredClient, DukascopyClient, DukascopyClientBuilder,
192        DEFAULT_CACHE_SIZE, DEFAULT_MAX_IDLE_CONNECTIONS, DEFAULT_TIMEOUT_SECS, DUKASCOPY_BASE_URL,
193    };
194    pub use crate::core::instrument::{
195        resolve_instrument_config, CurrencyCategory, DefaultInstrumentProvider,
196        HasInstrumentConfig, InstrumentConfig, InstrumentProvider, OverrideInstrumentProvider,
197        DIVISOR_2_DECIMALS, DIVISOR_3_DECIMALS, DIVISOR_5_DECIMALS,
198    };
199    pub use crate::core::parser::{DukascopyParser, ParsedTick, TICK_SIZE_BYTES};
200    pub use crate::market::last_available_tick_time;
201}
202
203// ============================================================================
204// Prelude module
205// ============================================================================
206
207/// Prelude module - import everything commonly needed.
208///
209/// ```
210/// use dukascopy_fx::prelude::*;
211/// ```
212pub mod prelude {
213    pub use crate::api::{download, download_range, Ticker};
214    pub use crate::error::DukascopyError;
215    pub use crate::market::{is_market_open, is_weekend, MarketStatus};
216    pub use crate::models::{CurrencyExchange, CurrencyPair};
217    pub use crate::time::{
218        date, datetime, days_ago, hours_ago, now, weeks_ago, DateTime, Duration, Utc,
219    };
220    pub use crate::{datetime, ticker};
221    pub use crate::{get_rate, get_rate_for_pair, get_rates_range, get_rates_range_for_pair};
222    pub use crate::{Error, Result};
223}