Feature/add dividend and fees to position detail dialog (#1643)
* Add dividend and fees to position detail dialog * Update changelog
This commit is contained in:
parent
2e7f46ad78
commit
f7597c213d
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added the dividend and fees to the position detail dialog
|
||||||
- Added support to link a (wealth) item to an account
|
- Added support to link a (wealth) item to an account
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -7,6 +7,8 @@ import { Tag } from '@prisma/client';
|
|||||||
|
|
||||||
export interface PortfolioPositionDetail {
|
export interface PortfolioPositionDetail {
|
||||||
averagePrice: number;
|
averagePrice: number;
|
||||||
|
dividendInBaseCurrency: number;
|
||||||
|
feeInBaseCurrency: number;
|
||||||
firstBuyDate: string;
|
firstBuyDate: string;
|
||||||
grossPerformance: number;
|
grossPerformance: number;
|
||||||
grossPerformancePercent: number;
|
grossPerformancePercent: number;
|
||||||
|
@ -82,6 +82,7 @@ describe('PortfolioCalculator', () => {
|
|||||||
averagePrice: new Big('0'),
|
averagePrice: new Big('0'),
|
||||||
currency: 'CHF',
|
currency: 'CHF',
|
||||||
dataSource: 'YAHOO',
|
dataSource: 'YAHOO',
|
||||||
|
fee: new Big('3.2'),
|
||||||
firstBuyDate: '2021-11-22',
|
firstBuyDate: '2021-11-22',
|
||||||
grossPerformance: new Big('-12.6'),
|
grossPerformance: new Big('-12.6'),
|
||||||
grossPerformancePercentage: new Big('-0.0440867739678096571'),
|
grossPerformancePercentage: new Big('-0.0440867739678096571'),
|
||||||
|
@ -71,6 +71,7 @@ describe('PortfolioCalculator', () => {
|
|||||||
averagePrice: new Big('136.6'),
|
averagePrice: new Big('136.6'),
|
||||||
currency: 'CHF',
|
currency: 'CHF',
|
||||||
dataSource: 'YAHOO',
|
dataSource: 'YAHOO',
|
||||||
|
fee: new Big('1.55'),
|
||||||
firstBuyDate: '2021-11-30',
|
firstBuyDate: '2021-11-30',
|
||||||
grossPerformance: new Big('24.6'),
|
grossPerformance: new Big('24.6'),
|
||||||
grossPerformancePercentage: new Big('0.09004392386530014641'),
|
grossPerformancePercentage: new Big('0.09004392386530014641'),
|
||||||
|
@ -82,6 +82,7 @@ describe('PortfolioCalculator', () => {
|
|||||||
averagePrice: new Big('320.43'),
|
averagePrice: new Big('320.43'),
|
||||||
currency: 'CHF',
|
currency: 'CHF',
|
||||||
dataSource: 'YAHOO',
|
dataSource: 'YAHOO',
|
||||||
|
fee: new Big('0'),
|
||||||
firstBuyDate: '2015-01-01',
|
firstBuyDate: '2015-01-01',
|
||||||
grossPerformance: new Big('27172.74'),
|
grossPerformance: new Big('27172.74'),
|
||||||
grossPerformancePercentage: new Big('42.40043067128546016291'),
|
grossPerformancePercentage: new Big('42.40043067128546016291'),
|
||||||
|
@ -82,6 +82,7 @@ describe('PortfolioCalculator', () => {
|
|||||||
averagePrice: new Big('75.80'),
|
averagePrice: new Big('75.80'),
|
||||||
currency: 'CHF',
|
currency: 'CHF',
|
||||||
dataSource: 'YAHOO',
|
dataSource: 'YAHOO',
|
||||||
|
fee: new Big('4.25'),
|
||||||
firstBuyDate: '2022-03-07',
|
firstBuyDate: '2022-03-07',
|
||||||
grossPerformance: new Big('21.93'),
|
grossPerformance: new Big('21.93'),
|
||||||
grossPerformancePercentage: new Big('0.14465699208443271768'),
|
grossPerformancePercentage: new Big('0.14465699208443271768'),
|
||||||
|
@ -102,6 +102,7 @@ describe('PortfolioCalculator', () => {
|
|||||||
averagePrice: new Big('0'),
|
averagePrice: new Big('0'),
|
||||||
currency: 'CHF',
|
currency: 'CHF',
|
||||||
dataSource: 'YAHOO',
|
dataSource: 'YAHOO',
|
||||||
|
fee: new Big('0'),
|
||||||
firstBuyDate: '2022-03-07',
|
firstBuyDate: '2022-03-07',
|
||||||
grossPerformance: new Big('19.86'),
|
grossPerformance: new Big('19.86'),
|
||||||
grossPerformancePercentage: new Big('0.13100263852242744063'),
|
grossPerformancePercentage: new Big('0.13100263852242744063'),
|
||||||
|
@ -431,6 +431,7 @@ export class PortfolioCalculator {
|
|||||||
: item.investment.div(item.quantity),
|
: item.investment.div(item.quantity),
|
||||||
currency: item.currency,
|
currency: item.currency,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
|
fee: item.fee,
|
||||||
firstBuyDate: item.firstBuyDate,
|
firstBuyDate: item.firstBuyDate,
|
||||||
grossPerformance: !hasErrors ? grossPerformance ?? null : null,
|
grossPerformance: !hasErrors ? grossPerformance ?? null : null,
|
||||||
grossPerformancePercentage: !hasErrors
|
grossPerformancePercentage: !hasErrors
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
MAX_CHART_ITEMS,
|
MAX_CHART_ITEMS,
|
||||||
UNKNOWN_KEY
|
UNKNOWN_KEY
|
||||||
} from '@ghostfolio/common/config';
|
} from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, getSum, parseDate } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
Accounts,
|
Accounts,
|
||||||
EnhancedSymbolProfile,
|
EnhancedSymbolProfile,
|
||||||
@ -678,6 +678,8 @@ export class PortfolioService {
|
|||||||
return {
|
return {
|
||||||
tags,
|
tags,
|
||||||
averagePrice: undefined,
|
averagePrice: undefined,
|
||||||
|
dividendInBaseCurrency: undefined,
|
||||||
|
feeInBaseCurrency: undefined,
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
@ -744,12 +746,23 @@ export class PortfolioService {
|
|||||||
averagePrice,
|
averagePrice,
|
||||||
currency,
|
currency,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
fee,
|
||||||
firstBuyDate,
|
firstBuyDate,
|
||||||
marketPrice,
|
marketPrice,
|
||||||
quantity,
|
quantity,
|
||||||
transactionCount
|
transactionCount
|
||||||
} = position;
|
} = position;
|
||||||
|
|
||||||
|
const dividendInBaseCurrency = getSum(
|
||||||
|
orders
|
||||||
|
.filter(({ type }) => {
|
||||||
|
return type === 'DIVIDEND';
|
||||||
|
})
|
||||||
|
.map(({ valueInBaseCurrency }) => {
|
||||||
|
return new Big(valueInBaseCurrency);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Convert investment, gross and net performance to currency of user
|
// Convert investment, gross and net performance to currency of user
|
||||||
const investment = this.exchangeRateDataService.toCurrency(
|
const investment = this.exchangeRateDataService.toCurrency(
|
||||||
position.investment?.toNumber(),
|
position.investment?.toNumber(),
|
||||||
@ -836,6 +849,12 @@ export class PortfolioService {
|
|||||||
tags,
|
tags,
|
||||||
transactionCount,
|
transactionCount,
|
||||||
averagePrice: averagePrice.toNumber(),
|
averagePrice: averagePrice.toNumber(),
|
||||||
|
dividendInBaseCurrency: dividendInBaseCurrency.toNumber(),
|
||||||
|
feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
||||||
|
fee.toNumber(),
|
||||||
|
SymbolProfile.currency,
|
||||||
|
userCurrency
|
||||||
|
),
|
||||||
grossPerformancePercent:
|
grossPerformancePercent:
|
||||||
position.grossPerformancePercentage?.toNumber(),
|
position.grossPerformancePercentage?.toNumber(),
|
||||||
historicalData: historicalDataArray,
|
historicalData: historicalDataArray,
|
||||||
@ -892,6 +911,8 @@ export class PortfolioService {
|
|||||||
SymbolProfile,
|
SymbolProfile,
|
||||||
tags,
|
tags,
|
||||||
averagePrice: 0,
|
averagePrice: 0,
|
||||||
|
dividendInBaseCurrency: 0,
|
||||||
|
feeInBaseCurrency: 0,
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
|
@ -35,6 +35,7 @@ export class RedactValuesInResponseInterceptor<T>
|
|||||||
'balanceInBaseCurrency',
|
'balanceInBaseCurrency',
|
||||||
'comment',
|
'comment',
|
||||||
'convertedBalance',
|
'convertedBalance',
|
||||||
|
'dividendInBaseCurrency',
|
||||||
'fee',
|
'fee',
|
||||||
'feeInBaseCurrency',
|
'feeInBaseCurrency',
|
||||||
'filteredValueInBaseCurrency',
|
'filteredValueInBaseCurrency',
|
||||||
|
@ -37,6 +37,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
public countries: {
|
public countries: {
|
||||||
[code: string]: { name: string; value: number };
|
[code: string]: { name: string; value: number };
|
||||||
};
|
};
|
||||||
|
public dividendInBaseCurrency: number;
|
||||||
|
public feeInBaseCurrency: number;
|
||||||
public firstBuyDate: string;
|
public firstBuyDate: string;
|
||||||
public grossPerformance: number;
|
public grossPerformance: number;
|
||||||
public grossPerformancePercent: number;
|
public grossPerformancePercent: number;
|
||||||
@ -78,6 +80,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
.subscribe(
|
.subscribe(
|
||||||
({
|
({
|
||||||
averagePrice,
|
averagePrice,
|
||||||
|
dividendInBaseCurrency,
|
||||||
|
feeInBaseCurrency,
|
||||||
firstBuyDate,
|
firstBuyDate,
|
||||||
grossPerformance,
|
grossPerformance,
|
||||||
grossPerformancePercent,
|
grossPerformancePercent,
|
||||||
@ -98,7 +102,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
this.averagePrice = averagePrice;
|
this.averagePrice = averagePrice;
|
||||||
this.benchmarkDataItems = [];
|
this.benchmarkDataItems = [];
|
||||||
this.countries = {};
|
this.countries = {};
|
||||||
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
|
this.dividendInBaseCurrency = dividendInBaseCurrency;
|
||||||
|
this.feeInBaseCurrency = feeInBaseCurrency;
|
||||||
this.firstBuyDate = firstBuyDate;
|
this.firstBuyDate = firstBuyDate;
|
||||||
this.grossPerformance = grossPerformance;
|
this.grossPerformance = grossPerformance;
|
||||||
this.grossPerformancePercent = grossPerformancePercent;
|
this.grossPerformancePercent = grossPerformancePercent;
|
||||||
@ -123,6 +128,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
|||||||
this.netPerformancePercent = netPerformancePercent;
|
this.netPerformancePercent = netPerformancePercent;
|
||||||
this.orders = orders;
|
this.orders = orders;
|
||||||
this.quantity = quantity;
|
this.quantity = quantity;
|
||||||
|
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
|
||||||
this.sectors = {};
|
this.sectors = {};
|
||||||
this.SymbolProfile = SymbolProfile;
|
this.SymbolProfile = SymbolProfile;
|
||||||
this.tags = tags.map(({ id, name }) => {
|
this.tags = tags.map(({ id, name }) => {
|
||||||
|
@ -119,6 +119,26 @@
|
|||||||
>Investment</gf-value
|
>Investment</gf-value
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-6 mb-3">
|
||||||
|
<gf-value
|
||||||
|
i18n
|
||||||
|
size="medium"
|
||||||
|
[currency]="data.baseCurrency"
|
||||||
|
[locale]="data.locale"
|
||||||
|
[value]="dividendInBaseCurrency"
|
||||||
|
>Dividend</gf-value
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 mb-3">
|
||||||
|
<gf-value
|
||||||
|
i18n
|
||||||
|
size="medium"
|
||||||
|
[currency]="data.baseCurrency"
|
||||||
|
[locale]="data.locale"
|
||||||
|
[value]="feeInBaseCurrency"
|
||||||
|
>Fees</gf-value
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="col-6 mb-3">
|
<div class="col-6 mb-3">
|
||||||
<gf-value
|
<gf-value
|
||||||
i18n
|
i18n
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as currencies from '@dinero.js/currencies';
|
import * as currencies from '@dinero.js/currencies';
|
||||||
import { DataSource } from '@prisma/client';
|
import { DataSource } from '@prisma/client';
|
||||||
|
import Big from 'big.js';
|
||||||
import { getDate, getMonth, getYear, parse, subDays } from 'date-fns';
|
import { getDate, getMonth, getYear, parse, subDays } from 'date-fns';
|
||||||
import { de, es, fr, it, nl, pt } from 'date-fns/locale';
|
import { de, es, fr, it, nl, pt } from 'date-fns/locale';
|
||||||
|
|
||||||
@ -139,6 +140,14 @@ export function getNumberFormatGroup(aLocale?: string) {
|
|||||||
}).value;
|
}).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSum(aArray: Big[]) {
|
||||||
|
if (aArray?.length > 0) {
|
||||||
|
return aArray.reduce((a, b) => a.plus(b), new Big(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Big(0);
|
||||||
|
}
|
||||||
|
|
||||||
export function getTextColor(aColorScheme: ColorScheme) {
|
export function getTextColor(aColorScheme: ColorScheme) {
|
||||||
const cssVariable = getCssVariable(
|
const cssVariable = getCssVariable(
|
||||||
aColorScheme === 'DARK' ||
|
aColorScheme === 'DARK' ||
|
||||||
|
@ -5,6 +5,7 @@ export interface TimelinePosition {
|
|||||||
averagePrice: Big;
|
averagePrice: Big;
|
||||||
currency: string;
|
currency: string;
|
||||||
dataSource: DataSource;
|
dataSource: DataSource;
|
||||||
|
fee: Big;
|
||||||
firstBuyDate: string;
|
firstBuyDate: string;
|
||||||
grossPerformance: Big;
|
grossPerformance: Big;
|
||||||
grossPerformancePercentage: Big;
|
grossPerformancePercentage: Big;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user