diff --git a/index.d.ts b/index.d.ts index 3948613..8175ee6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -13,4 +13,18 @@ declare module "investing-com-api" { price_low: number, price_close: number, }[]>; + + export function getHistoricalData( + input: string, + resolution: '5' | '60' | 'D' | 'W' | 'M', + from: Date, + to: Date + ): Promise<{ + date: number, + value: number, + price_open: number, + price_high: number, + price_low: number, + price_close: number, + }[]>; } diff --git a/index.js b/index.js index 7d07b10..98a2149 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ const puppeteer = require('puppeteer'); const { mapping } = require('./mapping'); const { getJsonContent, mapResponse } = require('./functions'); +const getHistoricalData = require('./src/getHistoricalData') const validPeriod = ['P1D', 'P1W', 'P1M', 'P3M', 'P6M', 'P1Y', 'P5Y', 'MAX']; const validInterval = ['PT1M', 'PT5M', 'PT15M', 'PT30M', 'PT1H', 'PT5H', 'P1D', 'P1W', 'P1M']; @@ -83,4 +84,7 @@ async function investing(input, period = 'P1M', interval = 'P1D', pointscount = // investing('1').then(console.log); -exports.investing = investing; +module.exports = { + investing, + getHistoricalData +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1be07f1..96538f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "eslint": "^8.28.0", "eslint-config-google": "^0.14.0", "jest": "^29.3.1", - "jest-puppeteer": "^10.0.1" + "jest-puppeteer": "^10.0.1", + "nock": "^14.0.0-beta.15" } }, "node_modules/@ampproject/remapping": { @@ -1157,6 +1158,24 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mswjs/interceptors": { + "version": "0.36.5", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.36.5.tgz", + "integrity": "sha512-aQ8WF5zQwOdcxLsxSEk9Jd01GgGb80xxqCaiDDlewhtwqpSm8MOvUHslwPydVirasdW09++NxDNNftm1vLY8yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1192,6 +1211,31 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@puppeteer/browsers": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", @@ -3169,6 +3213,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4014,6 +4065,13 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4260,6 +4318,21 @@ "node": ">= 0.4.0" } }, + "node_modules/nock": { + "version": "14.0.0-beta.15", + "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.0-beta.15.tgz", + "integrity": "sha512-rp72chatxoZbR/2cYHwtb+IX6n6kkanYKGN2PKn4c12JBrj9n4xGUKFykuQHB+Gkz3fynlikFbMH2LI6VoebuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mswjs/interceptors": "^0.36.4", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4342,6 +4415,13 @@ "node": ">=0.10.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4630,6 +4710,16 @@ "node": ">= 6" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/proxy-agent": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", @@ -5078,6 +5168,13 @@ "bare-events": "^2.2.0" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6448,6 +6545,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@mswjs/interceptors": { + "version": "0.36.5", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.36.5.tgz", + "integrity": "sha512-aQ8WF5zQwOdcxLsxSEk9Jd01GgGb80xxqCaiDDlewhtwqpSm8MOvUHslwPydVirasdW09++NxDNNftm1vLY8yA==", + "dev": true, + "requires": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6474,6 +6585,28 @@ "fastq": "^1.6.0" } }, + "@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "requires": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "@puppeteer/browsers": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", @@ -7936,6 +8069,12 @@ "is-extglob": "^2.1.1" } }, + "is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -8587,6 +8726,12 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -8776,6 +8921,17 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" }, + "nock": { + "version": "14.0.0-beta.15", + "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.0-beta.15.tgz", + "integrity": "sha512-rp72chatxoZbR/2cYHwtb+IX6n6kkanYKGN2PKn4c12JBrj9n4xGUKFykuQHB+Gkz3fynlikFbMH2LI6VoebuQ==", + "dev": true, + "requires": { + "@mswjs/interceptors": "^0.36.4", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8840,6 +8996,12 @@ "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true }, + "outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9046,6 +9208,12 @@ "sisteransi": "^1.0.5" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "proxy-agent": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", @@ -9357,6 +9525,12 @@ "text-decoder": "^1.1.0" } }, + "strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", diff --git a/package.json b/package.json index c5469a9..07fcf21 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "lint": "eslint ./", - "test": "jest --forceExit", + "test": "jest", "coverage": "jest --coverage --forceExit" }, "repository": { @@ -31,7 +31,9 @@ }, "homepage": "https://github.com/DavideViolante/investing-com-api#readme", "jest": { - "coveragePathIgnorePatterns": ["functions.js"] + "coveragePathIgnorePatterns": [ + "functions.js" + ] }, "dependencies": { "puppeteer": "^22.15.0" @@ -40,6 +42,7 @@ "eslint": "^8.28.0", "eslint-config-google": "^0.14.0", "jest": "^29.3.1", - "jest-puppeteer": "^10.0.1" + "jest-puppeteer": "^10.0.1", + "nock": "^14.0.0-beta.15" } } diff --git a/src/getHistoricalData.js b/src/getHistoricalData.js new file mode 100644 index 0000000..e5a4165 --- /dev/null +++ b/src/getHistoricalData.js @@ -0,0 +1,47 @@ +const { mapResponse } = require('../functions') + +const buildUrl = ({ input, resolution, from, to } = {}) => { + const query = new URLSearchParams({ + symbol: input, + resolution, + from: from && from.getTime() / 1000, + to: to && to.getTime() / 1000, + }) + return 'https://tvc6.investing.com/d8f62270e64f9eb6e4e6a07c3ffeab0b/1729428526/9/9/16/history?' + query +} + +const getHistoricalData = async (params) => { + const response = await fetch(buildUrl(params),{ + headers: new Headers({ + "upgrade-insecure-requests": "1" + }) + }); + + // TODO da testare + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + + const json = await response.json(); + // TODO da testare + if (json.s != "ok") { + throw new Error(json.s); + } + + const array = [] + if (Array.isArray(json.t)) { + for (let index = 0; index < json.t.length; index++) { + array.push([ + json.t[index], + json.o[index], + json.h[index], + json.l[index], + json.c[index] + ]) + } + } + + return mapResponse(array) +} + +module.exports = getHistoricalData \ No newline at end of file diff --git a/test/getHistoricalData.spec.js b/test/getHistoricalData.spec.js new file mode 100644 index 0000000..5842704 --- /dev/null +++ b/test/getHistoricalData.spec.js @@ -0,0 +1,105 @@ +const nock = require('nock') +const { getHistoricalData } = require('../index'); + +describe('Tests for getHistoricalData()', () => { + const scope = nock('https://tvc6.investing.com') + + afterEach(() => { + nock.cleanAll() + }) + + it('should return an async array', async () => { + scope + .get('/d8f62270e64f9eb6e4e6a07c3ffeab0b/1729428526/9/9/16/history') + .query(true) + .reply(200, { s: "ok" }) + await expect(getHistoricalData()).resolves.toBeInstanceOf(Array) + }) + + it('should call investing.com history api', async () => { + scope + .get('/d8f62270e64f9eb6e4e6a07c3ffeab0b/1729428526/9/9/16/history') + .query(true) + .reply(200, { s: "ok" }) + await getHistoricalData({ + input: '1', + resolution: 'D', + from: new Date(1729123200000), + to: new Date(1729209600000) + }) + expect(scope.isDone()).toBeTruthy() + }) + + it('should return historical data for a specified data range', async () => { + scope + .get('/d8f62270e64f9eb6e4e6a07c3ffeab0b/1729428526/9/9/16/history') + .query({ + symbol: '1', + resolution: 'D', + from: 1729123200, + to: 1729209600 + }) + .reply(200, { + "t": [ + 1729123200, + 1729209600 + ], + "c": [ + 1.08309996128082008937099089962430298328399658203125, + 1.0865999460220299166479662744677625596523284912109375 + ], + "o": [ + 1.086099982261659935289799250313080847263336181640625, + 1.08309996128082008937099089962430298328399658203125 + ], + "h": [ + 1.0872999429702800977537435755948536098003387451171875, + 1.086899995803829899188031049561686813831329345703125 + ], + "l": [ + 1.0809999704360999661645337255322374403476715087890625, + 1.0822999477386499034281541753443889319896697998046875 + ], + "v": [ + "n/a", + "n/a" + ], + "vo": [ + "n/a", + "n/a" + ], + "vac": [ + "n/a", + "n/a" + ], + "s": "ok" + }) + + const data = await getHistoricalData({ + input: '1', + resolution: 'D', + from: new Date(1729123200000), + to: new Date(1729209600000) + }) + + const expected = [ + { + date: 1729123200, + value: 1.086099982261659935289799250313080847263336181640625, + price_open: 1.086099982261659935289799250313080847263336181640625, + price_high: 1.0872999429702800977537435755948536098003387451171875, + price_low: 1.0809999704360999661645337255322374403476715087890625, + price_close: 1.08309996128082008937099089962430298328399658203125, + }, + { + date: 1729209600, + value: 1.08309996128082008937099089962430298328399658203125, + price_open: 1.08309996128082008937099089962430298328399658203125, + price_high: 1.086899995803829899188031049561686813831329345703125, + price_low: 1.0822999477386499034281541753443889319896697998046875, + price_close: 1.0865999460220299166479662744677625596523284912109375, + } + ] + expect(data).toEqual(expected) + }) + }) \ No newline at end of file