From 15dc953bc7766ae01921440fec72a1b91badd5a5 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Wed, 9 Apr 2025 07:32:27 +0200 Subject: [PATCH 1/3] Deactivate asset profile on delisting --- CHANGELOG.md | 1 + .../asset-profile-delisted.error.ts | 6 +++++ .../yahoo-finance/yahoo-finance.service.ts | 18 +++++++++----- .../data-gathering.processor.ts | 24 ++++++++++++++++--- 4 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fd591b934..ba90bb9d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added the data gathering status column to the historical market data table of the admin control +- Deactivated asset profiles that has been delisted by Yahoo Finance ### Changed diff --git a/apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts b/apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts new file mode 100644 index 000000000..715001598 --- /dev/null +++ b/apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts @@ -0,0 +1,6 @@ +export class AssetProfileDelistedError extends Error { + constructor(message: string) { + super(message); + this.name = 'AssetProfileDelistedError'; + } +} diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 72ae1ff97..58a9432d8 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -31,6 +31,8 @@ import { } from 'yahoo-finance2/dist/esm/src/modules/historical'; import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote'; +import { AssetProfileDelistedError } from './asset-profile-delisted.error'; + @Injectable() export class YahooFinanceService implements DataProviderInterface { public constructor( @@ -143,12 +145,16 @@ export class YahooFinanceService implements DataProviderInterface { return response; } catch (error) { - throw new Error( - `Could not get historical market data for ${symbol} (${this.getName()}) from ${format( - from, - DATE_FORMAT - )} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}` - ); + if (error.message === 'No data found, symbol may be delisted') { + throw new AssetProfileDelistedError(error.message); + } else { + throw new Error( + `Could not get historical market data for ${symbol} (${this.getName()}) from ${format( + from, + DATE_FORMAT + )} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}` + ); + } } } diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts index eedad7475..3e011f6e6 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts @@ -1,6 +1,8 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; +import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/yahoo-finance/asset-profile-delisted.error'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DATA_GATHERING_QUEUE, DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY, @@ -33,7 +35,8 @@ export class DataGatheringProcessor { public constructor( private readonly dataGatheringService: DataGatheringService, private readonly dataProviderService: DataProviderService, - private readonly marketDataService: MarketDataService + private readonly marketDataService: MarketDataService, + private readonly symbolProfileService: SymbolProfileService ) {} @Process({ @@ -76,8 +79,9 @@ export class DataGatheringProcessor { name: GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME }) public async gatherHistoricalMarketData(job: Job) { + const { dataSource, date, symbol } = job.data; + try { - const { dataSource, date, symbol } = job.data; let currentDate = parseISO(date as unknown as string); Logger.log( @@ -142,12 +146,26 @@ export class DataGatheringProcessor { `DataGatheringProcessor (${GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME})` ); } catch (error) { + if (error instanceof AssetProfileDelistedError) { + const updatedSymbolProfile: Prisma.SymbolProfileUpdateInput = { + isActive: false + }; + + await this.symbolProfileService.updateSymbolProfile( + { + dataSource, + symbol + }, + updatedSymbolProfile + ); + } + Logger.error( error, `DataGatheringProcessor (${GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME})` ); - throw new Error(error); + throw error; } } } From e657ca97d44ab1482273f01ebe0a04fca057b02b Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Wed, 9 Apr 2025 07:38:39 +0200 Subject: [PATCH 2/3] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba90bb9d0..a9df19597 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added the data gathering status column to the historical market data table of the admin control -- Deactivated asset profiles that has been delisted by Yahoo Finance +- Deactivated asset profile on delisting (Yahoo Finance) ### Changed From 0ea845100b7beff5094da993d723ffa4036e3888 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Fri, 11 Apr 2025 09:55:12 +0200 Subject: [PATCH 3/3] Code review changes --- CHANGELOG.md | 2 +- .../asset-profile-delisted.error.ts | 2 +- .../yahoo-finance/yahoo-finance.service.ts | 3 +-- .../data-gathering/data-gathering.processor.ts | 12 ++++++------ 4 files changed, 9 insertions(+), 10 deletions(-) rename apps/api/src/services/data-provider/{yahoo-finance => errors}/asset-profile-delisted.error.ts (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9df19597..6a3a2c46e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added the data gathering status column to the historical market data table of the admin control -- Deactivated asset profile on delisting (Yahoo Finance) +- Deactivated asset profiles automatically on delisting in the _Yahoo Finance_ service ### Changed diff --git a/apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts b/apps/api/src/services/data-provider/errors/asset-profile-delisted.error.ts similarity index 75% rename from apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts rename to apps/api/src/services/data-provider/errors/asset-profile-delisted.error.ts index 715001598..d326ee74b 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/asset-profile-delisted.error.ts +++ b/apps/api/src/services/data-provider/errors/asset-profile-delisted.error.ts @@ -1,5 +1,5 @@ export class AssetProfileDelistedError extends Error { - constructor(message: string) { + public constructor(message: string) { super(message); this.name = 'AssetProfileDelistedError'; } diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 58a9432d8..d8f4a143f 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -1,5 +1,6 @@ import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service'; import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service'; +import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error'; import { DataProviderInterface, GetAssetProfileParams, @@ -31,8 +32,6 @@ import { } from 'yahoo-finance2/dist/esm/src/modules/historical'; import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote'; -import { AssetProfileDelistedError } from './asset-profile-delisted.error'; - @Injectable() export class YahooFinanceService implements DataProviderInterface { public constructor( diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts index 3e011f6e6..d7fcc5ab3 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts @@ -1,5 +1,5 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; -import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/yahoo-finance/asset-profile-delisted.error'; +import { AssetProfileDelistedError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-delisted.error'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; @@ -147,17 +147,17 @@ export class DataGatheringProcessor { ); } catch (error) { if (error instanceof AssetProfileDelistedError) { - const updatedSymbolProfile: Prisma.SymbolProfileUpdateInput = { - isActive: false - }; - await this.symbolProfileService.updateSymbolProfile( { dataSource, symbol }, - updatedSymbolProfile + { + isActive: false + } ); + + await job.discard(); } Logger.error(