Browse Source

Fix start date of date ranges in local time zone

pull/8099/head
Thomas Kaul 5 days ago
parent
commit
78b998b2f5
  1. 9
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  2. 41
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts
  3. 8
      apps/api/src/app/portfolio/portfolio.service.spec.ts
  4. 2
      apps/api/src/main.ts
  5. 22
      libs/common/src/lib/calculation-helper.spec.ts
  6. 20
      libs/common/src/lib/calculation-helper.ts

9
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -64,7 +64,6 @@ import {
format,
isAfter,
isBefore,
isEqual,
isFuture,
isPast,
isWithinInterval,
@ -1418,8 +1417,8 @@ export abstract class PortfolioCalculator {
let netPerformanceAtStartDate: number;
let netPerformanceWithCurrencyEffectAtStartDate: number;
const endDate = this.toChartDate(end);
const startDate = this.toChartDate(start);
const endDate = resetHours(end);
const startDate = resetHours(start);
for (const historicalDataItem of historicalData) {
const date = resetHours(parseDate(historicalDataItem.date));
@ -1914,8 +1913,4 @@ export abstract class PortfolioCalculator {
await this.initialize(attempt + 1);
}
}
private toChartDate(aDate: Date) {
return isEqual(aDate, endOfDay(aDate)) ? resetHours(aDate) : aDate;
}
}

41
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts

@ -226,5 +226,46 @@ describe('PortfolioCalculator', () => {
})
).toEqual(['2016-12-31', '2017-12-31']);
});
it('with a year to date date range', async () => {
const snapshot = await portfolioCalculator.getSnapshot();
snapshot.historicalData = [
getHistoricalDataItem({
date: '2016-12-30',
investment: 100,
netPerformance: 0
}),
getHistoricalDataItem({
date: '2016-12-31',
investment: 100,
netPerformance: 10
}),
getHistoricalDataItem({
date: '2017-01-03',
investment: 100,
netPerformance: 20
})
];
jest.useFakeTimers().setSystemTime(new Date('2017-01-04T02:30:00.000Z'));
const { endDate, startDate } = getIntervalFromDateRange({
dateRange: 'ytd'
});
const { chart } = await portfolioCalculator.getPerformance({
end: endDate,
start: startDate
});
jest.useRealTimers();
expect(
chart.map(({ date }) => {
return date;
})
).toEqual(['2016-12-31', '2017-01-03']);
});
});
});

8
apps/api/src/app/portfolio/portfolio.service.spec.ts

@ -16,7 +16,7 @@ import {
TAG_ID_EXCLUDE_FROM_ANALYSIS,
UNKNOWN_KEY
} from '@ghostfolio/common/config';
import { parseDate, resetHours } from '@ghostfolio/common/helper';
import { parseDate } from '@ghostfolio/common/helper';
import {
Activity,
AssetProfileIdentifier,
@ -1045,7 +1045,7 @@ describe('PortfolioService', () => {
expect(getPerformanceOfCalculator).toHaveBeenCalledTimes(3);
expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(2, {
end: endOfDay(parseDate('2024-12-31')),
start: resetHours(parseDate('2024-06-15'))
start: parseDate('2024-06-15')
});
expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(3, {
end: endOfDay(parseDate('2025-06-15')),
@ -1099,11 +1099,11 @@ describe('PortfolioService', () => {
expect(getPerformanceOfCalculator).toHaveBeenCalledTimes(3);
expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(1, {
end: endOfDay(parseDate('2026-01-01')),
start: resetHours(parseDate('2025-01-01'))
start: parseDate('2025-01-01')
});
expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(2, {
end: endOfDay(parseDate('2025-12-31')),
start: resetHours(parseDate('2025-01-01'))
start: parseDate('2025-01-01')
});
expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(3, {
end: endOfDay(parseDate('2026-01-01')),

2
apps/api/src/main.ts

@ -31,8 +31,6 @@ import { EnvHttpProxyAgent, setGlobalDispatcher } from 'undici';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
process.env.TZ = 'UTC';
const logger = new Logger('Bootstrap');
const processWarningLogger = new Logger('ProcessWarning');

22
libs/common/src/lib/calculation-helper.spec.ts

@ -6,6 +6,7 @@ import {
getIntervalFromDateRange
} from './calculation-helper';
import { DATE_FORMAT } from './helper';
import { DateRange } from './types';
describe('CalculationHelper', () => {
describe('interval from date range', () => {
@ -23,6 +24,27 @@ describe('CalculationHelper', () => {
// part of the interval
expect(startDate.getTime()).toEqual(new Date(2024, 0, 1).getTime() - 1);
});
it('Get interval of a date range relative to today', () => {
jest.useFakeTimers().setSystemTime(new Date(2024, 9, 10, 22, 0));
expect(
(['1d', 'mtd', 'wtd', 'ytd', '1y', '5y'] as DateRange[]).map(
(dateRange) => {
return getIntervalFromDateRange({ dateRange }).startDate;
}
)
).toEqual([
new Date(2024, 9, 9),
new Date(2024, 8, 30),
new Date(2024, 9, 6),
new Date(2023, 11, 31),
new Date(2023, 9, 10),
new Date(2019, 9, 10)
]);
jest.useRealTimers();
});
});
describe('annualized performance percentage', () => {

20
libs/common/src/lib/calculation-helper.ts

@ -3,6 +3,7 @@ import {
endOfDay,
endOfYear,
max,
startOfDay,
startOfMonth,
startOfWeek,
startOfYear,
@ -12,7 +13,6 @@ import {
} from 'date-fns';
import { isFinite, isNumber } from 'lodash-es';
import { resetHours } from './helper';
import { DateRange } from './types';
export function getAnnualizedPerformancePercent({
@ -48,31 +48,25 @@ export function getIntervalFromDateRange(params: {
switch (dateRange) {
case '1d':
startDate = max([startDate, subDays(resetHours(new Date()), 1)]);
startDate = max([startDate, subDays(startOfDay(new Date()), 1)]);
break;
case 'mtd':
startDate = max([
startDate,
subDays(startOfMonth(resetHours(new Date())), 1)
]);
startDate = max([startDate, subDays(startOfMonth(new Date()), 1)]);
break;
case 'wtd':
startDate = max([
startDate,
subDays(startOfWeek(resetHours(new Date()), { weekStartsOn: 1 }), 1)
subDays(startOfWeek(new Date(), { weekStartsOn: 1 }), 1)
]);
break;
case 'ytd':
startDate = max([
startDate,
subDays(startOfYear(resetHours(new Date())), 1)
]);
startDate = max([startDate, subDays(startOfYear(new Date()), 1)]);
break;
case '1y':
startDate = max([startDate, subYears(resetHours(new Date()), 1)]);
startDate = max([startDate, subYears(startOfDay(new Date()), 1)]);
break;
case '5y':
startDate = max([startDate, subYears(resetHours(new Date()), 5)]);
startDate = max([startDate, subYears(startOfDay(new Date()), 5)]);
break;
case 'max':
break;

Loading…
Cancel
Save