| Crates.io | dukascopy-fx |
| lib.rs | dukascopy-fx |
| version | 0.3.0 |
| created_at | 2025-02-06 21:26:26.48998+00 |
| updated_at | 2026-01-20 09:56:15.95472+00 |
| description | A Rust library for fetching historical forex data with minute precision from Dukascopy. Supports all forex pairs, metals (XAU, XAG), and automatic price scaling. |
| homepage | |
| repository | https://github.com/Kluzko/dukascopy-fx |
| max_upload_size | |
| id | 1546108 |
| size | 190,495 |
A production-ready Rust library for fetching historical forex data from Dukascopy, inspired by Python's yfinance.
Ticker object with history() method"1d", "1w", "1mo", "1y" for easy time ranges[dependencies]
dukascopy-fx = "0.3"
tokio = { version = "1", features = ["full"] }
use dukascopy_fx::{Ticker, datetime};
#[tokio::main]
async fn main() -> dukascopy_fx::Result<()> {
// Create a ticker - yfinance style!
let ticker = Ticker::new("EUR", "USD");
// Get recent rate
let rate = ticker.rate().await?;
println!("EUR/USD: {}", rate.rate);
// Get last week of hourly data
let history = ticker.history("1w").await?;
println!("Got {} records", history.len());
// Get rate at specific time
let rate = ticker.rate_at(datetime!(2024-01-15 14:30 UTC)).await?;
println!("Rate at 2024-01-15: {}", rate.rate);
Ok(())
}
use dukascopy_fx::{Ticker, datetime};
// Create tickers - multiple ways
let eur_usd = Ticker::new("EUR", "USD");
let gold = Ticker::xau_usd(); // Convenience constructor
let ticker: Ticker = "GBP/JPY".parse()?; // Parse from string
let ticker = ticker!("USD/CHF"); // Using macro
// Get historical data with period strings
let daily = ticker.history("1d").await?; // Last 24 hours
let weekly = ticker.history("1w").await?; // Last 7 days
let monthly = ticker.history("1mo").await?; // Last 30 days
let yearly = ticker.history("1y").await?; // Last 365 days
// Custom date range
use dukascopy_fx::time::{days_ago, weeks_ago};
let history = ticker.history_range(weeks_ago(2), days_ago(1)).await?;
// Change sampling interval (default: 1 hour)
use dukascopy_fx::time::Duration;
let ticker_30min = Ticker::new("EUR", "USD").interval(Duration::minutes(30));
let history = ticker_30min.history("1d").await?; // ~48 records instead of ~24
use dukascopy_fx::{Ticker, download};
let tickers = vec![
Ticker::eur_usd(),
Ticker::gbp_usd(),
Ticker::usd_jpy(),
Ticker::xau_usd(),
];
let data = download(&tickers, "1w").await?;
for (ticker, rates) in data {
println!("{}: {} records", ticker.symbol(), rates.len());
}
use dukascopy_fx::{get_rate, get_rates_range, datetime};
use dukascopy_fx::time::Duration;
// Single rate
let rate = get_rate("EUR", "USD", datetime!(2024-01-15 14:30 UTC)).await?;
println!("Rate: {}, Bid: {}, Ask: {}", rate.rate, rate.bid, rate.ask);
// Range of rates
let rates = get_rates_range(
"EUR", "USD",
datetime!(2024-01-15 10:00 UTC),
datetime!(2024-01-15 18:00 UTC),
Duration::hours(1),
).await?;
No need to add chrono to your dependencies - we re-export everything you need:
use dukascopy_fx::time::{DateTime, Utc, Duration, now, days_ago, weeks_ago};
use dukascopy_fx::datetime;
// Convenient time helpers
let current = now();
let yesterday = days_ago(1);
let last_week = weeks_ago(1);
// datetime! macro - multiple formats
let ts = datetime!(2024-01-15 14:30 UTC); // Hour and minute
let ts = datetime!(2024-01-15 14:30:45 UTC); // With seconds
let ts = datetime!(2024-01-15 UTC); // Midnight
use dukascopy_fx::{is_market_open, is_weekend, get_market_status, MarketStatus, datetime};
let saturday = datetime!(2024-01-06 12:00 UTC);
if is_weekend(saturday) {
println!("It's the weekend");
}
if !is_market_open(saturday) {
println!("Market is closed");
}
match get_market_status(saturday) {
MarketStatus::Open => println!("Market is open"),
MarketStatus::Weekend { reopens_at } => {
println!("Closed for weekend, reopens {}", reopens_at);
}
MarketStatus::Holiday { name, reopens_at } => {
println!("Holiday: {:?}, reopens {}", name, reopens_at);
}
}
use dukascopy_fx::{Ticker, DukascopyError, datetime};
let ticker = Ticker::new("EUR", "USD");
match ticker.rate_at(datetime!(2024-01-15 14:30 UTC)).await {
Ok(rate) => println!("Rate: {}", rate.rate),
Err(e) if e.is_retryable() => {
// Network error, rate limit - safe to retry
println!("Retryable error: {}", e);
}
Err(e) if e.is_not_found() => {
// No data for this timestamp (too old, future date, etc.)
println!("No data available: {}", e);
}
Err(e) if e.is_validation_error() => {
// Invalid currency code
println!("Invalid input: {}", e);
}
Err(e) => println!("Error: {}", e),
}
use dukascopy_fx::advanced::{DukascopyClientBuilder, InstrumentConfig};
let client = DukascopyClientBuilder::new()
.cache_size(500) // LRU cache entries (default: 100)
.timeout_secs(60) // HTTP timeout (default: 30)
.with_instrument_config( // Custom instrument config
"BTC", "USD",
InstrumentConfig::new(100.0, 2),
)
.build();
Request data for Saturday? The library automatically returns Friday's last available rate:
let saturday = datetime!(2024-01-06 15:00 UTC);
let rate = ticker.rate_at(saturday).await?;
// rate.timestamp will be Friday ~21:59 UTC, not Saturday
Different instruments have different decimal places - handled automatically:
| Instrument | Example Rate | Decimals |
|---|---|---|
| EUR/USD | 1.08505 | 5 |
| USD/JPY | 154.325 | 3 |
| XAU/USD | 2645.50 | 2-3 |
The library caches decompressed hourly data (LRU, 100 entries default). Requesting multiple timestamps within the same hour only fetches data once:
// These share the same cached hourly data file:
ticker.rate_at(datetime!(2024-01-15 14:05 UTC)).await?;
ticker.rate_at(datetime!(2024-01-15 14:30 UTC)).await?;
ticker.rate_at(datetime!(2024-01-15 14:55 UTC)).await?;
Dukascopy may rate-limit aggressive requests. For bulk downloads, the library handles this gracefully, but consider adding delays for very large requests:
for ticker in tickers {
let data = ticker.history("1mo").await?;
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
| Type | Divisor | Decimals | Examples |
|---|---|---|---|
| Standard Forex | 100,000 | 5 | EUR/USD, GBP/USD, AUD/USD, USD/PLN |
| JPY Pairs | 1,000 | 3 | USD/JPY, EUR/JPY, GBP/JPY |
| Metals | 1,000 | 3 | XAU/USD (Gold), XAG/USD (Silver) |
| RUB Pairs | 1,000 | 3 | USD/RUB, EUR/RUB |
500+ instruments available - if Dukascopy has it, this library can fetch it.
cargo run --example basic # Basic Ticker usage
cargo run --example advanced # Batch downloads, market hours, error handling
cargo run --example batch_download # Download multiple tickers
cargo run --example weekend_handling # Weekend data behavior
history("1w") is simpler than calculating datesTicker is cheap to cloneMIT License - see LICENSE
This library uses Dukascopy's publicly available API for research and educational purposes. Not affiliated with Dukascopy Bank SA. Data provided "as-is" without warranty.