Browse Source

Bugfix/benchmark dates in time zones other than UTC (#8117)

* Fix benchmark dates

* Update changelog
pull/8121/head
Thomas Kaul 5 days ago
committed by GitHub
parent
commit
5aee101e8e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 80
      apps/api/src/app/endpoints/benchmarks/benchmarks.service.time-zone.spec.ts
  3. 16
      apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts

1
CHANGELOG.md

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed the _Copy AI prompt to clipboard_ actions on the analysis page in impersonation mode to be based on the impersonated user (experimental)
- Fixed the start date of the date ranges for instances in time zones with a negative UTC offset
- Fixed the dates of the benchmark in the benchmark comparator on the analysis page for instances in time zones with a negative UTC offset
- Fixed the value of the accounts by excluding the cash balance when filtering by an asset class other than liquidity on the accounts page and the allocations page
- Fixed the value of the accounts by excluding the cash balance when filtering by an asset class other than liquidity in the server of the Model Context Protocol (MCP) (experimental)

80
apps/api/src/app/endpoints/benchmarks/benchmarks.service.time-zone.spec.ts

@ -0,0 +1,80 @@
/**
* @jest-environment <rootDir>/jest-environment-tz.js
* @jest-environment-options {"timeZone": "America/New_York"}
*/
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.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';
import { parseDate } from '@ghostfolio/common/helper';
import { DataSource } from '@prisma/client';
import { BenchmarksService } from './benchmarks.service';
describe('BenchmarksService in a time zone behind UTC', () => {
let benchmarksService: BenchmarksService;
beforeEach(() => {
benchmarksService = new BenchmarksService(
new BenchmarkService(null, null, null, null, null, null),
{
getExchangeRatesByCurrency: jest.fn().mockResolvedValue({})
} as unknown as ExchangeRateDataService,
{
getRange: jest.fn().mockResolvedValue([]),
// The market data is stored at midnight (UTC)
marketDataItems: jest.fn().mockResolvedValue([
{ date: new Date('2010-07-18T00:00:00.000Z'), marketPrice: 50 },
{ date: new Date('2010-07-19T00:00:00.000Z'), marketPrice: 100 }
])
} as unknown as MarketDataService,
{
getPerformance: jest.fn().mockResolvedValue({
chart: [{ date: '2010-07-18' }, { date: '2010-07-19' }]
})
} as unknown as PortfolioService,
{
get: jest.fn().mockResolvedValue({ currency: 'USD', marketPrice: 200 })
} as unknown as SymbolService
);
});
describe('getMarketDataForUser', () => {
const getMarketDataForUser = ({
endDate = parseDate('2010-07-19')
}: { endDate?: Date } = {}) => {
return benchmarksService.getMarketDataForUser({
endDate,
dataSource: DataSource.COINGECKO,
dateRange: 'max',
startDate: parseDate('2010-07-18'),
symbol: 'bitcoin',
userId: 'user-id',
userSettings: { baseCurrency: 'USD' }
});
};
it('returns the dates of the market data without a shift', async () => {
await expect(getMarketDataForUser()).resolves.toEqual({
marketData: [
{ date: '2010-07-18', value: 0 },
{ date: '2010-07-19', value: 100 }
]
});
});
it('returns the end date after the market data without a shift', async () => {
await expect(
getMarketDataForUser({ endDate: parseDate('2010-07-20') })
).resolves.toEqual({
marketData: [
{ date: '2010-07-18', value: 0 },
{ date: '2010-07-19', value: 100 },
{ date: '2010-07-20', value: 300 }
]
});
});
});
});

16
apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts

@ -12,6 +12,7 @@ import {
} from '@ghostfolio/common/interfaces';
import { DateRange } from '@ghostfolio/common/types';
import { utc } from '@date-fns/utc';
import { Injectable, Logger } from '@nestjs/common';
import { format, isSameDay, min } from 'date-fns';
import { isNumber } from 'lodash-es';
@ -104,7 +105,8 @@ export class BenchmarksService {
this.logger.error(
`No historical market data has been found for ${symbol} (${dataSource}) since ${format(
baselineDate,
DATE_FORMAT
DATE_FORMAT,
{ in: utc }
)}`
);
@ -125,7 +127,8 @@ export class BenchmarksService {
this.logger.error(
`No historical market data has been found for ${symbol} (${dataSource}) at ${format(
baselineDate,
DATE_FORMAT
DATE_FORMAT,
{ in: utc }
)}`
);
@ -135,8 +138,9 @@ export class BenchmarksService {
this.logger.warn(
`The market data of ${symbol} (${dataSource}) starts at ${format(
firstMarketDataItem.date,
DATE_FORMAT
)}, after the baseline date ${format(baselineDate, DATE_FORMAT)}`
DATE_FORMAT,
{ in: utc }
)}, after the baseline date ${format(baselineDate, DATE_FORMAT, { in: utc })}`
);
}
@ -147,6 +151,8 @@ export class BenchmarksService {
targetCurrency: userCurrency
});
// TODO: Format the keys of the exchange rates in UTC, together with the
// market data in the portfolio calculator
const exchangeRateAtStartDate =
exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[
format(firstMarketDataItem.date, DATE_FORMAT)
@ -164,7 +170,7 @@ export class BenchmarksService {
: 1;
marketData.push({
date: format(marketDataItem.date, DATE_FORMAT),
date: format(marketDataItem.date, DATE_FORMAT, { in: utc }),
value:
marketPriceAtStartDate === 0
? 0

Loading…
Cancel
Save