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