dukascopy-fx
A Rust library for fetching historical forex (currency exchange) data from Dukascopy's tick data API. This library provides a simple and efficient way to retrieve exchange rates with minute-level precision.
Why This Library?
Free APIs providing historical forex data with tick-level precision are hard to find. Dukascopy's API is free and offers high-precision tick data for a wide range of currency pairs, metals, and other instruments dating back to 2003.
Key Benefits:
- Free Data: No API keys or subscriptions required
- High Precision: Tick-level data with millisecond timestamps
- Wide Coverage: 500+ instruments including forex, metals, indices
- Historical Depth: Data available from 2003 for major pairs
- Automatic Scaling: Correct price divisors for all instrument types
Features
- Fetch Historical Forex Data: Retrieve tick data for specific currency pairs and timestamps
- Automatic Instrument Detection: Correct price scaling for JPY pairs, metals (XAU, XAG), RUB pairs, and standard forex
- Weekend Handling: Automatically fetches last available tick from Friday for weekend timestamps
- Caching: LRU cache reduces redundant API requests
- Market Hours Utilities: Check if market is open, get next market open time
- Error Handling: Detailed error types with context and retry classification
- Type-Safe Currency Pairs: Parse from strings, validate codes, common pairs as constants
- Batch Fetching: Fetch rates over time ranges efficiently
Installation
Add this to your Cargo.toml:
[]
= "0.2.0"
= { = "1", = ["full"] }
= "0.4"
Quick Start
use ;
use ;
async
Supported Instruments
The library automatically detects instrument types and applies correct price scaling:
| Type | Divisor | Decimals | Examples |
|---|---|---|---|
| Standard Forex | 100,000 | 5 | EUR/USD, GBP/USD, AUD/USD, USD/PLN, EUR/CHF |
| JPY Pairs | 1,000 | 3 | USD/JPY, EUR/JPY, GBP/JPY, AUD/JPY |
| Metals | 1,000 | 3 | XAU/USD (Gold), XAG/USD (Silver), XAU/EUR |
| RUB Pairs | 1,000 | 3 | USD/RUB, EUR/RUB |
Common Currency Pairs Available
Major Pairs: EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, USD/CAD, NZD/USD
Cross Pairs: EUR/GBP, EUR/JPY, GBP/JPY, EUR/CHF, EUR/AUD, GBP/CHF
Exotic Pairs: USD/PLN, USD/TRY, USD/ZAR, USD/MXN, EUR/PLN, USD/RUB
Metals: XAU/USD, XAG/USD, XAU/EUR, XAG/EUR
API Reference
CurrencyPair
The CurrencyPair struct represents a forex pair with type-safe construction:
use CurrencyPair;
// Construction methods
let pair = new; // From strings (auto-uppercase)
let pair = try_new?; // With validation
let pair: CurrencyPair = "EUR/USD".parse?; // Parse with slash
let pair: CurrencyPair = "EURUSD".parse?; // Parse without slash
// Predefined pairs for convenience
let pair = eur_usd; // EUR/USD
let pair = gbp_usd; // GBP/USD
let pair = usd_jpy; // USD/JPY
let pair = usd_chf; // USD/CHF
let pair = aud_usd; // AUD/USD
let pair = usd_cad; // USD/CAD
let pair = nzd_usd; // NZD/USD
let pair = xau_usd; // Gold
let pair = xag_usd; // Silver
// Methods
pair.from // Source currency: "EUR"
pair.to // Target currency: "USD"
pair.as_symbol // Combined: "EURUSD"
pair.inverse // Reversed: CurrencyPair { USD, EUR }
format! // Display: "EUR/USD"
DukascopyFxService
The main service for fetching exchange rates:
use ;
use ;
let pair = eur_usd;
let timestamp = Utc.with_ymd_and_hms.unwrap;
// Fetch single rate
let exchange = get_exchange_rate.await?;
// Fetch rates over a time range
let start = Utc.with_ymd_and_hms.unwrap;
let end = Utc.with_ymd_and_hms.unwrap;
let rates = get_exchange_rates_range.await?;
// Get last tick of a specific hour
let exchange = get_last_tick_of_hour.await?;
CurrencyExchange
The response structure containing rate information:
// Methods
exchange.spread // Calculate spread: ask - bid
exchange.spread_pips // Spread in pips (instrument-aware)
Market Hours
The forex market operates 24/5, from Sunday evening to Friday evening UTC:
| Period | Sunday Open (UTC) | Friday Close (UTC) |
|---|---|---|
| Winter (Nov-Mar) | 22:00 | 22:00 |
| Summer (Mar-Nov) | 21:00 | 21:00 |
Market Hours Utilities
use ;
use ;
let timestamp = Utc.with_ymd_and_hms.unwrap; // Saturday
// Simple checks
if is_weekend
if !is_market_open
// Detailed status with reopen time
match get_market_status
Weekend Handling
When you request data for a weekend timestamp, the library automatically returns the last available tick from Friday before market close:
// Request for Saturday - automatically gets Friday's last tick
let saturday = Utc.with_ymd_and_hms.unwrap;
let exchange = get_exchange_rate.await?;
// exchange.timestamp will be Friday around 21:59 UTC
assert_eq!;
Error Handling
The library provides detailed error types with classification methods:
use DukascopyError;
match get_exchange_rate.await
Error Types
| Error | Description | Retryable |
|---|---|---|
HttpError |
Network or HTTP errors | Yes |
RateLimitExceeded |
API rate limit hit | Yes |
Timeout |
Request timed out | Yes |
DataNotFound |
No data for timestamp/pair | No |
InvalidCurrencyCode |
Invalid currency code | No |
InvalidTickData |
Corrupted data | No |
LzmaError |
Decompression failed | No |
Retry Pattern
async
Examples
The library includes several examples in the examples/ directory:
Basic Usage
Demonstrates simple rate fetching for different currency pairs.
Advanced Usage
Demonstrates:
- Fetching multiple pairs
- Different instrument types
- Market hours utilities
- Error handling patterns
- Spread analysis
- Time range fetching
Batch Download
Demonstrates efficient batch downloading of historical data with CSV export.
Caching
The library uses an LRU (Least Recently Used) cache to minimize API requests:
- Cache Size: 100 entries (decompressed hourly data)
- Cache Key: Full URL (includes pair, date, hour)
- Scope: Process-global, shared across all calls
Cache Management
use DukascopyClient;
// Check cache size
let size = cache_len.await?;
println!;
// Clear cache (force fresh data)
clear_cache.await?;
Data Source Details
URL Format
Data is fetched from Dukascopy's public tick data API:
https://datafeed.dukascopy.com/datafeed/{PAIR}/{YEAR}/{MONTH}/{DAY}/{HOUR}h_ticks.bi5
{PAIR}: Combined pair symbol (e.g., "EURUSD"){YEAR}: 4-digit year{MONTH}: 0-indexed month (00-11){DAY}: Day of month (01-31){HOUR}: Hour (0-23)
Binary Format
Files are LZMA compressed. After decompression, each tick is 20 bytes:
| Bytes | Type | Description |
|---|---|---|
| 0-3 | u32 BE | Milliseconds from hour start |
| 4-7 | u32 BE | Ask price (raw, divide by divisor) |
| 8-11 | u32 BE | Bid price (raw, divide by divisor) |
| 12-15 | f32 BE | Ask volume |
| 16-19 | f32 BE | Bid volume |
Data Availability
- Start Date: Varies by instrument (2003 for major pairs)
- End Date: Previous hour (data is hourly)
- Frequency: Every price change (tick-level)
- Coverage: ~500+ instruments
Performance Tips
-
Use Caching: The library caches decompressed data. Avoid clearing cache unnecessarily.
-
Batch Requests: Use
get_exchange_rates_range()for multiple timestamps in the same hour - it only fetches once. -
Avoid Weekends: Check
is_weekend()before making requests if you need current data. -
Handle Errors: Use
is_retryable()to implement retry logic for transient failures. -
Reuse Pairs:
CurrencyPairis cheap to clone. Create once and reuse.
Limitations
- Historical Only: No real-time streaming data
- Hourly Granularity: Data is organized by hour; fetching spans multiple files
- Rate Limits: Dukascopy may rate-limit aggressive requests
- No Guarantees: Data accuracy depends on Dukascopy's service
- Weekend Gaps: No data from Friday close to Sunday open
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run tests (
cargo test) - Run lints (
cargo clippy) - Format code (
cargo fmt) - Commit your changes
- Push to the branch
- Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Disclaimer
This library uses Dukascopy's publicly available tick data API for research and educational purposes. It is not affiliated with, endorsed by, or vetted by Dukascopy Bank SA. Use at your own risk.
Important Notes:
- Data is provided "as-is" without warranty
- Not suitable for production trading without validation
- Respect Dukascopy's terms of service
- Consider rate limiting your requests
Related Projects
- dukascopy-node - Node.js library
- duka - Python downloader
- go-duka - Go downloader