diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts index 63187c05c..61ee39027 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts @@ -1,7 +1,11 @@ import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { FireWealth, User } from '@ghostfolio/common/interfaces'; +import { + FireWealth, + User, + FireCalculation +} from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfFireCalculatorComponent } from '@ghostfolio/ui/fire-calculator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; @@ -44,6 +48,10 @@ export class GfFirePageComponent implements OnDestroy, OnInit { public withdrawalRatePerMonth: Big; public withdrawalRatePerYear: Big; + public projectedWithdrawalRatePerMonth: Big; + public projectedWithdrawalRatePerYear: Big; + public fireCalculation: FireCalculation; + private unsubscribeSubject = new Subject(); public constructor( @@ -170,6 +178,7 @@ export class GfFirePageComponent implements OnDestroy, OnInit { this.user = user; this.calculateWithdrawalRates(); + this.calculateProjectedWithdrawalRates(); this.changeDetectorRef.markForCheck(); }); @@ -211,6 +220,11 @@ export class GfFirePageComponent implements OnDestroy, OnInit { }); } + public onCalculationComplete(calculation: FireCalculation) { + this.fireCalculation = calculation; + this.calculateProjectedWithdrawalRates(); + } + public ngOnDestroy() { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); @@ -225,4 +239,19 @@ export class GfFirePageComponent implements OnDestroy, OnInit { this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12); } } + + private calculateProjectedWithdrawalRates() { + if ( + this.fireWealth && + this.user?.settings?.safeWithdrawalRate && + this.fireCalculation.projectedTotalAmount + ) { + this.projectedWithdrawalRatePerYear = new Big( + this.fireCalculation.projectedTotalAmount + ).mul(this.user.settings.safeWithdrawalRate); + + this.projectedWithdrawalRatePerMonth = + this.projectedWithdrawalRatePerYear.div(12); + } + } } diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.html b/apps/client/src/app/pages/portfolio/fire/fire-page.html index ce51717fa..6d4b500ae 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.html +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.html @@ -28,6 +28,7 @@ [retirementDate]="user?.settings?.retirementDate" [savingsRate]="user?.settings?.savingsRate" (annualInterestRateChanged)="onAnnualInterestRateChange($event)" + (calculationComplete)="onCalculationComplete($event)" (projectedTotalAmountChanged)="onProjectedTotalAmountChange($event)" (retirementDateChanged)="onRetirementDateChange($event)" (savingsRateChanged)="onSavingsRateChange($event)" @@ -62,75 +63,127 @@ } @else {
- If you retire today, you would be able to withdraw -   - +
+ If you retire today, you would be able to withdraw +   + +   + per year +   + or +   + +   + per month, +   + based on your total assets of +   + + +   + and a safe withdrawal rate (SWR) of + @if ( + !hasImpersonationId && + hasPermissionToUpdateUserSettings && + user?.settings?.isExperimentalFeatures + ) { + . + } @else { +   + . + } +
+ +
+ By +   + {{ + fireCalculation?.retirementDate | date: 'MMMM yyyy' + }} + ,   - per year -   - or -   - + this is projected to increase to   - per month, -   - based on your total assets of -   - - -   - and a safe withdrawal rate (SWR) of - @if ( - !hasImpersonationId && - hasPermissionToUpdateUserSettings && - user?.settings?.isExperimentalFeatures - ) { - . - } @else { +   + or +   + +   + per month, +   + assuming a   . - } + [precision]="2" + [value]="fireCalculation?.annualInterestRate" + /> +   + annual interest rate. +
} diff --git a/libs/common/src/lib/interfaces/fire-calculation.interface.ts b/libs/common/src/lib/interfaces/fire-calculation.interface.ts new file mode 100644 index 000000000..747d4d35b --- /dev/null +++ b/libs/common/src/lib/interfaces/fire-calculation.interface.ts @@ -0,0 +1,5 @@ +export interface FireCalculation { + retirementDate: Date; + projectedTotalAmount: number; + annualInterestRate: number; +} diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 1d7991e40..c6fc8ca3d 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -18,6 +18,7 @@ import type { DataProviderInfo } from './data-provider-info.interface'; import type { EnhancedSymbolProfile } from './enhanced-symbol-profile.interface'; import type { FilterGroup } from './filter-group.interface'; import type { Filter } from './filter.interface'; +import type { FireCalculation } from './fire-calculation.interface'; import type { FireWealth } from './fire-wealth.interface'; import type { HistoricalDataItem } from './historical-data-item.interface'; import type { HoldingWithParents } from './holding-with-parents.interface'; @@ -141,6 +142,7 @@ export { Filter, FilterGroup, FireWealth, + FireCalculation, HistoricalDataItem, HistoricalResponse, Holding, diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts index 44276ec43..4fd3e20af 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -4,6 +4,7 @@ import { } from '@ghostfolio/common/chart-helper'; import { primaryColorRgb } from '@ghostfolio/common/config'; import { getLocale } from '@ghostfolio/common/helper'; +import { FireCalculation } from '@ghostfolio/common/interfaces'; import { ColorScheme } from '@ghostfolio/common/types'; import { CommonModule } from '@angular/common'; @@ -91,6 +92,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { @Output() projectedTotalAmountChanged = new EventEmitter(); @Output() retirementDateChanged = new EventEmitter(); @Output() savingsRateChanged = new EventEmitter(); + @Output() calculationComplete = new EventEmitter(); @ViewChild('chartCanvas') chartCanvas: ElementRef; @@ -131,6 +133,18 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy { this.initialize(); }); + this.calculatorForm.valueChanges + .pipe(debounceTime(500), takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + const { retirementDate, projectedTotalAmount } = + this.calculatorForm.getRawValue(); + this.calculationComplete.emit({ + retirementDate, + projectedTotalAmount, + annualInterestRate: this.getR() + }); + }); + this.calculatorForm .get('annualInterestRate') .valueChanges.pipe(debounceTime(500), takeUntil(this.unsubscribeSubject))