From c30201b1a95f9e172bb5cc03b46a5811637e09fd Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Sat, 21 Oct 2023 11:56:09 +0200 Subject: [PATCH] Setup OpenFIGI --- apps/api/src/app/import/import.service.ts | 9 +++ .../data-gathering/data-gathering.service.ts | 9 +++ .../data-enhancer/data-enhancer.module.ts | 10 ++- .../openfigi/openfigi.service.ts | 73 +++++++++++++++++++ libs/common/src/lib/helper.ts | 9 +++ .../migration.sql | 5 ++ prisma/schema.prisma | 3 + 7 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts create mode 100644 prisma/migrations/20231021094346_added_figi_figi_composite_and_figi_share_class_to_symbol_profile/migration.sql diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 83d062b83..8fd35f8dd 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -280,6 +280,9 @@ export class ImportService { createdAt, currency, dataSource, + figi, + figiComposite, + figiShareClass, id, isin, name, @@ -350,6 +353,9 @@ export class ImportService { createdAt, currency, dataSource, + figi, + figiComposite, + figiShareClass, id, isin, name, @@ -509,6 +515,9 @@ export class ImportService { comment: null, countries: null, createdAt: undefined, + figi: null, + figiComposite: null, + figiShareClass: null, id: undefined, isin: null, name: null, diff --git a/apps/api/src/services/data-gathering/data-gathering.service.ts b/apps/api/src/services/data-gathering/data-gathering.service.ts index 34645b9ea..78531b745 100644 --- a/apps/api/src/services/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/data-gathering/data-gathering.service.ts @@ -164,6 +164,9 @@ export class DataGatheringService { countries, currency, dataSource, + figi, + figiComposite, + figiShareClass, isin, name, sectors, @@ -178,6 +181,9 @@ export class DataGatheringService { countries, currency, dataSource, + figi, + figiComposite, + figiShareClass, isin, name, sectors, @@ -189,6 +195,9 @@ export class DataGatheringService { assetSubClass, countries, currency, + figi, + figiComposite, + figiShareClass, isin, name, sectors, diff --git a/apps/api/src/services/data-provider/data-enhancer/data-enhancer.module.ts b/apps/api/src/services/data-provider/data-enhancer/data-enhancer.module.ts index 069309508..23d64ac86 100644 --- a/apps/api/src/services/data-provider/data-enhancer/data-enhancer.module.ts +++ b/apps/api/src/services/data-provider/data-enhancer/data-enhancer.module.ts @@ -1,5 +1,6 @@ import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; +import { OpenFigiDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/openfigi/openfigi.service'; import { TrackinsightDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/trackinsight/trackinsight.service'; import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service'; import { Module } from '@nestjs/common'; @@ -9,6 +10,7 @@ import { DataEnhancerService } from './data-enhancer.service'; @Module({ exports: [ DataEnhancerService, + OpenFigiDataEnhancerService, TrackinsightDataEnhancerService, YahooFinanceDataEnhancerService, 'DataEnhancers' @@ -16,15 +18,21 @@ import { DataEnhancerService } from './data-enhancer.service'; imports: [ConfigurationModule, CryptocurrencyModule], providers: [ DataEnhancerService, + OpenFigiDataEnhancerService, TrackinsightDataEnhancerService, YahooFinanceDataEnhancerService, { inject: [ + OpenFigiDataEnhancerService, TrackinsightDataEnhancerService, YahooFinanceDataEnhancerService ], provide: 'DataEnhancers', - useFactory: (trackinsight, yahooFinance) => [trackinsight, yahooFinance] + useFactory: (openfigi, trackinsight, yahooFinance) => [ + openfigi, + trackinsight, + yahooFinance + ] } ] }) diff --git a/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts b/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts new file mode 100644 index 000000000..8e22662e1 --- /dev/null +++ b/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts @@ -0,0 +1,73 @@ +import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface'; +import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config'; +import { parseSymbol } from '@ghostfolio/common/helper'; +import { Injectable } from '@nestjs/common'; +import { SymbolProfile } from '@prisma/client'; +import got from 'got'; + +@Injectable() +export class OpenFigiDataEnhancerService implements DataEnhancerInterface { + private static baseUrl = 'https://api.openfigi.com'; + + public async enhance({ + response, + symbol + }: { + response: Partial; + symbol: string; + }): Promise> { + if ( + !( + response.assetClass === 'EQUITY' && + (response.assetSubClass === 'ETF' || response.assetSubClass === 'STOCK') + ) + ) { + return response; + } + + const { exchange, ticker } = parseSymbol({ + symbol, + dataSource: response.dataSource + }); + + let abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + + const mappings = await got + .post(`${OpenFigiDataEnhancerService.baseUrl}/v3/mapping`, { + json: [{ exchCode: exchange, idType: 'TICKER', idValue: ticker }], + // @ts-ignore + signal: abortController.signal + }) + .json(); + + if (mappings?.length === 1 && mappings[0].data?.length === 1) { + const { compositeFIGI, figi, shareClassFIGI } = mappings[0].data[0]; + + if (figi) { + response.figi = figi; + } + + if (compositeFIGI) { + response.figiComposite = compositeFIGI; + } + + if (shareClassFIGI) { + response.figiShareClass = shareClassFIGI; + } + } + + return response; + } + + public getName() { + return 'OPENFIGI'; + } + + public getTestSymbol() { + return undefined; + } +} diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 03fc250b8..7b8c72eab 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -322,6 +322,15 @@ export function parseDate(date: string): Date | null { return parseISO(date); } +export function parseSymbol({ dataSource, symbol }: UniqueAsset) { + const [ticker, exchange] = symbol.split('.'); + + return { + exchange: exchange ?? (dataSource === 'YAHOO' ? 'US' : undefined), + ticker + }; +} + export function prettifySymbol(aSymbol: string): string { return aSymbol?.replace(ghostfolioScraperApiSymbolPrefix, ''); } diff --git a/prisma/migrations/20231021094346_added_figi_figi_composite_and_figi_share_class_to_symbol_profile/migration.sql b/prisma/migrations/20231021094346_added_figi_figi_composite_and_figi_share_class_to_symbol_profile/migration.sql new file mode 100644 index 000000000..eb8abbe5a --- /dev/null +++ b/prisma/migrations/20231021094346_added_figi_figi_composite_and_figi_share_class_to_symbol_profile/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "SymbolProfile" +ADD COLUMN "figi" TEXT, +ADD COLUMN "figiComposite" TEXT, +ADD COLUMN "figiShareClass" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a4d4e028e..015431e6c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -132,6 +132,9 @@ model SymbolProfile { createdAt DateTime @default(now()) currency String dataSource DataSource + figi String? + figiComposite String? + figiShareClass String? id String @id @default(uuid()) isin String? name String?