diff --git a/CHANGELOG.md b/CHANGELOG.md index 43275ac61..204cd3e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added support for more cryptocurrency symbols like _Avalanche_, _Polygon_, _SHIBA INU_ etc. + ### Changed - Changed the data provider service to handle a dynamic list of services diff --git a/apps/api/src/services/cryptocurrency/cryptocurrency.module.ts b/apps/api/src/services/cryptocurrency/cryptocurrency.module.ts new file mode 100644 index 000000000..8820205eb --- /dev/null +++ b/apps/api/src/services/cryptocurrency/cryptocurrency.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { CryptocurrencyService } from './cryptocurrency.service'; + +@Module({ + providers: [CryptocurrencyService], + exports: [CryptocurrencyService] +}) +export class CryptocurrencyModule {} diff --git a/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts b/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts new file mode 100644 index 000000000..1ae645208 --- /dev/null +++ b/apps/api/src/services/cryptocurrency/cryptocurrency.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; + +const cryptocurrencies = require('cryptocurrencies'); + +const customCryptocurrencies = require('./custom-cryptocurrencies.json'); + +@Injectable() +export class CryptocurrencyService { + private combinedCryptocurrencies: string[]; + + public constructor() {} + + public isCrypto(aSymbol = '') { + const cryptocurrencySymbol = aSymbol.substring(0, aSymbol.length - 3); + return this.getCryptocurrencies().includes(cryptocurrencySymbol); + } + + private getCryptocurrencies() { + if (!this.combinedCryptocurrencies) { + this.combinedCryptocurrencies = [ + ...cryptocurrencies.symbols(), + ...Object.keys(customCryptocurrencies) + ]; + } + + return this.combinedCryptocurrencies; + } +} diff --git a/apps/api/src/services/cryptocurrency/custom-cryptocurrencies.json b/apps/api/src/services/cryptocurrency/custom-cryptocurrencies.json new file mode 100644 index 000000000..3e181c6a7 --- /dev/null +++ b/apps/api/src/services/cryptocurrency/custom-cryptocurrencies.json @@ -0,0 +1,6 @@ +{ + "1INCH": "1inch", + "AVAX": "Avalanche", + "MATIC": "Polygon", + "SHIB": "Shiba Inu" +} 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 9ade183a5..234d4d694 100644 --- a/apps/api/src/services/data-provider/data-provider.module.ts +++ b/apps/api/src/services/data-provider/data-provider.module.ts @@ -1,4 +1,5 @@ import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; +import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; import { TrackinsightDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/trackinsight/trackinsight.service'; import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service'; import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service'; @@ -10,7 +11,7 @@ import { AlphaVantageService } from './alpha-vantage/alpha-vantage.service'; import { DataProviderService } from './data-provider.service'; @Module({ - imports: [ConfigurationModule, PrismaModule], + imports: [ConfigurationModule, CryptocurrencyModule, PrismaModule], providers: [ AlphaVantageService, DataProviderService, diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index bba2b8b82..ff975251a 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -1,6 +1,7 @@ import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface'; +import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { IDataGatheringItem, IDataProviderHistoricalResponse, @@ -13,7 +14,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, MarketData } from '@prisma/client'; import { format } from 'date-fns'; import { isEmpty } from 'lodash'; -import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; @Injectable() export class DataProviderService { diff --git a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts index ebc72e922..5f99c8614 100644 --- a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts +++ b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts @@ -1,11 +1,11 @@ import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { Granularity } from '@ghostfolio/common/types'; +import { DataSource } from '@prisma/client'; import { IDataProviderHistoricalResponse, IDataProviderResponse } from '../../interfaces/interfaces'; -import { DataSource } from '@prisma/client'; export interface DataProviderInterface { canHandle(symbol: string): boolean; 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 0a87c2a16..925a33df9 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,6 +1,7 @@ import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; +import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service'; import { UNKNOWN_KEY } from '@ghostfolio/common/config'; -import { DATE_FORMAT, isCrypto, isCurrency } from '@ghostfolio/common/helper'; +import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper'; import { Granularity } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; @@ -26,7 +27,9 @@ import { export class YahooFinanceService implements DataProviderInterface { private yahooFinanceHostname = 'https://query1.finance.yahoo.com'; - public constructor() {} + public constructor( + private readonly cryptocurrencyService: CryptocurrencyService + ) {} public canHandle(symbol: string) { return true; @@ -65,7 +68,8 @@ export class YahooFinanceService implements DataProviderInterface { dataSource: DataSource.YAHOO, exchange: this.parseExchange(value.price?.exchangeName), marketState: - value.price?.marketState === 'REGULAR' || isCrypto(symbol) + value.price?.marketState === 'REGULAR' || + this.cryptocurrencyService.isCrypto(symbol) ? MarketState.open : MarketState.closed, marketPrice: value.price?.regularMarketPrice || 0, @@ -249,7 +253,10 @@ export class YahooFinanceService implements DataProviderInterface { ) { if (isCurrency(aSymbol.substring(0, aSymbol.length - 3))) { return `${aSymbol}=X`; - } else if (isCrypto(aSymbol) || isCrypto(aSymbol.replace('1', ''))) { + } else if ( + this.cryptocurrencyService.isCrypto(aSymbol) || + this.cryptocurrencyService.isCrypto(aSymbol.replace('1', '')) + ) { // Add a dash before the last three characters // BTCUSD -> BTC-USD // DOGEUSD -> DOGE-USD diff --git a/libs/common/src/lib/customCryptocurrencies.json b/libs/common/src/lib/customCryptocurrencies.json deleted file mode 100644 index 342414d0d..000000000 --- a/libs/common/src/lib/customCryptocurrencies.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "SHIB": "SHIBA INU USD", - "SOL1": "Solana", - "DOT1": "Polkadot", - "LUNA1": "Terra", - "AVAX": "Avalanche", - "MATIC": "MaticNetwork", - "ATOM1": "Cosmos", - "FTT1": "FTXToken", - "1INCH": "1inch" -} diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 157d932ea..02ce9b84b 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -3,11 +3,6 @@ import { getDate, getMonth, getYear, parse, subDays } from 'date-fns'; import { ghostfolioScraperApiSymbolPrefix } from './config'; -const cryptocurrencies = require('cryptocurrencies'); - -const customSymbolList = require('./customCryptocurrencies.json') -customSymbolList.symbols = () => Object.keys(customSymbolList); - export const DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f'; export function capitalize(aString: string) { @@ -53,9 +48,9 @@ export function getUtc(aDateString: string) { return new Date( Date.UTC( - parseInt(yearString), - parseInt(monthString) - 1, - parseInt(dayString) + parseInt(yearString, 10), + parseInt(monthString, 10) - 1, + parseInt(dayString, 10) ) ); } @@ -82,11 +77,6 @@ export function groupBy( return map; } -export function isCrypto(aSymbol = '') { - const cryptocurrencySymbol = aSymbol.substring(0, aSymbol.length - 3); - return cryptocurrencies.symbols().includes(cryptocurrencySymbol) || customSymbolList.symbols().includes(cryptocurrencySymbol); -} - export function isCurrency(aSymbol = '') { return currencies[aSymbol]; }