Browse Source

Added Time Weighted Performance to front end

pull/5027/head
Daniel Devaud 2 years ago
parent
commit
b6c24a53af
  1. 49
      apps/api/src/app/portfolio/portfolio-calculator.ts
  2. 4
      apps/api/src/app/user/update-user-setting.dto.ts
  3. 1
      apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts
  4. 27
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  5. 6
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html
  6. 1
      libs/common/src/lib/interfaces/user-settings.interface.ts

49
apps/api/src/app/portfolio/portfolio-calculator.ts

@ -240,15 +240,15 @@ export class PortfolioCalculator {
this.getRelevantStartAndEndDates(start, end, dates, step);
const dataGartheringDates = [
...dates,
...this.orders.map((o) => {
let dateParsed = Date.parse(o.date);
if (isBefore(dateParsed, end) && isAfter(dateParsed, start)) {
let date = new Date(dateParsed);
if (dates.indexOf(date) === -1) {
return date;
}
}
})
...this.orders
.filter((o) => {
let dateParsed = Date.parse(o.date);
return isBefore(dateParsed, end) && isAfter(dateParsed, start);
})
.map((o) => {
let dateParsed = Date.parse(o.date);
return new Date(dateParsed);
})
];
const { dataProviderInfos, values: marketSymbols } =
@ -285,12 +285,26 @@ export class PortfolioCalculator {
valuesBySymbol
);
let valuesBySymbolShortend: {
[symbol: string]: {
currentValues: { [date: string]: Big };
investmentValues: { [date: string]: Big };
maxInvestmentValues: { [date: string]: Big };
netPerformanceValues: { [date: string]: Big };
netPerformanceValuesPercentage: { [date: string]: Big };
};
} = {};
Object.keys(valuesBySymbol).forEach((k) => {
if (valuesBySymbol[k].currentValues) {
Object.assign(valuesBySymbolShortend, { [k]: valuesBySymbol[k] });
}
});
return dates.map((date: Date, index: number, dates: Date[]) => {
let previousDate: Date = index > 0 ? dates[index - 1] : null;
return this.calculatePerformance(
date,
previousDate,
valuesBySymbol,
valuesBySymbolShortend,
calculateTimeWeightedPerformance
);
});
@ -347,17 +361,23 @@ export class PortfolioCalculator {
);
if (
calculateTimeWeightedPerformance &&
previousTotalInvestmentValue.toNumber() &&
symbolValues.netPerformanceValuesPercentage
symbolValues.netPerformanceValuesPercentage &&
(
symbolValues.currentValues?.[previousDateString] ?? new Big(0)
).toNumber()
) {
const previousValue =
symbolValues.currentValues?.[previousDateString] ?? new Big(0);
const netPerformance =
symbolValues.netPerformanceValuesPercentage?.[dateString] ??
new Big(0);
const timeWeightedPerformanceContribution = previousValue
.div(previousTotalInvestmentValue)
.mul(netPerformance)
.mul(100);
timeWeightedPerformance = timeWeightedPerformance.plus(
previousValue.div(previousTotalInvestmentValue).mul(netPerformance)
timeWeightedPerformanceContribution
);
}
}
@ -1590,7 +1610,7 @@ export class PortfolioCalculator {
.minus(1);
} else if (
order.type === 'STAKE' &&
marketSymbolMap[order.date][order.symbol] &&
marketSymbolMap[order.date] &&
((marketSymbolMap[previousOrder.date][
previousOrder.symbol
]?.toNumber() &&
@ -1613,6 +1633,7 @@ export class PortfolioCalculator {
netPerformanceValuesPercentage[order.date] = new Big(-1);
} else if (
previousOrder.type === 'STAKE' &&
marketSymbolMap[previousOrder.date] &&
marketSymbolMap[previousOrder.date][previousOrder.symbol]?.toNumber()
) {
netPerformanceValuesPercentage[order.date] = order.unitPrice

4
apps/api/src/app/user/update-user-setting.dto.ts

@ -68,8 +68,4 @@ export class UpdateUserSettingDto {
@IsIn(<ViewMode[]>['DEFAULT', 'ZEN'])
@IsOptional()
viewMode?: ViewMode;
@IsIn(<string[]>['N', 'B', 'O'])
@IsOptional()
timeWeightedPerformance?: string;
}

1
apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts

@ -116,6 +116,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
borderColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
borderWidth: 2,
borderDash: [5, 5],
data: this.timeWeightedPerformanceDataItems.map(({ date, value }) => {
return { x: parseDate(date).getTime(), y: value };
}),

27
apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts

@ -68,6 +68,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
public placeholder = '';
public portfolioEvolutionDataLabel = $localize`Deposit`;
public streaks: PortfolioInvestments['streaks'];
public timeWeightedPerformance: string = 'N';
public top3: Position[];
public unitCurrentStreak: string;
public unitLongestStreak: string;
@ -222,21 +223,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
}
public onTimeWeightedPerformanceChanged(timeWeightedPerformance: string) {
this.dataService
.putUserSetting({ timeWeightedPerformance })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.userService.remove();
this.timeWeightedPerformance = timeWeightedPerformance;
this.userService
.get()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
});
this.update();
}
public onChangeGroupBy(aMode: GroupBy) {
@ -343,7 +332,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
filters: this.activeFilters,
range: this.user?.settings?.dateRange,
timeWeightedPerformance:
this.user?.settings?.timeWeightedPerformance === 'N' ? false : true
this.timeWeightedPerformance === 'N' ? false : true
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ chart, firstOrderDate }) => {
@ -378,15 +367,19 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
date,
value: netPerformanceInPercentage
});
if ((this.user?.settings?.timeWeightedPerformance ?? 'N') !== 'N') {
if ((this.timeWeightedPerformance ?? 'N') !== 'N') {
let lastPerformance = 0;
if (index > 0) {
lastPerformance = new Big(
chart[index - 1].timeWeightedPerformance
)
.div(100)
.plus(1)
.mul(new Big(chart[index].timeWeightedPerformance).plus(1))
.mul(
new Big(chart[index].timeWeightedPerformance).div(100).plus(1)
)
.minus(1)
.mul(100)
.toNumber();
}
chart[index].timeWeightedPerformance = lastPerformance;

6
apps/client/src/app/pages/portfolio/analysis/analysis-page.html

@ -25,8 +25,8 @@
[daysInMarket]="daysInMarket"
[isLoading]="isLoadingBenchmarkComparator || isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[performanceDataItems]="performanceDataItemsInPercentage"
[timeWeightedPerformanceDataItems]="performanceDataItemsTimeWeightedInPercentage"
[performanceDataItems]="timeWeightedPerformance === 'O' ? [] :performanceDataItemsInPercentage"
[timeWeightedPerformanceDataItems]="timeWeightedPerformance === 'N' ? [] :performanceDataItemsTimeWeightedInPercentage"
[user]="user"
(benchmarkChanged)="onChangeBenchmark($event)"
></gf-benchmark-comparator>
@ -37,7 +37,7 @@
>
<span i18n>Include time-weighted performance </span>
<gf-toggle
[defaultValue]="user?.settings?.timeWeightedPerformance ?? 'N'"
[defaultValue]="timeWeightedPerformance"
[isLoading]="isLoadingBenchmarkComparator || isLoadingInvestmentChart"
[options]="timeWeightedPerformanceOptions"
(change)="onTimeWeightedPerformanceChanged($event.value)"

1
libs/common/src/lib/interfaces/user-settings.interface.ts

@ -15,5 +15,4 @@ export interface UserSettings {
retirementDate?: string;
savingsRate?: number;
viewMode?: ViewMode;
timeWeightedPerformance?: string;
}

Loading…
Cancel
Save