Browse Source

Merge pull request #150 from dandevaud/feature/Smaller-Improvements

Feature/smaller improvements
pull/5027/head
dandevaud 5 months ago
committed by GitHub
parent
commit
a9e69b046b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 16
      apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts
  2. 17
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  3. 28
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html
  4. 10
      libs/common/src/lib/calculation-helper.ts
  5. 13
      libs/ui/src/lib/assistant/assistant.component.ts
  6. 36
      libs/ui/src/lib/holdings-table/holdings-table.component.html
  7. 13
      libs/ui/src/lib/holdings-table/holdings-table.component.ts

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

@ -55,7 +55,6 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
@Input() isLoading: boolean; @Input() isLoading: boolean;
@Input() locale = getLocale(); @Input() locale = getLocale();
@Input() performanceDataItems: LineChartItem[]; @Input() performanceDataItems: LineChartItem[];
@Input() timeWeightedPerformanceDataItems: LineChartItem[];
@Input() user: User; @Input() user: User;
@Output() benchmarkChanged = new EventEmitter<string>(); @Output() benchmarkChanged = new EventEmitter<string>();
@ -86,10 +85,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
permissions.accessAdminControl permissions.accessAdminControl
); );
if ( if (this.performanceDataItems) {
this.performanceDataItems ||
this.timeWeightedPerformanceDataItems?.length > 0
) {
this.initialize(); this.initialize();
} }
} }
@ -120,16 +116,6 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
}), }),
label: $localize`Portfolio` label: $localize`Portfolio`
}, },
{
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 };
}),
label: $localize`Portfolio (time-weighted)`
},
{ {
backgroundColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`, backgroundColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`,
borderColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`, borderColor: `rgb(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b})`,

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

@ -69,7 +69,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
[]; [];
public portfolioEvolutionDataLabel = $localize`Investment`; public portfolioEvolutionDataLabel = $localize`Investment`;
public streaks: PortfolioInvestments['streaks']; public streaks: PortfolioInvestments['streaks'];
public timeWeightedPerformance: string = 'N';
public top3: PortfolioPosition[]; public top3: PortfolioPosition[];
public unitCurrentStreak: string; public unitCurrentStreak: string;
public unitLongestStreak: string; public unitLongestStreak: string;
@ -165,12 +164,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
}); });
} }
public onTimeWeightedPerformanceChanged(timeWeightedPerformance: string) {
this.timeWeightedPerformance = timeWeightedPerformance;
this.update();
}
public onChangeGroupBy(aMode: GroupBy) { public onChangeGroupBy(aMode: GroupBy) {
this.mode = aMode; this.mode = aMode;
this.fetchDividendsAndInvestments(); this.fetchDividendsAndInvestments();
@ -263,9 +256,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchPortfolioPerformance({ .fetchPortfolioPerformance({
filters: this.userService.getFilters(), filters: this.userService.getFilters(),
range: this.user?.settings?.dateRange, range: this.user?.settings?.dateRange
timeWeightedPerformance:
this.timeWeightedPerformance === 'N' ? false : true
}) })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ chart, firstOrderDate, performance }) => { .subscribe(({ chart, firstOrderDate, performance }) => {
@ -304,12 +295,6 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
date, date,
value: netPerformanceInPercentageWithCurrencyEffect value: netPerformanceInPercentageWithCurrencyEffect
}); });
if ((this.timeWeightedPerformance ?? 'N') !== 'N') {
this.performanceDataItemsTimeWeightedInPercentage.push({
date,
value: chart[index].timeWeightedPerformance
});
}
} }
this.isLoadingInvestmentChart = false; this.isLoadingInvestmentChart = false;

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

@ -42,36 +42,10 @@
[colorScheme]="user?.settings?.colorScheme" [colorScheme]="user?.settings?.colorScheme"
[isLoading]="isLoadingBenchmarkComparator || isLoadingInvestmentChart" [isLoading]="isLoadingBenchmarkComparator || isLoadingInvestmentChart"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[performanceDataItems]=" [performanceDataItems]="performanceDataItemsInPercentage"
timeWeightedPerformance === 'O'
? []
: performanceDataItemsInPercentage
"
[timeWeightedPerformanceDataItems]="
timeWeightedPerformance === 'N'
? []
: performanceDataItemsTimeWeightedInPercentage
"
[user]="user" [user]="user"
(benchmarkChanged)="onChangeBenchmark($event)" (benchmarkChanged)="onChangeBenchmark($event)"
></gf-benchmark-comparator> ></gf-benchmark-comparator>
<div>
<div class="col-md-6 col-xs-12 d-flex">
<div
class="align-items-center d-flex flex-grow-1 h6 mb-0 py-2 text-truncate"
>
<span i18n>Include time-weighted performance </span>
<gf-toggle
[defaultValue]="timeWeightedPerformance"
[isLoading]="
isLoadingBenchmarkComparator || isLoadingInvestmentChart
"
[options]="timeWeightedPerformanceOptions"
(change)="onTimeWeightedPerformanceChanged($event.value)"
></gf-toggle>
</div>
</div>
</div>
</div> </div>
</div> </div>

10
libs/common/src/lib/calculation-helper.ts

@ -7,6 +7,7 @@ import {
startOfWeek, startOfWeek,
startOfYear, startOfYear,
subDays, subDays,
subMonths,
subYears subYears
} from 'date-fns'; } from 'date-fns';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
@ -55,12 +56,21 @@ export function getIntervalFromDateRange(
subDays(startOfWeek(resetHours(new Date()), { weekStartsOn: 1 }), 1) subDays(startOfWeek(resetHours(new Date()), { weekStartsOn: 1 }), 1)
]); ]);
break; break;
case '1w':
startDate = max([startDate, subDays(resetHours(new Date()), 7)]);
break;
case 'ytd': case 'ytd':
startDate = max([ startDate = max([
startDate, startDate,
subDays(startOfYear(resetHours(new Date())), 1) subDays(startOfYear(resetHours(new Date())), 1)
]); ]);
break; break;
case '1m':
startDate = max([startDate, subMonths(resetHours(new Date()), 1)]);
break;
case '3m':
startDate = max([startDate, subMonths(resetHours(new Date()), 3)]);
break;
case '1y': case '1y':
startDate = max([startDate, subYears(resetHours(new Date()), 1)]); startDate = max([startDate, subYears(resetHours(new Date()), 1)]);
break; break;

13
libs/ui/src/lib/assistant/assistant.component.ts

@ -222,10 +222,23 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
label: $localize`Week to date` + ' (' + $localize`WTD` + ')', label: $localize`Week to date` + ' (' + $localize`WTD` + ')',
value: 'wtd' value: 'wtd'
}, },
{
label: '1 ' + $localize`Week` + ' (' + $localize`1W` + ')',
value: '1w'
},
{ {
label: $localize`Month to date` + ' (' + $localize`MTD` + ')', label: $localize`Month to date` + ' (' + $localize`MTD` + ')',
value: 'mtd' value: 'mtd'
}, },
{
label: '1 ' + $localize`Month` + ' (' + $localize`1M` + ')',
value: '1m'
},
{
label: '3 ' + $localize`Month` + ' (' + $localize`3M` + ')',
value: '3m'
},
{ {
label: $localize`Year to date` + ' (' + $localize`YTD` + ')', label: $localize`Year to date` + ' (' + $localize`YTD` + ')',
value: 'ytd' value: 'ytd'

36
libs/ui/src/lib/holdings-table/holdings-table.component.html

@ -16,6 +16,7 @@
[tooltip]="element.name" [tooltip]="element.name"
/> />
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container> </ng-container>
<ng-container matColumnDef="nameWithSymbol"> <ng-container matColumnDef="nameWithSymbol">
@ -42,6 +43,7 @@
<small class="text-muted">{{ element.symbol }}</small> <small class="text-muted">{{ element.symbol }}</small>
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell>Total</td>
</ng-container> </ng-container>
<ng-container matColumnDef="dateOfFirstActivity"> <ng-container matColumnDef="dateOfFirstActivity">
@ -66,6 +68,7 @@
/> />
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container> </ng-container>
<ng-container <ng-container
matColumnDef="marketPrice" matColumnDef="marketPrice"
@ -89,6 +92,7 @@
/> />
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container> </ng-container>
<ng-container <ng-container
@ -116,6 +120,14 @@
/> />
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell>
<gf-value
[isCurrency]="true"
[locale]="locale"
[value]="totalValue"
></gf-value>
</td>
</ng-container> </ng-container>
<ng-container <ng-container
@ -140,6 +152,7 @@
/> />
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container> </ng-container>
<ng-container <ng-container
@ -168,6 +181,15 @@
/> />
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell>
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[locale]="locale"
[value]="totalChange"
></gf-value>
</div>
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="performanceInPercentage" stickyEnd> <ng-container matColumnDef="performanceInPercentage" stickyEnd>
@ -194,6 +216,15 @@
/> />
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" mat-footer-cell>
<div class="d-flex justify-content-end">
<gf-value
[isPercent]="true"
[locale]="locale"
[value]="totalChangePercentage"
></gf-value>
</div>
</td>
</ng-container> </ng-container>
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> <tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
@ -213,6 +244,11 @@
}) })
" "
></tr> ></tr>
<tr
*matFooterRowDef="displayedColumns"
mat-footer-row
[ngClass]="{ hidden: isLoading }"
></tr>
</table> </table>
</div> </div>

13
libs/ui/src/lib/holdings-table/holdings-table.component.ts

@ -70,6 +70,10 @@ export class GfHoldingsTableComponent implements OnChanges, OnDestroy {
public isLoading = true; public isLoading = true;
public routeQueryParams: Subscription; public routeQueryParams: Subscription;
protected totalValue = 0;
protected totalChange = 0;
protected totalChangePercentage = 0;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
public ngOnChanges() { public ngOnChanges() {
@ -93,6 +97,15 @@ export class GfHoldingsTableComponent implements OnChanges, OnDestroy {
this.dataSource = new MatTableDataSource(this.holdings); this.dataSource = new MatTableDataSource(this.holdings);
this.dataSource.paginator = this.paginator; this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort; this.dataSource.sort = this.sort;
this.totalValue = this.dataSource.data.reduce(
(sum, current) => sum + current.valueInBaseCurrency,
0
);
this.totalChange = this.dataSource.data.reduce(
(sum, current) => sum + current.netPerformancePercentWithCurrencyEffect,
0
);
this.totalChangePercentage = (this.totalChange / this.totalValue) * 100;
if (this.holdings) { if (this.holdings) {
this.isLoading = false; this.isLoading = false;

Loading…
Cancel
Save