Dukascopy DataFeed Guide
The Dukascopy library provides access to free historical market data from Dukascopy Bank SA, covering 1600+ financial instruments.
What is Dukascopy?
Dukascopy Bank SA is a Swiss bank that provides free historical market data (ticks and bars) through their public servers. The TheoryCraft Dukascopy library makes it easy to download and stream this data directly into your backtesting pipelines.
This makes Dukascopy an ideal data source for backtesting forex, equities, commodities, and cryptocurrency strategies.
Available Instruments
| Category | Examples | Count |
|---|---|---|
| Forex Majors | EUR/USD, GBP/USD, USD/JPY | 7 |
| Forex Crosses | EUR/GBP, GBP/JPY, AUD/NZD | 290+ |
| Metals | XAU/USD (Gold), XAG/USD (Silver) | 50+ |
| Stocks | AAPL.US/USD, MSFT.US/USD, GOOGL.US/USD | 1000+ |
| Commodities | Oil, Natural Gas | 10+ |
| Agriculturals | Wheat, Corn, Soybeans | 6 |
Installation
As a Library
Add to your mix.exs:
def deps do
[
{:dukascopy, github: "theorycraft-trading/dukascopy"}
]
end
As a CLI Tool
Install the command-line interface:
$> mix escript.install github theorycraft-trading/dukascopy
CLI Usage
The CLI provides two commands: download and search.
Search for Instruments
Find available instruments by symbol:
# Search for EUR pairs
$> dukascopy search EUR
# Search for Apple stock
$> dukascopy search AAPL
# Search for Bitcoin
$> dukascopy search BTC
Download Data
Download historical data in various formats:
# Daily EUR/USD data for 2024
$> dukascopy download -i EUR/USD --from 2024-01-01 --to 2024-12-31
# 5-minute bars in JSON format
$> dukascopy download -i EUR/USD -t m5 --from 2024-01-01 -f json
# Tick data (very granular)
$> dukascopy download -i EUR/USD -t tick --from 2024-01-01 --to 2024-01-02
# Hourly Gold data with ask prices
$> dukascopy download -i XAU/USD -t h1 --from 2024-01-01 -p ask
CLI Options
| Flag | Default | Description |
|---|---|---|
-i, --instrument |
- | Instrument symbol (required) |
--from |
- | Start date YYYY-MM-DD (required) |
--to |
now | End date (YYYY-MM-DD or "now") |
-t, --timeframe |
D | tick, m1, m5, m15, m30, h1, h4, D, W, M |
-p, --price-type |
bid | bid, ask, mid |
-v, --volume-units |
millions | millions, thousands, units |
--flats |
false | Include zero-volume bars |
-f, --format |
csv | csv, json, ndjson, none |
-o, --output |
./download | Output directory |
--filename |
- | Custom filename (without extension) |
-s, --silent |
false | No header output |
--timezone |
Etc/UTC | Timezone (e.g., America/New_York) |
--utc-offset |
0 | UTC offset in minutes |
--market-open |
00:00:00 | Market open time (HH:MM:SS) |
--weekly-open |
monday | Week start day |
--batch-size |
10 | Parallel downloads per batch |
--batch-pause |
1000 | Pause between batches (ms) |
--cache |
false | Enable file caching |
--cache-path |
.dukascopy-cache | Cache folder path |
--retries |
3 | Retries per request |
--retry-pause |
500 | Pause between retries (ms) |
--proxy |
- | Proxy URL (http://[user:pass@]host:port or socks5://host:port) |
--continue-on-error |
false | Continue on fetch errors (WARNING: may cause data gaps) |
-h, --help |
- | Display help |
Elixir API
Instruments Module
iex> alias Dukascopy.Instruments
iex> Instruments.all()
["EUR/USD", "GBP/USD", "AAPL.US/USD", ...]
iex> Instruments.forex() # All forex
iex> Instruments.forex_majors() # EUR/USD, GBP/USD, USD/JPY, etc.
iex> Instruments.forex_crosses() # EUR/GBP, GBP/JPY, etc.
iex> Instruments.metals() # XAU/USD, XAG/USD, etc.
iex> Instruments.stocks() # AAPL.US/USD, MSFT.US/USD, etc.
iex> Instruments.commodities() # Oil, Natural Gas, etc.
iex> Instruments.agriculturals() # Wheat, Corn, etc.
iex> Instruments.search("XAU")
["XAU/USD"]
iex> Instruments.search("AAPL")
["AAPL.US/USD"]
Streaming Data
The Dukascopy.stream/3 function creates a lazy stream of market data.
iex> stream = Dukascopy.stream("EUR/USD", :tick, from: ~D[2024-01-01], to: ~D[2024-01-02])
iex> for tick <- stream do
...> IO.puts("#{tick.time}: bid=#{tick.bid} ask=#{tick.ask}")
...> end
2024-01-01 00:00:00.123Z: bid=1.10452 ask=1.10455
2024-01-01 00:00:00.456Z: bid=1.10453 ask=1.10456
2024-01-01 00:00:00.789Z: bid=1.10451 ask=1.10454
...
iex> stream = Dukascopy.stream("EUR/USD", :m5, from: ~D[2024-01-01], to: ~D[2024-01-31])
iex> for bar <- stream do
...> IO.puts("#{bar.time}: O=#{bar.open} H=#{bar.high} L=#{bar.low} C=#{bar.close}")
...> end
2024-01-01 00:00:00Z: O=1.10450 H=1.10480 L=1.10445 C=1.10472
2024-01-01 00:05:00Z: O=1.10472 H=1.10495 L=1.10468 C=1.10491
2024-01-01 00:10:00Z: O=1.10491 H=1.10502 L=1.10485 C=1.10498
...
Stream Options
Date Range (required):
Provide either :from and :to (half-open interval [from, to)) or :date_range (inclusive Date.Range).
# Using from/to
iex> Dukascopy.stream("EUR/USD", :h1, from: ~D[2024-01-01], to: ~D[2024-02-01])
# Using date_range
iex> Dukascopy.stream("EUR/USD", :h1, date_range: Date.range(~D[2024-01-01], ~D[2024-01-31]))
All Options:
| Option | Default | Description |
|---|---|---|
:price_type |
:bid |
:bid, :ask, or :mid |
:timezone |
"Etc/UTC" |
Timezone string (supports DST) |
:volume_units |
:millions |
:millions, :thousands, or :units |
:ignore_flats |
true |
Skip zero-volume bars |
:batch_size |
10 |
Parallel requests per batch |
:pause_between_batches_ms |
1000 |
Delay between batches (ms) |
:use_cache |
false |
Enable file caching |
:cache_folder_path |
".dukascopy-cache" |
Cache directory |
:max_retries |
3 |
Retry attempts per request |
:market_open |
~T[00:00:00] |
Market open time for alignment |
:weekly_open |
:monday |
Week start day |
Price Types
iex> Dukascopy.stream("EUR/USD", :h1, price_type: :bid, from: ~D[2024-01-01]) # Bid (default)
iex> Dukascopy.stream("EUR/USD", :h1, price_type: :ask, from: ~D[2024-01-01]) # Ask
iex> Dukascopy.stream("EUR/USD", :h1, price_type: :mid, from: ~D[2024-01-01]) # Mid
Integration with TheoryCraft
Use Dukascopy.DataFeed as a data source in your MarketSource pipeline:
iex> alias TheoryCraft.MarketSource
iex> opts = [
...> instrument: "EUR/USD",
...> granularity: :tick,
...> from: ~D[2024-01-01],
...> to: ~D[2024-01-31]
...> ]
iex> market =
...> %MarketSource{}
...> |> MarketSource.add_data({Dukascopy.DataFeed, opts}, name: "EURUSD")
...> |> MarketSource.resample("m5", name: "EURUSD_m5")
...> |> MarketSource.resample("h1", name: "EURUSD_h1")
iex> for event <- MarketSource.stream(market) do
...> IO.inspect(event)
...> end
Timeframe Reference
Timeframes can be atoms or strings (e.g., :m5 or "m5").
The 1 is optional: D1 = D, h1 = h, etc...
| Pattern | Description | Examples |
|---|---|---|
:tick |
Raw tick data | ticks |
t<N> |
N ticks per bar | t5, t100 |
s<N> |
N-second bars | s30 |
m<N> |
N-minute bars | m1, m5, m15, m30 |
h<N> |
N-hour bars | h1, h4 |
D<N> |
N-day bars | D, D3 |
W<N> |
N-week bars | W |
M<N> |
N-month bars | M |
Tips and Best Practices
Use Caching
Enable caching to avoid redundant downloads:
iex> Dukascopy.stream("EUR/USD", :h1,
...> from: ~D[2024-01-01],
...> to: ~D[2024-06-30],
...> use_cache: true
...> )
$> dukascopy download -i EUR/USD -t h1 --from 2024-01-01 --to 2024-06-30 --cache
Start with Higher Timeframes
Start with higher timeframes for faster prototyping, then move to lower timeframes for more precise data:
# Fast iteration during development
iex> Dukascopy.stream("EUR/USD", :D, from: ~D[2020-01-01], to: ~D[2024-01-01])
# Switch to detailed data for final testing
iex> Dukascopy.stream("EUR/USD", :m1, from: ~D[2024-01-01], to: ~D[2024-03-01])
# Fast iteration during development
$> dukascopy download -i EUR/USD -t D --from 2020-01-01 --to 2024-01-01
# Switch to detailed data for final testing
$> dukascopy download -i EUR/USD -t m1 --from 2024-01-01 --to 2024-03-01
Handle Market Hours
Forex markets close on weekends. Your data will have gaps from Friday evening to Sunday evening.
Next Steps
- Understanding DataFeeds - Learn about DataFeed concepts
- Visualization with Kino - Visualize your downloaded data