Feature/improve chart calculation (#1226)
* Improve chart calculation * Update changelog
This commit is contained in:
parent
0cc42ffd7c
commit
3b2f13850c
@ -5,6 +5,13 @@ 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
|
||||||
|
|
||||||
|
- Supported units in the line chart component
|
||||||
|
- Added a new chart calculation engine (experimental)
|
||||||
|
|
||||||
## 1.186.2 - 03.09.2022
|
## 1.186.2 - 03.09.2022
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -172,15 +172,12 @@ export class PortfolioCalculator {
|
|||||||
start: Date,
|
start: Date,
|
||||||
end = new Date(Date.now())
|
end = new Date(Date.now())
|
||||||
): Promise<CurrentPositions> {
|
): Promise<CurrentPositions> {
|
||||||
const transactionPointsInRange =
|
const transactionPointsBeforeEndDate =
|
||||||
this.transactionPoints?.filter((transactionPoint) => {
|
this.transactionPoints?.filter((transactionPoint) => {
|
||||||
return isWithinInterval(parseDate(transactionPoint.date), {
|
return isBefore(parseDate(transactionPoint.date), end);
|
||||||
start,
|
|
||||||
end
|
|
||||||
});
|
|
||||||
}) ?? [];
|
}) ?? [];
|
||||||
|
|
||||||
if (!transactionPointsInRange.length) {
|
if (!transactionPointsBeforeEndDate.length) {
|
||||||
return {
|
return {
|
||||||
currentValue: new Big(0),
|
currentValue: new Big(0),
|
||||||
grossPerformance: new Big(0),
|
grossPerformance: new Big(0),
|
||||||
@ -194,32 +191,34 @@ export class PortfolioCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastTransactionPoint =
|
const lastTransactionPoint =
|
||||||
transactionPointsInRange[transactionPointsInRange.length - 1];
|
transactionPointsBeforeEndDate[transactionPointsBeforeEndDate.length - 1];
|
||||||
|
|
||||||
let firstTransactionPoint: TransactionPoint = null;
|
let firstTransactionPoint: TransactionPoint = null;
|
||||||
let firstIndex = transactionPointsInRange.length;
|
let firstIndex = transactionPointsBeforeEndDate.length;
|
||||||
const dates = [];
|
const dates = [];
|
||||||
const dataGatheringItems: IDataGatheringItem[] = [];
|
const dataGatheringItems: IDataGatheringItem[] = [];
|
||||||
const currencies: { [symbol: string]: string } = {};
|
const currencies: { [symbol: string]: string } = {};
|
||||||
|
|
||||||
dates.push(resetHours(start));
|
dates.push(resetHours(start));
|
||||||
for (const item of transactionPointsInRange[firstIndex - 1].items) {
|
for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) {
|
||||||
dataGatheringItems.push({
|
dataGatheringItems.push({
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
symbol: item.symbol
|
symbol: item.symbol
|
||||||
});
|
});
|
||||||
currencies[item.symbol] = item.currency;
|
currencies[item.symbol] = item.currency;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < transactionPointsInRange.length; i++) {
|
for (let i = 0; i < transactionPointsBeforeEndDate.length; i++) {
|
||||||
if (
|
if (
|
||||||
!isBefore(parseDate(transactionPointsInRange[i].date), start) &&
|
!isBefore(parseDate(transactionPointsBeforeEndDate[i].date), start) &&
|
||||||
firstTransactionPoint === null
|
firstTransactionPoint === null
|
||||||
) {
|
) {
|
||||||
firstTransactionPoint = transactionPointsInRange[i];
|
firstTransactionPoint = transactionPointsBeforeEndDate[i];
|
||||||
firstIndex = i;
|
firstIndex = i;
|
||||||
}
|
}
|
||||||
if (firstTransactionPoint !== null) {
|
if (firstTransactionPoint !== null) {
|
||||||
dates.push(resetHours(parseDate(transactionPointsInRange[i].date)));
|
dates.push(
|
||||||
|
resetHours(parseDate(transactionPointsBeforeEndDate[i].date))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Query,
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
UseInterceptors
|
UseInterceptors,
|
||||||
|
Version
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
@ -110,6 +111,26 @@ export class PortfolioController {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('chart')
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
@Version('2')
|
||||||
|
public async getChartV2(
|
||||||
|
@Headers('impersonation-id') impersonationId: string,
|
||||||
|
@Query('range') range
|
||||||
|
): Promise<PortfolioChart> {
|
||||||
|
const historicalDataContainer = await this.portfolioService.getChartV2(
|
||||||
|
impersonationId,
|
||||||
|
range
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
chart: historicalDataContainer.items,
|
||||||
|
hasError: false,
|
||||||
|
isAllTimeHigh: false,
|
||||||
|
isAllTimeLow: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Get('details')
|
@Get('details')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@UseInterceptors(RedactValuesInResponseInterceptor)
|
@UseInterceptors(RedactValuesInResponseInterceptor)
|
||||||
|
@ -57,6 +57,7 @@ import {
|
|||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import Big from 'big.js';
|
import Big from 'big.js';
|
||||||
import {
|
import {
|
||||||
|
addDays,
|
||||||
differenceInDays,
|
differenceInDays,
|
||||||
endOfToday,
|
endOfToday,
|
||||||
format,
|
format,
|
||||||
@ -71,7 +72,7 @@ import {
|
|||||||
subDays,
|
subDays,
|
||||||
subYears
|
subYears
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { isEmpty, sortBy, uniq, uniqBy } from 'lodash';
|
import { isEmpty, last, sortBy, uniq, uniqBy } from 'lodash';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HistoricalDataContainer,
|
HistoricalDataContainer,
|
||||||
@ -85,6 +86,7 @@ const emergingMarkets = require('../../assets/countries/emerging-markets.json');
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PortfolioService {
|
export class PortfolioService {
|
||||||
|
private static readonly MAX_CHART_ITEMS = 250;
|
||||||
private baseCurrency: string;
|
private baseCurrency: string;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@ -354,6 +356,78 @@ export class PortfolioService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getChartV2(
|
||||||
|
aImpersonationId: string,
|
||||||
|
aDateRange: DateRange = 'max'
|
||||||
|
): Promise<HistoricalDataContainer> {
|
||||||
|
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
|
||||||
|
|
||||||
|
const { portfolioOrders, transactionPoints } =
|
||||||
|
await this.getTransactionPoints({
|
||||||
|
userId
|
||||||
|
});
|
||||||
|
|
||||||
|
const portfolioCalculator = new PortfolioCalculator({
|
||||||
|
currency: this.request.user.Settings.currency,
|
||||||
|
currentRateService: this.currentRateService,
|
||||||
|
orders: portfolioOrders
|
||||||
|
});
|
||||||
|
|
||||||
|
portfolioCalculator.setTransactionPoints(transactionPoints);
|
||||||
|
if (transactionPoints.length === 0) {
|
||||||
|
return {
|
||||||
|
isAllTimeHigh: false,
|
||||||
|
isAllTimeLow: false,
|
||||||
|
items: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const endDate = new Date();
|
||||||
|
|
||||||
|
const portfolioStart = parseDate(transactionPoints[0].date);
|
||||||
|
const startDate = this.getStartDate(aDateRange, portfolioStart);
|
||||||
|
|
||||||
|
const daysInMarket = differenceInDays(new Date(), startDate);
|
||||||
|
const step = Math.round(
|
||||||
|
daysInMarket / Math.min(daysInMarket, PortfolioService.MAX_CHART_ITEMS)
|
||||||
|
);
|
||||||
|
|
||||||
|
const items: HistoricalDataItem[] = [];
|
||||||
|
|
||||||
|
let currentEndDate = startDate;
|
||||||
|
|
||||||
|
while (isBefore(currentEndDate, endDate)) {
|
||||||
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||||
|
startDate,
|
||||||
|
currentEndDate
|
||||||
|
);
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
date: format(currentEndDate, DATE_FORMAT),
|
||||||
|
value: currentPositions.netPerformancePercentage.toNumber() * 100
|
||||||
|
});
|
||||||
|
|
||||||
|
currentEndDate = addDays(currentEndDate, step);
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
|
if (last(items)?.date !== format(today, DATE_FORMAT)) {
|
||||||
|
// Add today
|
||||||
|
const { netPerformancePercentage } =
|
||||||
|
await portfolioCalculator.getCurrentPositions(startDate, today);
|
||||||
|
items.push({
|
||||||
|
date: format(today, DATE_FORMAT),
|
||||||
|
value: netPerformancePercentage.toNumber() * 100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isAllTimeHigh: false,
|
||||||
|
isAllTimeLow: false,
|
||||||
|
items: items
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async getDetails(
|
public async getDetails(
|
||||||
aImpersonationId: string,
|
aImpersonationId: string,
|
||||||
aUserId: string,
|
aUserId: string,
|
||||||
|
@ -5,6 +5,10 @@ export class UpdateUserSettingDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
emergencyFund?: number;
|
emergencyFund?: number;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
@IsOptional()
|
||||||
|
isExperimentalFeatures?: boolean;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
isRestrictedView?: boolean;
|
isRestrictedView?: boolean;
|
||||||
|
@ -106,7 +106,10 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
|
|||||||
this.isLoadingPerformance = true;
|
this.isLoadingPerformance = true;
|
||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
.fetchChart({ range: this.dateRange })
|
.fetchChart({
|
||||||
|
range: this.dateRange,
|
||||||
|
version: this.user?.settings?.isExperimentalFeatures ? 2 : 1
|
||||||
|
})
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((chartData) => {
|
.subscribe((chartData) => {
|
||||||
this.historicalDataItems = chartData.chart.map((chartDataItem) => {
|
this.historicalDataItems = chartData.chart.map((chartDataItem) => {
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
<gf-line-chart
|
<gf-line-chart
|
||||||
class="position-absolute"
|
class="position-absolute"
|
||||||
symbol="Performance"
|
symbol="Performance"
|
||||||
[currency]="user?.settings?.baseCurrency"
|
|
||||||
[historicalDataItems]="historicalDataItems"
|
[historicalDataItems]="historicalDataItems"
|
||||||
[hidden]="historicalDataItems?.length === 0"
|
[hidden]="historicalDataItems?.length === 0"
|
||||||
[locale]="user?.settings?.locale"
|
[locale]="user?.settings?.locale"
|
||||||
@ -24,6 +23,7 @@
|
|||||||
[showLoader]="false"
|
[showLoader]="false"
|
||||||
[showXAxis]="false"
|
[showXAxis]="false"
|
||||||
[showYAxis]="false"
|
[showYAxis]="false"
|
||||||
|
[unit]="user?.settings?.isExperimentalFeatures ? '%' : user?.settings?.baseCurrency"
|
||||||
></gf-line-chart>
|
></gf-line-chart>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -249,10 +249,10 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
private getTooltipPluginConfiguration() {
|
private getTooltipPluginConfiguration() {
|
||||||
return {
|
return {
|
||||||
...getTooltipOptions(
|
...getTooltipOptions({
|
||||||
this.isInPercent ? undefined : this.currency,
|
locale: this.isInPercent ? undefined : this.locale,
|
||||||
this.isInPercent ? undefined : this.locale
|
unit: this.isInPercent ? undefined : this.currency
|
||||||
),
|
}),
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
position: <unknown>'top',
|
position: <unknown>'top',
|
||||||
xAlign: 'center',
|
xAlign: 'center',
|
||||||
|
@ -23,13 +23,13 @@
|
|||||||
class="mb-4"
|
class="mb-4"
|
||||||
benchmarkLabel="Average Unit Price"
|
benchmarkLabel="Average Unit Price"
|
||||||
[benchmarkDataItems]="benchmarkDataItems"
|
[benchmarkDataItems]="benchmarkDataItems"
|
||||||
[currency]="SymbolProfile?.currency"
|
|
||||||
[historicalDataItems]="historicalDataItems"
|
[historicalDataItems]="historicalDataItems"
|
||||||
[locale]="data.locale"
|
[locale]="data.locale"
|
||||||
[showGradient]="true"
|
[showGradient]="true"
|
||||||
[showXAxis]="true"
|
[showXAxis]="true"
|
||||||
[showYAxis]="true"
|
[showYAxis]="true"
|
||||||
[symbol]="data.symbol"
|
[symbol]="data.symbol"
|
||||||
|
[unit]="SymbolProfile?.currency"
|
||||||
></gf-line-chart>
|
></gf-line-chart>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -226,6 +226,24 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onExperimentalFeaturesChange(aEvent: MatSlideToggleChange) {
|
||||||
|
this.dataService
|
||||||
|
.putUserSetting({ isExperimentalFeatures: aEvent.checked })
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.userService.remove();
|
||||||
|
|
||||||
|
this.userService
|
||||||
|
.get()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((user) => {
|
||||||
|
this.user = user;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public onRedeemCoupon() {
|
public onRedeemCoupon() {
|
||||||
let couponCode = prompt($localize`Please enter your coupon code:`);
|
let couponCode = prompt($localize`Please enter your coupon code:`);
|
||||||
couponCode = couponCode?.trim();
|
couponCode = couponCode?.trim();
|
||||||
|
@ -188,6 +188,22 @@
|
|||||||
></mat-slide-toggle>
|
></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
*ngIf="hasPermissionToUpdateUserSettings && user?.subscription"
|
||||||
|
class="align-items-center d-flex mt-4 py-1"
|
||||||
|
>
|
||||||
|
<div class="pr-1 w-50">
|
||||||
|
<div i18n>Experimental Features</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-1 w-50">
|
||||||
|
<mat-slide-toggle
|
||||||
|
color="primary"
|
||||||
|
[checked]="user.settings.isExperimentalFeatures"
|
||||||
|
[disabled]="!hasPermissionToUpdateUserSettings"
|
||||||
|
(change)="onExperimentalFeaturesChange($event)"
|
||||||
|
></mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="align-items-center d-flex mt-4 py-1">
|
<div class="align-items-center d-flex mt-4 py-1">
|
||||||
<div class="pr-1 w-50" i18n>User ID</div>
|
<div class="pr-1 w-50" i18n>User ID</div>
|
||||||
<div class="pl-1 w-50">{{ user?.id }}</div>
|
<div class="pl-1 w-50">{{ user?.id }}</div>
|
||||||
|
@ -185,8 +185,8 @@ export class DataService {
|
|||||||
return this.http.get<BenchmarkResponse>('/api/v1/benchmark');
|
return this.http.get<BenchmarkResponse>('/api/v1/benchmark');
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchChart({ range }: { range: DateRange }) {
|
public fetchChart({ range, version }: { range: DateRange; version: number }) {
|
||||||
return this.http.get<PortfolioChart>('/api/v1/portfolio/chart', {
|
return this.http.get<PortfolioChart>(`/api/v${version}/portfolio/chart`, {
|
||||||
params: { range }
|
params: { range }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -642,7 +642,7 @@
|
|||||||
<target state="translated">Aktuelle Marktstimmung</target>
|
<target state="translated">Aktuelle Marktstimmung</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html</context>
|
||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">12</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="e95ae009d0bdb45fcc656e8b65248cf7396080d5" datatype="html">
|
<trans-unit id="e95ae009d0bdb45fcc656e8b65248cf7396080d5" datatype="html">
|
||||||
@ -1058,7 +1058,7 @@
|
|||||||
<target state="translated">Bitte gib den Betrag deines Notfallfonds ein:</target>
|
<target state="translated">Bitte gib den Betrag deines Notfallfonds ein:</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts</context>
|
||||||
<context context-type="linenumber">48</context>
|
<context context-type="linenumber">52</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="fc61416d48adb7af122b8697e806077eb251fb57" datatype="html">
|
<trans-unit id="fc61416d48adb7af122b8697e806077eb251fb57" datatype="html">
|
||||||
@ -1262,7 +1262,7 @@
|
|||||||
<target state="translated">Bitte gebe deinen Gutscheincode ein:</target>
|
<target state="translated">Bitte gebe deinen Gutscheincode ein:</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">230</context>
|
<context context-type="linenumber">248</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4420880039966769543" datatype="html">
|
<trans-unit id="4420880039966769543" datatype="html">
|
||||||
@ -1270,7 +1270,7 @@
|
|||||||
<target state="translated">Gutscheincode konnte nicht eingelöst werden</target>
|
<target state="translated">Gutscheincode konnte nicht eingelöst werden</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">240</context>
|
<context context-type="linenumber">258</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4819099731531004979" datatype="html">
|
<trans-unit id="4819099731531004979" datatype="html">
|
||||||
@ -1278,7 +1278,7 @@
|
|||||||
<target state="translated">Gutscheincode wurde eingelöst</target>
|
<target state="translated">Gutscheincode wurde eingelöst</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">252</context>
|
<context context-type="linenumber">270</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7967484035994732534" datatype="html">
|
<trans-unit id="7967484035994732534" datatype="html">
|
||||||
@ -1286,7 +1286,7 @@
|
|||||||
<target state="translated">Neu laden</target>
|
<target state="translated">Neu laden</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">253</context>
|
<context context-type="linenumber">271</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7963559562180316948" datatype="html">
|
<trans-unit id="7963559562180316948" datatype="html">
|
||||||
@ -1294,7 +1294,7 @@
|
|||||||
<target state="translated">Möchtest du diese Anmeldemethode wirklich löschen?</target>
|
<target state="translated">Möchtest du diese Anmeldemethode wirklich löschen?</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">299</context>
|
<context context-type="linenumber">317</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
|
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
|
||||||
@ -1382,7 +1382,7 @@
|
|||||||
<target state="new">Locale</target>
|
<target state="new">Locale</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">134</context>
|
<context context-type="linenumber">135</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4402006eb2c97591dd8c87a5bd8f721fe9e4dc00" datatype="html">
|
<trans-unit id="4402006eb2c97591dd8c87a5bd8f721fe9e4dc00" datatype="html">
|
||||||
@ -1390,7 +1390,7 @@
|
|||||||
<target state="translated">Datums- und Zahlenformat</target>
|
<target state="translated">Datums- und Zahlenformat</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">136</context>
|
<context context-type="linenumber">137</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="234d001ccf20d47ac6a2846bb029eebb61444d15" datatype="html">
|
<trans-unit id="234d001ccf20d47ac6a2846bb029eebb61444d15" datatype="html">
|
||||||
@ -1398,7 +1398,7 @@
|
|||||||
<target state="translated">Ansicht</target>
|
<target state="translated">Ansicht</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">159</context>
|
<context context-type="linenumber">160</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9ae348ee3a7319c2fc4794fa8bc425999d355f8f" datatype="html">
|
<trans-unit id="9ae348ee3a7319c2fc4794fa8bc425999d355f8f" datatype="html">
|
||||||
@ -1406,7 +1406,7 @@
|
|||||||
<target state="translated">Einloggen mit Fingerabdruck</target>
|
<target state="translated">Einloggen mit Fingerabdruck</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">180</context>
|
<context context-type="linenumber">181</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="83c4d4d764d2e2725ab8e919ec16ac400e1f290a" datatype="html">
|
<trans-unit id="83c4d4d764d2e2725ab8e919ec16ac400e1f290a" datatype="html">
|
||||||
@ -1414,7 +1414,7 @@
|
|||||||
<target state="translated">Benutzer ID</target>
|
<target state="translated">Benutzer ID</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">191</context>
|
<context context-type="linenumber">208</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9021c579c084e68d9db06a569d76f024111c6c54" datatype="html">
|
<trans-unit id="9021c579c084e68d9db06a569d76f024111c6c54" datatype="html">
|
||||||
@ -1422,7 +1422,7 @@
|
|||||||
<target state="translated">Zugangsberechtigung</target>
|
<target state="translated">Zugangsberechtigung</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">200</context>
|
<context context-type="linenumber">217</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5e41f1b4c46ad9e0a9bc83fa36445483aa5cc324" datatype="html">
|
<trans-unit id="5e41f1b4c46ad9e0a9bc83fa36445483aa5cc324" datatype="html">
|
||||||
@ -2641,6 +2641,14 @@
|
|||||||
<context context-type="linenumber">4,7</context>
|
<context context-type="linenumber">4,7</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="03b120b05e0922e5e830c3466fda9ee0bfbf59e9" datatype="html">
|
||||||
|
<source>Experimental Features</source>
|
||||||
|
<target state="translated">Experimentelle Funktionen</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
|
<context context-type="linenumber">196</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -583,7 +583,7 @@
|
|||||||
<source>Current Market Mood</source>
|
<source>Current Market Mood</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html</context>
|
||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">12</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="e95ae009d0bdb45fcc656e8b65248cf7396080d5" datatype="html">
|
<trans-unit id="e95ae009d0bdb45fcc656e8b65248cf7396080d5" datatype="html">
|
||||||
@ -957,7 +957,7 @@
|
|||||||
<source>Please enter the amount of your emergency fund:</source>
|
<source>Please enter the amount of your emergency fund:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts</context>
|
||||||
<context context-type="linenumber">48</context>
|
<context context-type="linenumber">52</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="fc61416d48adb7af122b8697e806077eb251fb57" datatype="html">
|
<trans-unit id="fc61416d48adb7af122b8697e806077eb251fb57" datatype="html">
|
||||||
@ -1137,35 +1137,35 @@
|
|||||||
<source>Please enter your coupon code:</source>
|
<source>Please enter your coupon code:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">230</context>
|
<context context-type="linenumber">248</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4420880039966769543" datatype="html">
|
<trans-unit id="4420880039966769543" datatype="html">
|
||||||
<source>Could not redeem coupon code</source>
|
<source>Could not redeem coupon code</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">240</context>
|
<context context-type="linenumber">258</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4819099731531004979" datatype="html">
|
<trans-unit id="4819099731531004979" datatype="html">
|
||||||
<source>Coupon code has been redeemed</source>
|
<source>Coupon code has been redeemed</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">252</context>
|
<context context-type="linenumber">270</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7967484035994732534" datatype="html">
|
<trans-unit id="7967484035994732534" datatype="html">
|
||||||
<source>Reload</source>
|
<source>Reload</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">253</context>
|
<context context-type="linenumber">271</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7963559562180316948" datatype="html">
|
<trans-unit id="7963559562180316948" datatype="html">
|
||||||
<source>Do you really want to remove this sign in method?</source>
|
<source>Do you really want to remove this sign in method?</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
|
||||||
<context context-type="linenumber">299</context>
|
<context context-type="linenumber">317</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
|
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
|
||||||
@ -1243,42 +1243,42 @@
|
|||||||
<source>Locale</source>
|
<source>Locale</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">134</context>
|
<context context-type="linenumber">135</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4402006eb2c97591dd8c87a5bd8f721fe9e4dc00" datatype="html">
|
<trans-unit id="4402006eb2c97591dd8c87a5bd8f721fe9e4dc00" datatype="html">
|
||||||
<source>Date and number format</source>
|
<source>Date and number format</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">136</context>
|
<context context-type="linenumber">137</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="234d001ccf20d47ac6a2846bb029eebb61444d15" datatype="html">
|
<trans-unit id="234d001ccf20d47ac6a2846bb029eebb61444d15" datatype="html">
|
||||||
<source>View Mode</source>
|
<source>View Mode</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">159</context>
|
<context context-type="linenumber">160</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9ae348ee3a7319c2fc4794fa8bc425999d355f8f" datatype="html">
|
<trans-unit id="9ae348ee3a7319c2fc4794fa8bc425999d355f8f" datatype="html">
|
||||||
<source>Sign in with fingerprint</source>
|
<source>Sign in with fingerprint</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">180</context>
|
<context context-type="linenumber">181</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="83c4d4d764d2e2725ab8e919ec16ac400e1f290a" datatype="html">
|
<trans-unit id="83c4d4d764d2e2725ab8e919ec16ac400e1f290a" datatype="html">
|
||||||
<source>User ID</source>
|
<source>User ID</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">191</context>
|
<context context-type="linenumber">208</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9021c579c084e68d9db06a569d76f024111c6c54" datatype="html">
|
<trans-unit id="9021c579c084e68d9db06a569d76f024111c6c54" datatype="html">
|
||||||
<source>Granted Access</source>
|
<source>Granted Access</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
<context context-type="linenumber">200</context>
|
<context context-type="linenumber">217</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5e41f1b4c46ad9e0a9bc83fa36445483aa5cc324" datatype="html">
|
<trans-unit id="5e41f1b4c46ad9e0a9bc83fa36445483aa5cc324" datatype="html">
|
||||||
@ -2359,6 +2359,13 @@
|
|||||||
<context context-type="linenumber">6</context>
|
<context context-type="linenumber">6</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="03b120b05e0922e5e830c3466fda9ee0bfbf59e9" datatype="html">
|
||||||
|
<source>Experimental Features</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
|
||||||
|
<context context-type="linenumber">196</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
@ -2,7 +2,13 @@ import { Chart, TooltipPosition } from 'chart.js';
|
|||||||
|
|
||||||
import { getBackgroundColor, getTextColor } from './helper';
|
import { getBackgroundColor, getTextColor } from './helper';
|
||||||
|
|
||||||
export function getTooltipOptions(currency = '', locale = '') {
|
export function getTooltipOptions({
|
||||||
|
locale = '',
|
||||||
|
unit = ''
|
||||||
|
}: {
|
||||||
|
locale?: string;
|
||||||
|
unit?: string;
|
||||||
|
} = {}) {
|
||||||
return {
|
return {
|
||||||
backgroundColor: getBackgroundColor(),
|
backgroundColor: getBackgroundColor(),
|
||||||
bodyColor: `rgb(${getTextColor()})`,
|
bodyColor: `rgb(${getTextColor()})`,
|
||||||
@ -15,11 +21,11 @@ export function getTooltipOptions(currency = '', locale = '') {
|
|||||||
label += ': ';
|
label += ': ';
|
||||||
}
|
}
|
||||||
if (context.parsed.y !== null) {
|
if (context.parsed.y !== null) {
|
||||||
if (currency) {
|
if (unit) {
|
||||||
label += `${context.parsed.y.toLocaleString(locale, {
|
label += `${context.parsed.y.toLocaleString(locale, {
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
minimumFractionDigits: 2
|
minimumFractionDigits: 2
|
||||||
})} ${currency}`;
|
})} ${unit}`;
|
||||||
} else {
|
} else {
|
||||||
label += context.parsed.y.toFixed(2);
|
label += context.parsed.y.toFixed(2);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { ViewMode } from '@prisma/client';
|
|||||||
|
|
||||||
export interface UserSettings {
|
export interface UserSettings {
|
||||||
baseCurrency?: string;
|
baseCurrency?: string;
|
||||||
|
isExperimentalFeatures?: boolean;
|
||||||
isRestrictedView?: boolean;
|
isRestrictedView?: boolean;
|
||||||
language?: string;
|
language?: string;
|
||||||
locale: string;
|
locale: string;
|
||||||
|
@ -47,7 +47,6 @@ import { LineChartItem } from './interfaces/line-chart.interface';
|
|||||||
export class LineChartComponent implements AfterViewInit, OnChanges, OnDestroy {
|
export class LineChartComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||||
@Input() benchmarkDataItems: LineChartItem[] = [];
|
@Input() benchmarkDataItems: LineChartItem[] = [];
|
||||||
@Input() benchmarkLabel = '';
|
@Input() benchmarkLabel = '';
|
||||||
@Input() currency: string;
|
|
||||||
@Input() historicalDataItems: LineChartItem[];
|
@Input() historicalDataItems: LineChartItem[];
|
||||||
@Input() locale: string;
|
@Input() locale: string;
|
||||||
@Input() showGradient = false;
|
@Input() showGradient = false;
|
||||||
@ -56,6 +55,7 @@ export class LineChartComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
@Input() showXAxis = false;
|
@Input() showXAxis = false;
|
||||||
@Input() showYAxis = false;
|
@Input() showYAxis = false;
|
||||||
@Input() symbol: string;
|
@Input() symbol: string;
|
||||||
|
@Input() unit: string;
|
||||||
@Input() yMax: number;
|
@Input() yMax: number;
|
||||||
@Input() yMaxLabel: string;
|
@Input() yMaxLabel: string;
|
||||||
@Input() yMin: number;
|
@Input() yMin: number;
|
||||||
@ -259,7 +259,7 @@ export class LineChartComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
private getTooltipPluginConfiguration() {
|
private getTooltipPluginConfiguration() {
|
||||||
return {
|
return {
|
||||||
...getTooltipOptions(this.currency, this.locale),
|
...getTooltipOptions({ locale: this.locale, unit: this.unit }),
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
position: <unknown>'top',
|
position: <unknown>'top',
|
||||||
xAlign: 'center',
|
xAlign: 'center',
|
||||||
|
@ -349,7 +349,7 @@ export class PortfolioProportionChartComponent
|
|||||||
|
|
||||||
private getTooltipPluginConfiguration(data: ChartConfiguration['data']) {
|
private getTooltipPluginConfiguration(data: ChartConfiguration['data']) {
|
||||||
return {
|
return {
|
||||||
...getTooltipOptions(this.baseCurrency, this.locale),
|
...getTooltipOptions({ locale: this.locale, unit: this.baseCurrency }),
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context) => {
|
label: (context) => {
|
||||||
const labelIndex =
|
const labelIndex =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user