From 92477c913712b11e268858ab5e936bb63f1feca1 Mon Sep 17 00:00:00 2001 From: enen92 <92enen@gmail.com> Date: Thu, 6 Feb 2025 13:00:20 +0000 Subject: [PATCH 1/2] Move currency list to new currency module --- apps/api/src/app/admin/admin.module.ts | 2 + apps/api/src/app/admin/admin.service.ts | 5 +- apps/api/src/app/app.controller.ts | 3 + apps/api/src/app/app.module.ts | 2 + .../ghostfolio/ghostfolio.module.ts | 1 + apps/api/src/app/info/info.module.ts | 4 +- apps/api/src/app/info/info.service.ts | 6 +- ...aln-buy-and-sell-in-two-activities.spec.ts | 7 +- ...folio-calculator-baln-buy-and-sell.spec.ts | 7 +- .../twr/portfolio-calculator-baln-buy.spec.ts | 7 +- ...ator-btcusd-buy-and-sell-partially.spec.ts | 7 +- .../twr/portfolio-calculator-fee.spec.ts | 7 +- .../portfolio-calculator-googl-buy.spec.ts | 7 +- .../twr/portfolio-calculator-item.spec.ts | 7 +- .../portfolio-calculator-liability.spec.ts | 7 +- ...-calculator-msft-buy-with-dividend.spec.ts | 7 +- .../portfolio-calculator-no-orders.spec.ts | 7 +- ...ulator-novn-buy-and-sell-partially.spec.ts | 7 +- ...folio-calculator-novn-buy-and-sell.spec.ts | 7 +- .../src/services/currency/currency.module.ts | 13 ++++ .../src/services/currency/currency.service.ts | 77 +++++++++++++++++++ .../exchange-rate-data.module.ts | 2 + .../exchange-rate-data.service.ts | 73 ++---------------- 23 files changed, 127 insertions(+), 145 deletions(-) create mode 100644 apps/api/src/services/currency/currency.module.ts create mode 100644 apps/api/src/services/currency/currency.service.ts diff --git a/apps/api/src/app/admin/admin.module.ts b/apps/api/src/app/admin/admin.module.ts index 81c58ff03..02aff2aed 100644 --- a/apps/api/src/app/admin/admin.module.ts +++ b/apps/api/src/app/admin/admin.module.ts @@ -4,6 +4,7 @@ import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscriptio import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { CurrencyModule } from '@ghostfolio/api/services/currency/currency.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; @@ -23,6 +24,7 @@ import { QueueModule } from './queue/queue.module'; ApiModule, BenchmarkModule, ConfigurationModule, + CurrencyModule, DataGatheringModule, DataProviderModule, ExchangeRateDataModule, diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index fb6e90f5d..0c04a839d 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -3,6 +3,7 @@ import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { environment } from '@ghostfolio/api/environments/environment'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { CurrencyService } from '@ghostfolio/api/services/currency/currency.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; @@ -49,6 +50,7 @@ import { groupBy } from 'lodash'; export class AdminService { public constructor( private readonly benchmarkService: BenchmarkService, + private readonly currencyService: CurrencyService, private readonly configurationService: ConfigurationService, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, @@ -112,7 +114,7 @@ export class AdminService { } public async get(): Promise { - const exchangeRates = this.exchangeRateDataService + const exchangeRates = this.currencyService .getCurrencies() .filter((currency) => { return currency !== DEFAULT_CURRENCY; @@ -513,6 +515,7 @@ export class AdminService { if (key === PROPERTY_IS_READ_ONLY_MODE && value === 'true') { await this.putSetting(PROPERTY_IS_USER_SIGNUP_ENABLED, 'false'); } else if (key === PROPERTY_CURRENCIES) { + await this.currencyService.initialize(); await this.exchangeRateDataService.initialize(); } diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index 28437391f..0df16fd9a 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -1,3 +1,4 @@ +import { CurrencyService } from '@ghostfolio/api/services/currency/currency.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { Controller } from '@nestjs/common'; @@ -5,6 +6,7 @@ import { Controller } from '@nestjs/common'; @Controller() export class AppController { public constructor( + private readonly currencyService: CurrencyService, private readonly exchangeRateDataService: ExchangeRateDataService ) { this.initialize(); @@ -12,6 +14,7 @@ export class AppController { private async initialize() { try { + await this.currencyService.initialize(); await this.exchangeRateDataService.initialize(); } catch {} } diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 6d097aeff..81755b7f0 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -22,6 +22,7 @@ import { ServeStaticModule } from '@nestjs/serve-static'; import { StatusCodes } from 'http-status-codes'; import { join } from 'path'; +import { CurrencyModule } from '../services/currency/currency.module'; import { AccessModule } from './access/access.module'; import { AccountModule } from './account/account.module'; import { AdminModule } from './admin/admin.module'; @@ -75,6 +76,7 @@ import { UserModule } from './user/user.module'; CacheModule, ConfigModule.forRoot(), ConfigurationModule, + CurrencyModule, DataGatheringModule, DataProviderModule, EventEmitterModule.forRoot(), diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts index 01691bcf4..657e81d17 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts @@ -1,6 +1,7 @@ import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; +import { CurrencyModule } from '@ghostfolio/api/services/currency/currency.module'; import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service'; import { CoinGeckoService } from '@ghostfolio/api/services/data-provider/coingecko/coingecko.service'; import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service'; diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 7903ac397..20c3debf1 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -4,8 +4,8 @@ import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.mo import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { CurrencyModule } from '@ghostfolio/api/services/currency/currency.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; -import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; @@ -21,9 +21,9 @@ import { InfoService } from './info.service'; imports: [ BenchmarkModule, ConfigurationModule, + CurrencyModule, DataGatheringModule, DataProviderModule, - ExchangeRateDataModule, JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 1af41520d..364b9811b 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -3,7 +3,7 @@ import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { CurrencyService } from '@ghostfolio/api/services/currency/currency.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { DEFAULT_CURRENCY, @@ -41,7 +41,7 @@ export class InfoService { public constructor( private readonly benchmarkService: BenchmarkService, private readonly configurationService: ConfigurationService, - private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly currencyService: CurrencyService, private readonly jwtService: JwtService, private readonly platformService: PlatformService, private readonly propertyService: PropertyService, @@ -127,7 +127,7 @@ export class InfoService { statistics, subscriptionOffers, baseCurrency: DEFAULT_CURRENCY, - currencies: this.exchangeRateDataService.getCurrencies() + currencies: this.currencyService.getCurrencies() }; } diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index deb3cd72f..799657cbc 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -63,12 +63,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts index 7b4d53b2f..1d0d73ae3 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -63,12 +63,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts index 002cbd5e9..ea193dd19 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts @@ -63,12 +63,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 640de3985..941a676eb 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -76,12 +76,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts index 6f030a73d..6f459c30e 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts @@ -63,12 +63,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts index 4e25c17f7..a035be088 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts @@ -76,12 +76,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts index 7fc5c526d..a867c976c 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts @@ -63,12 +63,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts index 5fa90e94c..3f0f786fd 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts @@ -63,12 +63,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts index 543985424..257771620 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -76,12 +76,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts index 84898490f..e2fe14be2 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts @@ -58,12 +58,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 37f22e2f6..5b5991ce6 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -77,12 +77,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts index caf196f54..f84b13676 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -77,12 +77,7 @@ describe('PortfolioCalculator', () => { currentRateService = new CurrentRateService(null, null, null, null); - exchangeRateDataService = new ExchangeRateDataService( - null, - null, - null, - null - ); + exchangeRateDataService = new ExchangeRateDataService(null, null, null); portfolioSnapshotService = new PortfolioSnapshotService(null); diff --git a/apps/api/src/services/currency/currency.module.ts b/apps/api/src/services/currency/currency.module.ts new file mode 100644 index 000000000..00e3f4b1f --- /dev/null +++ b/apps/api/src/services/currency/currency.module.ts @@ -0,0 +1,13 @@ +import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { CurrencyService } from '@ghostfolio/api/services/currency/currency.service'; +import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; +import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; + +import { Module } from '@nestjs/common'; + +@Module({ + exports: [CurrencyService], + imports: [ConfigurationModule, PrismaModule, PropertyModule], + providers: [CurrencyService] +}) +export class CurrencyModule {} diff --git a/apps/api/src/services/currency/currency.service.ts b/apps/api/src/services/currency/currency.service.ts new file mode 100644 index 000000000..a87b1fe62 --- /dev/null +++ b/apps/api/src/services/currency/currency.service.ts @@ -0,0 +1,77 @@ +import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; +import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { + DEFAULT_CURRENCY, + DERIVED_CURRENCIES, + PROPERTY_CURRENCIES +} from '@ghostfolio/common/config'; + +import { Injectable } from '@nestjs/common'; +import { uniq } from 'lodash'; + +@Injectable() +export class CurrencyService { + private currencies: string[] = []; + + public constructor( + private readonly prismaService: PrismaService, + private readonly propertyService: PropertyService + ) {} + + public getCurrencies() { + return this.currencies?.length > 0 ? this.currencies : [DEFAULT_CURRENCY]; + } + + public async initialize() { + this.currencies = await this.prepareCurrencies(); + } + + private async prepareCurrencies(): Promise { + let currencies: string[] = [DEFAULT_CURRENCY]; + + ( + await this.prismaService.account.findMany({ + distinct: ['currency'], + orderBy: [{ currency: 'asc' }], + select: { currency: true }, + where: { + currency: { + not: null + } + } + }) + ).forEach(({ currency }) => { + currencies.push(currency); + }); + + ( + await this.prismaService.symbolProfile.findMany({ + distinct: ['currency'], + orderBy: [{ currency: 'asc' }], + select: { currency: true } + }) + ).forEach(({ currency }) => { + currencies.push(currency); + }); + + const customCurrencies = (await this.propertyService.getByKey( + PROPERTY_CURRENCIES + )) as string[]; + + if (customCurrencies?.length > 0) { + currencies = currencies.concat(customCurrencies); + } + + // Add derived currencies + currencies.push('USX'); + + for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { + if (currencies.includes(currency) || currencies.includes(rootCurrency)) { + currencies.push(currency); + currencies.push(rootCurrency); + } + } + + return uniq(currencies).filter(Boolean).sort(); + } +} diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.module.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.module.ts index 3063ddaf5..623e49f45 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.module.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.module.ts @@ -1,4 +1,5 @@ import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { CurrencyModule } from '@ghostfolio/api/services/currency/currency.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; @@ -11,6 +12,7 @@ import { Module } from '@nestjs/common'; exports: [ExchangeRateDataService], imports: [ ConfigurationModule, + CurrencyModule, DataProviderModule, MarketDataModule, PrismaModule, diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index db95a3487..3d041db92 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -1,14 +1,9 @@ import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; +import { CurrencyService } from '@ghostfolio/api/services/currency/currency.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; -import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; -import { PropertyService } from '@ghostfolio/api/services/property/property.service'; -import { - DEFAULT_CURRENCY, - DERIVED_CURRENCIES, - PROPERTY_CURRENCIES -} from '@ghostfolio/common/config'; +import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT, getYesterday, @@ -23,26 +18,20 @@ import { isToday, subDays } from 'date-fns'; -import { isNumber, uniq } from 'lodash'; +import { isNumber } from 'lodash'; import ms from 'ms'; @Injectable() export class ExchangeRateDataService { - private currencies: string[] = []; private currencyPairs: IDataGatheringItem[] = []; private exchangeRates: { [currencyPair: string]: number } = {}; public constructor( + private readonly currencyService: CurrencyService, private readonly dataProviderService: DataProviderService, - private readonly marketDataService: MarketDataService, - private readonly prismaService: PrismaService, - private readonly propertyService: PropertyService + private readonly marketDataService: MarketDataService ) {} - public getCurrencies() { - return this.currencies?.length > 0 ? this.currencies : [DEFAULT_CURRENCY]; - } - public getCurrencyPairs() { return this.currencyPairs; } @@ -133,7 +122,6 @@ export class ExchangeRateDataService { } public async initialize() { - this.currencies = await this.prepareCurrencies(); this.currencyPairs = []; this.exchangeRates = {}; @@ -141,7 +129,7 @@ export class ExchangeRateDataService { currency1, currency2, dataSource - } of this.prepareCurrencyPairs(this.currencies)) { + } of this.prepareCurrencyPairs(this.currencyService.getCurrencies())) { this.currencyPairs.push({ dataSource, symbol: `${currency1}${currency2}` @@ -469,55 +457,6 @@ export class ExchangeRateDataService { return factors; } - private async prepareCurrencies(): Promise { - let currencies: string[] = [DEFAULT_CURRENCY]; - - ( - await this.prismaService.account.findMany({ - distinct: ['currency'], - orderBy: [{ currency: 'asc' }], - select: { currency: true }, - where: { - currency: { - not: null - } - } - }) - ).forEach(({ currency }) => { - currencies.push(currency); - }); - - ( - await this.prismaService.symbolProfile.findMany({ - distinct: ['currency'], - orderBy: [{ currency: 'asc' }], - select: { currency: true } - }) - ).forEach(({ currency }) => { - currencies.push(currency); - }); - - const customCurrencies = (await this.propertyService.getByKey( - PROPERTY_CURRENCIES - )) as string[]; - - if (customCurrencies?.length > 0) { - currencies = currencies.concat(customCurrencies); - } - - // Add derived currencies - currencies.push('USX'); - - for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { - if (currencies.includes(currency) || currencies.includes(rootCurrency)) { - currencies.push(currency); - currencies.push(rootCurrency); - } - } - - return uniq(currencies).filter(Boolean).sort(); - } - private prepareCurrencyPairs(aCurrencies: string[]) { return aCurrencies .filter((currency) => { From e6ef2f52897a8605ab651409c85a57ca1e76018f Mon Sep 17 00:00:00 2001 From: enen92 <92enen@gmail.com> Date: Thu, 6 Feb 2025 17:41:50 +0000 Subject: [PATCH 2/2] Yahoo Finance: Allow searching for cryptocurrencies in the user configured currencies --- .../ghostfolio/ghostfolio.module.ts | 1 + .../cryptocurrency/cryptocurrency.service.ts | 12 ++++------- .../data-provider/data-provider.module.ts | 2 ++ .../yahoo-finance/yahoo-finance.service.ts | 20 ++++++++----------- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts index 657e81d17..a6136e395 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts @@ -26,6 +26,7 @@ import { GhostfolioService } from './ghostfolio.service'; @Module({ controllers: [GhostfolioController], imports: [ + CurrencyModule, CryptocurrencyModule, DataProviderModule, MarketDataModule, diff --git a/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts b/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts index b814fc186..4aa46d15c 100644 --- a/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts +++ b/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts @@ -1,5 +1,3 @@ -import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; - import { Injectable } from '@nestjs/common'; const cryptocurrencies = require('../../assets/cryptocurrencies/cryptocurrencies.json'); @@ -10,12 +8,10 @@ export class CryptocurrencyService { private combinedCryptocurrencies: string[]; public isCryptocurrency(aSymbol = '') { - const cryptocurrencySymbol = aSymbol.substring(0, aSymbol.length - 3); - - return ( - aSymbol.endsWith(DEFAULT_CURRENCY) && - this.getCryptocurrencies().includes(cryptocurrencySymbol) - ); + const cryptocurrencySymbol = aSymbol.includes('-') + ? aSymbol.split('-')[0] + : aSymbol; + return this.getCryptocurrencies().includes(cryptocurrencySymbol); } private getCryptocurrencies() { diff --git a/apps/api/src/services/data-provider/data-provider.module.ts b/apps/api/src/services/data-provider/data-provider.module.ts index 71b54f01e..03f11e395 100644 --- a/apps/api/src/services/data-provider/data-provider.module.ts +++ b/apps/api/src/services/data-provider/data-provider.module.ts @@ -1,6 +1,7 @@ import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; +import { CurrencyModule } from '@ghostfolio/api/services/currency/currency.module'; import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service'; import { CoinGeckoService } from '@ghostfolio/api/services/data-provider/coingecko/coingecko.service'; import { EodHistoricalDataService } from '@ghostfolio/api/services/data-provider/eod-historical-data/eod-historical-data.service'; @@ -24,6 +25,7 @@ import { DataProviderService } from './data-provider.service'; @Module({ imports: [ ConfigurationModule, + CurrencyModule, CryptocurrencyModule, DataEnhancerModule, MarketDataModule, 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 27da18ab0..1e16e51c0 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,4 +1,5 @@ import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service'; +import { CurrencyService } from '@ghostfolio/api/services/currency/currency.service'; import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service'; import { DataProviderInterface, @@ -11,7 +12,6 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DataProviderInfo, @@ -33,6 +33,7 @@ import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote'; @Injectable() export class YahooFinanceService implements DataProviderInterface { public constructor( + private readonly currencyService: CurrencyService, private readonly cryptocurrencyService: CryptocurrencyService, private readonly yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService ) {} @@ -247,21 +248,16 @@ export class YahooFinanceService implements DataProviderInterface { .filter(({ quoteType, symbol }) => { return ( (quoteType === 'CRYPTOCURRENCY' && - this.cryptocurrencyService.isCryptocurrency( - symbol.replace( - new RegExp(`-${DEFAULT_CURRENCY}$`), - DEFAULT_CURRENCY - ) - )) || + this.currencyService.getCurrencies().some((currency) => { + return this.cryptocurrencyService.isCryptocurrency( + symbol.replace(new RegExp(`-${currency}$`), '') + ); + })) || quoteTypes.includes(quoteType) ); }) .filter(({ quoteType, symbol }) => { - if (quoteType === 'CRYPTOCURRENCY') { - // Only allow cryptocurrencies in base currency to avoid having redundancy in the database. - // Transactions need to be converted manually to the base currency before - return symbol.includes(DEFAULT_CURRENCY); - } else if (quoteType === 'FUTURE') { + if (quoteType === 'FUTURE') { // Allow GC=F, but not MGC=F return symbol.length === 4; }