Compare commits

..

17 Commits

Author SHA1 Message Date
6e4660295a Release 1.194.0 (#1269) 2022-09-17 21:26:08 +02:00
d4c3a9d1e8 Feature/add percentage visualization of the current filter (#1268)
* Add percentage visualization of the active filter

* Update changelog
2022-09-17 21:24:09 +02:00
263f6b32f2 Bugfix/fix performance chart calculation (#1267)
* Respect end date in performance chart calculation

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>

* Update changelog

Co-Authored-By: gizmodus <11334553+gizmodus@users.noreply.github.com>
2022-09-17 08:33:04 +02:00
637f31ae3b Add instruction for NODE_ENV: production (#1266) 2022-09-17 08:31:56 +02:00
547e27c7a1 Feature/set node env in docker compose files (#1261)
* Set NODE_ENV to production

* Update changelog
2022-09-15 17:20:03 +02:00
f10dc176f2 Feature/clean up german localization (#1260)
* Clean up German localization

* Set up Italian

* Update changelog
2022-09-15 16:58:00 +02:00
0a966e46cd Improve translation (#1258) 2022-09-15 10:37:07 +02:00
4f281d25e1 Release 1.193.0 (#1257) 2022-09-14 20:11:47 +02:00
aaba8c35c2 Feature/extend pricing page with referral section (#1256)
* Add referral section

* Update changelog
2022-09-14 20:10:35 +02:00
7d27cb3398 Bugfix/use base currency in exchange rate service instead of usd (#1255)
* Change from USD to base currency

* Update changelog
2022-09-14 19:56:18 +02:00
91678028b5 Bugfix/fix missing assets during local development (#1253)
* Extend setup for development (missing assets)

* Update changelog
2022-09-14 19:26:59 +02:00
5e3cac8ac9 Feature/sort benchmarks by name (#1250)
* Sort benchmarks by name

* Update changelog
2022-09-12 13:34:06 +02:00
33f20b6b48 Release 1.192.0 (#1249) 2022-09-11 09:21:48 +02:00
e4fd255dd7 Bugfix/improve loading indicator of benchmark comparator (#1247)
* Improve loading indicator

* Update changelog
2022-09-11 09:20:15 +02:00
e320aa91f7 Feature/simplify benchmark configuration (#1248)
* Simplify benchmark configuration

* Update changelog
2022-09-11 09:19:50 +02:00
0fcfa6c1bd Bugfix/improve error handling in benchmark calculation (#1246)
* Improve error handling

* Update changelog
2022-09-10 18:58:31 +02:00
42d32ed652 Feature/upgrade yahoo finance2 to version 2.3.6 (#1245)
* Upgrade yahoo-finance2 to version 2.3.6

* Update changelog
2022-09-10 18:57:41 +02:00
33 changed files with 2779 additions and 283 deletions

View File

@ -5,6 +5,49 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 1.194.0 - 17.09.2022
### Added
- Added `NODE_ENV: production` to the `docker-compose` files (`docker-compose.yml` and `docker-compose.build.yml`)
- Visualized the percentage of the active filter on the allocations page
### Changed
- Improved the language localization for German (`de`)
### Fixed
- Respected the end date in the performance chart calculation
### Todo
Set `NODE_ENV: production` as in [docker-compose.yml](https://github.com/ghostfolio/ghostfolio/blob/main/docker/docker-compose.yml)
## 1.193.0 - 14.09.2022
### Changed
- Sorted the benchmarks by name
- Extended the pricing page
### Fixed
- Fixed the calculations of the exchange rate service by changing `USD` to the base currency
- Fixed the missing assets during the local development
## 1.192.0 - 11.09.2022
### Changed
- Simplified the configuration of the benchmarks: `symbolProfileId` instead of `dataSource` and `symbol`
- Upgraded `yahoo-finance2` from version `2.3.3` to `2.3.6`
### Fixed
- Improved the loading indicator of the benchmark comparator
- Improved the error handling in the benchmark calculation
## 1.191.0 - 10.09.2022
### Changed

View File

@ -153,6 +153,7 @@ Please follow the instructions of the Ghostfolio [Unraid Community App](https://
### Setup
1. Run `yarn install`
1. Run `yarn build:dev` to build the source code including the assets
1. Run `docker-compose --env-file ./.env -f docker/docker-compose.dev.yml up -d` to start [PostgreSQL](https://www.postgresql.org) and [Redis](https://redis.io)
1. Run `yarn database:setup` to initialize the database schema and populate your database with (example) data
1. Start the server and the client (see [_Development_](#Development))

View File

@ -136,6 +136,10 @@
"baseHref": "/en/",
"localize": ["en"]
},
"development-it": {
"baseHref": "/it/",
"localize": ["it"]
},
"production": {
"fileReplacements": [
{
@ -180,6 +184,9 @@
"development-en": {
"browserTarget": "client:build:development-en"
},
"development-it": {
"browserTarget": "client:build:development-it"
},
"production": {
"browserTarget": "client:build:production"
}
@ -191,7 +198,7 @@
"browserTarget": "client:build",
"includeContext": true,
"outputPath": "src/locales",
"targetFiles": ["messages.de.xlf"]
"targetFiles": ["messages.de.xlf", "messages.it.xlf"]
}
},
"lint": {
@ -214,6 +221,10 @@
"de": {
"baseHref": "/de/",
"translation": "apps/client/src/locales/messages.de.xlf"
},
"it": {
"baseHref": "/it/",
"translation": "apps/client/src/locales/messages.it.xlf"
}
},
"sourceLocale": "en"

View File

@ -12,6 +12,7 @@ import {
UniqueAsset
} from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common';
import { SymbolProfile } from '@prisma/client';
import Big from 'big.js';
import { format } from 'date-fns';
import ms from 'ms';
@ -31,7 +32,11 @@ export class BenchmarkService {
) {}
public calculateChangeInPercentage(baseValue: number, currentValue: number) {
return new Big(currentValue).div(baseValue).minus(1).toNumber();
if (baseValue && currentValue) {
return new Big(currentValue).div(baseValue).minus(1).toNumber();
}
return 0;
}
public async getBenchmarks({ useCache = true } = {}): Promise<
@ -51,25 +56,25 @@ export class BenchmarkService {
} catch {}
}
const benchmarkAssets: UniqueAsset[] =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as UniqueAsset[]) ?? [];
const benchmarkAssetProfiles = await this.getBenchmarkAssetProfiles();
const promises: Promise<number>[] = [];
const [quotes, assetProfiles] = await Promise.all([
this.dataProviderService.getQuotes(benchmarkAssets),
this.symbolProfileService.getSymbolProfiles(benchmarkAssets)
]);
const quotes = await this.dataProviderService.getQuotes(
benchmarkAssetProfiles.map(({ dataSource, symbol }) => {
return { dataSource, symbol };
})
);
for (const benchmarkAsset of benchmarkAssets) {
promises.push(this.marketDataService.getMax(benchmarkAsset));
for (const { dataSource, symbol } of benchmarkAssetProfiles) {
promises.push(this.marketDataService.getMax({ dataSource, symbol }));
}
const allTimeHighs = await Promise.all(promises);
benchmarks = allTimeHighs.map((allTimeHigh, index) => {
const { marketPrice } = quotes[benchmarkAssets[index].symbol] ?? {};
const { marketPrice } =
quotes[benchmarkAssetProfiles[index].symbol] ?? {};
let performancePercentFromAllTimeHigh = 0;
@ -84,12 +89,7 @@ export class BenchmarkService {
marketCondition: this.getMarketCondition(
performancePercentFromAllTimeHigh
),
name: assetProfiles.find(({ dataSource, symbol }) => {
return (
dataSource === benchmarkAssets[index].dataSource &&
symbol === benchmarkAssets[index].symbol
);
})?.name,
name: benchmarkAssetProfiles[index].name,
performances: {
allTimeHigh: {
performancePercent: performancePercentFromAllTimeHigh
@ -107,22 +107,28 @@ export class BenchmarkService {
return benchmarks;
}
public async getBenchmarkAssetProfiles(): Promise<UniqueAsset[]> {
const benchmarkAssets: UniqueAsset[] =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as UniqueAsset[]) ?? [];
const assetProfiles = await this.symbolProfileService.getSymbolProfiles(
benchmarkAssets
);
return assetProfiles.map(({ dataSource, symbol }) => {
return {
dataSource,
symbol
};
public async getBenchmarkAssetProfiles(): Promise<Partial<SymbolProfile>[]> {
const symbolProfileIds: string[] = (
((await this.propertyService.getByKey(PROPERTY_BENCHMARKS)) as {
symbolProfileId: string;
}[]) ?? []
).map(({ symbolProfileId }) => {
return symbolProfileId;
});
const assetProfiles =
await this.symbolProfileService.getSymbolProfilesByIds(symbolProfileIds);
return assetProfiles
.map(({ dataSource, id, name, symbol }) => {
return {
dataSource,
id,
name,
symbol
};
})
.sort((a, b) => a.name.localeCompare(b.name));
}
public async getMarketDataBySymbol({

View File

@ -4,22 +4,36 @@ import * as path from 'path';
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
import { Injectable, NestMiddleware } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NextFunction, Request, Response } from 'express';
@Injectable()
export class FrontendMiddleware implements NestMiddleware {
public indexHtmlDe = fs.readFileSync(
this.getPathOfIndexHtmlFile('de'),
'utf8'
);
public indexHtmlEn = fs.readFileSync(
this.getPathOfIndexHtmlFile(DEFAULT_LANGUAGE_CODE),
'utf8'
);
public indexHtmlDe = '';
public indexHtmlEn = '';
public isProduction: boolean;
public constructor(
private readonly configService: ConfigService,
private readonly configurationService: ConfigurationService
) {}
) {
const NODE_ENV =
this.configService.get<'development' | 'production'>('NODE_ENV') ??
'development';
this.isProduction = NODE_ENV === 'production';
try {
this.indexHtmlDe = fs.readFileSync(
this.getPathOfIndexHtmlFile('de'),
'utf8'
);
this.indexHtmlEn = fs.readFileSync(
this.getPathOfIndexHtmlFile(DEFAULT_LANGUAGE_CODE),
'utf8'
);
} catch {}
}
public use(req: Request, res: Response, next: NextFunction) {
let featureGraphicPath = 'assets/cover.png';
@ -31,7 +45,11 @@ export class FrontendMiddleware implements NestMiddleware {
featureGraphicPath = 'assets/images/blog/500-stars-on-github.jpg';
}
if (req.path.startsWith('/api/') || this.isFileRequest(req.url)) {
if (
req.path.startsWith('/api/') ||
this.isFileRequest(req.url) ||
!this.isProduction
) {
// Skip
next();
} else if (req.path === '/de' || req.path.startsWith('/de/')) {

View File

@ -967,6 +967,10 @@ export class PortfolioCalculator {
feesAtStartDate = fees;
grossPerformanceAtStartDate = grossPerformance;
}
if (i === indexOfEndOrder) {
break;
}
}
timeWeightedGrossPerformancePercentage =

View File

@ -168,13 +168,19 @@ export class PortfolioController {
})
];
const { accounts, holdings, hasErrors } =
await this.portfolioService.getDetails(
impersonationId,
this.request.user.id,
range,
filters
);
const {
accounts,
filteredValueInBaseCurrency,
filteredValueInPercentage,
hasErrors,
holdings,
totalValueInBaseCurrency
} = await this.portfolioService.getDetails(
impersonationId,
this.request.user.id,
range,
filters
);
if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
hasError = true;
@ -234,8 +240,11 @@ export class PortfolioController {
return {
accounts,
filteredValueInBaseCurrency,
filteredValueInPercentage,
hasError,
holdings
holdings,
totalValueInBaseCurrency
};
}

View File

@ -474,12 +474,21 @@ export class PortfolioService {
});
const holdings: PortfolioDetails['holdings'] = {};
const totalInvestment = currentPositions.totalInvestment.plus(
cashDetails.balanceInBaseCurrency
);
const totalValue = currentPositions.currentValue.plus(
const totalInvestmentInBaseCurrency = currentPositions.totalInvestment.plus(
cashDetails.balanceInBaseCurrency
);
let filteredValueInBaseCurrency = currentPositions.currentValue;
if (
aFilters?.length === 0 ||
(aFilters?.length === 1 &&
aFilters[0].type === 'ASSET_CLASS' &&
aFilters[0].id === 'CASH')
) {
filteredValueInBaseCurrency = filteredValueInBaseCurrency.plus(
cashDetails.balanceInBaseCurrency
);
}
const dataGatheringItems = currentPositions.positions.map((position) => {
return {
@ -540,10 +549,12 @@ export class PortfolioService {
holdings[item.symbol] = {
markets,
allocationCurrent: totalValue.eq(0)
allocationCurrent: filteredValueInBaseCurrency.eq(0)
? 0
: value.div(totalValue).toNumber(),
allocationInvestment: item.investment.div(totalInvestment).toNumber(),
: value.div(filteredValueInBaseCurrency).toNumber(),
allocationInvestment: item.investment
.div(totalInvestmentInBaseCurrency)
.toNumber(),
assetClass: symbolProfile.assetClass,
assetSubClass: symbolProfile.assetSubClass,
countries: symbolProfile.countries,
@ -577,8 +588,8 @@ export class PortfolioService {
cashDetails,
emergencyFund,
userCurrency,
investment: totalInvestment,
value: totalValue
investment: totalInvestmentInBaseCurrency,
value: filteredValueInBaseCurrency
});
for (const symbol of Object.keys(cashPositions)) {
@ -594,7 +605,18 @@ export class PortfolioService {
filters: aFilters
});
return { accounts, holdings, hasErrors: currentPositions.hasErrors };
const summary = await this.getSummary(aImpersonationId);
return {
accounts,
holdings,
filteredValueInBaseCurrency: filteredValueInBaseCurrency.toNumber(),
filteredValueInPercentage: summary.netWorth
? filteredValueInBaseCurrency.div(summary.netWorth).toNumber()
: 0,
hasErrors: currentPositions.hasErrors,
totalValueInBaseCurrency: summary.netWorth
};
}
public async getPosition(

View File

@ -1,10 +1,8 @@
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import type { DateRange, ViewMode } from '@ghostfolio/common/types';
import {
IsBoolean,
IsIn,
IsNumber,
IsObject,
IsOptional,
IsString
} from 'class-validator';
@ -14,9 +12,9 @@ export class UpdateUserSettingDto {
@IsString()
baseCurrency?: string;
@IsObject()
@IsString()
@IsOptional()
benchmark?: UniqueAsset;
benchmark?: string;
@IsIn(<DateRange[]>['1d', '1y', '5y', 'max', 'ytd'])
@IsOptional()

View File

@ -102,10 +102,10 @@ export class UserController {
public async updateUserSetting(@Body() data: UpdateUserSettingDto) {
if (
size(data) === 1 &&
data.dateRange &&
(data.benchmark || data.dateRange) &&
this.request.user.role === 'DEMO'
) {
// Allow date range change for demo user
// Allow benchmark or date range change for demo user
} else if (
!hasPermission(
this.request.user.permissions,

View File

@ -41,6 +41,14 @@ export class RedactValuesInResponseInterceptor<T>
return activity;
});
}
if (data.filteredValueInBaseCurrency) {
data.filteredValueInBaseCurrency = null;
}
if (data.totalValueInBaseCurrency) {
data.totalValueInBaseCurrency = null;
}
}
return data;

View File

@ -183,10 +183,10 @@ export class YahooFinanceService implements DataProviderInterface {
for (const historicalItem of historicalResult) {
let marketPrice = historicalItem.close;
if (symbol === 'USDGBp') {
if (symbol === `${this.baseCurrency}GBp`) {
// Convert GPB to GBp (pence)
marketPrice = new Big(marketPrice).mul(100).toNumber();
} else if (symbol === 'USDILA') {
} else if (symbol === `${this.baseCurrency}ILA`) {
// Convert ILS to ILA
marketPrice = new Big(marketPrice).mul(100).toNumber();
}
@ -246,9 +246,12 @@ export class YahooFinanceService implements DataProviderInterface {
marketPrice: quote.regularMarketPrice || 0
};
if (symbol === 'USDGBP' && yahooFinanceSymbols.includes('USDGBp=X')) {
if (
symbol === `${this.baseCurrency}GBP` &&
yahooFinanceSymbols.includes(`${this.baseCurrency}GBp=X`)
) {
// Convert GPB to GBp (pence)
response['USDGBp'] = {
response[`${this.baseCurrency}GBp`] = {
...response[symbol],
currency: 'GBp',
marketPrice: new Big(response[symbol].marketPrice)
@ -256,11 +259,11 @@ export class YahooFinanceService implements DataProviderInterface {
.toNumber()
};
} else if (
symbol === 'USDILS' &&
yahooFinanceSymbols.includes('USDILA=X')
symbol === `${this.baseCurrency}ILS` &&
yahooFinanceSymbols.includes(`${this.baseCurrency}ILA=X`)
) {
// Convert ILS to ILA
response['USDILA'] = {
response[`${this.baseCurrency}ILA`] = {
...response[symbol],
currency: 'ILA',
marketPrice: new Big(response[symbol].marketPrice)
@ -270,9 +273,9 @@ export class YahooFinanceService implements DataProviderInterface {
}
}
if (yahooFinanceSymbols.includes('USDUSX=X')) {
if (yahooFinanceSymbols.includes(`${this.baseCurrency}USX=X`)) {
// Convert USD to USX (cent)
response['USDUSX'] = {
response[`${this.baseCurrency}USX`] = {
currency: 'USX',
dataSource: this.getName(),
marketPrice: new Big(1).mul(100).toNumber(),

View File

@ -99,10 +99,12 @@ export class ExchangeRateDataService {
this.exchangeRates[symbol] = resultExtended[symbol]?.[date]?.marketPrice;
if (!this.exchangeRates[symbol]) {
// Not found, calculate indirectly via USD
// Not found, calculate indirectly via base currency
this.exchangeRates[symbol] =
resultExtended[`${currency1}${'USD'}`]?.[date]?.marketPrice *
resultExtended[`${'USD'}${currency2}`]?.[date]?.marketPrice;
resultExtended[`${currency1}${this.baseCurrency}`]?.[date]
?.marketPrice *
resultExtended[`${this.baseCurrency}${currency2}`]?.[date]
?.marketPrice;
// Calculate the opposite direction
this.exchangeRates[`${currency2}${currency1}`] =
@ -126,9 +128,11 @@ export class ExchangeRateDataService {
if (this.exchangeRates[`${aFromCurrency}${aToCurrency}`]) {
factor = this.exchangeRates[`${aFromCurrency}${aToCurrency}`];
} else {
// Calculate indirectly via USD
const factor1 = this.exchangeRates[`${aFromCurrency}${'USD'}`];
const factor2 = this.exchangeRates[`${'USD'}${aToCurrency}`];
// Calculate indirectly via base currency
const factor1 =
this.exchangeRates[`${aFromCurrency}${this.baseCurrency}`];
const factor2 =
this.exchangeRates[`${this.baseCurrency}${aToCurrency}`];
factor = factor1 * factor2;

View File

@ -64,6 +64,23 @@ export class SymbolProfileService {
.then((symbolProfiles) => this.getSymbols(symbolProfiles));
}
public async getSymbolProfilesByIds(
symbolProfileIds: string[]
): Promise<EnhancedSymbolProfile[]> {
return this.prismaService.symbolProfile
.findMany({
include: { SymbolProfileOverrides: true },
where: {
id: {
in: symbolProfileIds.map((symbolProfileId) => {
return symbolProfileId;
})
}
}
})
.then((symbolProfiles) => this.getSymbols(symbolProfiles));
}
/**
* @deprecated
*/

View File

@ -14,14 +14,13 @@
<mat-label i18n>Compare with...</mat-label>
<mat-select
name="benchmark"
[compareWith]="compareUniqueAssets"
[value]="benchmark"
(selectionChange)="onChangeBenchmark($event.value)"
>
<mat-option
*ngFor="let currentBenchmark of benchmarks"
[value]="currentBenchmark"
>{{ currentBenchmark.symbol }}</mat-option
*ngFor="let symbolProfile of benchmarks"
[value]="symbolProfile.id"
>{{ symbolProfile.name }}</mat-option
>
</mat-select>
</mat-form-field>

View File

@ -39,6 +39,7 @@ import {
Tooltip
} from 'chart.js';
import annotationPlugin from 'chartjs-plugin-annotation';
import { SymbolProfile } from '@prisma/client';
@Component({
selector: 'gf-benchmark-comparator',
@ -48,21 +49,21 @@ import annotationPlugin from 'chartjs-plugin-annotation';
})
export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
@Input() benchmarkDataItems: LineChartItem[] = [];
@Input() benchmark: UniqueAsset;
@Input() benchmarks: UniqueAsset[];
@Input() benchmark: string;
@Input() benchmarks: Partial<SymbolProfile>[];
@Input() daysInMarket: number;
@Input() isLoading: boolean;
@Input() locale: string;
@Input() performanceDataItems: LineChartItem[];
@Input() user: User;
@Output() benchmarkChanged = new EventEmitter<UniqueAsset>();
@Output() benchmarkChanged = new EventEmitter<string>();
@Output() dateRangeChanged = new EventEmitter<DateRange>();
@ViewChild('chartCanvas') chartCanvas;
public chart: Chart<any>;
public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS;
public isLoading = true;
public constructor() {
Chart.register(
@ -85,18 +86,8 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
}
}
public compareUniqueAssets(
uniqueAsset1: UniqueAsset,
uniqueAsset2: UniqueAsset
) {
return (
uniqueAsset1?.dataSource === uniqueAsset2?.dataSource &&
uniqueAsset1?.symbol === uniqueAsset2?.symbol
);
}
public onChangeBenchmark(benchmark: UniqueAsset) {
this.benchmarkChanged.next(benchmark);
public onChangeBenchmark(symbolProfileId: string) {
this.benchmarkChanged.next(symbolProfileId);
}
public onChangeDateRange(dateRange: DateRange) {
@ -108,8 +99,6 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
}
private initialize() {
this.isLoading = true;
const data = {
datasets: [
{
@ -218,8 +207,6 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
});
}
}
this.isLoading = false;
}
private getTooltipPluginConfiguration() {

View File

@ -108,7 +108,6 @@
<div class="row">
<div class="col-xs-12 col-md-4 my-2">
<gf-value
i18n
size="large"
subLabel="(Last 24 hours)"
[value]="statistics?.activeUsers1d ?? '-'"
@ -117,7 +116,6 @@
</div>
<div class="col-xs-12 col-md-4 my-2">
<gf-value
i18n
size="large"
subLabel="(Last 30 days)"
[value]="statistics?.newUsers30d ?? '-'"
@ -126,7 +124,6 @@
</div>
<div class="col-xs-12 col-md-4 my-2">
<gf-value
i18n
size="large"
subLabel="(Last 30 days)"
[value]="statistics?.activeUsers30d ?? '-'"
@ -139,7 +136,6 @@
href="https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg"
>
<gf-value
i18n
size="large"
[value]="statistics?.slackCommunityUsers ?? '-'"
>Users in Slack community</gf-value
@ -152,7 +148,6 @@
href="https://github.com/ghostfolio/ghostfolio/graphs/contributors"
>
<gf-value
i18n
size="large"
[value]="statistics?.gitHubContributors ?? '-'"
>Contributors on GitHub</gf-value
@ -165,7 +160,6 @@
href="https://github.com/ghostfolio/ghostfolio/stargazers"
>
<gf-value
i18n
size="large"
[value]="statistics?.gitHubStargazers ?? '-'"
>Stars on GitHub</gf-value

View File

@ -10,6 +10,22 @@
></gf-activities-filter>
</div>
</div>
<div class="row">
<div class="col">
<mat-card class="mb-3">
<mat-card-header>
<mat-card-title i18n>Proportion of Net Worth</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-progress-bar
mode="determinate"
[title]="(portfolioDetails?.filteredValueInPercentage * 100).toFixed(2) + '%'"
[value]="portfolioDetails?.filteredValueInPercentage * 100"
></mat-progress-bar>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="proportion-charts row">
<div class="col-md-4">
<mat-card class="mb-3">

View File

@ -1,6 +1,7 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module';
@ -22,7 +23,8 @@ import { AllocationsPageComponent } from './allocations-page.component';
GfToggleModule,
GfWorldMapChartModule,
GfValueModule,
MatCardModule
MatCardModule,
MatProgressBarModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

View File

@ -28,4 +28,33 @@
}
}
}
.mat-progress-bar {
border-radius: 0.25rem;
height: 0.5rem;
::ng-deep {
.mat-progress-bar-background {
fill: rgb(var(--palette-background-unselected-chip));
}
.mat-progress-bar-buffer {
background-color: rgb(var(--palette-background-unselected-chip));
}
}
}
}
:host-context(.is-dark-theme) {
.mat-progress-bar {
::ng-deep {
.mat-progress-bar-background {
fill: rgb(var(--palette-background-unselected-chip-dark));
}
.mat-progress-bar-buffer {
background-color: rgb(var(--palette-background-unselected-chip-dark));
}
}
}
}

View File

@ -5,11 +5,11 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
HistoricalDataItem,
Position,
UniqueAsset,
User
} from '@ghostfolio/common/interfaces';
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import { DateRange, GroupBy, ToggleOption } from '@ghostfolio/common/types';
import { SymbolProfile } from '@prisma/client';
import { differenceInDays } from 'date-fns';
import { sortBy } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector';
@ -24,7 +24,7 @@ import { takeUntil } from 'rxjs/operators';
})
export class AnalysisPageComponent implements OnDestroy, OnInit {
public benchmarkDataItems: HistoricalDataItem[] = [];
public benchmarks: UniqueAsset[];
public benchmarks: Partial<SymbolProfile>[];
public bottom3: Position[];
public daysInMarket: number;
public deviceType: string;
@ -32,6 +32,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
public hasImpersonationId: boolean;
public investments: InvestmentItem[];
public investmentsByMonth: InvestmentItem[];
public isLoadingBenchmarkComparator: boolean;
public mode: GroupBy;
public modeOptions: ToggleOption[] = [
{ label: $localize`Monthly`, value: 'month' },
@ -75,9 +76,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
});
}
public onChangeBenchmark(benchmark: UniqueAsset) {
public onChangeBenchmark(symbolProfileId: string) {
this.dataService
.putUserSetting({ benchmark })
.putUserSetting({ benchmark: symbolProfileId })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.userService.remove();
@ -122,6 +123,8 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
private update() {
if (this.user.settings.isExperimentalFeatures) {
this.isLoadingBenchmarkComparator = true;
this.dataService
.fetchChart({ range: this.user?.settings?.dateRange, version: 2 })
.pipe(takeUntil(this.unsubscribeSubject))
@ -179,9 +182,15 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
private updateBenchmarkDataItems() {
if (this.user.settings.benchmark) {
const { dataSource, symbol } =
this.benchmarks.find(({ id }) => {
return id === this.user.settings.benchmark;
}) ?? {};
this.dataService
.fetchBenchmarkBySymbol({
...this.user.settings.benchmark,
dataSource,
symbol,
startDate: this.firstOrderDate
})
.pipe(takeUntil(this.unsubscribeSubject))
@ -193,8 +202,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
};
});
this.isLoadingBenchmarkComparator = false;
this.changeDetectorRef.markForCheck();
});
} else {
this.isLoadingBenchmarkComparator = false;
}
}
}

View File

@ -8,6 +8,7 @@
[benchmarkDataItems]="benchmarkDataItems"
[benchmarks]="benchmarks"
[daysInMarket]="daysInMarket"
[isLoading]="isLoadingBenchmarkComparator"
[locale]="user?.settings?.locale"
[performanceDataItems]="performanceDataItems"
[user]="user"

View File

@ -10,6 +10,15 @@
get started. Due to the time it saves, this will be the best option
for most people. The revenue is used for covering the hosting costs.
</p>
<p *ngIf="user?.subscription?.type === 'Basic'">
If you plan to open an account at <i>DEGIRO</i>, <i>frankly</i>,
<i>Interactive Brokers</i>, <i>Swissquote</i>, or <i>VIAC</i>, please
<a href="mailto:hi@ghostfol.io?Subject=Referral link for..."
>contact us</a
>
to use our referral link and get a Ghostfolio Premium membership for
one year.
</p>
<p>
If you prefer to run Ghostfolio on your own infrastructure, please
find the source code and further instructions on

View File

@ -15,7 +15,7 @@
</trans-unit>
<trans-unit id="ccb2a809018b32a96c813ae69126ce05976109ce" datatype="html">
<source>The risk of loss in trading can be substantial. It is not advisable to invest money you may need in the short term.</source>
<target state="translated">Das Ausfallrisiko beim Börsenhandel kann erheblich sein. Es ist nicht ratsam, Geld zu investieren, welches sie kurzfristig benötigen.</target>
<target state="translated">Das Ausfallrisiko beim Börsenhandel kann erheblich sein. Es ist nicht ratsam, Geld zu investieren, welches Sie kurzfristig benötigen.</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/app.component.html</context>
<context context-type="linenumber">55,56</context>
@ -1031,7 +1031,7 @@
</trans-unit>
<trans-unit id="67251f04518ae452230c68a748b3fa2838b4db74" datatype="html">
<source>Net Worth</source>
<target state="translated">Reinvermögen</target>
<target state="translated">Gesamtvermögen</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context>
<context context-type="linenumber">179</context>
@ -1262,7 +1262,7 @@
<target state="translated">Bitte gebe deinen Gutscheincode ein:</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">248</context>
<context context-type="linenumber">225</context>
</context-group>
</trans-unit>
<trans-unit id="4420880039966769543" datatype="html">
@ -1270,7 +1270,7 @@
<target state="translated">Gutscheincode konnte nicht eingelöst werden</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">258</context>
<context context-type="linenumber">235</context>
</context-group>
</trans-unit>
<trans-unit id="4819099731531004979" datatype="html">
@ -1278,7 +1278,7 @@
<target state="translated">Gutscheincode wurde eingelöst</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">270</context>
<context context-type="linenumber">247</context>
</context-group>
</trans-unit>
<trans-unit id="7967484035994732534" datatype="html">
@ -1286,7 +1286,7 @@
<target state="translated">Neu laden</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">271</context>
<context context-type="linenumber">248</context>
</context-group>
</trans-unit>
<trans-unit id="7963559562180316948" datatype="html">
@ -1294,7 +1294,7 @@
<target state="translated">Möchtest du diese Anmeldemethode wirklich löschen?</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">317</context>
<context context-type="linenumber">294</context>
</context-group>
</trans-unit>
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
@ -1323,7 +1323,7 @@
</trans-unit>
<trans-unit id="f147d0f7f965cccee2e77294cba8e1b88021fa08" datatype="html">
<source>Upgrade</source>
<target state="new">Upgrade</target>
<target state="translated">Upgrade</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
<context context-type="linenumber">37</context>
@ -1379,7 +1379,7 @@
</trans-unit>
<trans-unit id="6b939b00e8481ed8aa8a24d8add7a209d7116759" datatype="html">
<source>Locale</source>
<target state="new">Locale</target>
<target state="translated">Lokalität</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
<context context-type="linenumber">135</context>
@ -1618,7 +1618,7 @@
<target state="translated">Nach Konto</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">17</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="b79f5520c0cb9a00bd589e8a4c86ffcf5ae439d7" datatype="html">
@ -1626,7 +1626,7 @@
<target state="translated">Nach Währung</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">42</context>
<context context-type="linenumber">58</context>
</context-group>
</trans-unit>
<trans-unit id="8288ff761f2d259625d2e5a3d96db727926d9cd4" datatype="html">
@ -1634,7 +1634,7 @@
<target state="translated">Nach Asset Class</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">70</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="b64539bb7815eb3275b55ad723d3897fc6ba8d23" datatype="html">
@ -1642,7 +1642,7 @@
<target state="translated">Nach Position</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">98</context>
<context context-type="linenumber">114</context>
</context-group>
</trans-unit>
<trans-unit id="9f86714c9a6b74e13c96ab02102ce40c34fe13b9" datatype="html">
@ -1650,7 +1650,7 @@
<target state="translated">Nach Sektor</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">126</context>
<context context-type="linenumber">142</context>
</context-group>
</trans-unit>
<trans-unit id="7017e0e26b53ef322c3e3bbf95f06a85487a12b2" datatype="html">
@ -1658,7 +1658,7 @@
<target state="translated">Nach Kontinent</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">155</context>
<context context-type="linenumber">171</context>
</context-group>
</trans-unit>
<trans-unit id="f27e9dd8de80176286e02312e694cb8d1e485a5d" datatype="html">
@ -1666,7 +1666,7 @@
<target state="translated">Nach Land</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">183</context>
<context context-type="linenumber">199</context>
</context-group>
</trans-unit>
<trans-unit id="85780db87ac6c9f202615ac63754551c061e7236" datatype="html">
@ -1674,7 +1674,7 @@
<target state="translated">Regionen</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">214</context>
<context context-type="linenumber">230</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -1706,7 +1706,7 @@
<target state="translated">Zeitstrahl der Investitionen</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">102</context>
<context context-type="linenumber">105</context>
</context-group>
</trans-unit>
<trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html">
@ -1714,7 +1714,7 @@
<target state="translated">Gewinner</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">23</context>
<context context-type="linenumber">26</context>
</context-group>
</trans-unit>
<trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html">
@ -1722,7 +1722,7 @@
<target state="translated">Verlierer</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">59</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="5857197365507636437" datatype="html">
@ -2036,6 +2036,10 @@
<trans-unit id="9201103587777813545" datatype="html">
<source>Portfolio</source>
<target state="translated">Portfolio</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context>
<context context-type="linenumber">111</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page-routing.module.ts</context>
<context context-type="linenumber">12</context>
@ -2059,7 +2063,7 @@
</trans-unit>
<trans-unit id="a3d148b40a389fda0665eb583c9e434ec5ee1ced" datatype="html">
<source> Ghostfolio empowers you to keep track of your wealth. </source>
<target state="new"/>
<target state="translated">Ghostfolio verschafft Ihnen den Überblick über Ihr Vermögen.</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
<context context-type="linenumber">132,134</context>
@ -2270,7 +2274,7 @@
<target state="translated">Beta</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context>
<context context-type="linenumber">4</context>
<context context-type="linenumber">5</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
@ -2414,7 +2418,7 @@
<target state="translated">Entwickelte Länder</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">240</context>
<context context-type="linenumber">256</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2426,7 +2430,7 @@
<target state="translated">Schwellenländer</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">249</context>
<context context-type="linenumber">265</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2438,7 +2442,7 @@
<target state="translated">Andere Länder</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">258</context>
<context context-type="linenumber">274</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2453,50 +2457,6 @@
<context context-type="linenumber">136</context>
</context-group>
</trans-unit>
<trans-unit id="1de491c923555d6422bc6f1146357eb2b47853da" datatype="html">
<source>Active Users</source>
<target state="new">Active Users</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">115</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">133</context>
</context-group>
</trans-unit>
<trans-unit id="8c4cfd77b7b3d7917de13bec98a8a74890f95618" datatype="html">
<source>New Users</source>
<target state="new">New Users</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">124</context>
</context-group>
</trans-unit>
<trans-unit id="c0eb011366e597e23542be386e8bc0d53470b520" datatype="html">
<source>Users in Slack community</source>
<target state="new">Users in Slack community</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">145</context>
</context-group>
</trans-unit>
<trans-unit id="be99161cc904867871ab172df77b736d3b27dfc5" datatype="html">
<source>Contributors on GitHub</source>
<target state="new">Contributors on GitHub</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">158</context>
</context-group>
</trans-unit>
<trans-unit id="8d3932a9eba50bc101c2b8c329e7b4ea033cde97" datatype="html">
<source>Stars on GitHub</source>
<target state="new">Stars on GitHub</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">171</context>
</context-group>
</trans-unit>
<trans-unit id="e34e2478d2d30c9d01758d01b7212411171b9bd5" datatype="html">
<source>Projected Total Amount</source>
<target state="translated">Projizierter Gesamtbetrag</target>
@ -2507,7 +2467,7 @@
</trans-unit>
<trans-unit id="2937311350146031865" datatype="html">
<source>Initial</source>
<target state="new">Beginn</target>
<target state="translated">Beginn</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
<context context-type="linenumber">57</context>
@ -2526,7 +2486,7 @@
<target state="translated">Monatlich</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
<context context-type="linenumber">30</context>
<context context-type="linenumber">38</context>
</context-group>
</trans-unit>
<trans-unit id="1975246224413290232" datatype="html">
@ -2534,19 +2494,15 @@
<target state="translated">Aufsummiert</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
<context context-type="linenumber">31</context>
<context context-type="linenumber">39</context>
</context-group>
</trans-unit>
<trans-unit id="5213771062241898526" datatype="html">
<source>Deposit</source>
<target state="translated">Einlage</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context>
<context context-type="linenumber">131</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/investment-chart/investment-chart.component.ts</context>
<context context-type="linenumber">130</context>
<context context-type="linenumber">132</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
@ -2563,7 +2519,7 @@
</trans-unit>
<trans-unit id="1054498214311181686" datatype="html">
<source>Savings</source>
<target state="new">Ersparnisse</target>
<target state="translated">Ersparnisse</target>
<context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
<context context-type="linenumber">296</context>
@ -2606,7 +2562,7 @@
<target state="translated">Filtern nach...</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
<context context-type="linenumber">129</context>
<context context-type="linenumber">128</context>
</context-group>
</trans-unit>
<trans-unit id="2078421919111943467" datatype="html">
@ -2662,7 +2618,7 @@
<target state="translated">Benchmarks</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context>
<context context-type="linenumber">3</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="44fcf77e86dc038202ebad6b46d1d833d60d781b" datatype="html">
@ -2670,7 +2626,7 @@
<target state="translated">Vergleichen mit...</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context>
<context context-type="linenumber">12</context>
<context context-type="linenumber">14</context>
</context-group>
</trans-unit>
<trans-unit id="1931353503905413384" datatype="html">
@ -2678,7 +2634,15 @@
<target state="translated">Benchmark</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context>
<context context-type="linenumber">149</context>
<context context-type="linenumber">120</context>
</context-group>
</trans-unit>
<trans-unit id="4210837540bca56dca96fcc451518659d06ad02a" datatype="html">
<source>Proportion of Net Worth</source>
<target state="translated">Anteil am Gesamtvermögen</target>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
</body>

File diff suppressed because it is too large Load Diff

View File

@ -1137,35 +1137,35 @@
<source>Please enter your coupon code:</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">248</context>
<context context-type="linenumber">225</context>
</context-group>
</trans-unit>
<trans-unit id="4420880039966769543" datatype="html">
<source>Could not redeem coupon code</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">258</context>
<context context-type="linenumber">235</context>
</context-group>
</trans-unit>
<trans-unit id="4819099731531004979" datatype="html">
<source>Coupon code has been redeemed</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">270</context>
<context context-type="linenumber">247</context>
</context-group>
</trans-unit>
<trans-unit id="7967484035994732534" datatype="html">
<source>Reload</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">271</context>
<context context-type="linenumber">248</context>
</context-group>
</trans-unit>
<trans-unit id="7963559562180316948" datatype="html">
<source>Do you really want to remove this sign in method?</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.component.ts</context>
<context context-type="linenumber">317</context>
<context context-type="linenumber">294</context>
</context-group>
</trans-unit>
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
@ -1453,56 +1453,56 @@
<source>By Account</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">17</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="b79f5520c0cb9a00bd589e8a4c86ffcf5ae439d7" datatype="html">
<source>By Currency</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">42</context>
<context context-type="linenumber">58</context>
</context-group>
</trans-unit>
<trans-unit id="8288ff761f2d259625d2e5a3d96db727926d9cd4" datatype="html">
<source>By Asset Class</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">70</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="b64539bb7815eb3275b55ad723d3897fc6ba8d23" datatype="html">
<source>By Holding</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">98</context>
<context context-type="linenumber">114</context>
</context-group>
</trans-unit>
<trans-unit id="9f86714c9a6b74e13c96ab02102ce40c34fe13b9" datatype="html">
<source>By Sector</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">126</context>
<context context-type="linenumber">142</context>
</context-group>
</trans-unit>
<trans-unit id="7017e0e26b53ef322c3e3bbf95f06a85487a12b2" datatype="html">
<source>By Continent</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">155</context>
<context context-type="linenumber">171</context>
</context-group>
</trans-unit>
<trans-unit id="f27e9dd8de80176286e02312e694cb8d1e485a5d" datatype="html">
<source>By Country</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">183</context>
<context context-type="linenumber">199</context>
</context-group>
</trans-unit>
<trans-unit id="85780db87ac6c9f202615ac63754551c061e7236" datatype="html">
<source>Regions</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">214</context>
<context context-type="linenumber">230</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -1531,21 +1531,21 @@
<source>Investment Timeline</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">102</context>
<context context-type="linenumber">105</context>
</context-group>
</trans-unit>
<trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html">
<source>Top</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">23</context>
<context context-type="linenumber">26</context>
</context-group>
</trans-unit>
<trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html">
<source>Bottom</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">59</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="5857197365507636437" datatype="html">
@ -1824,6 +1824,10 @@
</trans-unit>
<trans-unit id="9201103587777813545" datatype="html">
<source>Portfolio</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context>
<context context-type="linenumber">111</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page-routing.module.ts</context>
<context context-type="linenumber">12</context>
@ -2029,7 +2033,7 @@
<source>Beta</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context>
<context context-type="linenumber">4</context>
<context context-type="linenumber">5</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/account/account-page.html</context>
@ -2100,7 +2104,7 @@
<source>Developed Markets</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">240</context>
<context context-type="linenumber">256</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2140,7 +2144,7 @@
<source>Other Markets</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">258</context>
<context context-type="linenumber">274</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2151,7 +2155,7 @@
<source>Emerging Markets</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">249</context>
<context context-type="linenumber">265</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2193,45 +2197,6 @@
<context context-type="linenumber">136</context>
</context-group>
</trans-unit>
<trans-unit id="1de491c923555d6422bc6f1146357eb2b47853da" datatype="html">
<source>Active Users</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">115</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">133</context>
</context-group>
</trans-unit>
<trans-unit id="8c4cfd77b7b3d7917de13bec98a8a74890f95618" datatype="html">
<source>New Users</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">124</context>
</context-group>
</trans-unit>
<trans-unit id="8d3932a9eba50bc101c2b8c329e7b4ea033cde97" datatype="html">
<source>Stars on GitHub</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">171</context>
</context-group>
</trans-unit>
<trans-unit id="be99161cc904867871ab172df77b736d3b27dfc5" datatype="html">
<source>Contributors on GitHub</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">158</context>
</context-group>
</trans-unit>
<trans-unit id="c0eb011366e597e23542be386e8bc0d53470b520" datatype="html">
<source>Users in Slack community</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/about-page.html</context>
<context context-type="linenumber">145</context>
</context-group>
</trans-unit>
<trans-unit id="e34e2478d2d30c9d01758d01b7212411171b9bd5" datatype="html">
<source>Projected Total Amount</source>
<context-group purpose="location">
@ -2250,7 +2215,7 @@
<source>Accumulating</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
<context context-type="linenumber">31</context>
<context context-type="linenumber">39</context>
</context-group>
</trans-unit>
<trans-unit id="2937311350146031865" datatype="html">
@ -2269,13 +2234,9 @@
</trans-unit>
<trans-unit id="5213771062241898526" datatype="html">
<source>Deposit</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context>
<context context-type="linenumber">131</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/investment-chart/investment-chart.component.ts</context>
<context context-type="linenumber">130</context>
<context context-type="linenumber">132</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
@ -2293,7 +2254,7 @@
<source>Monthly</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
<context context-type="linenumber">30</context>
<context context-type="linenumber">38</context>
</context-group>
</trans-unit>
<trans-unit id="8511b16abcf065252b350d64e337ba2447db3ffb" datatype="html">
@ -2339,7 +2300,7 @@
<source>Filter by...</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
<context context-type="linenumber">129</context>
<context context-type="linenumber">128</context>
</context-group>
</trans-unit>
<trans-unit id="303469635941752458" datatype="html">
@ -2378,21 +2339,28 @@
<source>Benchmark</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context>
<context context-type="linenumber">149</context>
<context context-type="linenumber">120</context>
</context-group>
</trans-unit>
<trans-unit id="1b25c6e22f822e07a3e4d5aae4edc5b41fe083c2" datatype="html">
<source>Benchmarks</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context>
<context context-type="linenumber">3</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="44fcf77e86dc038202ebad6b46d1d833d60d781b" datatype="html">
<source>Compare with...</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context>
<context context-type="linenumber">12</context>
<context context-type="linenumber">14</context>
</context-group>
</trans-unit>
<trans-unit id="4210837540bca56dca96fcc451518659d06ad02a" datatype="html">
<source>Proportion of Net Worth</source>
<context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
</body>

View File

@ -6,6 +6,7 @@ services:
- ../.env
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=prefer
NODE_ENV: production
REDIS_HOST: 'redis'
REDIS_PASSWORD: ${REDIS_PASSWORD}
ports:

View File

@ -6,6 +6,7 @@ services:
- ../.env
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=prefer
NODE_ENV: production
REDIS_HOST: 'redis'
REDIS_PASSWORD: ${REDIS_PASSWORD}
ports:

View File

@ -1,12 +1,11 @@
import { Tag } from '@prisma/client';
import { SymbolProfile, Tag } from '@prisma/client';
import { Statistics } from './statistics.interface';
import { Subscription } from './subscription.interface';
import { UniqueAsset } from './unique-asset.interface';
export interface InfoItem {
baseCurrency: string;
benchmarks: UniqueAsset[];
benchmarks: Partial<SymbolProfile>[];
currencies: string[];
demoAuthToken: string;
fearAndGreedDataSource?: string;

View File

@ -10,5 +10,8 @@ export interface PortfolioDetails {
original: number;
};
};
filteredValueInBaseCurrency?: number;
filteredValueInPercentage: number;
holdings: { [symbol: string]: PortfolioPosition };
totalValueInBaseCurrency?: number;
}

View File

@ -1,10 +1,8 @@
import { DateRange, ViewMode } from '@ghostfolio/common/types';
import { UniqueAsset } from './unique-asset.interface';
export interface UserSettings {
baseCurrency?: string;
benchmark?: UniqueAsset;
benchmark?: string;
dateRange?: DateRange;
emergencyFund?: number;
isExperimentalFeatures?: boolean;

View File

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "1.191.0",
"version": "1.194.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"scripts": {
@ -126,7 +126,7 @@
"svgmap": "2.6.0",
"twitter-api-v2": "1.10.3",
"uuid": "8.3.2",
"yahoo-finance2": "2.3.3",
"yahoo-finance2": "2.3.6",
"zone.js": "0.11.8"
},
"devDependencies": {

View File

@ -20374,10 +20374,10 @@ y18n@^5.0.5:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yahoo-finance2@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/yahoo-finance2/-/yahoo-finance2-2.3.3.tgz#a3b537f3bec31b99f075aa44125c9520a42cdfe3"
integrity sha512-qvgo5tFkrYRh1anbTargIY7Fa7FRiYZfb+iTnY1SUfBZ9HP8OvreTYDfLz/1w23PGgFi4w9uysGEwVrUVZ5Hlw==
yahoo-finance2@2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/yahoo-finance2/-/yahoo-finance2-2.3.6.tgz#4ba27d33385f5520752e96da3cf4df93ad26ce7a"
integrity sha512-qE4Nu4DY4XSAL+RzYXSaWFQBIYyBbMDaND1VoMGnmekWBGS8+/3GxfajXrzEfxxwFvT4tM/1i4G64OVxKE0EMA==
dependencies:
ajv "8.10.0"
ajv-formats "2.1.1"