Skip to content

Commit 44d7ca4

Browse files
feat: new instruments Nov 2024 (#199)
1 parent cd92729 commit 44d7ca4

11 files changed

+71398
-53
lines changed

README.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ const { getHistoricalRates } = require('dukascopy-node');
127127
* [Switzerland 🇨🇭 (28)](#switzerland)
128128
* [United Kingdom 🇬🇧 (102)](#uk)
129129
* [United States 🇺🇸 (608)](#us)
130+
* [ETFs 📈 (16)](#etf_cfd)
130131
<hr>
131132
<h3 id="bnd_cfd">Bonds 📊</h3>
132133

@@ -369,7 +370,7 @@ const { getHistoricalRates } = require('dukascopy-node');
369370
|[UK 100 Index](https://www.dukascopy-node.app/instrument/gbridxgbp)|`gbridxgbp`|Apr 5, 1988|
370371
|[Spain 35 Index](https://www.dukascopy-node.app/instrument/espidxeur)|`espidxeur`|Jun 2, 2014|
371372
|[Switzerland 20 Index](https://www.dukascopy-node.app/instrument/cheidxchf)|`cheidxchf`|Jan 3, 1992|
372-
|[Italy 40 Index](https://www.dukascopy-node.app/instrument/itaidxeur)|`itaidxeur`|Jun 2, 2014|
373+
|[Italy 40 Index](https://www.dukascopy-node.app/instrument/itaidxeur)|`itaidxeur`|Jul 29, 2016|
373374
|[Netherlands 25 Index](https://www.dukascopy-node.app/instrument/nldidxeur)|`nldidxeur`|Dec 1, 2017|
374375
|[Poland 20 Index](https://www.dukascopy-node.app/instrument/plnidxpln)|`plnidxpln`|Dec 1, 2017|
375376
<h3 id="idx_africa">Africa 🌍</h3>
@@ -1534,6 +1535,27 @@ const { getHistoricalRates } = require('dukascopy-node');
15341535
|[Zoom Video Communications (A Class)](https://www.dukascopy-node.app/instrument/zmususd)|`zmususd`|Sep 30, 2020|
15351536
|[ZSCALER INC](https://www.dukascopy-node.app/instrument/zsususd)|`zsususd`|May 13, 2022|
15361537
|[ZOETIS INC](https://www.dukascopy-node.app/instrument/ztsususd)|`ztsususd`|May 16, 2022|
1538+
<h3 id="etf_cfd">ETFs 📈</h3>
1539+
1540+
|Instrument|id|Earliest data (UTC)|
1541+
|-|-|-|
1542+
|[ARK 21Shares Active Bitcoin Ethereum Strategy ETF Fund](https://www.dukascopy-node.app/instrument/arkiususd)|`arkiususd`|Apr 25, 2024|
1543+
|[Global X Cybersecurity UCITS ETF Fund](https://www.dukascopy-node.app/instrument/bugggbgbx)|`bugggbgbx`|Oct 21, 2024|
1544+
|[Lyxor Smart Overnight Return - UCITS ETF C-GBP](https://www.dukascopy-node.app/instrument/csh2gbgbx)|`csh2gbgbx`|Jan 2, 2024|
1545+
|[WisdomTree Cybersecurity UCITS ETF Fund](https://www.dukascopy-node.app/instrument/cysegbgbx)|`cysegbgbx`|Jan 2, 2024|
1546+
|[iShares MSCI Europe Health Care Sector UCITS ETF Fund](https://www.dukascopy-node.app/instrument/esihgbgbx)|`esihgbgbx`|Oct 24, 2024|
1547+
|[iShares Physical Gold ETC Fund](https://www.dukascopy-node.app/instrument/iglnususd)|`iglnususd`|Apr 3, 2023|
1548+
|[iShares S&P 500 Financials Sector UCITS ETF](https://www.dukascopy-node.app/instrument/iufsususd)|`iufsususd`|Jan 2, 2024|
1549+
|[iShares MSCI Global Semiconductors UCITS ETF](https://www.dukascopy-node.app/instrument/semigbgbx)|`semigbgbx`|Oct 21, 2024|
1550+
|[Invesco Physical Gold ETC Fund](https://www.dukascopy-node.app/instrument/sgldususd)|`sgldususd`|Jan 2, 2024|
1551+
|[VanEck Semiconductor ETF Fund](https://www.dukascopy-node.app/instrument/smhususd)|`smhususd`|Jan 2, 2024|
1552+
|[Lyxor Smart Overnight Return - UCITS ETF C-USD](https://www.dukascopy-node.app/instrument/smtcususd)|`smtcususd`|Nov 21, 2023|
1553+
|[Wisdomtree Artificial Intelligence And Innovation Fund](https://www.dukascopy-node.app/instrument/wtaiususd)|`wtaiususd`|Jan 2, 2024|
1554+
|[Xtrackers FTSE Developed Europe Real Estate UCITS ETF](https://www.dukascopy-node.app/instrument/xdergbgbx)|`xdergbgbx`|Jan 2, 2024|
1555+
|[Xtrackers MSCI World Health Care UCITS ETF Fund](https://www.dukascopy-node.app/instrument/xdwhususd)|`xdwhususd`|Jan 2, 2024|
1556+
|[Xtrackers MSCI World Information Technology UCITS ETF](https://www.dukascopy-node.app/instrument/xdwtususd)|`xdwtususd`|Jan 2, 2024|
1557+
|[Invesco Real Estate S&P US Select Sector UCITS ETF](https://www.dukascopy-node.app/instrument/xresususd)|`xresususd`|Jan 2, 2024|
1558+
15371559

15381560
---
15391561

src/utils/instrument-meta-data/generate-data.ts

+25-25
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,31 @@ const saveFile = promisify(fs.writeFile);
1111

1212
const OUTPUT_FOLDER = path.resolve(__dirname, 'generated');
1313

14-
Promise.all([getActualStartDates(), getRawMetaData()])
15-
.then(([actualStartDates, rawMetaData]) => {
16-
return Promise.all([
17-
saveFile(
18-
path.resolve(OUTPUT_FOLDER, `raw-meta-data-${new Date().toISOString().slice(0, 10)}.json`),
19-
JSON.stringify(rawMetaData, null, 2)
20-
),
21-
generateMeta(
22-
rawMetaData.instruments,
23-
actualStartDates,
24-
path.resolve(OUTPUT_FOLDER, 'instrument-meta-data.json')
25-
),
26-
generateInstrumentEnum(
27-
rawMetaData.instruments,
28-
path.resolve(OUTPUT_FOLDER, 'instrument-enum.ts')
29-
),
30-
generateInstrumentGroupData(
31-
rawMetaData,
32-
path.resolve(OUTPUT_FOLDER, 'instrument-groups.json')
33-
)
34-
]);
35-
})
36-
.then(() => {
37-
console.log('Done');
38-
});
14+
async function run() {
15+
const [actualStartDates, rawMetaData] = await Promise.all([
16+
getActualStartDates(),
17+
getRawMetaData()
18+
]);
19+
await saveFile(
20+
path.resolve(OUTPUT_FOLDER, `raw-meta-data-${new Date().toISOString().slice(0, 10)}.json`),
21+
JSON.stringify(rawMetaData, null, 2)
22+
);
23+
24+
const metaDataFilePath = path.resolve(OUTPUT_FOLDER, 'instrument-meta-data.json');
25+
26+
await generateMeta(rawMetaData.instruments, actualStartDates, metaDataFilePath);
27+
28+
await generateInstrumentEnum(metaDataFilePath, path.resolve(OUTPUT_FOLDER, 'instrument-enum.ts'));
29+
30+
await generateInstrumentGroupData(
31+
rawMetaData,
32+
path.resolve(OUTPUT_FOLDER, 'instrument-groups.json')
33+
);
34+
35+
console.log('Done');
36+
}
37+
38+
run();
3939

4040
async function getActualStartDates(): Promise<ActualStartDates> {
4141
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

src/utils/instrument-meta-data/generate-group-data.ts

+30-8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export function generateInstrumentGroupData(
1414
metadata: MetaDataResponse,
1515
path: string
1616
): Promise<void> {
17+
const currentGroupDataRaw = fs.readFileSync(path, 'utf8');
18+
const currentGroupData = JSON.parse(currentGroupDataRaw) as GroupData[];
19+
1720
const data = Object.keys(metadata.groups).reduce<GroupData[]>((all, group) => {
1821
const hasInstruments = metadata.groups[group]?.instruments?.length;
1922
if (!hasInstruments) {
@@ -27,17 +30,36 @@ export function generateInstrumentGroupData(
2730
return all;
2831
}
2932

30-
all.push({
31-
id: group.toLowerCase().replace(/\s/g, '-'),
32-
instruments: metadata.groups[group].instruments
33-
.filter(inst => !!metadata.instruments[inst])
34-
.map(inst => generateIdName(metadata.instruments[inst].historical_filename, inst))
35-
});
33+
const id = group.toLowerCase().replace(/\s/g, '-');
34+
35+
const matchedGroup = currentGroupData.find(item => item.id === id);
36+
37+
if (matchedGroup) {
38+
matchedGroup.instruments = matchedGroup.instruments.concat(
39+
metadata.groups[group].instruments
40+
.filter(inst => !!metadata.instruments[inst])
41+
.map(inst => generateIdName(metadata.instruments[inst].historical_filename, inst))
42+
);
43+
} else {
44+
all.push({
45+
id,
46+
instruments: metadata.groups[group].instruments
47+
.filter(inst => !!metadata.instruments[inst])
48+
.map(inst => generateIdName(metadata.instruments[inst].historical_filename, inst))
49+
});
50+
}
3651

3752
return all;
38-
}, []);
53+
}, currentGroupData);
54+
55+
const dedupedGroups = data.map<GroupData>(item => {
56+
return {
57+
id: item.id,
58+
instruments: Array.from(new Set(item.instruments))
59+
};
60+
});
3961

40-
return saveFile(path, JSON.stringify(data, null, 2)).then(() => {
62+
return saveFile(path, JSON.stringify(dedupedGroups, null, 2)).then(() => {
4163
console.log('instrument groups generated!', path);
4264
});
4365
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
/* eslint-disable no-console */
22
import fs from 'fs';
33
import { promisify } from 'util';
4-
import { MetaDataResponse } from './generate-data.types';
5-
import { generateIdName } from './generate-id-name';
4+
import { InstrumentMetaDataMap } from './generate-meta';
65
const saveFile = promisify(fs.writeFile);
76

8-
export function generateInstrumentEnum(
9-
instruments: MetaDataResponse['instruments'],
10-
path: string
11-
): Promise<void> {
12-
const enumKeys = Object.keys(instruments).map(inst => {
13-
const { name, description, historical_filename } = instruments[inst];
7+
export function generateInstrumentEnum(fromPath: string, toPath: string): Promise<void> {
8+
const instrumentsmetaData = fs.readFileSync(fromPath, 'utf8');
9+
const instrumentsMetaData = JSON.parse(instrumentsmetaData) as InstrumentMetaDataMap;
10+
11+
const enumKeys = Object.entries(instrumentsMetaData).map(([cleanName, inst]) => {
12+
const { name, description } = inst;
1413

15-
const cleanName = generateIdName(historical_filename, inst);
1614
const key = /^\d/.test(cleanName) ? `'${cleanName}'` : cleanName;
1715

1816
const line = `
@@ -31,7 +29,7 @@ export function generateInstrumentEnum(
3129
'export type InstrumentType = keyof typeof Instrument;'
3230
].join('\n');
3331

34-
return saveFile(path, strings).then(() => {
35-
console.log('instruments enum generated!', path);
32+
return saveFile(toPath, strings).then(() => {
33+
console.log('instruments enum generated!', toPath);
3634
});
3735
}

src/utils/instrument-meta-data/generate-instrument-md.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable prettier/prettier */
12
/* eslint-disable no-console */
23
import fs from 'fs';
34
import path from 'path';
@@ -18,6 +19,7 @@ const titleMap: Record<string, { emoji: string; title: string }> = {
1819
cmd_agricultural: { emoji: '☕', title: 'Agricultural commodities' },
1920
cmd_energy: { emoji: '⚡', title: 'Energy commodities' },
2021
cmd_metals: { emoji: '⚙️', title: 'Metals commodities' },
22+
etf_cfd: { emoji: '📈', title: 'ETFs' },
2123
etf_cfd_us: { emoji: '🇺🇸📈', title: 'United States ETFs' },
2224
etf_cfd_de: { emoji: '🇩🇪📈', title: 'Germany ETFs' },
2325
etf_cfd_fr: { emoji: '🇫🇷📈', title: 'France ETFs' },
@@ -74,9 +76,8 @@ const titleMap: Record<string, { emoji: string; title: string }> = {
7476

7577
const instrumentTable = instrumentGroups
7678
.map(({ id, instruments }) => {
77-
const groupTitle = `<h3 id="${id}">${titleMap[id].title} ${
78-
titleMap[id].emoji || ''
79-
}</h3>\n`;
79+
const groupTitle = `<h3 id="${id}">${titleMap[id].title} ${titleMap[id].emoji || ''
80+
}</h3>\n`;
8081

8182
const listBody = instruments
8283
.map(instrumentId => {
@@ -87,7 +88,7 @@ const titleMap: Record<string, { emoji: string; title: string }> = {
8788
startMonthForHourlyCandles,
8889
startYearForDailyCandles
8990
} = instrumentMetaData[
90-
instrumentId as keyof typeof instrumentMetaData
91+
instrumentId as keyof typeof instrumentMetaData
9192
] as InstrumentMetaData;
9293

9394
const dates = [

src/utils/instrument-meta-data/generate-meta.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ const decimalFactorOverride: Record<string, number> = {
2424
lnkusd: 1000
2525
};
2626

27+
export type InstrumentMetaDataMap = Record<string, InstrumentMetaData>;
28+
2729
export function generateMeta(
2830
instruments: MetaDataResponse['instruments'],
2931
actualStartDates: ActualStartDates,
3032
path: string
3133
): Promise<void> {
32-
const data = Object.keys(instruments).reduce<Record<string, InstrumentMetaData>>((all, inst) => {
34+
const currentValueFromFile = fs.readFileSync(path, 'utf8');
35+
const currentValue = JSON.parse(currentValueFromFile) as InstrumentMetaDataMap;
36+
37+
const data = Object.keys(instruments).reduce((all, inst) => {
3338
const {
3439
name,
3540
description,
@@ -66,7 +71,7 @@ export function generateMeta(
6671
};
6772

6873
return all;
69-
}, {});
74+
}, currentValue);
7075

7176
return saveFile(path, JSON.stringify(data, null, 2)).then(() => {
7277
console.log('Meta data generated!', path);

src/utils/instrument-meta-data/generated/instrument-enum.ts

+96
Original file line numberDiff line numberDiff line change
@@ -7601,5 +7601,101 @@ export enum Instrument {
76017601
* *ZURN.CH/CHF*
76027602
*/
76037603
zurnchchf='zurnchchf',
7604+
7605+
/**
7606+
* ### ARK 21Shares Active Bitcoin Ethereum Strategy ETF Fund
7607+
* *ARKI.US/USD*
7608+
*/
7609+
arkiususd='arkiususd',
7610+
7611+
/**
7612+
* ### Global X Cybersecurity UCITS ETF Fund
7613+
* *BUGG.GB/GBP*
7614+
*/
7615+
bugggbgbx='bugggbgbx',
7616+
7617+
/**
7618+
* ### Lyxor Smart Overnight Return - UCITS ETF C-GBP
7619+
* *CSH2.GB/GBX*
7620+
*/
7621+
csh2gbgbx='csh2gbgbx',
7622+
7623+
/**
7624+
* ### WisdomTree Cybersecurity UCITS ETF Fund
7625+
* *CYSE.GB/GBX*
7626+
*/
7627+
cysegbgbx='cysegbgbx',
7628+
7629+
/**
7630+
* ### iShares MSCI Europe Health Care Sector UCITS ETF Fund
7631+
* *ESIH.GB/GBP*
7632+
*/
7633+
esihgbgbx='esihgbgbx',
7634+
7635+
/**
7636+
* ### iShares Physical Gold ETC Fund
7637+
* *IGLN.US/USD*
7638+
*/
7639+
iglnususd='iglnususd',
7640+
7641+
/**
7642+
* ### iShares S&P 500 Financials Sector UCITS ETF
7643+
* *IUFS.US/USD*
7644+
*/
7645+
iufsususd='iufsususd',
7646+
7647+
/**
7648+
* ### iShares MSCI Global Semiconductors UCITS ETF
7649+
* *SEMI.GB/GBP*
7650+
*/
7651+
semigbgbx='semigbgbx',
7652+
7653+
/**
7654+
* ### Invesco Physical Gold ETC Fund
7655+
* *SGLD.US/USD*
7656+
*/
7657+
sgldususd='sgldususd',
7658+
7659+
/**
7660+
* ### VanEck Semiconductor ETF Fund
7661+
* *SMH.US/USD*
7662+
*/
7663+
smhususd='smhususd',
7664+
7665+
/**
7666+
* ### Lyxor Smart Overnight Return - UCITS ETF C-USD
7667+
* *SMTC.US/USD*
7668+
*/
7669+
smtcususd='smtcususd',
7670+
7671+
/**
7672+
* ### Wisdomtree Artificial Intelligence And Innovation Fund
7673+
* *WTAI.US/USD*
7674+
*/
7675+
wtaiususd='wtaiususd',
7676+
7677+
/**
7678+
* ### Xtrackers FTSE Developed Europe Real Estate UCITS ETF
7679+
* *XDER.GB/GBX*
7680+
*/
7681+
xdergbgbx='xdergbgbx',
7682+
7683+
/**
7684+
* ### Xtrackers MSCI World Health Care UCITS ETF Fund
7685+
* *XDWH.US/USD*
7686+
*/
7687+
xdwhususd='xdwhususd',
7688+
7689+
/**
7690+
* ### Xtrackers MSCI World Information Technology UCITS ETF
7691+
* *XDWT.US/USD*
7692+
*/
7693+
xdwtususd='xdwtususd',
7694+
7695+
/**
7696+
* ### Invesco Real Estate S&P US Select Sector UCITS ETF
7697+
* *XRES.US/USD*
7698+
*/
7699+
xresususd='xresususd',
76047700
}
76057701
export type InstrumentType = keyof typeof Instrument;

src/utils/instrument-meta-data/generated/instrument-groups.json

+21
Original file line numberDiff line numberDiff line change
@@ -1439,5 +1439,26 @@
14391439
"zsususd",
14401440
"ztsususd"
14411441
]
1442+
},
1443+
{
1444+
"id": "etf_cfd",
1445+
"instruments": [
1446+
"arkiususd",
1447+
"bugggbgbx",
1448+
"csh2gbgbx",
1449+
"cysegbgbx",
1450+
"esihgbgbx",
1451+
"iglnususd",
1452+
"iufsususd",
1453+
"semigbgbx",
1454+
"sgldususd",
1455+
"smhususd",
1456+
"smtcususd",
1457+
"wtaiususd",
1458+
"xdergbgbx",
1459+
"xdwhususd",
1460+
"xdwtususd",
1461+
"xresususd"
1462+
]
14421463
}
14431464
]

0 commit comments

Comments
 (0)