Compare commits

..

16 Commits

Author SHA1 Message Date
c0ace51ee9 Release 1.195.0 (#1277) 2022-09-20 20:23:41 +02:00
b1b5689242 Feature/improve performance of chart calculation (#1271)
* Improve 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>

* Improve chart tooltip of benchmark comparator

* Update changelog

Co-authored-by: gizmodus <11334553+gizmodus@users.noreply.github.com>
2022-09-20 20:22:01 +02:00
b68cdaf8ea Add issue template (#1275) 2022-09-19 21:22:54 +02:00
b387a80a0d Add bullet (#1270) 2022-09-17 21:29:37 +02:00
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
26 changed files with 2950 additions and 274 deletions

37
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,37 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: ''
---
The Issue tracker is **ONLY** used for reporting bugs. New features should be discussed on our [Slack channel](https://ghostfolio.slack.com) or in [Discussions](https://github.com/ghostfolio/ghostfolio/discussions).
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
Steps to reproduce the behavior:
1.
2.
3.
4.
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Logs**
<!-- If applicable, add logs to help explain your problem. -->
**Environment (please complete the following information):**
- Ghostfolio Version [e.g. 1.194.0]
- Browser [e.g. chrome]
- OS
**Additional context**
<!-- Add any other context about the problem here. -->

View File

@ -5,6 +5,47 @@ 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).
## 1.195.0 - 20.09.2022
### Changed
- Improved the algorithm of the performance chart calculation
### Fixed
- Improved the chart tooltip of the benchmark comparator
## 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 ## 1.192.0 - 11.09.2022
### Changed ### Changed

View File

@ -153,6 +153,7 @@ Please follow the instructions of the Ghostfolio [Unraid Community App](https://
### Setup ### Setup
1. Run `yarn install` 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 `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. 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)) 1. Start the server and the client (see [_Development_](#Development))

View File

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

View File

@ -4,7 +4,10 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data
import { MarketDataService } from '@ghostfolio/api/services/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service';
import { PROPERTY_BENCHMARKS } from '@ghostfolio/common/config'; import {
MAX_CHART_ITEMS,
PROPERTY_BENCHMARKS
} from '@ghostfolio/common/config';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { import {
BenchmarkMarketDataDetails, BenchmarkMarketDataDetails,
@ -16,7 +19,6 @@ import { SymbolProfile } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
import { format } from 'date-fns'; import { format } from 'date-fns';
import ms from 'ms'; import ms from 'ms';
import { v4 as uuidv4 } from 'uuid';
@Injectable() @Injectable()
export class BenchmarkService { export class BenchmarkService {
@ -119,14 +121,16 @@ export class BenchmarkService {
const assetProfiles = const assetProfiles =
await this.symbolProfileService.getSymbolProfilesByIds(symbolProfileIds); await this.symbolProfileService.getSymbolProfilesByIds(symbolProfileIds);
return assetProfiles.map(({ dataSource, id, name, symbol }) => { return assetProfiles
return { .map(({ dataSource, id, name, symbol }) => {
dataSource, return {
id, dataSource,
name, id,
symbol name,
}; symbol
}); };
})
.sort((a, b) => a.name.localeCompare(b.name));
} }
public async getMarketDataBySymbol({ public async getMarketDataBySymbol({
@ -155,27 +159,38 @@ export class BenchmarkService {
}) })
]); ]);
marketDataItems.push({ const step = Math.round(
...currentSymbolItem, marketDataItems.length / Math.min(marketDataItems.length, MAX_CHART_ITEMS)
createdAt: new Date(), );
date: new Date(),
id: uuidv4()
});
const marketPriceAtStartDate = marketDataItems?.[0]?.marketPrice ?? 0; const marketPriceAtStartDate = marketDataItems?.[0]?.marketPrice ?? 0;
return { return {
marketData: marketDataItems.map((marketDataItem) => { marketData: [
return { ...marketDataItems
date: format(marketDataItem.date, DATE_FORMAT), .filter((marketDataItem, index) => {
return index % step === 0;
})
.map((marketDataItem) => {
return {
date: format(marketDataItem.date, DATE_FORMAT),
value:
marketPriceAtStartDate === 0
? 0
: this.calculateChangeInPercentage(
marketPriceAtStartDate,
marketDataItem.marketPrice
) * 100
};
}),
{
date: format(new Date(), DATE_FORMAT),
value: value:
marketPriceAtStartDate === 0 this.calculateChangeInPercentage(
? 0 marketPriceAtStartDate,
: this.calculateChangeInPercentage( currentSymbolItem.marketPrice
marketPriceAtStartDate, ) * 100
marketDataItem.marketPrice }
) * 100 ]
};
})
}; };
} }

View File

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

View File

@ -16,12 +16,11 @@ import {
isBefore, isBefore,
isSameMonth, isSameMonth,
isSameYear, isSameYear,
isWithinInterval,
max, max,
min, min,
set set
} from 'date-fns'; } from 'date-fns';
import { first, flatten, isNumber, sortBy } from 'lodash'; import { first, flatten, isNumber, last, sortBy } from 'lodash';
import { CurrentRateService } from './current-rate.service'; import { CurrentRateService } from './current-rate.service';
import { CurrentPositions } from './interfaces/current-positions.interface'; import { CurrentPositions } from './interfaces/current-positions.interface';
@ -168,6 +167,131 @@ export class PortfolioCalculator {
this.transactionPoints = transactionPoints; this.transactionPoints = transactionPoints;
} }
public async getChartData(start: Date, end = new Date(Date.now()), step = 1) {
const symbols: { [symbol: string]: boolean } = {};
const transactionPointsBeforeEndDate =
this.transactionPoints?.filter((transactionPoint) => {
return isBefore(parseDate(transactionPoint.date), end);
}) ?? [];
const firstIndex = transactionPointsBeforeEndDate.length;
const dates: Date[] = [];
const dataGatheringItems: IDataGatheringItem[] = [];
const currencies: { [symbol: string]: string } = {};
let day = start;
while (isBefore(day, end)) {
dates.push(resetHours(day));
day = addDays(day, step);
}
dates.push(resetHours(end));
for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) {
dataGatheringItems.push({
dataSource: item.dataSource,
symbol: item.symbol
});
currencies[item.symbol] = item.currency;
symbols[item.symbol] = true;
}
const marketSymbols = await this.currentRateService.getValues({
currencies,
dataGatheringItems,
dateQuery: {
in: dates
},
userCurrency: this.currency
});
const marketSymbolMap: {
[date: string]: { [symbol: string]: Big };
} = {};
for (const marketSymbol of marketSymbols) {
const dateString = format(marketSymbol.date, DATE_FORMAT);
if (!marketSymbolMap[dateString]) {
marketSymbolMap[dateString] = {};
}
if (marketSymbol.marketPriceInBaseCurrency) {
marketSymbolMap[dateString][marketSymbol.symbol] = new Big(
marketSymbol.marketPriceInBaseCurrency
);
}
}
const netPerformanceValuesBySymbol: {
[symbol: string]: { [date: string]: Big };
} = {};
const investmentValuesBySymbol: {
[symbol: string]: { [date: string]: Big };
} = {};
const totalNetPerformanceValues: { [date: string]: Big } = {};
const totalInvestmentValues: { [date: string]: Big } = {};
for (const symbol of Object.keys(symbols)) {
const { netPerformanceValues, investmentValues } = this.getSymbolMetrics({
end,
marketSymbolMap,
start,
step,
symbol,
isChartMode: true
});
netPerformanceValuesBySymbol[symbol] = netPerformanceValues;
investmentValuesBySymbol[symbol] = investmentValues;
}
for (const currentDate of dates) {
const dateString = format(currentDate, DATE_FORMAT);
for (const symbol of Object.keys(netPerformanceValuesBySymbol)) {
totalNetPerformanceValues[dateString] =
totalNetPerformanceValues[dateString] ?? new Big(0);
if (netPerformanceValuesBySymbol[symbol]?.[dateString]) {
totalNetPerformanceValues[dateString] = totalNetPerformanceValues[
dateString
].add(netPerformanceValuesBySymbol[symbol][dateString]);
}
totalInvestmentValues[dateString] =
totalInvestmentValues[dateString] ?? new Big(0);
if (investmentValuesBySymbol[symbol]?.[dateString]) {
totalInvestmentValues[dateString] = totalInvestmentValues[
dateString
].add(investmentValuesBySymbol[symbol][dateString]);
}
}
}
const isInPercentage = true;
return Object.keys(totalNetPerformanceValues).map((date) => {
return isInPercentage
? {
date,
value: totalInvestmentValues[date].eq(0)
? 0
: totalNetPerformanceValues[date]
.div(totalInvestmentValues[date])
.mul(100)
.toNumber()
}
: {
date,
value: totalNetPerformanceValues[date].toNumber()
};
});
}
public async getCurrentPositions( public async getCurrentPositions(
start: Date, start: Date,
end = new Date(Date.now()) end = new Date(Date.now())
@ -710,15 +834,19 @@ export class PortfolioCalculator {
private getSymbolMetrics({ private getSymbolMetrics({
end, end,
isChartMode = false,
marketSymbolMap, marketSymbolMap,
start, start,
step = 1,
symbol symbol
}: { }: {
end: Date; end: Date;
isChartMode?: boolean;
marketSymbolMap: { marketSymbolMap: {
[date: string]: { [symbol: string]: Big }; [date: string]: { [symbol: string]: Big };
}; };
start: Date; start: Date;
step?: number;
symbol: string; symbol: string;
}) { }) {
let orders: PortfolioOrderItem[] = this.orders.filter((order) => { let orders: PortfolioOrderItem[] = this.orders.filter((order) => {
@ -767,10 +895,12 @@ export class PortfolioCalculator {
let grossPerformanceFromSells = new Big(0); let grossPerformanceFromSells = new Big(0);
let initialValue: Big; let initialValue: Big;
let investmentAtStartDate: Big; let investmentAtStartDate: Big;
const investmentValues: { [date: string]: Big } = {};
let lastAveragePrice = new Big(0); let lastAveragePrice = new Big(0);
let lastTransactionInvestment = new Big(0); let lastTransactionInvestment = new Big(0);
let lastValueOfInvestmentBeforeTransaction = new Big(0); let lastValueOfInvestmentBeforeTransaction = new Big(0);
let maxTotalInvestment = new Big(0); let maxTotalInvestment = new Big(0);
const netPerformanceValues: { [date: string]: Big } = {};
let timeWeightedGrossPerformancePercentage = new Big(1); let timeWeightedGrossPerformancePercentage = new Big(1);
let timeWeightedNetPerformancePercentage = new Big(1); let timeWeightedNetPerformancePercentage = new Big(1);
let totalInvestment = new Big(0); let totalInvestment = new Big(0);
@ -805,6 +935,41 @@ export class PortfolioCalculator {
unitPrice: unitPriceAtEndDate unitPrice: unitPriceAtEndDate
}); });
let day = start;
let lastUnitPrice: Big;
if (isChartMode) {
const datesWithOrders = {};
for (const order of orders) {
datesWithOrders[order.date] = true;
}
while (isBefore(day, end)) {
const hasDate = datesWithOrders[format(day, DATE_FORMAT)];
if (!hasDate) {
orders.push({
symbol,
currency: null,
date: format(day, DATE_FORMAT),
dataSource: null,
fee: new Big(0),
name: '',
quantity: new Big(0),
type: TypeOfOrder.BUY,
unitPrice:
marketSymbolMap[format(day, DATE_FORMAT)]?.[symbol] ??
lastUnitPrice
});
}
lastUnitPrice = last(orders).unitPrice;
day = addDays(day, step);
}
}
// Sort orders so that the start and end placeholder order are at the right // Sort orders so that the start and end placeholder order are at the right
// position // position
orders = sortBy(orders, (order) => { orders = sortBy(orders, (order) => {
@ -967,6 +1132,18 @@ export class PortfolioCalculator {
feesAtStartDate = fees; feesAtStartDate = fees;
grossPerformanceAtStartDate = grossPerformance; grossPerformanceAtStartDate = grossPerformance;
} }
if (isChartMode && i > indexOfStartOrder) {
netPerformanceValues[order.date] = grossPerformance
.minus(grossPerformanceAtStartDate)
.minus(fees.minus(feesAtStartDate));
investmentValues[order.date] = totalInvestment;
}
if (i === indexOfEndOrder) {
break;
}
} }
timeWeightedGrossPerformancePercentage = timeWeightedGrossPerformancePercentage =
@ -1052,7 +1229,9 @@ export class PortfolioCalculator {
return { return {
initialValue, initialValue,
grossPerformancePercentage, grossPerformancePercentage,
investmentValues,
netPerformancePercentage, netPerformancePercentage,
netPerformanceValues,
hasErrors: totalUnits.gt(0) && (!initialValue || !unitPriceAtEndDate), hasErrors: totalUnits.gt(0) && (!initialValue || !unitPriceAtEndDate),
netPerformance: totalNetPerformance, netPerformance: totalNetPerformance,
grossPerformance: totalGrossPerformance grossPerformance: totalGrossPerformance

View File

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

View File

@ -21,6 +21,7 @@ import { ImpersonationService } from '@ghostfolio/api/services/impersonation.ser
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service';
import { import {
ASSET_SUB_CLASS_EMERGENCY_FUND, ASSET_SUB_CLASS_EMERGENCY_FUND,
MAX_CHART_ITEMS,
UNKNOWN_KEY UNKNOWN_KEY
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
@ -57,7 +58,6 @@ 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,
@ -72,7 +72,7 @@ import {
subDays, subDays,
subYears subYears
} from 'date-fns'; } from 'date-fns';
import { isEmpty, last, sortBy, uniq, uniqBy } from 'lodash'; import { isEmpty, sortBy, uniq, uniqBy } from 'lodash';
import { import {
HistoricalDataContainer, HistoricalDataContainer,
@ -86,7 +86,6 @@ 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(
@ -388,43 +387,19 @@ export class PortfolioService {
const daysInMarket = differenceInDays(new Date(), startDate); const daysInMarket = differenceInDays(new Date(), startDate);
const step = Math.round( const step = Math.round(
daysInMarket / Math.min(daysInMarket, PortfolioService.MAX_CHART_ITEMS) daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS)
); );
const items: HistoricalDataItem[] = []; const items = await portfolioCalculator.getChartData(
startDate,
let currentEndDate = startDate; endDate,
step
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 { return {
items,
isAllTimeHigh: false, isAllTimeHigh: false,
isAllTimeLow: false, isAllTimeLow: false
items: items
}; };
} }
@ -474,12 +449,21 @@ export class PortfolioService {
}); });
const holdings: PortfolioDetails['holdings'] = {}; const holdings: PortfolioDetails['holdings'] = {};
const totalInvestment = currentPositions.totalInvestment.plus( const totalInvestmentInBaseCurrency = currentPositions.totalInvestment.plus(
cashDetails.balanceInBaseCurrency
);
const totalValue = currentPositions.currentValue.plus(
cashDetails.balanceInBaseCurrency 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) => { const dataGatheringItems = currentPositions.positions.map((position) => {
return { return {
@ -540,10 +524,12 @@ export class PortfolioService {
holdings[item.symbol] = { holdings[item.symbol] = {
markets, markets,
allocationCurrent: totalValue.eq(0) allocationCurrent: filteredValueInBaseCurrency.eq(0)
? 0 ? 0
: value.div(totalValue).toNumber(), : value.div(filteredValueInBaseCurrency).toNumber(),
allocationInvestment: item.investment.div(totalInvestment).toNumber(), allocationInvestment: item.investment
.div(totalInvestmentInBaseCurrency)
.toNumber(),
assetClass: symbolProfile.assetClass, assetClass: symbolProfile.assetClass,
assetSubClass: symbolProfile.assetSubClass, assetSubClass: symbolProfile.assetSubClass,
countries: symbolProfile.countries, countries: symbolProfile.countries,
@ -577,8 +563,8 @@ export class PortfolioService {
cashDetails, cashDetails,
emergencyFund, emergencyFund,
userCurrency, userCurrency,
investment: totalInvestment, investment: totalInvestmentInBaseCurrency,
value: totalValue value: filteredValueInBaseCurrency
}); });
for (const symbol of Object.keys(cashPositions)) { for (const symbol of Object.keys(cashPositions)) {
@ -594,7 +580,18 @@ export class PortfolioService {
filters: aFilters 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( public async getPosition(

View File

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

View File

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

View File

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

View File

@ -23,11 +23,7 @@ import {
getTextColor, getTextColor,
parseDate parseDate
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
import { import { LineChartItem, User } from '@ghostfolio/common/interfaces';
LineChartItem,
UniqueAsset,
User
} from '@ghostfolio/common/interfaces';
import { DateRange } from '@ghostfolio/common/types'; import { DateRange } from '@ghostfolio/common/types';
import { import {
Chart, Chart,
@ -215,7 +211,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
locale: this.locale, locale: this.locale,
unit: '%' unit: '%'
}), }),
mode: 'index', mode: 'x',
position: <unknown>'top', position: <unknown>'top',
xAlign: 'center', xAlign: 'center',
yAlign: 'bottom' yAlign: 'bottom'

View File

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

View File

@ -10,6 +10,22 @@
></gf-activities-filter> ></gf-activities-filter>
</div> </div>
</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="proportion-charts row">
<div class="col-md-4"> <div class="col-md-4">
<mat-card class="mb-3"> <mat-card class="mb-3">

View File

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

@ -10,6 +10,15 @@
get started. Due to the time it saves, this will be the best option 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. for most people. The revenue is used for covering the hosting costs.
</p> </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> <p>
If you prefer to run Ghostfolio on your own infrastructure, please If you prefer to run Ghostfolio on your own infrastructure, please
find the source code and further instructions on find the source code and further instructions on

View File

@ -15,7 +15,7 @@
</trans-unit> </trans-unit>
<trans-unit id="ccb2a809018b32a96c813ae69126ce05976109ce" datatype="html"> <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> <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-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/app.component.html</context> <context context-type="sourcefile">apps/client/src/app/app.component.html</context>
<context context-type="linenumber">55,56</context> <context context-type="linenumber">55,56</context>
@ -1031,7 +1031,7 @@
</trans-unit> </trans-unit>
<trans-unit id="67251f04518ae452230c68a748b3fa2838b4db74" datatype="html"> <trans-unit id="67251f04518ae452230c68a748b3fa2838b4db74" datatype="html">
<source>Net Worth</source> <source>Net Worth</source>
<target state="translated">Reinvermögen</target> <target state="translated">Gesamtvermögen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context> <context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context>
<context context-type="linenumber">179</context> <context context-type="linenumber">179</context>
@ -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">248</context> <context context-type="linenumber">225</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">258</context> <context context-type="linenumber">235</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">270</context> <context context-type="linenumber">247</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">271</context> <context context-type="linenumber">248</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">317</context> <context context-type="linenumber">294</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html"> <trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
@ -1323,7 +1323,7 @@
</trans-unit> </trans-unit>
<trans-unit id="f147d0f7f965cccee2e77294cba8e1b88021fa08" datatype="html"> <trans-unit id="f147d0f7f965cccee2e77294cba8e1b88021fa08" datatype="html">
<source>Upgrade</source> <source>Upgrade</source>
<target state="new">Upgrade</target> <target state="translated">Upgrade</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">37</context> <context context-type="linenumber">37</context>
@ -1379,7 +1379,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6b939b00e8481ed8aa8a24d8add7a209d7116759" datatype="html"> <trans-unit id="6b939b00e8481ed8aa8a24d8add7a209d7116759" datatype="html">
<source>Locale</source> <source>Locale</source>
<target state="new">Locale</target> <target state="translated">Lokalität</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">135</context> <context context-type="linenumber">135</context>
@ -1618,7 +1618,7 @@
<target state="translated">Nach Konto</target> <target state="translated">Nach Konto</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="b79f5520c0cb9a00bd589e8a4c86ffcf5ae439d7" datatype="html"> <trans-unit id="b79f5520c0cb9a00bd589e8a4c86ffcf5ae439d7" datatype="html">
@ -1626,7 +1626,7 @@
<target state="translated">Nach Währung</target> <target state="translated">Nach Währung</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8288ff761f2d259625d2e5a3d96db727926d9cd4" datatype="html"> <trans-unit id="8288ff761f2d259625d2e5a3d96db727926d9cd4" datatype="html">
@ -1634,7 +1634,7 @@
<target state="translated">Nach Asset Class</target> <target state="translated">Nach Asset Class</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="b64539bb7815eb3275b55ad723d3897fc6ba8d23" datatype="html"> <trans-unit id="b64539bb7815eb3275b55ad723d3897fc6ba8d23" datatype="html">
@ -1642,7 +1642,7 @@
<target state="translated">Nach Position</target> <target state="translated">Nach Position</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9f86714c9a6b74e13c96ab02102ce40c34fe13b9" datatype="html"> <trans-unit id="9f86714c9a6b74e13c96ab02102ce40c34fe13b9" datatype="html">
@ -1650,7 +1650,7 @@
<target state="translated">Nach Sektor</target> <target state="translated">Nach Sektor</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7017e0e26b53ef322c3e3bbf95f06a85487a12b2" datatype="html"> <trans-unit id="7017e0e26b53ef322c3e3bbf95f06a85487a12b2" datatype="html">
@ -1658,7 +1658,7 @@
<target state="translated">Nach Kontinent</target> <target state="translated">Nach Kontinent</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="f27e9dd8de80176286e02312e694cb8d1e485a5d" datatype="html"> <trans-unit id="f27e9dd8de80176286e02312e694cb8d1e485a5d" datatype="html">
@ -1666,7 +1666,7 @@
<target state="translated">Nach Land</target> <target state="translated">Nach Land</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="85780db87ac6c9f202615ac63754551c061e7236" datatype="html"> <trans-unit id="85780db87ac6c9f202615ac63754551c061e7236" datatype="html">
@ -1674,7 +1674,7 @@
<target state="translated">Regionen</target> <target state="translated">Regionen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <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> <target state="translated">Zeitstrahl der Investitionen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html"> <trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html">
@ -1714,7 +1714,7 @@
<target state="translated">Gewinner</target> <target state="translated">Gewinner</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html"> <trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html">
@ -1722,7 +1722,7 @@
<target state="translated">Verlierer</target> <target state="translated">Verlierer</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5857197365507636437" datatype="html"> <trans-unit id="5857197365507636437" datatype="html">
@ -2036,6 +2036,10 @@
<trans-unit id="9201103587777813545" datatype="html"> <trans-unit id="9201103587777813545" datatype="html">
<source>Portfolio</source> <source>Portfolio</source>
<target state="translated">Portfolio</target> <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-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page-routing.module.ts</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page-routing.module.ts</context>
<context context-type="linenumber">12</context> <context context-type="linenumber">12</context>
@ -2059,7 +2063,7 @@
</trans-unit> </trans-unit>
<trans-unit id="a3d148b40a389fda0665eb583c9e434ec5ee1ced" datatype="html"> <trans-unit id="a3d148b40a389fda0665eb583c9e434ec5ee1ced" datatype="html">
<source> Ghostfolio empowers you to keep track of your wealth. </source> <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-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
<context context-type="linenumber">132,134</context> <context context-type="linenumber">132,134</context>
@ -2270,7 +2274,7 @@
<target state="translated">Beta</target> <target state="translated">Beta</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context> <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>
<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>
@ -2414,7 +2418,7 @@
<target state="translated">Entwickelte Länder</target> <target state="translated">Entwickelte Länder</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2426,7 +2430,7 @@
<target state="translated">Schwellenländer</target> <target state="translated">Schwellenländer</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <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> <target state="translated">Andere Länder</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <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 context-type="linenumber">136</context>
</context-group> </context-group>
</trans-unit> </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"> <trans-unit id="e34e2478d2d30c9d01758d01b7212411171b9bd5" datatype="html">
<source>Projected Total Amount</source> <source>Projected Total Amount</source>
<target state="translated">Projizierter Gesamtbetrag</target> <target state="translated">Projizierter Gesamtbetrag</target>
@ -2507,7 +2467,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2937311350146031865" datatype="html"> <trans-unit id="2937311350146031865" datatype="html">
<source>Initial</source> <source>Initial</source>
<target state="new">Beginn</target> <target state="translated">Beginn</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
<context context-type="linenumber">57</context> <context context-type="linenumber">57</context>
@ -2526,7 +2486,7 @@
<target state="translated">Monatlich</target> <target state="translated">Monatlich</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1975246224413290232" datatype="html"> <trans-unit id="1975246224413290232" datatype="html">
@ -2534,19 +2494,15 @@
<target state="translated">Aufsummiert</target> <target state="translated">Aufsummiert</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5213771062241898526" datatype="html"> <trans-unit id="5213771062241898526" datatype="html">
<source>Deposit</source> <source>Deposit</source>
<target state="translated">Einlage</target> <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-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/investment-chart/investment-chart.component.ts</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
@ -2563,7 +2519,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1054498214311181686" datatype="html"> <trans-unit id="1054498214311181686" datatype="html">
<source>Savings</source> <source>Savings</source>
<target state="new">Ersparnisse</target> <target state="translated">Ersparnisse</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
<context context-type="linenumber">296</context> <context context-type="linenumber">296</context>
@ -2606,7 +2562,7 @@
<target state="translated">Filtern nach...</target> <target state="translated">Filtern nach...</target>
<context-group purpose="location"> <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="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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2078421919111943467" datatype="html"> <trans-unit id="2078421919111943467" datatype="html">
@ -2662,7 +2618,7 @@
<target state="translated">Benchmarks</target> <target state="translated">Benchmarks</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="44fcf77e86dc038202ebad6b46d1d833d60d781b" datatype="html"> <trans-unit id="44fcf77e86dc038202ebad6b46d1d833d60d781b" datatype="html">
@ -2670,7 +2626,7 @@
<target state="translated">Vergleichen mit...</target> <target state="translated">Vergleichen mit...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1931353503905413384" datatype="html"> <trans-unit id="1931353503905413384" datatype="html">
@ -2678,7 +2634,15 @@
<target state="translated">Benchmark</target> <target state="translated">Benchmark</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context> <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> </context-group>
</trans-unit> </trans-unit>
</body> </body>

File diff suppressed because it is too large Load Diff

View File

@ -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">248</context> <context context-type="linenumber">225</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">258</context> <context context-type="linenumber">235</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">270</context> <context context-type="linenumber">247</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">271</context> <context context-type="linenumber">248</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">317</context> <context context-type="linenumber">294</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html"> <trans-unit id="29881a45dafbe5aa05cd9d0441a4c0c2fb06df92" datatype="html">
@ -1453,56 +1453,56 @@
<source>By Account</source> <source>By Account</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="b79f5520c0cb9a00bd589e8a4c86ffcf5ae439d7" datatype="html"> <trans-unit id="b79f5520c0cb9a00bd589e8a4c86ffcf5ae439d7" datatype="html">
<source>By Currency</source> <source>By Currency</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8288ff761f2d259625d2e5a3d96db727926d9cd4" datatype="html"> <trans-unit id="8288ff761f2d259625d2e5a3d96db727926d9cd4" datatype="html">
<source>By Asset Class</source> <source>By Asset Class</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="b64539bb7815eb3275b55ad723d3897fc6ba8d23" datatype="html"> <trans-unit id="b64539bb7815eb3275b55ad723d3897fc6ba8d23" datatype="html">
<source>By Holding</source> <source>By Holding</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9f86714c9a6b74e13c96ab02102ce40c34fe13b9" datatype="html"> <trans-unit id="9f86714c9a6b74e13c96ab02102ce40c34fe13b9" datatype="html">
<source>By Sector</source> <source>By Sector</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7017e0e26b53ef322c3e3bbf95f06a85487a12b2" datatype="html"> <trans-unit id="7017e0e26b53ef322c3e3bbf95f06a85487a12b2" datatype="html">
<source>By Continent</source> <source>By Continent</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="f27e9dd8de80176286e02312e694cb8d1e485a5d" datatype="html"> <trans-unit id="f27e9dd8de80176286e02312e694cb8d1e485a5d" datatype="html">
<source>By Country</source> <source>By Country</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="85780db87ac6c9f202615ac63754551c061e7236" datatype="html"> <trans-unit id="85780db87ac6c9f202615ac63754551c061e7236" datatype="html">
<source>Regions</source> <source>Regions</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -1531,21 +1531,21 @@
<source>Investment Timeline</source> <source>Investment Timeline</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html"> <trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html">
<source>Top</source> <source>Top</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html"> <trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html">
<source>Bottom</source> <source>Bottom</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5857197365507636437" datatype="html"> <trans-unit id="5857197365507636437" datatype="html">
@ -1824,6 +1824,10 @@
</trans-unit> </trans-unit>
<trans-unit id="9201103587777813545" datatype="html"> <trans-unit id="9201103587777813545" datatype="html">
<source>Portfolio</source> <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-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page-routing.module.ts</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page-routing.module.ts</context>
<context context-type="linenumber">12</context> <context context-type="linenumber">12</context>
@ -2029,7 +2033,7 @@
<source>Beta</source> <source>Beta</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context> <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>
<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>
@ -2100,7 +2104,7 @@
<source>Developed Markets</source> <source>Developed Markets</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2140,7 +2144,7 @@
<source>Other Markets</source> <source>Other Markets</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context>
@ -2151,7 +2155,7 @@
<source>Emerging Markets</source> <source>Emerging Markets</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.html</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/public/public-page.html</context> <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 context-type="linenumber">136</context>
</context-group> </context-group>
</trans-unit> </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"> <trans-unit id="e34e2478d2d30c9d01758d01b7212411171b9bd5" datatype="html">
<source>Projected Total Amount</source> <source>Projected Total Amount</source>
<context-group purpose="location"> <context-group purpose="location">
@ -2250,7 +2215,7 @@
<source>Accumulating</source> <source>Accumulating</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2937311350146031865" datatype="html"> <trans-unit id="2937311350146031865" datatype="html">
@ -2269,13 +2234,9 @@
</trans-unit> </trans-unit>
<trans-unit id="5213771062241898526" datatype="html"> <trans-unit id="5213771062241898526" datatype="html">
<source>Deposit</source> <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-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/investment-chart/investment-chart.component.ts</context> <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>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
@ -2293,7 +2254,7 @@
<source>Monthly</source> <source>Monthly</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8511b16abcf065252b350d64e337ba2447db3ffb" datatype="html"> <trans-unit id="8511b16abcf065252b350d64e337ba2447db3ffb" datatype="html">
@ -2339,7 +2300,7 @@
<source>Filter by...</source> <source>Filter by...</source>
<context-group purpose="location"> <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="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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="303469635941752458" datatype="html"> <trans-unit id="303469635941752458" datatype="html">
@ -2378,21 +2339,28 @@
<source>Benchmark</source> <source>Benchmark</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1b25c6e22f822e07a3e4d5aae4edc5b41fe083c2" datatype="html"> <trans-unit id="1b25c6e22f822e07a3e4d5aae4edc5b41fe083c2" datatype="html">
<source>Benchmarks</source> <source>Benchmarks</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context> <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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="44fcf77e86dc038202ebad6b46d1d833d60d781b" datatype="html"> <trans-unit id="44fcf77e86dc038202ebad6b46d1d833d60d781b" datatype="html">
<source>Compare with...</source> <source>Compare with...</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.html</context> <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> </context-group>
</trans-unit> </trans-unit>
</body> </body>

View File

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

View File

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

View File

@ -67,6 +67,8 @@ export const GATHER_HISTORICAL_MARKET_DATA_PROCESS_OPTIONS: JobOptions = {
} }
}; };
export const MAX_CHART_ITEMS = 365;
export const PROPERTY_BENCHMARKS = 'BENCHMARKS'; export const PROPERTY_BENCHMARKS = 'BENCHMARKS';
export const PROPERTY_COUPONS = 'COUPONS'; export const PROPERTY_COUPONS = 'COUPONS';
export const PROPERTY_CURRENCIES = 'CURRENCIES'; export const PROPERTY_CURRENCIES = 'CURRENCIES';

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "1.192.0", "version": "1.195.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {