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

CategoryExamplesCount
Forex MajorsEUR/USD, GBP/USD, USD/JPY7
Forex CrossesEUR/GBP, GBP/JPY, AUD/NZD290+
MetalsXAU/USD (Gold), XAG/USD (Silver)50+
StocksAAPL.US/USD, MSFT.US/USD, GOOGL.US/USD1000+
CommoditiesOil, Natural Gas10+
AgriculturalsWheat, Corn, Soybeans6

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

FlagDefaultDescription
-i, --instrument-Instrument symbol (required)
--from-Start date YYYY-MM-DD (required)
--tonowEnd date (YYYY-MM-DD or "now")
-t, --timeframeDtick, m1, m5, m15, m30, h1, h4, D, W, M
-p, --price-typebidbid, ask, mid
-v, --volume-unitsmillionsmillions, thousands, units
--flatsfalseInclude zero-volume bars
-f, --formatcsvcsv, json, ndjson
-o, --output./downloadOutput directory
--filename-Custom filename (without extension)
-s, --silentfalseNo header output
--timezoneEtc/UTCTimezone (e.g., America/New_York)
--utc-offset0UTC offset in minutes
--market-open00:00:00Market open time (HH:MM:SS)
--weekly-openmondayWeek start day
--batch-size10Parallel downloads per batch
--batch-pause1000Pause between batches (ms)
--cachefalseEnable file caching
--cache-path.dukascopy-cacheCache folder path
--retries3Retries per request
--retry-pause500Pause between retries (ms)
-h, --help-Display help

Elixir API

Instruments Module

alias Dukascopy.Instruments

Instruments.all()
["EUR/USD", "GBP/USD", "AAPL.US/USD", ...]

Instruments.forex()          # All forex
Instruments.forex_majors()   # EUR/USD, GBP/USD, USD/JPY, etc.
Instruments.forex_crosses()  # EUR/GBP, GBP/JPY, etc.
Instruments.metals()         # XAU/USD, XAG/USD, etc.
Instruments.stocks()         # AAPL.US/USD, MSFT.US/USD, etc.
Instruments.commodities()    # Oil, Natural Gas, etc.
Instruments.agriculturals()  # Wheat, Corn, etc.

Instruments.search("XAU")
["XAU/USD"]

Instruments.search("AAPL")
["AAPL.US/USD"]

Streaming Data

The Dukascopy.stream/3 function creates a lazy stream of market data.

stream = Dukascopy.stream("EUR/USD", :ticks, from: ~D[2024-01-01], to: ~D[2024-01-02])
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
...

stream = Dukascopy.stream("EUR/USD", :m5, from: ~D[2024-01-01], to: ~D[2024-01-31])
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
Dukascopy.stream("EUR/USD", :h1, from: ~D[2024-01-01], to: ~D[2024-02-01])

# Using date_range
Dukascopy.stream("EUR/USD", :h1, date_range: Date.range(~D[2024-01-01], ~D[2024-01-31]))

All Options:

OptionDefaultDescription
:price_type:bid:bid, :ask, or :mid
:timezone"Etc/UTC"Timezone string (supports DST)
:volume_units:millions:millions, :thousands, or :units
:ignore_flatstrueSkip zero-volume bars
:batch_size10Parallel requests per batch
:pause_between_batches_ms1000Delay between batches (ms)
:use_cachefalseEnable file caching
:cache_folder_path".dukascopy-cache"Cache directory
:max_retries3Retry attempts per request
:market_open~T[00:00:00]Market open time for alignment
:weekly_open:mondayWeek start day

Price Types

Dukascopy.stream("EUR/USD", :h1, price_type: :bid, from: ~D[2024-01-01])  # Bid (default)
Dukascopy.stream("EUR/USD", :h1, price_type: :ask, from: ~D[2024-01-01])  # Ask
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:

alias TheoryCraft.MarketSource

opts = [
  instrument: "EUR/USD",
  granularity: :ticks,
  from: ~D[2024-01-01],
  to: ~D[2024-01-31]
]

market =
  %MarketSource{}
  |> MarketSource.add_data({Dukascopy.DataFeed, opts}, name: "EURUSD")
  |> MarketSource.resample("m5", name: "EURUSD_m5")
  |> MarketSource.resample("h1", name: "EURUSD_h1")

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...

PatternDescriptionExamples
:ticksRaw tick dataticks
t<N>N ticks per bart5, t100
s<N>N-second barss30
m<N>N-minute barsm1, m5, m15, m30
h<N>N-hour barsh1, h4
D<N>N-day barsD, D3
W<N>N-week barsW
M<N>N-month barsM

Tips and Best Practices

Use Caching

Enable caching to avoid redundant downloads:

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
Dukascopy.stream("EUR/USD", :D, from: ~D[2020-01-01], to: ~D[2024-01-01])

# Switch to detailed data for final testing
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