From 7fa6eda45d0ca3b02e4ad7fcec52629a25fabc0b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 18 Jan 2023 21:37:22 +0100 Subject: [PATCH] Feature/remove emergency fund as asset class (#1615) * Remove emergency fund as asset class * Update changelog --- CHANGELOG.md | 1 + .../src/app/portfolio/portfolio.service.ts | 68 +++++++++++-------- libs/common/src/lib/config.ts | 4 +- .../portfolio-position.interface.ts | 2 +- .../holdings-table.component.ts | 6 +- 5 files changed, 44 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc3e2fdfe..7970a9229 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the date formatting in the tooltip of the dividend timeline grouped by month / year - Improved the date formatting in the tooltip of the investment timeline grouped by month / year - Reduced the execution interval of the data gathering to every 4 hours +- Removed emergency fund as an asset class ## 1.227.1 - 2023-01-14 diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 67941f12b..17747bae4 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -20,7 +20,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service'; import { - ASSET_SUB_CLASS_EMERGENCY_FUND, + EMERGENCY_FUND_TAG_ID, MAX_CHART_ITEMS, UNKNOWN_KEY } from '@ghostfolio/common/config'; @@ -575,7 +575,6 @@ export class PortfolioService { ) { const cashPositions = await this.getCashPositions({ cashDetails, - emergencyFund, userCurrency, investment: totalInvestmentInBaseCurrency, value: filteredValueInBaseCurrency @@ -595,6 +594,41 @@ export class PortfolioService { withExcludedAccounts }); + if ( + filters?.length === 1 && + filters[0].id === 'EMERGENCY_FUND_TAG_ID' && + filters[0].type === 'TAG' + ) { + const cashPositions = await this.getCashPositions({ + cashDetails, + userCurrency, + investment: totalInvestmentInBaseCurrency, + value: filteredValueInBaseCurrency + }); + + const emergencyFundInCash = emergencyFund + .minus( + this.getEmergencyFundPositionsValueInBaseCurrency({ + activities: orders + }) + ) + .toNumber(); + + accounts[UNKNOWN_KEY] = { + balance: 0, + currency: userCurrency, + current: emergencyFundInCash, + name: UNKNOWN_KEY, + original: emergencyFundInCash + }; + + holdings[userCurrency] = { + ...cashPositions[userCurrency], + investment: emergencyFundInCash, + value: emergencyFundInCash + }; + } + const summary = await this.getSummary({ impersonationId, userCurrency, @@ -1184,16 +1218,14 @@ export class PortfolioService { private async getCashPositions({ cashDetails, - emergencyFund, investment, userCurrency, value }: { cashDetails: CashDetails; - emergencyFund: Big; investment: Big; - value: Big; userCurrency: string; + value: Big; }) { const cashPositions: PortfolioDetails['holdings'] = { [userCurrency]: this.getInitialCashPosition({ @@ -1224,28 +1256,6 @@ export class PortfolioService { } } - if (emergencyFund.gt(0)) { - cashPositions[ASSET_SUB_CLASS_EMERGENCY_FUND] = { - ...cashPositions[userCurrency], - assetSubClass: ASSET_SUB_CLASS_EMERGENCY_FUND, - investment: emergencyFund.toNumber(), - name: ASSET_SUB_CLASS_EMERGENCY_FUND, - symbol: ASSET_SUB_CLASS_EMERGENCY_FUND, - value: emergencyFund.toNumber() - }; - - cashPositions[userCurrency].investment = new Big( - cashPositions[userCurrency].investment - ) - .minus(emergencyFund) - .toNumber(); - cashPositions[userCurrency].value = new Big( - cashPositions[userCurrency].value - ) - .minus(emergencyFund) - .toNumber(); - } - for (const symbol of Object.keys(cashPositions)) { // Calculate allocations for each currency cashPositions[symbol].allocationCurrent = value.gt(0) @@ -1359,8 +1369,8 @@ export class PortfolioService { }) { const emergencyFundOrders = activities.filter((activity) => { return ( - activity.tags?.some(({ name }) => { - return name === 'EMERGENCY_FUND'; + activity.tags?.some(({ id }) => { + return id === EMERGENCY_FUND_TAG_ID; }) ?? false ); }); diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 91e83e2e0..577644116 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -33,8 +33,6 @@ export const warnColorRgb = { b: 69 }; -export const ASSET_SUB_CLASS_EMERGENCY_FUND = 'EMERGENCY_FUND'; - export const DATA_GATHERING_QUEUE = 'DATA_GATHERING_QUEUE'; export const DATA_GATHERING_QUEUE_PRIORITY_LOW = Number.MAX_SAFE_INTEGER; export const DATA_GATHERING_QUEUE_PRIORITY_HIGH = 1; @@ -43,6 +41,8 @@ export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy'; export const DEFAULT_LANGUAGE_CODE = 'en'; export const DEFAULT_PAGE_SIZE = 50; +export const EMERGENCY_FUND_TAG_ID = '4452656d-9fa4-4bd0-ba38-70492e31d180'; + export const GATHER_ASSET_PROFILE_PROCESS = 'GATHER_ASSET_PROFILE'; export const GATHER_ASSET_PROFILE_PROCESS_OPTIONS: JobOptions = { attempts: 10, diff --git a/libs/common/src/lib/interfaces/portfolio-position.interface.ts b/libs/common/src/lib/interfaces/portfolio-position.interface.ts index c0a4288d5..c0c0dd6fb 100644 --- a/libs/common/src/lib/interfaces/portfolio-position.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-position.interface.ts @@ -8,7 +8,7 @@ export interface PortfolioPosition { allocationCurrent: number; allocationInvestment: number; assetClass?: AssetClass; - assetSubClass?: AssetSubClass | 'CASH' | 'EMERGENCY_FUND'; + assetSubClass?: AssetSubClass | 'CASH'; countries: Country[]; currency: string; dataSource: DataSource; diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts index 6a56a9565..6ff3c1c7f 100644 --- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts +++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts @@ -13,7 +13,6 @@ import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; import { Router } from '@angular/router'; -import { ASSET_SUB_CLASS_EMERGENCY_FUND } from '@ghostfolio/common/config'; import { PortfolioPosition, UniqueAsset } from '@ghostfolio/common/interfaces'; import { AssetClass, Order as OrderModel } from '@prisma/client'; import { Subject, Subscription } from 'rxjs'; @@ -42,10 +41,7 @@ export class HoldingsTableComponent implements OnChanges, OnDestroy, OnInit { public dataSource: MatTableDataSource = new MatTableDataSource(); public displayedColumns = []; - public ignoreAssetSubClasses = [ - AssetClass.CASH.toString(), - ASSET_SUB_CLASS_EMERGENCY_FUND - ]; + public ignoreAssetSubClasses = [AssetClass.CASH.toString()]; public isLoading = true; public routeQueryParams: Subscription;