diff --git a/apps/api/src/app/core/portfolio-calculator.spec.ts b/apps/api/src/app/core/portfolio-calculator.spec.ts index 9de8ff26..f4d5173d 100644 --- a/apps/api/src/app/core/portfolio-calculator.spec.ts +++ b/apps/api/src/app/core/portfolio-calculator.spec.ts @@ -7,7 +7,7 @@ import { TimelineSpecification } from '@ghostfolio/api/app/core/interfaces/timel import { TransactionPoint } from '@ghostfolio/api/app/core/interfaces/transaction-point.interface'; import { PortfolioCalculator } from '@ghostfolio/api/app/core/portfolio-calculator'; import { OrderType } from '@ghostfolio/api/models/order-type'; -import { resetHours } from '@ghostfolio/common/helper'; +import { DATE_FORMAT, resetHours } from '@ghostfolio/common/helper'; import { Currency } from '@prisma/client'; import Big from 'big.js'; import { @@ -25,7 +25,7 @@ function mockGetValue(symbol: string, date: Date) { if (isSameDay(today, date)) { return { marketPrice: 213.32 }; } else { - const startDate = parse('2019-02-01', 'yyyy-MM-dd', new Date()); + const startDate = parse('2019-02-01', DATE_FORMAT, new Date()); const daysInBetween = differenceInCalendarDays(date, startDate); const marketPrice = new Big('144.38').plus( @@ -36,10 +36,10 @@ function mockGetValue(symbol: string, date: Date) { } else if (symbol === 'AMZN') { return { marketPrice: 2021.99 }; } else if (symbol === 'TSLA') { - if (isSameDay(parse('2021-07-26', 'yyyy-MM-dd', new Date()), date)) { + if (isSameDay(parse('2021-07-26', DATE_FORMAT, new Date()), date)) { return { marketPrice: 657.62 }; } - if (isSameDay(parse('2021-01-02', 'yyyy-MM-dd', new Date()), date)) { + if (isSameDay(parse('2021-01-02', DATE_FORMAT, new Date()), date)) { return { marketPrice: 666.66 }; } return { marketPrice: 0 }; @@ -607,7 +607,7 @@ describe('PortfolioCalculator', () => { .spyOn(Date, 'now') .mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26 const currentPositions = await portfolioCalculator.getCurrentPositions( - parse('2020-01-21', 'yyyy-MM-dd', new Date()) + parse('2020-01-21', DATE_FORMAT, new Date()) ); spy.mockRestore(); @@ -642,7 +642,7 @@ describe('PortfolioCalculator', () => { .spyOn(Date, 'now') .mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26 const currentPositions = await portfolioCalculator.getCurrentPositions( - parse('2021-01-01', 'yyyy-MM-dd', new Date()) + parse('2021-01-01', DATE_FORMAT, new Date()) ); spy.mockRestore(); @@ -677,7 +677,7 @@ describe('PortfolioCalculator', () => { .spyOn(Date, 'now') .mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26 const currentPositions = await portfolioCalculator.getCurrentPositions( - parse('2021-01-02', 'yyyy-MM-dd', new Date()) + parse('2021-01-02', DATE_FORMAT, new Date()) ); spy.mockRestore(); @@ -712,7 +712,7 @@ describe('PortfolioCalculator', () => { .spyOn(Date, 'now') .mockImplementation(() => new Date(Date.UTC(2020, 9, 24)).getTime()); // 2020-10-24 const currentPositions = await portfolioCalculator.getCurrentPositions( - parse('2019-01-01', 'yyyy-MM-dd', new Date()) + parse('2019-01-01', DATE_FORMAT, new Date()) ); spy.mockRestore(); @@ -788,7 +788,7 @@ describe('PortfolioCalculator', () => { // gross performance percentage: 1.100526008 * 1.034838024 = 1.138866159 => 13.89 % const currentPositions = await portfolioCalculator.getCurrentPositions( - parse('2020-01-01', 'yyyy-MM-dd', new Date()) + parse('2020-01-01', DATE_FORMAT, new Date()) ); spy.mockRestore(); diff --git a/apps/api/src/app/experimental/experimental.controller.ts b/apps/api/src/app/experimental/experimental.controller.ts index f8535ac5..0736a001 100644 --- a/apps/api/src/app/experimental/experimental.controller.ts +++ b/apps/api/src/app/experimental/experimental.controller.ts @@ -1,4 +1,5 @@ import { baseCurrency, benchmarks } from '@ghostfolio/common/config'; +import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { isApiTokenAuthorized } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -82,7 +83,7 @@ export class ExperimentalController { let date = new Date(); if (dateString) { - date = parse(dateString, 'yyyy-MM-dd', new Date()); + date = parse(dateString, DATE_FORMAT, new Date()); } return this.experimentalService.getValue(orders, date, baseCurrency); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 7a861780..885ce675 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -14,7 +14,7 @@ import { ImpersonationService } from '@ghostfolio/api/services/impersonation.ser import { IOrder } from '@ghostfolio/api/services/interfaces/interfaces'; import { Type } from '@ghostfolio/api/services/interfaces/interfaces'; import { RulesService } from '@ghostfolio/api/services/rules.service'; -import { parseDate } from '@ghostfolio/common/helper'; +import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { PortfolioItem, PortfolioOverview, @@ -57,6 +57,7 @@ import { export class PortfolioService { public constructor( private readonly accountService: AccountService, + private readonly currentRateService: CurrentRateService, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, private readonly impersonationService: ImpersonationService, @@ -64,8 +65,7 @@ export class PortfolioService { private readonly redisCacheService: RedisCacheService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly rulesService: RulesService, - private readonly userService: UserService, - private readonly currentRateService: CurrentRateService + private readonly userService: UserService ) {} public async createPortfolio(aUserId: string): Promise<Portfolio> { @@ -163,32 +163,31 @@ export class PortfolioService { if (transactionPoints.length === 0) { return []; } - const dateFormat = 'yyyy-MM-dd'; let portfolioStart = parse( transactionPoints[0].date, - dateFormat, + DATE_FORMAT, new Date() ); portfolioStart = this.getStartDate(aDateRange, portfolioStart); const timelineSpecification: TimelineSpecification[] = [ { - start: format(portfolioStart, dateFormat), + start: format(portfolioStart, DATE_FORMAT), accuracy: 'day' } ]; const timeline = await portfolioCalculator.calculateTimeline( timelineSpecification, - format(new Date(), dateFormat) + format(new Date(), DATE_FORMAT) ); return timeline .filter((timelineItem) => timelineItem !== null) .map((timelineItem) => ({ date: timelineItem.date, - value: timelineItem.grossPerformance.toNumber(), - marketPrice: timelineItem.value + marketPrice: timelineItem.value, + value: timelineItem.grossPerformance.toNumber() })); } @@ -270,7 +269,7 @@ export class PortfolioService { for (const [date, { marketPrice }] of Object.entries( historicalData[aSymbol] )) { - const currentDate = parse(date, 'yyyy-MM-dd', new Date()); + const currentDate = parse(date, DATE_FORMAT, new Date()); if ( isSameDay(currentDate, parseISO(orders[0]?.getDate())) || isAfter(currentDate, parseISO(orders[0]?.getDate())) @@ -533,7 +532,7 @@ export class PortfolioService { const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ currency: order.currency, - date: format(order.date, 'yyyy-MM-dd'), + date: format(order.date, DATE_FORMAT), name: order.SymbolProfile?.name, quantity: new Big(order.quantity), symbol: order.symbol, diff --git a/apps/api/src/models/portfolio.spec.ts b/apps/api/src/models/portfolio.spec.ts index ebd888c4..053ef4f8 100644 --- a/apps/api/src/models/portfolio.spec.ts +++ b/apps/api/src/models/portfolio.spec.ts @@ -1,6 +1,6 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { UNKNOWN_KEY, baseCurrency } from '@ghostfolio/common/config'; -import { getUtc, getYesterday } from '@ghostfolio/common/helper'; +import { DATE_FORMAT, getUtc, getYesterday } from '@ghostfolio/common/helper'; import { AccountType, Currency, @@ -30,8 +30,8 @@ jest.mock('../app/account/account.service', () => { jest.mock('../services/data-provider.service', () => { return { DataProviderService: jest.fn().mockImplementation(() => { - const today = format(new Date(), 'yyyy-MM-dd'); - const yesterday = format(getYesterday(), 'yyyy-MM-dd'); + const today = format(new Date(), DATE_FORMAT); + const yesterday = format(getYesterday(), DATE_FORMAT); return { get: () => { diff --git a/apps/api/src/models/portfolio.ts b/apps/api/src/models/portfolio.ts index b961333b..1ea84182 100644 --- a/apps/api/src/models/portfolio.ts +++ b/apps/api/src/models/portfolio.ts @@ -1,7 +1,12 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface'; import { UNKNOWN_KEY, ghostfolioCashSymbol } from '@ghostfolio/common/config'; -import { getToday, getYesterday, resetHours } from '@ghostfolio/common/helper'; +import { + DATE_FORMAT, + getToday, + getYesterday, + resetHours +} from '@ghostfolio/common/helper'; import { PortfolioItem, PortfolioPerformance, @@ -731,7 +736,7 @@ export class Portfolio implements PortfolioInterface { investment: 0, investmentInOriginalCurrency: 0, marketPrice: - historicalData[symbol]?.[format(currentDate, 'yyyy-MM-dd')] + historicalData[symbol]?.[format(currentDate, DATE_FORMAT)] ?.marketPrice || 0, quantity: 0, transactionCount: 0 @@ -774,7 +779,7 @@ export class Portfolio implements PortfolioInterface { investment: 0, investmentInOriginalCurrency: 0, marketPrice: - historicalData[symbol]?.[format(yesterday, 'yyyy-MM-dd')] + historicalData[symbol]?.[format(yesterday, DATE_FORMAT)] ?.marketPrice || 0, name: '', quantity: 0, diff --git a/apps/api/src/services/data-gathering.service.ts b/apps/api/src/services/data-gathering.service.ts index 2fbc4655..e726ba0a 100644 --- a/apps/api/src/services/data-gathering.service.ts +++ b/apps/api/src/services/data-gathering.service.ts @@ -1,5 +1,6 @@ import { benchmarks, currencyPairs } from '@ghostfolio/common/config'; import { + DATE_FORMAT, getUtc, isGhostfolioScraperApiSymbol, isRakutenRapidApiSymbol, @@ -193,11 +194,11 @@ export class DataGatheringService { ) ) { if ( - historicalData[symbol]?.[format(currentDate, 'yyyy-MM-dd')] + historicalData[symbol]?.[format(currentDate, DATE_FORMAT)] ?.marketPrice ) { lastMarketPrice = - historicalData[symbol]?.[format(currentDate, 'yyyy-MM-dd')] + historicalData[symbol]?.[format(currentDate, DATE_FORMAT)] ?.marketPrice; } diff --git a/apps/api/src/services/data-provider.service.ts b/apps/api/src/services/data-provider.service.ts index e920399e..98fbc6ee 100644 --- a/apps/api/src/services/data-provider.service.ts +++ b/apps/api/src/services/data-provider.service.ts @@ -1,4 +1,5 @@ import { + DATE_FORMAT, isGhostfolioScraperApiSymbol, isRakutenRapidApiSymbol } from '@ghostfolio/common/helper'; @@ -100,9 +101,9 @@ export class DataProviderService { const rangeQuery = from && to - ? `AND date >= '${format(from, 'yyyy-MM-dd')}' AND date <= '${format( + ? `AND date >= '${format(from, DATE_FORMAT)}' AND date <= '${format( to, - 'yyyy-MM-dd' + DATE_FORMAT )}'` : ''; @@ -120,7 +121,7 @@ export class DataProviderService { r[symbol] = { ...(r[symbol] || {}), - [format(new Date(date), 'yyyy-MM-dd')]: { marketPrice } + [format(new Date(date), DATE_FORMAT)]: { marketPrice } }; return r; diff --git a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 7632c418..9bfc861a 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts +++ b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts @@ -1,4 +1,5 @@ import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; +import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { Granularity } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { DataSource } from '@prisma/client'; @@ -66,8 +67,8 @@ export class AlphaVantageService implements DataProviderInterface { historicalData['Time Series (Digital Currency Daily)'] ).sort()) { if ( - isAfter(from, parse(key, 'yyyy-MM-dd', new Date())) && - isBefore(to, parse(key, 'yyyy-MM-dd', new Date())) + isAfter(from, parse(key, DATE_FORMAT, new Date())) && + isBefore(to, parse(key, DATE_FORMAT, new Date())) ) { response[symbol][key] = { marketPrice: parseFloat(timeSeries['4a. close (USD)']) diff --git a/apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts b/apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts index 8b8e4290..32544806 100644 --- a/apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts @@ -1,4 +1,5 @@ import { + DATE_FORMAT, getYesterday, isGhostfolioScraperApiSymbol } from '@ghostfolio/common/helper'; @@ -95,7 +96,7 @@ export class GhostfolioScraperApiService implements DataProviderInterface { return { [symbol]: { - [format(getYesterday(), 'yyyy-MM-dd')]: { + [format(getYesterday(), DATE_FORMAT)]: { marketPrice: value } } @@ -109,14 +110,13 @@ export class GhostfolioScraperApiService implements DataProviderInterface { public async getScraperConfigurations(): Promise<ScraperConfig[]> { try { - const { - value: scraperConfigString - } = await this.prisma.property.findFirst({ - select: { - value: true - }, - where: { key: 'SCRAPER_CONFIG' } - }); + const { value: scraperConfigString } = + await this.prisma.property.findFirst({ + select: { + value: true + }, + where: { key: 'SCRAPER_CONFIG' } + }); return JSON.parse(scraperConfigString); } catch {} diff --git a/apps/api/src/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service.ts b/apps/api/src/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service.ts index 5b5682bd..6b8fa7c8 100644 --- a/apps/api/src/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service.ts @@ -1,4 +1,5 @@ import { + DATE_FORMAT, getToday, getYesterday, isRakutenRapidApiSymbol @@ -117,7 +118,7 @@ export class RakutenRapidApiService implements DataProviderInterface { return { 'GF.FEAR_AND_GREED_INDEX': { - [format(getYesterday(), 'yyyy-MM-dd')]: { + [format(getYesterday(), DATE_FORMAT)]: { marketPrice: fgi.previousClose.value } } 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 250909aa..c9ef1f69 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,11 @@ import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { UNKNOWN_KEY } from '@ghostfolio/common/config'; -import { isCrypto, isCurrency, parseCurrency } from '@ghostfolio/common/helper'; +import { + DATE_FORMAT, + isCrypto, + isCurrency, + parseCurrency +} from '@ghostfolio/common/helper'; import { Granularity } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { DataSource } from '@prisma/client'; @@ -103,8 +108,8 @@ export class YahooFinanceService implements DataProviderInterface { [symbol: string]: IYahooFinanceHistoricalResponse[]; } = await yahooFinance.historical({ symbols: yahooSymbols, - from: format(from, 'yyyy-MM-dd'), - to: format(to, 'yyyy-MM-dd') + from: format(from, DATE_FORMAT), + to: format(to, DATE_FORMAT) }); const response: { @@ -117,7 +122,7 @@ export class YahooFinanceService implements DataProviderInterface { response[symbol] = {}; timeSeries.forEach((timeSerie) => { - response[symbol][format(timeSerie.date, 'yyyy-MM-dd')] = { + response[symbol][format(timeSerie.date, DATE_FORMAT)] = { marketPrice: timeSerie.close, performance: timeSerie.open - timeSerie.close }; diff --git a/apps/api/src/services/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data.service.ts index 29ea3786..0d5f6787 100644 --- a/apps/api/src/services/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data.service.ts @@ -1,4 +1,4 @@ -import { getYesterday } from '@ghostfolio/common/helper'; +import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; import { Injectable } from '@nestjs/common'; import { Currency } from '@prisma/client'; import { format } from 'date-fns'; @@ -51,7 +51,7 @@ export class ExchangeRateDataService { this.pairs.forEach((pair) => { const [currency1, currency2] = pair.match(/.{1,3}/g); - const date = format(getYesterday(), 'yyyy-MM-dd'); + const date = format(getYesterday(), DATE_FORMAT); this.currencies[pair] = resultExtended[pair]?.[date]?.marketPrice; diff --git a/apps/client/src/app/components/performance-chart-dialog/performance-chart-dialog.component.ts b/apps/client/src/app/components/performance-chart-dialog/performance-chart-dialog.component.ts index f8adcec1..5dffaae6 100644 --- a/apps/client/src/app/components/performance-chart-dialog/performance-chart-dialog.component.ts +++ b/apps/client/src/app/components/performance-chart-dialog/performance-chart-dialog.component.ts @@ -6,6 +6,7 @@ import { } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DataService } from '@ghostfolio/client/services/data.service'; +import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { isToday, parse } from 'date-fns'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -66,7 +67,7 @@ export class PerformanceChartDialog { value: benchmarkItem.value * coefficient }); } else if ( - isToday(parse(historicalDataItem.date, 'yyyy-MM-dd', new Date())) + isToday(parse(historicalDataItem.date, DATE_FORMAT, new Date())) ) { this.benchmarkDataItems.push({ date: historicalDataItem.date, diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts index e8ffbfad..947a9f21 100644 --- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts +++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts @@ -7,6 +7,7 @@ import { } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DataService } from '@ghostfolio/client/services/data.service'; +import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { format, isSameMonth, isToday, parseISO } from 'date-fns'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -115,13 +116,13 @@ export class PositionDetailDialog implements OnDestroy { } else { // Add market price this.historicalDataItems.push({ - date: format(new Date(), 'yyyy-MM-dd'), + date: format(new Date(), DATE_FORMAT), value: this.marketPrice }); // Add benchmark this.benchmarkDataItems.push({ - date: format(new Date(), 'yyyy-MM-dd'), + date: format(new Date(), DATE_FORMAT), value: averagePrice }); }