Feature/break down emergency fund by cash and assets (#2159)
* Break down emergency fund in cash and assets * Update changelog
This commit is contained in:
parent
455a2d2e92
commit
bdf72164b1
@ -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…
x
Reference in New Issue
Block a user