Feature/break down emergency fund by cash and assets (#2159)

* Break down emergency fund in cash and assets

* Update changelog
pull/2164/head
Thomas Kaul 1 year ago committed by GitHub
parent 455a2d2e92
commit bdf72164b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Broken down the emergency fund by cash and assets
## 1.290.0 - 2023-07-16 ## 1.290.0 - 2023-07-16
### Added ### Added

@ -1,4 +1,4 @@
import { DataSource, Type as TypeOfOrder } from '@prisma/client'; import { DataSource, Tag, Type as TypeOfOrder } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
export interface PortfolioOrder { export interface PortfolioOrder {
@ -9,6 +9,7 @@ export interface PortfolioOrder {
name: string; name: string;
quantity: Big; quantity: Big;
symbol: string; symbol: string;
tags?: Tag[];
type: TypeOfOrder; type: TypeOfOrder;
unitPrice: Big; unitPrice: Big;
} }

@ -1,4 +1,4 @@
import { DataSource } from '@prisma/client'; import { DataSource, Tag } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
export interface TransactionPointSymbol { export interface TransactionPointSymbol {
@ -9,5 +9,6 @@ export interface TransactionPointSymbol {
investment: Big; investment: Big;
quantity: Big; quantity: Big;
symbol: string; symbol: string;
tags?: Tag[];
transactionCount: number; transactionCount: number;
} }

@ -114,6 +114,7 @@ export class PortfolioCalculator {
firstBuyDate: oldAccumulatedSymbol.firstBuyDate, firstBuyDate: oldAccumulatedSymbol.firstBuyDate,
quantity: newQuantity, quantity: newQuantity,
symbol: order.symbol, symbol: order.symbol,
tags: order.tags,
transactionCount: oldAccumulatedSymbol.transactionCount + 1 transactionCount: oldAccumulatedSymbol.transactionCount + 1
}; };
} else { } else {
@ -125,6 +126,7 @@ export class PortfolioCalculator {
investment: unitPrice.mul(order.quantity).mul(factor), investment: unitPrice.mul(order.quantity).mul(factor),
quantity: order.quantity.mul(factor), quantity: order.quantity.mul(factor),
symbol: order.symbol, symbol: order.symbol,
tags: order.tags,
transactionCount: 1 transactionCount: 1
}; };
} }
@ -492,6 +494,7 @@ export class PortfolioCalculator {
: null, : null,
quantity: item.quantity, quantity: item.quantity,
symbol: item.symbol, symbol: item.symbol,
tags: item.tags,
transactionCount: item.transactionCount transactionCount: item.transactionCount
}); });

@ -161,10 +161,12 @@ export class PortfolioController {
'emergencyFund', 'emergencyFund',
'excludedAccountsAndActivities', 'excludedAccountsAndActivities',
'fees', 'fees',
'fireWealth',
'items', 'items',
'liabilities', 'liabilities',
'netWorth', 'netWorth',
'totalBuy', 'totalBuy',
'totalInvestment',
'totalSell' 'totalSell'
]); ]);
} }

@ -583,6 +583,7 @@ export class PortfolioService {
quantity: item.quantity.toNumber(), quantity: item.quantity.toNumber(),
sectors: symbolProfile.sectors, sectors: symbolProfile.sectors,
symbol: item.symbol, symbol: item.symbol,
tags: item.tags,
transactionCount: item.transactionCount, transactionCount: item.transactionCount,
url: symbolProfile.url, url: symbolProfile.url,
valueInBaseCurrency: value.toNumber() valueInBaseCurrency: value.toNumber()
@ -628,7 +629,7 @@ export class PortfolioService {
const emergencyFundInCash = emergencyFund const emergencyFundInCash = emergencyFund
.minus( .minus(
this.getEmergencyFundPositionsValueInBaseCurrency({ this.getEmergencyFundPositionsValueInBaseCurrency({
activities: orders holdings
}) })
) )
.toNumber(); .toNumber();
@ -656,7 +657,7 @@ export class PortfolioService {
balanceInBaseCurrency: cashDetails.balanceInBaseCurrency, balanceInBaseCurrency: cashDetails.balanceInBaseCurrency,
emergencyFundPositionsValueInBaseCurrency: emergencyFundPositionsValueInBaseCurrency:
this.getEmergencyFundPositionsValueInBaseCurrency({ this.getEmergencyFundPositionsValueInBaseCurrency({
activities: orders holdings
}) })
}); });
@ -742,6 +743,7 @@ export class PortfolioService {
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.SymbolProfile.symbol, symbol: order.SymbolProfile.symbol,
tags: order.tags,
type: order.type, type: order.type,
unitPrice: new Big(order.unitPrice) unitPrice: new Big(order.unitPrice)
})); }));
@ -1392,13 +1394,13 @@ export class PortfolioService {
} }
private getEmergencyFundPositionsValueInBaseCurrency({ private getEmergencyFundPositionsValueInBaseCurrency({
activities holdings
}: { }: {
activities: Activity[]; holdings: PortfolioDetails['holdings'];
}) { }) {
const emergencyFundOrders = activities.filter((activity) => { const emergencyFundHoldings = Object.values(holdings).filter(({ tags }) => {
return ( return (
activity.tags?.some(({ id }) => { tags?.some(({ id }) => {
return id === EMERGENCY_FUND_TAG_ID; return id === EMERGENCY_FUND_TAG_ID;
}) ?? false }) ?? false
); );
@ -1406,18 +1408,9 @@ export class PortfolioService {
let valueInBaseCurrencyOfEmergencyFundPositions = new Big(0); let valueInBaseCurrencyOfEmergencyFundPositions = new Big(0);
for (const order of emergencyFundOrders) { for (const { value } of emergencyFundHoldings) {
if (order.type === 'BUY') { valueInBaseCurrencyOfEmergencyFundPositions =
valueInBaseCurrencyOfEmergencyFundPositions = valueInBaseCurrencyOfEmergencyFundPositions.plus(value);
valueInBaseCurrencyOfEmergencyFundPositions.plus(
order.valueInBaseCurrency
);
} else if (order.type === 'SELL') {
valueInBaseCurrencyOfEmergencyFundPositions =
valueInBaseCurrencyOfEmergencyFundPositions.minus(
order.valueInBaseCurrency
);
}
} }
return valueInBaseCurrencyOfEmergencyFundPositions.toNumber(); return valueInBaseCurrencyOfEmergencyFundPositions.toNumber();
@ -1476,6 +1469,7 @@ export class PortfolioService {
quantity: 0, quantity: 0,
sectors: [], sectors: [],
symbol: currency, symbol: currency,
tags: [],
transactionCount: 0, transactionCount: 0,
valueInBaseCurrency: balance valueInBaseCurrency: balance
}; };
@ -1687,7 +1681,16 @@ export class PortfolioService {
totalBuy, totalBuy,
totalSell, totalSell,
committedFunds: committedFunds.toNumber(), committedFunds: committedFunds.toNumber(),
emergencyFund: emergencyFund.toNumber(), emergencyFund: {
assets: emergencyFundPositionsValueInBaseCurrency,
cash: emergencyFund
.minus(emergencyFundPositionsValueInBaseCurrency)
.toNumber(),
total: emergencyFund.toNumber()
},
fireWealth: new Big(performanceInformation.performance.currentValue)
.minus(emergencyFundPositionsValueInBaseCurrency)
.toNumber(),
ordersCount: activities.filter(({ type }) => { ordersCount: activities.filter(({ type }) => {
return type === 'BUY' || type === 'SELL'; return type === 'BUY' || type === 'SELL';
}).length }).length
@ -1739,6 +1742,7 @@ export class PortfolioService {
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.SymbolProfile.symbol, symbol: order.SymbolProfile.symbol,
tags: order.tags,
type: order.type, type: order.type,
unitPrice: new Big( unitPrice: new Big(
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(

@ -163,7 +163,33 @@
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund" [value]="isLoading ? undefined : summary?.emergencyFund?.total"
></gf-value>
</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="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.cash"
></gf-value>
</div>
</div>
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 ml-3 text-truncate" i18n>Assets</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.assets"
></gf-value> ></gf-value>
</div> </div>
</div> </div>

@ -51,7 +51,7 @@ export class FirePageComponent implements OnDestroy, OnInit {
return; return;
} }
this.fireWealth = new Big(summary.currentValue); this.fireWealth = new Big(summary.fireWealth);
this.withdrawalRatePerYear = this.fireWealth.mul(4).div(100); this.withdrawalRatePerYear = this.fireWealth.mul(4).div(100);
this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12); this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12);

@ -1,4 +1,4 @@
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; import { AssetClass, AssetSubClass, DataSource, Tag } from '@prisma/client';
import { Market, MarketState } from '../types'; import { Market, MarketState } from '../types';
import { Country } from './country.interface'; import { Country } from './country.interface';
@ -26,8 +26,9 @@ export interface PortfolioPosition {
netPerformancePercent: number; netPerformancePercent: number;
quantity: number; quantity: number;
sectors: Sector[]; sectors: Sector[];
transactionCount: number;
symbol: string; symbol: string;
tags?: Tag[];
transactionCount: number;
type?: string; type?: string;
url?: string; url?: string;
valueInBaseCurrency?: number; valueInBaseCurrency?: number;

@ -5,9 +5,14 @@ export interface PortfolioSummary extends PortfolioPerformance {
cash: number; cash: number;
committedFunds: number; committedFunds: number;
dividend: number; dividend: number;
emergencyFund: number; emergencyFund: {
assets: number;
cash: number;
total: number;
};
excludedAccountsAndActivities: number; excludedAccountsAndActivities: number;
fees: number; fees: number;
fireWealth: number;
firstOrderDate: Date; firstOrderDate: Date;
items: number; items: number;
liabilities: number; liabilities: number;

@ -1,4 +1,4 @@
import { DataSource } from '@prisma/client'; import { DataSource, Tag } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
export interface TimelinePosition { export interface TimelinePosition {
@ -15,5 +15,6 @@ export interface TimelinePosition {
netPerformancePercentage: Big; netPerformancePercentage: Big;
quantity: Big; quantity: Big;
symbol: string; symbol: string;
tags?: Tag[];
transactionCount: number; transactionCount: number;
} }

Loading…
Cancel
Save