Bugfix/calculation of portfolio summary caused by future liabilities (#3342)
* Adapt date of future activities * Update changelog
This commit is contained in:
parent
d735e4db75
commit
4efd5cefd8
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue in the calculation of the portfolio summary caused by future liabilities
|
||||
|
||||
## 2.77.1 - 2024-04-27
|
||||
|
||||
### Added
|
||||
|
@ -88,21 +88,26 @@ export class OrderController {
|
||||
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId,
|
||||
@Query('accounts') filterByAccounts?: string,
|
||||
@Query('assetClasses') filterByAssetClasses?: string,
|
||||
@Query('range') dateRange: DateRange = 'max',
|
||||
@Query('range') dateRange?: DateRange,
|
||||
@Query('skip') skip?: number,
|
||||
@Query('sortColumn') sortColumn?: string,
|
||||
@Query('sortDirection') sortDirection?: Prisma.SortOrder,
|
||||
@Query('tags') filterByTags?: string,
|
||||
@Query('take') take?: number
|
||||
): Promise<Activities> {
|
||||
let endDate: Date;
|
||||
let startDate: Date;
|
||||
|
||||
if (dateRange) {
|
||||
({ endDate, startDate } = getInterval(dateRange));
|
||||
}
|
||||
|
||||
const filters = this.apiService.buildFiltersFromQueryParams({
|
||||
filterByAccounts,
|
||||
filterByAssetClasses,
|
||||
filterByTags
|
||||
});
|
||||
|
||||
const { endDate, startDate } = getInterval(dateRange);
|
||||
|
||||
const impersonationUserId =
|
||||
await this.impersonationService.validateImpersonationId(impersonationId);
|
||||
const userCurrency = this.request.user.Settings.settings.baseCurrency;
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
eachDayOfInterval,
|
||||
endOfDay,
|
||||
format,
|
||||
isAfter,
|
||||
isBefore,
|
||||
isSameDay,
|
||||
max,
|
||||
@ -44,13 +45,12 @@ import {
|
||||
subDays
|
||||
} from 'date-fns';
|
||||
import { first, last, uniq, uniqBy } from 'lodash';
|
||||
import ms from 'ms';
|
||||
|
||||
export abstract class PortfolioCalculator {
|
||||
protected static readonly ENABLE_LOGGING = false;
|
||||
|
||||
protected accountBalanceItems: HistoricalDataItem[];
|
||||
protected orders: PortfolioOrder[];
|
||||
protected activities: PortfolioOrder[];
|
||||
|
||||
private configurationService: ConfigurationService;
|
||||
private currency: string;
|
||||
@ -96,7 +96,7 @@ export abstract class PortfolioCalculator {
|
||||
this.exchangeRateDataService = exchangeRateDataService;
|
||||
this.isExperimentalFeatures = isExperimentalFeatures;
|
||||
|
||||
this.orders = activities
|
||||
this.activities = activities
|
||||
.map(
|
||||
({
|
||||
date,
|
||||
@ -107,6 +107,12 @@ export abstract class PortfolioCalculator {
|
||||
type,
|
||||
unitPrice
|
||||
}) => {
|
||||
if (isAfter(date, new Date(Date.now()))) {
|
||||
// Adapt date to today if activity is in future (e.g. liability)
|
||||
// to include it in the interval
|
||||
date = endOfDay(new Date(Date.now()));
|
||||
}
|
||||
|
||||
return {
|
||||
SymbolProfile,
|
||||
tags,
|
||||
@ -917,7 +923,7 @@ export abstract class PortfolioCalculator {
|
||||
tags,
|
||||
type,
|
||||
unitPrice
|
||||
} of this.orders) {
|
||||
} of this.activities) {
|
||||
let currentTransactionPointItem: TransactionPointSymbol;
|
||||
const oldAccumulatedSymbol = symbols[SymbolProfile.symbol];
|
||||
|
||||
|
@ -74,7 +74,7 @@ describe('PortfolioCalculator', () => {
|
||||
const activities: Activity[] = [
|
||||
{
|
||||
...activityDummyData,
|
||||
date: new Date('2022-01-01'),
|
||||
date: new Date('2023-01-01'), // Date in future
|
||||
fee: 0,
|
||||
quantity: 1,
|
||||
SymbolProfile: {
|
||||
@ -96,61 +96,12 @@ describe('PortfolioCalculator', () => {
|
||||
userId: userDummyData.id
|
||||
});
|
||||
|
||||
const portfolioSnapshot = await portfolioCalculator.computeSnapshot(
|
||||
parseDate('2022-01-01')
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
|
||||
expect(portfolioSnapshot).toEqual({
|
||||
currentValueInBaseCurrency: new Big('0'),
|
||||
errors: [],
|
||||
grossPerformance: new Big('0'),
|
||||
grossPerformancePercentage: new Big('0'),
|
||||
grossPerformancePercentageWithCurrencyEffect: new Big('0'),
|
||||
grossPerformanceWithCurrencyEffect: new Big('0'),
|
||||
hasErrors: true,
|
||||
netPerformance: new Big('0'),
|
||||
netPerformancePercentage: new Big('0'),
|
||||
netPerformancePercentageWithCurrencyEffect: new Big('0'),
|
||||
netPerformanceWithCurrencyEffect: new Big('0'),
|
||||
positions: [
|
||||
{
|
||||
averagePrice: new Big('3000'),
|
||||
currency: 'USD',
|
||||
dataSource: 'MANUAL',
|
||||
dividend: new Big('0'),
|
||||
dividendInBaseCurrency: new Big('0'),
|
||||
fee: new Big('0'),
|
||||
firstBuyDate: '2022-01-01',
|
||||
grossPerformance: null,
|
||||
grossPerformancePercentage: null,
|
||||
grossPerformancePercentageWithCurrencyEffect: null,
|
||||
grossPerformanceWithCurrencyEffect: null,
|
||||
investment: new Big('0'),
|
||||
investmentWithCurrencyEffect: new Big('0'),
|
||||
marketPrice: null,
|
||||
marketPriceInBaseCurrency: 3000,
|
||||
netPerformance: null,
|
||||
netPerformancePercentage: null,
|
||||
netPerformancePercentageWithCurrencyEffect: null,
|
||||
netPerformanceWithCurrencyEffect: null,
|
||||
quantity: new Big('0'),
|
||||
symbol: '55196015-1365-4560-aa60-8751ae6d18f8',
|
||||
tags: [],
|
||||
timeWeightedInvestment: new Big('0'),
|
||||
timeWeightedInvestmentWithCurrencyEffect: new Big('0'),
|
||||
transactionCount: 1,
|
||||
valueInBaseCurrency: new Big('0')
|
||||
}
|
||||
],
|
||||
totalFeesWithCurrencyEffect: new Big('0'),
|
||||
totalInterestWithCurrencyEffect: new Big('0'),
|
||||
totalInvestment: new Big('0'),
|
||||
totalInvestmentWithCurrencyEffect: new Big('0'),
|
||||
totalLiabilitiesWithCurrencyEffect: new Big('0'),
|
||||
totalValuablesWithCurrencyEffect: new Big('0')
|
||||
});
|
||||
const liabilitiesInBaseCurrency =
|
||||
await portfolioCalculator.getLiabilitiesInBaseCurrency();
|
||||
|
||||
expect(liabilitiesInBaseCurrency).toEqual(new Big(3000));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -203,7 +203,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
||||
let valueAtStartDateWithCurrencyEffect: Big;
|
||||
|
||||
// Clone orders to keep the original values in this.orders
|
||||
let orders: PortfolioOrderItem[] = cloneDeep(this.orders).filter(
|
||||
let orders: PortfolioOrderItem[] = cloneDeep(this.activities).filter(
|
||||
({ SymbolProfile }) => {
|
||||
return SymbolProfile.symbol === symbol;
|
||||
}
|
||||
|
@ -8,6 +8,13 @@ import { GetValuesParams } from './interfaces/get-values-params.interface';
|
||||
|
||||
function mockGetValue(symbol: string, date: Date) {
|
||||
switch (symbol) {
|
||||
case '55196015-1365-4560-aa60-8751ae6d18f8':
|
||||
if (isSameDay(parseDate('2022-01-31'), date)) {
|
||||
return { marketPrice: 3000 };
|
||||
}
|
||||
|
||||
return { marketPrice: 0 };
|
||||
|
||||
case 'BALN.SW':
|
||||
if (isSameDay(parseDate('2021-11-12'), date)) {
|
||||
return { marketPrice: 146 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user