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 11 months 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/),
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
### 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';
export interface PortfolioOrder {
@ -9,6 +9,7 @@ export interface PortfolioOrder {
name: string;
quantity: Big;
symbol: string;
tags?: Tag[];
type: TypeOfOrder;
unitPrice: Big;
}

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

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

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

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

@ -163,7 +163,33 @@
[isCurrency]="true"
[locale]="locale"
[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>
</div>
</div>

@ -51,7 +51,7 @@ export class FirePageComponent implements OnDestroy, OnInit {
return;
}
this.fireWealth = new Big(summary.currentValue);
this.fireWealth = new Big(summary.fireWealth);
this.withdrawalRatePerYear = this.fireWealth.mul(4).div(100);
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 { Country } from './country.interface';
@ -26,8 +26,9 @@ export interface PortfolioPosition {
netPerformancePercent: number;
quantity: number;
sectors: Sector[];
transactionCount: number;
symbol: string;
tags?: Tag[];
transactionCount: number;
type?: string;
url?: string;
valueInBaseCurrency?: number;

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

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

Loading…
Cancel
Save