Browse Source

Exclude cash in base currency from portfolio performance calculation

pull/7443/head
Thomas Kaul 1 month ago
parent
commit
cd71ed8852
  1. 196
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  2. 8
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.scss
  3. 27
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts

196
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -9,7 +9,7 @@
class="flex-nowrap px-3 py-1 row"
[hidden]="summary?.activityCount === null"
>
<div class="d-flex flex-grow-1 ml-3 text-truncate">
<div class="d-flex flex-grow-1 indent-1 text-truncate">
{{ summary?.activityCount }}
<ng-container i18n>{summary?.activityCount, plural,
=1 {activity}
@ -123,7 +123,7 @@
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 text-truncate ml-3">
<div class="flex-grow-1 indent-1 text-truncate">
<ng-container i18n>Net Performance</ng-container>
<abbr
class="d-none d-sm-inline-block initialism ml-2 text-muted"
@ -164,7 +164,17 @@
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 ml-3 text-truncate" i18n>Cash</div>
<div class="align-items-center d-flex flex-grow-1 indent-1">
<ng-container i18n>Cash</ng-container>
@if (!hasImpersonationId && summary?.totalValueInBaseCurrency > 0) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : cashPercentage"
/>
}
</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
@ -177,85 +187,48 @@
/>
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="align-items-center d-flex flex-grow-1 ml-4">
<ng-container i18n>Buying Power</ng-container>
@if (
!hasImpersonationId &&
summary?.totalValueInBaseCurrency > 0 &&
user?.settings?.isExperimentalFeatures
) {
@if (isLoading || summary?.emergencyFund?.cash > 0) {
<div class="flex-nowrap px-3 py-1 row small">
<div class="flex-grow-1 indent-2 text-truncate" i18n>Buying Power</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true"
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[value]="isLoading ? undefined : buyingPowerPercentage"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.cash"
/>
}
</div>
</div>
<div class="align-items-center d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.cash"
/>
<div class="flex-nowrap px-3 py-1 row small">
<div class="flex-grow-1 indent-2 text-truncate" i18n>Emergency Fund</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.cash"
/>
</div>
</div>
</div>
}
<div class="flex-nowrap px-3 py-1 row">
<div class="align-items-center d-flex flex-grow-1 ml-3">
<ng-container i18n>Emergency Fund</ng-container>
@if (
!hasImpersonationId &&
summary?.totalValueInBaseCurrency > 0 &&
user?.settings?.isExperimentalFeatures
) {
<div class="align-items-center d-flex flex-grow-1 indent-1">
<ng-container i18n>Holdings</ng-container>
@if (!hasImpersonationId && summary?.totalValueInBaseCurrency > 0) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : emergencyFundPercentage"
[value]="isLoading ? undefined : holdingsPercentage"
/>
}
</div>
<div
class="align-items-center d-flex justify-content-end"
[class.cursor-pointer]="
hasPermissionToUpdateUserSettings &&
!user?.settings?.isRestrictedView &&
user?.subscription?.type !== 'Basic'
"
(click)="
hasPermissionToUpdateUserSettings &&
!user?.settings?.isRestrictedView &&
user?.subscription?.type !== 'Basic' &&
onEditEmergencyFund()
"
>
@if (
hasPermissionToUpdateUserSettings &&
!isLoading &&
!user?.settings?.isRestrictedView &&
user?.subscription?.type !== 'Basic'
) {
<ion-icon
class="mr-1 text-muted"
name="ellipsis-horizontal-circle-outline"
/>
}
<gf-value
class="justify-content-end"
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.total"
/>
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 ml-4 text-truncate" i18n>in Cash</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
@ -264,32 +237,30 @@
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.cash"
[value]="isLoading ? undefined : holdingsInBaseCurrency"
/>
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 ml-4 text-truncate" i18n>in Holdings</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.assets"
/>
@if (isLoading || summary?.emergencyFund?.assets > 0) {
<div class="flex-nowrap px-3 py-1 row small">
<div class="flex-grow-1 indent-2 text-truncate" i18n>Emergency Fund</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.assets"
/>
</div>
</div>
</div>
}
<div class="flex-nowrap px-3 py-1 row">
<div class="align-items-center d-flex flex-grow-1">
<ng-container i18n>Excluded from Analysis</ng-container>
@if (
!hasImpersonationId &&
summary?.totalValueInBaseCurrency > 0 &&
user?.settings?.isExperimentalFeatures
) {
@if (!hasImpersonationId && summary?.totalValueInBaseCurrency > 0) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true"
@ -347,7 +318,7 @@
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 ml-3 text-truncate">
<div class="flex-grow-1 indent-1 text-truncate">
<ng-container i18n>Annualized Performance</ng-container>
</div>
<div class="flex-column flex-wrap justify-content-end">
@ -368,6 +339,53 @@
<div class="row">
<div class="col"><hr /></div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="align-items-center d-flex flex-grow-1">
<ng-container i18n>Emergency Fund</ng-container>
@if (!hasImpersonationId && summary?.totalValueInBaseCurrency > 0) {
<gf-value
class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : emergencyFundPercentage"
/>
}
</div>
<div
class="align-items-center d-flex justify-content-end"
[class.cursor-pointer]="
hasPermissionToUpdateUserSettings &&
!user?.settings?.isRestrictedView &&
user?.subscription?.type !== 'Basic'
"
(click)="
hasPermissionToUpdateUserSettings &&
!user?.settings?.isRestrictedView &&
user?.subscription?.type !== 'Basic' &&
onEditEmergencyFund()
"
>
@if (
hasPermissionToUpdateUserSettings &&
!isLoading &&
!user?.settings?.isRestrictedView &&
user?.subscription?.type !== 'Basic'
) {
<ion-icon
class="mr-1 text-muted"
name="ellipsis-horizontal-circle-outline"
/>
}
<gf-value
class="justify-content-end"
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.total"
/>
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 text-truncate" i18n>Interest</div>
<div class="justify-content-end">

8
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.scss

@ -1,3 +1,11 @@
:host {
display: block;
.indent-1 {
margin-left: 1rem;
}
.indent-2 {
margin-left: 2rem;
}
}

27
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts

@ -21,6 +21,7 @@ import {
ellipsisHorizontalCircleOutline,
informationCircleOutline
} from 'ionicons/icons';
import { isNumber } from 'lodash';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
@ -55,9 +56,10 @@ export class GfPortfolioSummaryComponent implements OnChanges {
addIcons({ ellipsisHorizontalCircleOutline, informationCircleOutline });
}
protected get buyingPowerPercentage() {
protected get cashPercentage() {
return this.summary?.totalValueInBaseCurrency
? this.summary.cash / this.summary.totalValueInBaseCurrency
? this.summary.totalCashInBaseCurrency /
this.summary.totalValueInBaseCurrency
: 0;
}
@ -75,6 +77,27 @@ export class GfPortfolioSummaryComponent implements OnChanges {
: 0;
}
protected get holdingsInBaseCurrency() {
if (
!isNumber(this.summary?.currentValueInBaseCurrency) ||
!isNumber(this.summary?.totalCashInBaseCurrency)
) {
return null;
}
return (
this.summary.currentValueInBaseCurrency -
this.summary.totalCashInBaseCurrency
);
}
protected get holdingsPercentage() {
return this.summary?.totalValueInBaseCurrency &&
isNumber(this.holdingsInBaseCurrency)
? this.holdingsInBaseCurrency / this.summary.totalValueInBaseCurrency
: 0;
}
public ngOnChanges() {
if (this.summary) {
if (

Loading…
Cancel
Save