# dukascopy-node: tick level market data without a data vendor contract

> dukascopy-node is a TypeScript library and CLI that downloads historical and real-time price tick data for forex, crypto, stocks, ETFs, commodities, bonds and CFDs. It is aimed at Node.js developers and quants who need raw tick history for backtesting without a paid terminal.

**Leo4815162342/dukascopy-node** — ✨ Download historical and real-time price tick data for Crypto, Stocks, ETFs, CFDs, Forex via CLI and Node.js ✨

- Repository: https://github.com/Leo4815162342/dukascopy-node
- Website: https://dukascopy-node.app
- Stars: 939 · Forks: 142
- Language: TypeScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/leo4815162342-dukascopy-node

## Tick level history, not a chart API

dukascopy-node is described as a Node.js library for downloading historical market tick data for Crypto, Stocks, ETFs, CFDs and Forex, and the README presents it as a way to download free historical and real-time market price tick data for stocks, crypto, commodities, bonds, currencies, CFDs and ETFs via Node.js and CLI.

The word that matters is tick. A tick record is a single price change with its own timestamp, and the README's example asks for `timeframe: 'tick'`. The alternative would be a service that hands you candles or OHLC bars, which is convenient and lossy: intrabar highs and lows, and the order in which prices arrived, are gone. For anyone replaying a strategy against history, that ordering is often the whole point.

The project ships as a single npm package with two surfaces. There is a programmatic API for Node.js and there is a CLI, so the same data can be exported to files for a research workflow or pulled inside a service. The package exposes `getHistoricalRates` for history and `getRealTimeRates` for live prices, and the bin entries in package.json register both a `dukascopy-node` and a `dukascopy-cli` command pointing at the same built CLI file.

## Instruments are addressed by id, and start dates differ

Everything is keyed by an instrument id such as `btcusd` or `eurusd`, and the README carries a long generated table of them, grouped by class. The counts are stated per group: forex currencies 290, forex major currencies 7, forex metals 51, United States 546, United Kingdom 104, Japan 53, Germany 43, Italy 38, France 38, crypto assets 29, United States ETFs 60, Germany ETFs 1, France ETFs 3, Hong Kong ETFs 4, agricultural commodities 6, energy commodities 4, metals commodities 3, bonds 3, mutual funds 9, and single entry groups for Africa, America, Asia, Europe, Austria, Belgium, Denmark, Finland, Hong Kong, Ireland, Mexico, Netherlands, Norway, Portugal, Spain, Sweden, Switzerland and crypto currencies.

Each row carries an earliest data date, and these dates are not uniform. The Euro Bund starts on May 2, 2016 and the UK Long Gilt on Dec 26, 2017, while the US T-Bond and most ETFs list Jan 1, 2000. The iShares NASDAQ 100 UCITS ETF entry starts on Feb 26, 2025.

That spread is the single most important operational fact for a backtest. Coverage is not a given; it is per instrument, and the table exists so you can check it before promising a test range. The repository generates this table programmatically, with `gen:instruments-md` and `gen:meta` scripts for instrument metadata.

## The CLI turns one instrument and date range into a file

The command line path is the shortest way to get data on disk. The README's example downloads one day of Bitcoin dollar ticks as CSV:

```bash
npx dukascopy-node -i btcusd -from 2019-01-13 -to 2019-01-14 -t tick -f csv
```

The flags map to the same concepts as the library options: `-i` for the instrument, `-from` and `-to` for the date range, `-t` for the timeframe and `-f` for the output format. Because the CLI is the same build as the library, you can script it in a shell or a Makefile and get files rather than objects.

That distinction matters for research work. Exporting to CSV once and analysing the file repeatedly is a different cost profile from issuing a request per query, and the quick start documentation lists separate pages for downloading data with cache and for downloading data with custom batching, which are the two controls for that behaviour. The `examples/` directory in the repository mirrors this with `basic`, `with-cache`, `with-custom-batching`, `with-debugging`, `with-tick-data` and `with-typescript` cases, so the intended usage patterns are visible before you write anything.

## getHistoricalRates and getRealTimeRates share one shape

The programmatic API is CommonJS with an async call. Historical data takes an instrument, a date range with `from` and `to` as Date objects, a timeframe and a format:

```javascript
const { getHistoricalRates } = require('dukascopy-node');

(async () => {
  try {
    const data = await getHistoricalRates({
      instrument: 'btcusd',
      dates: {
        from: new Date('2019-01-13'),
        to: new Date('2019-01-14')
      },
      timeframe: 'tick',
      format: 'json'
    });

    console.log(data);
  } catch (error) {
    console.log('error', error);
  }
})();
```

The real time call is the same shape with the date range dropped:

```javascript
const { getRealTimeRates } = require('dukascopy-node');

(async () => {
  try {
    const data = await getRealTimeRates({
      instrument: 'eurusd',
      timeframe: 'tick',
      format: 'json'
    });

    console.log(data);
  } catch (error) {
    console.log('error', error);
  }
})();
```

Both wrap their body in a try and catch, so failures arrive as rejected promises you can handle, which is the behaviour you want in a scheduled job rather than an unhandled rejection. The package publishes type definitions at `dist/index.d.ts` and an ES module build at `dist/esm/index.js` alongside the CommonJS entry, so both module systems are covered. The build itself uses `tsup-node` to emit ESM and CJS with declarations.

## Output formats, timezones and empty results are documented separately

The quick start list in the README is a good map of the decision points you will hit. There are pages for basic usage and output formats, downloading tick data, date formatting and converting timezones, dealing with empty data and errors, using cache, custom batching, TypeScript usage and debugging.

Two of those deserve attention before you build a pipeline. The first is dates and timezones. Market data arrives on a schedule, and the earliest data dates in the instrument tables are given in UTC, so how you express your own date boundaries determines which ticks you get. A range that starts at local midnight in one timezone is a different set of records than the same range in UTC.

The second is empty data and errors. The library returns an empty result in some situations rather than throwing, and the README's own example prints `No links found` style branching in other projects, but here the documented handling of empty data and errors has its own guide. Building a backtest loop that assumes every request returns rows is how you end up with silent gaps in a price series.

Caching deserves a mention too, since repeated historical queries over the same range are the normal case in research, and the cache and batching pages exist to control how that traffic is made.

## Where this source of data will not fit your strategy

The constraint that is easy to miss is what the data actually is. This is one broker's feed, Dukascopy's, reached through the public data that the project downloads. If your backtest assumes consolidated exchange volume, or that two instruments traded in a specific order across venues, a single broker's tick stream cannot tell you that. The library does not merge venues.

There is also the coverage boundary already mentioned: earliest data varies by instrument, from Jan 1, 2000 for several instruments down to Feb 26, 2025 for one ETF, and asking for a range before an instrument's start returns nothing useful.

Runtime requirements are simple and stated clearly: Node.js 18 or higher. The dependency list is small and pinned to exact versions, with `chalk`, `cli-progress`, `commander`, `dayjs`, `debug`, `fastest-validator` and `fs-extra`. The `fastest-validator` entry is worth noting, since it implies the options you pass are validated before the request goes out, which turns a typo into an error message instead of a silent empty result.

Finally, the free wording in the README is about access, not about a guarantee. The project does not promise availability of the upstream endpoint, and it has no documented rate limit or service level. If your pipeline depends on this, treat it as a source that can stop answering.

## MIT terms, tagged releases and a July 2026 push

The licence field on the repository says MIT, and package.json records the same licence, so commercial use, modification and redistribution are all permitted with the usual notice requirement. For a data download library that most teams will depend on rather than fork, that is the straightforward arrangement.

The release history shows steady tagging: v1.46.4 on 2026-03-09, v1.49.0 on 2026-07-16 and v1.50.0 on 2026-07-24, with the last push on 2026-07-24. The package version in package.json is 1.50.0, so the tags track the published package rather than drifting ahead of it.

The release process is scripted, with a `release` script that runs `standard-version` and pushes tags to master before publishing to npm, plus a `release:beta` script that publishes under the beta tag. The `CHANGELOG.md` file at the top level is generated by that flow, which means upgrade notes are available in the usual place. Since the project is above 1.0 and tagged regularly, a caret range in your own package.json is a reasonable default, and the changelog is where to check what changed before bumping.

## Conclusion

Adopt dukascopy-node when you need historical tick data for backtesting or research and want it delivered as CSV or JSON files through a CLI, or as an async call inside a Node.js service, without signing a data vendor agreement. Skip it if your strategy depends on consolidated exchange prints rather than a single broker feed, since the source here is Dukascopy's own data, or if your runtime is not Node.js 18 or newer. Verify first that the instrument id you plan to use exists in the instrument list and that its earliest data date covers the period you need, then check the `format` and `timeframe` options you will pass to `getHistoricalRates`. The licence field on the repository says MIT, and the last push was on 2026-07-24.

## FAQ

### How do I install dukascopy-node?

The README lists three package managers: `npm install dukascopy-node --save`, `yarn add dukascopy-node` and `pnpm add dukascopy-node`. Node.js 18 or higher is required.

### How do I download historical tick data with the dukascopy-node CLI?

The documented example is `npx dukascopy-node -i btcusd -from 2019-01-13 -to 2019-01-14 -t tick -f csv`, which writes one day of Bitcoin dollar ticks as CSV. The package registers both a `dukascopy-node` and a `dukascopy-cli` command.

### How do I get real-time prices in Node.js with dukascopy-node?

Call `getRealTimeRates` with an instrument, a timeframe and a format, for example `instrument: 'eurusd'`, `timeframe: 'tick'` and `format: 'json'`. The function is async, so failures arrive as a rejected promise that the example handles in a try and catch.

### Which instruments does dukascopy-node support and how far back does the data go?

The README lists instruments by id grouped by class, including 290 forex currencies, 29 crypto assets, 60 United States ETFs and 546 United States instruments. Each row gives an earliest data date, and these differ, for example Jan 1, 2000 for the US T-Bond and Feb 26, 2025 for the iShares NASDAQ 100 UCITS ETF.

### What output formats and options does dukascopy-node support?

The README examples pass `format: 'json'` in the API and `-f csv` on the command line. The quick start documentation lists separate pages for output formats, downloading tick data, date formatting and timezone conversion, empty data and errors, cache and custom batching.

### What licence is dukascopy-node released under?

The licence field on the repository says MIT, and package.json lists the same licence. The package version is 1.50.0 and the release script generates `CHANGELOG.md` with standard-version.

## Sources

- [Leo4815162342/dukascopy-node on GitHub](https://github.com/Leo4815162342/dukascopy-node)
- [License: MIT](https://github.com/Leo4815162342/dukascopy-node/blob/master/LICENSE)
- [Project website](https://dukascopy-node.app)
- [README](https://github.com/Leo4815162342/dukascopy-node/blob/master/README.md)
- [Releases](https://github.com/Leo4815162342/dukascopy-node/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/leo4815162342-dukascopy-node
