Sven Günther 18 hours ago
committed by GitHub
parent
commit
96b027b58e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 38
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  3. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  4. 468
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-dividend-yield-multi-asset.spec.ts
  5. 152
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
  6. 22
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts
  7. 9
      apps/api/src/app/portfolio/current-rate.service.mock.ts
  8. 5
      apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts
  9. 176
      apps/api/src/app/portfolio/portfolio.service.spec.ts
  10. 8
      apps/api/src/app/portfolio/portfolio.service.ts
  11. 21
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  12. 180
      apps/client/src/locales/messages.es.xlf
  13. 1
      libs/common/src/lib/interfaces/portfolio-summary.interface.ts
  14. 2
      libs/common/src/lib/models/portfolio-snapshot.ts
  15. 2
      libs/common/src/lib/models/timeline-position.ts

6
CHANGELOG.md

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Added the dividend yield (trailing twelve months) to the portfolio summary (experimental)
## 2.237.0 - 2026-02-08 ## 2.237.0 - 2026-02-08
### Changed ### Changed

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

@ -54,7 +54,8 @@ import {
isWithinInterval, isWithinInterval,
min, min,
startOfYear, startOfYear,
subDays subDays,
subYears
} from 'date-fns'; } from 'date-fns';
import { isNumber, sortBy, sum, uniqBy } from 'lodash'; import { isNumber, sortBy, sum, uniqBy } from 'lodash';
@ -118,6 +119,7 @@ export abstract class PortfolioCalculator {
this.activities = activities this.activities = activities
.map( .map(
({ ({
currency,
date, date,
feeInAssetProfileCurrency, feeInAssetProfileCurrency,
feeInBaseCurrency, feeInBaseCurrency,
@ -138,6 +140,7 @@ export abstract class PortfolioCalculator {
} }
return { return {
currency,
SymbolProfile, SymbolProfile,
tags, tags,
type, type,
@ -187,6 +190,7 @@ export abstract class PortfolioCalculator {
activitiesCount: 0, activitiesCount: 0,
createdAt: new Date(), createdAt: new Date(),
currentValueInBaseCurrency: new Big(0), currentValueInBaseCurrency: new Big(0),
dividendYieldTrailingTwelveMonths: 0,
errors: [], errors: [],
hasErrors: false, hasErrors: false,
historicalData: [], historicalData: [],
@ -403,7 +407,39 @@ export abstract class PortfolioCalculator {
}; };
} }
// Calculate dividend yield based on trailing twelve months of dividends and investment (cost basis)
const twelveMonthsAgo = subYears(this.endDate, 1);
const dividendsLast12Months = this.activities
.filter(({ SymbolProfile, type, date }) => {
return (
SymbolProfile.symbol === item.symbol &&
type === 'DIVIDEND' &&
isWithinInterval(new Date(date), {
start: twelveMonthsAgo,
end: this.endDate
})
);
})
.reduce((sum, activity) => {
const activityCurrency =
activity.currency ?? activity.SymbolProfile.currency;
const exchangeRate =
exchangeRatesByCurrency[`${activityCurrency}${this.currency}`]?.[
format(new Date(activity.date), DATE_FORMAT)
] ?? 1;
const dividendAmount = activity.quantity.mul(activity.unitPrice);
return sum.plus(dividendAmount.mul(exchangeRate));
}, new Big(0));
const dividendYieldTrailingTwelveMonths =
totalInvestmentWithCurrencyEffect.gt(0)
? dividendsLast12Months
.div(totalInvestmentWithCurrencyEffect)
.toNumber()
: 0;
positions.push({ positions.push({
dividendYieldTrailingTwelveMonths,
includeInTotalAssetValue, includeInTotalAssetValue,
timeWeightedInvestment, timeWeightedInvestment,
timeWeightedInvestmentWithCurrencyEffect, timeWeightedInvestmentWithCurrencyEffect,

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts

@ -237,6 +237,7 @@ describe('PortfolioCalculator', () => {
dateOfFirstActivity: '2023-12-31', dateOfFirstActivity: '2023-12-31',
dividend: new Big(0), dividend: new Big(0),
dividendInBaseCurrency: new Big(0), dividendInBaseCurrency: new Big(0),
dividendYieldTrailingTwelveMonths: 0,
fee: new Big(0), fee: new Big(0),
feeInBaseCurrency: new Big(0), feeInBaseCurrency: new Big(0),
grossPerformance: new Big(0), grossPerformance: new Big(0),

468
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-dividend-yield-multi-asset.spec.ts

@ -0,0 +1,468 @@
import {
activityDummyData,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper';
import { Activity } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
CurrentRateService: jest.fn().mockImplementation(() => {
return CurrentRateServiceMock;
})
};
});
jest.mock(
'@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service',
() => {
return {
PortfolioSnapshotService: jest.fn().mockImplementation(() => {
return PortfolioSnapshotServiceMock;
})
};
}
);
jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
return {
RedisCacheService: jest.fn().mockImplementation(() => {
return RedisCacheServiceMock;
})
};
});
describe('PortfolioCalculator', () => {
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
let exchangeRateDataService: ExchangeRateDataService;
let portfolioCalculatorFactory: PortfolioCalculatorFactory;
let portfolioSnapshotService: PortfolioSnapshotService;
let redisCacheService: RedisCacheService;
beforeEach(() => {
configurationService = new ConfigurationService();
currentRateService = new CurrentRateService(null, null, null, null);
exchangeRateDataService = new ExchangeRateDataService(
null,
null,
null,
null
);
portfolioSnapshotService = new PortfolioSnapshotService(null);
redisCacheService = new RedisCacheService(null, null);
portfolioCalculatorFactory = new PortfolioCalculatorFactory(
configurationService,
currentRateService,
exchangeRateDataService,
portfolioSnapshotService,
redisCacheService
);
});
describe('Multi-asset dividend yield', () => {
it('with MSFT and IBM positions verifies portfolio-wide dividend yield aggregation', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime());
const activities: Activity[] = [
// MSFT: 1 share @ 300, 4 quarterly dividends = 2.60 total
{
...activityDummyData,
date: new Date('2021-09-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'BUY',
unitPriceInAssetProfileCurrency: 300
},
{
...activityDummyData,
date: new Date('2022-08-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.65
},
{
...activityDummyData,
date: new Date('2022-11-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.65
},
{
...activityDummyData,
date: new Date('2023-02-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.65
},
{
...activityDummyData,
date: new Date('2023-05-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.65
},
// IBM: 1 share @ 200, 4 quarterly dividends = 6.60 total
{
...activityDummyData,
date: new Date('2021-10-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'BUY',
unitPriceInAssetProfileCurrency: 200
},
{
...activityDummyData,
date: new Date('2022-09-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 1.65
},
{
...activityDummyData,
date: new Date('2022-12-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 1.65
},
{
...activityDummyData,
date: new Date('2023-03-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 1.65
},
{
...activityDummyData,
date: new Date('2023-06-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 1.65
}
];
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
currency: 'USD',
userId: userDummyData.id
});
const portfolioSnapshot = await portfolioCalculator.computeSnapshot();
expect(portfolioSnapshot.positions).toHaveLength(2);
const msftPosition = portfolioSnapshot.positions.find(
({ symbol }) => symbol === 'MSFT'
);
const ibmPosition = portfolioSnapshot.positions.find(
({ symbol }) => symbol === 'IBM'
);
// MSFT: 2.60 dividends / 300 investment = 0.00867 (0.867%)
expect(msftPosition.dividendInBaseCurrency).toEqual(new Big('2.6'));
expect(msftPosition.investmentWithCurrencyEffect).toEqual(new Big('300'));
expect(msftPosition.dividendYieldTrailingTwelveMonths).toBeCloseTo(
2.6 / 300,
5
);
// IBM: 6.60 dividends / 200 investment = 0.033 (3.3%)
expect(ibmPosition.dividendInBaseCurrency).toEqual(new Big('6.6'));
expect(ibmPosition.investmentWithCurrencyEffect).toEqual(new Big('200'));
expect(ibmPosition.dividendYieldTrailingTwelveMonths).toBeCloseTo(
6.6 / 200,
5
);
// Portfolio-wide: (2.60 + 6.60) / (300 + 200) = 9.20 / 500 = 0.0184 (1.84%)
const totalDividends = new Big(msftPosition.dividendInBaseCurrency).plus(
ibmPosition.dividendInBaseCurrency
);
const totalInvestment = new Big(
msftPosition.investmentWithCurrencyEffect
).plus(ibmPosition.investmentWithCurrencyEffect);
expect(totalDividends.toNumber()).toBe(9.2);
expect(totalInvestment.toNumber()).toBe(500);
// Verify portfolio-level dividend yield aggregation
expect(portfolioSnapshot).toHaveProperty(
'dividendYieldTrailingTwelveMonths'
);
expect(portfolioSnapshot.dividendYieldTrailingTwelveMonths).toBeCloseTo(
0.0184,
4
);
});
it('ignores dividends older than 12 months when aggregating portfolio yield', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime());
const activities: Activity[] = [
// MSFT: 1 share @ 300, 3 dividends total (one older than 12 months)
{
...activityDummyData,
date: new Date('2021-09-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'BUY',
unitPriceInAssetProfileCurrency: 300
},
{
...activityDummyData,
date: new Date('2021-11-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.62
},
{
...activityDummyData,
date: new Date('2022-08-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.65
},
{
...activityDummyData,
date: new Date('2023-05-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.65
},
// IBM: 1 share @ 200, 2 dividends total (one older than 12 months)
{
...activityDummyData,
date: new Date('2021-10-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'BUY',
unitPriceInAssetProfileCurrency: 200
},
{
...activityDummyData,
date: new Date('2022-06-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 1.65
},
{
...activityDummyData,
date: new Date('2023-06-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'IBM',
symbol: 'IBM'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 1.65
}
];
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
currency: 'USD',
userId: userDummyData.id
});
const portfolioSnapshot = await portfolioCalculator.computeSnapshot();
const msftPosition = portfolioSnapshot.positions.find(
({ symbol }) => symbol === 'MSFT'
);
const ibmPosition = portfolioSnapshot.positions.find(
({ symbol }) => symbol === 'IBM'
);
expect(msftPosition.dividendInBaseCurrency).toEqual(new Big('1.92'));
expect(ibmPosition.dividendInBaseCurrency).toEqual(new Big('3.3'));
const msftDividendLast12Months = new Big('1.3');
const ibmDividendLast12Months = new Big('1.65');
const totalInvestment = new Big('500');
expect(msftPosition.dividendYieldTrailingTwelveMonths).toBeCloseTo(
msftDividendLast12Months.div(new Big('300')).toNumber(),
6
);
expect(ibmPosition.dividendYieldTrailingTwelveMonths).toBeCloseTo(
ibmDividendLast12Months.div(new Big('200')).toNumber(),
6
);
const expectedDividendYield = msftDividendLast12Months
.plus(ibmDividendLast12Months)
.div(totalInvestment)
.toNumber();
expect(portfolioSnapshot.dividendYieldTrailingTwelveMonths).toBeCloseTo(
expectedDividendYield,
6
);
});
});
});

152
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts

@ -79,7 +79,7 @@ describe('PortfolioCalculator', () => {
}); });
describe('get current positions', () => { describe('get current positions', () => {
it.only('with MSFT buy', async () => { it('with MSFT buy', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime()); jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime());
const activities: Activity[] = [ const activities: Activity[] = [
@ -178,5 +178,155 @@ describe('PortfolioCalculator', () => {
}) })
); );
}); });
it('with MSFT buy and four quarterly dividends to calculate annualized dividend yield', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2023-07-10').getTime());
const activities: Activity[] = [
{
...activityDummyData,
date: new Date('2021-09-16'),
feeInAssetProfileCurrency: 19,
feeInBaseCurrency: 19,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'BUY',
unitPriceInAssetProfileCurrency: 298.58
},
{
...activityDummyData,
date: new Date('2022-08-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.62
},
{
...activityDummyData,
date: new Date('2022-11-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.68
},
{
...activityDummyData,
date: new Date('2023-02-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.68
},
{
...activityDummyData,
date: new Date('2023-05-16'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: 'DIVIDEND',
unitPriceInAssetProfileCurrency: 0.68
}
];
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
currency: 'USD',
userId: userDummyData.id
});
const portfolioSnapshot = await portfolioCalculator.computeSnapshot();
expect(portfolioSnapshot).toMatchObject({
errors: [],
hasErrors: false,
positions: [
{
activitiesCount: 5,
averagePrice: new Big('298.58'),
currency: 'USD',
dataSource: 'YAHOO',
dateOfFirstActivity: '2021-09-16',
dividend: new Big('2.66'),
dividendInBaseCurrency: new Big('2.66'),
fee: new Big('19'),
quantity: new Big('1'),
symbol: 'MSFT',
tags: []
}
],
totalFeesWithCurrencyEffect: new Big('19'),
totalInterestWithCurrencyEffect: new Big('0'),
totalInvestment: new Big('298.58'),
totalInvestmentWithCurrencyEffect: new Big('298.58'),
totalLiabilitiesWithCurrencyEffect: new Big('0')
});
// Verify position-level dividend yield
const position = portfolioSnapshot.positions[0];
expect(position).toHaveProperty('dividendYieldTrailingTwelveMonths');
expect(position.dividendYieldTrailingTwelveMonths).toBeGreaterThan(0);
const expectedPositionYield = new Big(position.dividendInBaseCurrency)
.div(position.investmentWithCurrencyEffect)
.toNumber();
expect(position.dividendYieldTrailingTwelveMonths).toBeCloseTo(
expectedPositionYield,
10
);
expect(expectedPositionYield).toBeCloseTo(0.00891, 3); // ~0.89% yield on cost
// Verify portfolio-level dividend yield
expect(portfolioSnapshot).toHaveProperty(
'dividendYieldTrailingTwelveMonths'
);
expect(portfolioSnapshot.dividendYieldTrailingTwelveMonths).toBeCloseTo(
expectedPositionYield,
10
);
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
totalInvestmentValueWithCurrencyEffect: 298.58
})
);
});
}); });
}); });

22
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts

@ -34,6 +34,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
let grossPerformanceWithCurrencyEffect = new Big(0); let grossPerformanceWithCurrencyEffect = new Big(0);
let hasErrors = false; let hasErrors = false;
let netPerformance = new Big(0); let netPerformance = new Big(0);
let totalDividendsTrailingTwelveMonthsInBaseCurrency = new Big(0);
let totalFeesWithCurrencyEffect = new Big(0); let totalFeesWithCurrencyEffect = new Big(0);
const totalInterestWithCurrencyEffect = new Big(0); const totalInterestWithCurrencyEffect = new Big(0);
let totalInvestment = new Big(0); let totalInvestment = new Big(0);
@ -46,6 +47,15 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
return includeInTotalAssetValue; return includeInTotalAssetValue;
} }
)) { )) {
if (currentPosition.investmentWithCurrencyEffect) {
totalDividendsTrailingTwelveMonthsInBaseCurrency =
totalDividendsTrailingTwelveMonthsInBaseCurrency.plus(
new Big(currentPosition.dividendYieldTrailingTwelveMonths ?? 0).mul(
currentPosition.investmentWithCurrencyEffect
)
);
}
if (currentPosition.feeInBaseCurrency) { if (currentPosition.feeInBaseCurrency) {
totalFeesWithCurrencyEffect = totalFeesWithCurrencyEffect.plus( totalFeesWithCurrencyEffect = totalFeesWithCurrencyEffect.plus(
currentPosition.feeInBaseCurrency currentPosition.feeInBaseCurrency
@ -105,8 +115,17 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
} }
} }
// Calculate dividend yield for the entire portfolio based on trailing twelve months
const dividendYieldTrailingTwelveMonths =
totalInvestmentWithCurrencyEffect.gt(0)
? totalDividendsTrailingTwelveMonthsInBaseCurrency
.div(totalInvestmentWithCurrencyEffect)
.toNumber()
: 0;
return { return {
currentValueInBaseCurrency, currentValueInBaseCurrency,
dividendYieldTrailingTwelveMonths,
hasErrors, hasErrors,
positions, positions,
totalFeesWithCurrencyEffect, totalFeesWithCurrencyEffect,
@ -303,6 +322,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
// Add a synthetic order at the start and the end date // Add a synthetic order at the start and the end date
orders.push({ orders.push({
currency: undefined,
date: startDateString, date: startDateString,
fee: new Big(0), fee: new Big(0),
feeInBaseCurrency: new Big(0), feeInBaseCurrency: new Big(0),
@ -318,6 +338,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
}); });
orders.push({ orders.push({
currency: undefined,
date: endDateString, date: endDateString,
fee: new Big(0), fee: new Big(0),
feeInBaseCurrency: new Big(0), feeInBaseCurrency: new Big(0),
@ -359,6 +380,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
} }
} else { } else {
orders.push({ orders.push({
currency: undefined,
date: dateString, date: dateString,
fee: new Big(0), fee: new Big(0),
feeInBaseCurrency: new Big(0), feeInBaseCurrency: new Big(0),

9
apps/api/src/app/portfolio/current-rate.service.mock.ts

@ -64,6 +64,15 @@ function mockGetValue(symbol: string, date: Date) {
return { marketPrice: 0 }; return { marketPrice: 0 };
case 'IBM':
if (isSameDay(parseDate('2021-10-01'), date)) {
return { marketPrice: 140.5 };
} else if (isSameDay(parseDate('2023-07-10'), date)) {
return { marketPrice: 145.2 };
}
return { marketPrice: 0 };
case 'MSFT': case 'MSFT':
if (isSameDay(parseDate('2021-09-16'), date)) { if (isSameDay(parseDate('2021-09-16'), date)) {
return { marketPrice: 89.12 }; return { marketPrice: 89.12 };

5
apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts

@ -1,6 +1,9 @@
import { Activity } from '@ghostfolio/common/interfaces'; import { Activity } from '@ghostfolio/common/interfaces';
export interface PortfolioOrder extends Pick<Activity, 'tags' | 'type'> { export interface PortfolioOrder extends Pick<
Activity,
'currency' | 'tags' | 'type'
> {
date: string; date: string;
fee: Big; fee: Big;
feeInBaseCurrency: Big; feeInBaseCurrency: Big;

176
apps/api/src/app/portfolio/portfolio.service.spec.ts

@ -0,0 +1,176 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import {
activityDummyData,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
import { Activity } from '@ghostfolio/common/interfaces';
import { RequestWithUser } from '@ghostfolio/common/types';
import { Type as ActivityType } from '@prisma/client';
import { Big } from 'big.js';
import { PortfolioService } from './portfolio.service';
describe('PortfolioService', () => {
describe('getSummary', () => {
beforeEach(() => {
jest.useFakeTimers().setSystemTime(new Date('2023-07-10'));
});
afterEach(() => {
jest.useRealTimers();
jest.restoreAllMocks();
});
it('returns dividendYieldTrailingTwelveMonths from the calculator snapshot', async () => {
const activities: Activity[] = [
{
...activityDummyData,
currency: 'USD',
date: new Date('2023-06-01'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 2,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: ActivityType.BUY,
unitPrice: 50,
unitPriceInAssetProfileCurrency: 50,
value: 100,
valueInBaseCurrency: 100
},
{
...activityDummyData,
currency: 'USD',
date: new Date('2023-06-02'),
feeInAssetProfileCurrency: 0,
feeInBaseCurrency: 0,
quantity: 1,
SymbolProfile: {
...symbolProfileDummyData,
currency: 'USD',
dataSource: 'YAHOO',
name: 'Microsoft Inc.',
symbol: 'MSFT'
},
type: ActivityType.SELL,
unitPrice: 40,
unitPriceInAssetProfileCurrency: 40,
value: 40,
valueInBaseCurrency: 40
}
];
const exchangeRateDataService = {
toCurrency: jest.fn((value: number) => value)
};
const orderService = {
getOrders: jest.fn().mockResolvedValue({ activities })
};
const userService = {
user: jest.fn().mockResolvedValue({
id: userDummyData.id,
settings: {
settings: {
baseCurrency: DEFAULT_CURRENCY,
emergencyFund: 0
}
}
})
};
const accountService = {
getCashDetails: jest.fn().mockResolvedValue({
balanceInBaseCurrency: 1000
})
};
const impersonationService = {
validateImpersonationId: jest.fn().mockResolvedValue(undefined)
};
const request = {
user: {
id: userDummyData.id,
settings: { settings: { baseCurrency: DEFAULT_CURRENCY } }
}
} as RequestWithUser;
const portfolioCalculator = {
getDividendInBaseCurrency: jest.fn().mockResolvedValue(new Big(12)),
getFeesInBaseCurrency: jest.fn().mockResolvedValue(new Big(4)),
getInterestInBaseCurrency: jest.fn().mockResolvedValue(new Big(1)),
getLiabilitiesInBaseCurrency: jest.fn().mockResolvedValue(new Big(6)),
getSnapshot: jest.fn().mockResolvedValue({
dividendYieldTrailingTwelveMonths: 0.0123,
currentValueInBaseCurrency: new Big(500),
totalInvestment: new Big(400)
}),
getStartDate: jest.fn().mockReturnValue(new Date('2023-01-01'))
} as unknown as PortfolioCalculator;
const service = new PortfolioService(
{} as any,
accountService as any,
{} as any,
{} as any,
{} as any,
exchangeRateDataService as any,
{} as any,
impersonationService as any,
orderService as any,
request,
{} as any,
{} as any,
userService as any
);
jest.spyOn(service, 'getPerformance').mockResolvedValue({
performance: {
netPerformance: 20,
netPerformancePercentage: 0.05,
netPerformancePercentageWithCurrencyEffect: 0.05,
netPerformanceWithCurrencyEffect: 20
}
} as any);
const summary = await (service as any).getSummary({
balanceInBaseCurrency: 1000,
emergencyFundHoldingsValueInBaseCurrency: 0,
filteredValueInBaseCurrency: new Big(200),
impersonationId: userDummyData.id,
portfolioCalculator,
userCurrency: DEFAULT_CURRENCY,
userId: userDummyData.id
});
expect(portfolioCalculator.getSnapshot).toHaveBeenCalledTimes(1);
expect(summary).toMatchObject({
dividendYieldTrailingTwelveMonths: 0.0123,
cash: 1000,
committedFunds: 60,
dividendInBaseCurrency: 12,
fees: 4,
grossPerformance: 24,
grossPerformanceWithCurrencyEffect: 24,
interestInBaseCurrency: 1,
liabilitiesInBaseCurrency: 6,
totalBuy: 100,
totalInvestment: 400,
totalSell: 40,
totalValueInBaseCurrency: 1494
});
expect(summary.activityCount).toBe(2);
expect(summary.dateOfFirstActivity).toEqual(new Date('2023-01-01'));
});
});
});

8
apps/api/src/app/portfolio/portfolio.service.ts

@ -1860,8 +1860,11 @@ export class PortfolioService {
} }
} }
const { currentValueInBaseCurrency, totalInvestment } = const {
await portfolioCalculator.getSnapshot(); currentValueInBaseCurrency,
dividendYieldTrailingTwelveMonths,
totalInvestment
} = await portfolioCalculator.getSnapshot();
const { performance } = await this.getPerformance({ const { performance } = await this.getPerformance({
impersonationId, impersonationId,
@ -1963,6 +1966,7 @@ export class PortfolioService {
annualizedPerformancePercentWithCurrencyEffect, annualizedPerformancePercentWithCurrencyEffect,
cash, cash,
dateOfFirstActivity, dateOfFirstActivity,
dividendYieldTrailingTwelveMonths,
excludedAccountsAndActivities, excludedAccountsAndActivities,
netPerformance, netPerformance,
netPerformancePercentage, netPerformancePercentage,

21
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -374,4 +374,25 @@
/> />
</div> </div>
</div> </div>
@if (user?.settings?.isExperimentalFeatures) {
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 ml-3 text-truncate">
<ng-container i18n>Dividend Yield</ng-container> (<ng-container i18n
>Trailing Twelve Months</ng-container
>)
</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="
isLoading ? undefined : summary?.dividendYieldTrailingTwelveMonths
"
/>
</div>
</div>
}
</div> </div>

180
apps/client/src/locales/messages.es.xlf

@ -40,7 +40,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9153520284278555926" datatype="html"> <trans-unit id="9153520284278555926" datatype="html">
<source>please</source> <source>please</source>
<target state="translated">por favor</target> <target state="new">please</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">333</context> <context context-type="linenumber">333</context>
@ -84,7 +84,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1351814922314683865" datatype="html"> <trans-unit id="1351814922314683865" datatype="html">
<source>with</source> <source>with</source>
<target state="translated">con</target> <target state="new">with</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">87</context> <context context-type="linenumber">87</context>
@ -368,7 +368,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5611965261696422586" datatype="html"> <trans-unit id="5611965261696422586" datatype="html">
<source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="translated">y es impulsado por los esfuerzos de sus <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contribuidores<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="new">and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">49</context> <context context-type="linenumber">49</context>
@ -652,7 +652,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2395205455607568422" datatype="html"> <trans-unit id="2395205455607568422" datatype="html">
<source>No auto-renewal on membership.</source> <source>No auto-renewal on membership.</source>
<target state="translated">No se renueva automáticamente la membresía.</target> <target state="new">No auto-renewal on membership.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-membership/user-account-membership.html</context>
<context context-type="linenumber">74</context> <context context-type="linenumber">74</context>
@ -1096,7 +1096,7 @@
</trans-unit> </trans-unit>
<trans-unit id="366169681580494481" datatype="html"> <trans-unit id="366169681580494481" datatype="html">
<source>Performance with currency effect</source> <source>Performance with currency effect</source>
<target state="translated">Rendimiento con el efecto del tipo de cambio de divisa</target> <target state="new">Performance with currency effect</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">135</context> <context context-type="linenumber">135</context>
@ -1912,7 +1912,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6004588582437169024" datatype="html"> <trans-unit id="6004588582437169024" datatype="html">
<source>Current week</source> <source>Current week</source>
<target state="translated">Semana actual</target> <target state="new">Current week</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">191</context> <context context-type="linenumber">191</context>
@ -2076,7 +2076,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7500665368930738879" datatype="html"> <trans-unit id="7500665368930738879" datatype="html">
<source>or start a discussion at</source> <source>or start a discussion at</source>
<target state="translated">o iniciar una discusión en</target> <target state="new">or start a discussion at</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -2148,7 +2148,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2003818202621229370" datatype="html"> <trans-unit id="2003818202621229370" datatype="html">
<source>Sustainable retirement income</source> <source>Sustainable retirement income</source>
<target state="translated">Ingreso sostenible de retiro</target> <target state="new">Sustainable retirement income</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">41</context>
@ -2320,7 +2320,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1531212547408073567" datatype="html"> <trans-unit id="1531212547408073567" datatype="html">
<source>contact us</source> <source>contact us</source>
<target state="translated">contactarnos</target> <target state="new">contact us</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">336</context> <context context-type="linenumber">336</context>
@ -2420,7 +2420,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7934616470747135563" datatype="html"> <trans-unit id="7934616470747135563" datatype="html">
<source>Latest activities</source> <source>Latest activities</source>
<target state="translated">Últimas actividades</target> <target state="new">Latest activities</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">211</context> <context context-type="linenumber">211</context>
@ -2536,7 +2536,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5211792611718918888" datatype="html"> <trans-unit id="5211792611718918888" datatype="html">
<source>annual interest rate</source> <source>annual interest rate</source>
<target state="translated">tasa de interés anual</target> <target state="new">annual interest rate</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">185</context>
@ -2656,7 +2656,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7341990227686441824" datatype="html"> <trans-unit id="7341990227686441824" datatype="html">
<source>Could not validate form</source> <source>Could not validate form</source>
<target state="translated">No se pudo validar el formulario</target> <target state="new">Could not validate form</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">554</context> <context context-type="linenumber">554</context>
@ -2892,7 +2892,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8966698274727122602" datatype="html"> <trans-unit id="8966698274727122602" datatype="html">
<source>Authentication</source> <source>Authentication</source>
<target state="translated">Autenticación</target> <target state="new">Authentication</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">35</context> <context context-type="linenumber">35</context>
@ -3044,7 +3044,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3227075298129844075" datatype="html"> <trans-unit id="3227075298129844075" datatype="html">
<source>If you retire today, you would be able to withdraw</source> <source>If you retire today, you would be able to withdraw</source>
<target state="translated">Si te retirases hoy, podrías sacar</target> <target state="new">If you retire today, you would be able to withdraw</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">68</context> <context context-type="linenumber">68</context>
@ -3112,7 +3112,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7763941937414903315" datatype="html"> <trans-unit id="7763941937414903315" datatype="html">
<source>Looking for a student discount?</source> <source>Looking for a student discount?</source>
<target state="translated">¿Buscando un descuento para estudiantes?</target> <target state="new">Looking for a student discount?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">342</context> <context context-type="linenumber">342</context>
@ -3348,7 +3348,7 @@
</trans-unit> </trans-unit>
<trans-unit id="936060984157466006" datatype="html"> <trans-unit id="936060984157466006" datatype="html">
<source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source> <source>Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</source>
<target state="translated">Todo en <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Básico<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, s</target> <target state="new">Everything in <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>Basic<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/>, plus</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</context>
@ -3608,7 +3608,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7498591289549626867" datatype="html"> <trans-unit id="7498591289549626867" datatype="html">
<source>Could not save asset profile</source> <source>Could not save asset profile</source>
<target state="translated">No se pudo guardar el perfil del activo</target> <target state="new">Could not save asset profile</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">588</context> <context context-type="linenumber">588</context>
@ -3812,7 +3812,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7702646444963497962" datatype="html"> <trans-unit id="7702646444963497962" datatype="html">
<source>By</source> <source>By</source>
<target state="translated">Por</target> <target state="new">By</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">139</context> <context context-type="linenumber">139</context>
@ -3828,7 +3828,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4340477809050781416" datatype="html"> <trans-unit id="4340477809050781416" datatype="html">
<source>Current year</source> <source>Current year</source>
<target state="translated">Año actual</target> <target state="new">Current year</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</context>
@ -3864,7 +3864,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8319378030525016917" datatype="html"> <trans-unit id="8319378030525016917" datatype="html">
<source>Asset profile has been saved</source> <source>Asset profile has been saved</source>
<target state="translated">El perfil del activo ha sido guardado</target> <target state="new">Asset profile has been saved</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">578</context> <context context-type="linenumber">578</context>
@ -4056,7 +4056,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1468015720862673946" datatype="html"> <trans-unit id="1468015720862673946" datatype="html">
<source>View Details</source> <source>View Details</source>
<target state="translated">Ver detalles</target> <target state="new">View Details</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">225</context>
@ -4192,7 +4192,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3556628518893194463" datatype="html"> <trans-unit id="3556628518893194463" datatype="html">
<source>per week</source> <source>per week</source>
<target state="translated">por semana</target> <target state="new">per week</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">130</context>
@ -4216,7 +4216,7 @@
</trans-unit> </trans-unit>
<trans-unit id="577204259483334667" datatype="html"> <trans-unit id="577204259483334667" datatype="html">
<source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source> <source>and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</source>
<target state="translated">y compartimos agregados <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>métricas clave<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> del rendimiento de la plataforma</target> <target state="new">and we share aggregated <x id="START_LINK" ctype="x-a" equiv-text="&lt;a title=&quot;Open Startup&quot; [routerLink]=&quot;routerLinkOpenStartup&quot; &gt;"/>key metrics<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> of the platform’s performance</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">32</context> <context context-type="linenumber">32</context>
@ -4260,7 +4260,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5271053765919315173" datatype="html"> <trans-unit id="5271053765919315173" datatype="html">
<source>Website of Thomas Kaul</source> <source>Website of Thomas Kaul</source>
<target state="translated">Sitio web de Thomas Kaul</target> <target state="new">Website of Thomas Kaul</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">44</context> <context context-type="linenumber">44</context>
@ -4440,7 +4440,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2145636458848553570" datatype="html"> <trans-unit id="2145636458848553570" datatype="html">
<source>Sign in with OpenID Connect</source> <source>Sign in with OpenID Connect</source>
<target state="translated">Iniciar sesión con OpenID Connect</target> <target state="new">Sign in with OpenID Connect</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html</context>
<context context-type="linenumber">55</context> <context context-type="linenumber">55</context>
@ -4532,7 +4532,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5289957034780335504" datatype="html"> <trans-unit id="5289957034780335504" datatype="html">
<source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="translated">El código fuente está disponible completamente en <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>software de código abierto<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) bajo la <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>licencia AGPL-3.0<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="new">The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>
@ -4604,7 +4604,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3004519800638083911" datatype="html"> <trans-unit id="3004519800638083911" datatype="html">
<source>this is projected to increase to</source> <source>this is projected to increase to</source>
<target state="translated">esto se proyecta a aumentar a</target> <target state="new">this is projected to increase to</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">147</context>
@ -4656,7 +4656,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3627006945295714424" datatype="html"> <trans-unit id="3627006945295714424" datatype="html">
<source>Job ID</source> <source>Job ID</source>
<target state="translated">ID de trabajo</target> <target state="new">Job ID</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-jobs/admin-jobs.html</context>
<context context-type="linenumber">34</context> <context context-type="linenumber">34</context>
@ -4740,7 +4740,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8553460997100418147" datatype="html"> <trans-unit id="8553460997100418147" datatype="html">
<source>for</source> <source>for</source>
<target state="translated">para</target> <target state="new">for</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">128</context> <context context-type="linenumber">128</context>
@ -4764,7 +4764,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5134951682994822188" datatype="html"> <trans-unit id="5134951682994822188" datatype="html">
<source>Could not parse scraper configuration</source> <source>Could not parse scraper configuration</source>
<target state="translated">No se pudo analizar la configuración del scraper</target> <target state="new">Could not parse scraper configuration</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">509</context> <context context-type="linenumber">509</context>
@ -4808,7 +4808,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9187635907883145155" datatype="html"> <trans-unit id="9187635907883145155" datatype="html">
<source>Edit access</source> <source>Edit access</source>
<target state="translated">Editar acceso</target> <target state="new">Edit access</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@ -4880,7 +4880,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8984201769958269296" datatype="html"> <trans-unit id="8984201769958269296" datatype="html">
<source>Get access to 80’000+ tickers from over 50 exchanges</source> <source>Get access to 80’000+ tickers from over 50 exchanges</source>
<target state="translated">Obtén acceso a más de 80,000 tickers de más de 50 exchanges</target> <target state="new">Get access to 80’000+ tickers from over 50 exchanges</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">84</context>
@ -5064,7 +5064,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8014012170270529279" datatype="html"> <trans-unit id="8014012170270529279" datatype="html">
<source>less than</source> <source>less than</source>
<target state="translated">menos que</target> <target state="new">less than</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">129</context>
@ -5354,7 +5354,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4257439615478050183" datatype="html"> <trans-unit id="4257439615478050183" datatype="html">
<source>Ghostfolio Status</source> <source>Ghostfolio Status</source>
<target state="translated">Estado de Ghostfolio</target> <target state="new">Ghostfolio Status</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">62</context> <context context-type="linenumber">62</context>
@ -5362,7 +5362,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4275978599610634089" datatype="html"> <trans-unit id="4275978599610634089" datatype="html">
<source>with your university e-mail address</source> <source>with your university e-mail address</source>
<target state="translated">con tu dirección de correo electrónico de la universidad</target> <target state="new">with your university e-mail address</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">348</context> <context context-type="linenumber">348</context>
@ -5382,7 +5382,7 @@
</trans-unit> </trans-unit>
<trans-unit id="70768492340592330" datatype="html"> <trans-unit id="70768492340592330" datatype="html">
<source>and a safe withdrawal rate (SWR) of</source> <source>and a safe withdrawal rate (SWR) of</source>
<target state="translated">y una tasa de retiro segura (SWR) de</target> <target state="new">and a safe withdrawal rate (SWR) of</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">108</context> <context context-type="linenumber">108</context>
@ -5546,7 +5546,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5276907121760788823" datatype="html"> <trans-unit id="5276907121760788823" datatype="html">
<source>Request it</source> <source>Request it</source>
<target state="translated">Solicitar</target> <target state="new">Request it</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">344</context> <context context-type="linenumber">344</context>
@ -5602,7 +5602,7 @@
</trans-unit> </trans-unit>
<trans-unit id="page.fire.projected.1" datatype="html"> <trans-unit id="page.fire.projected.1" datatype="html">
<source>,</source> <source>,</source>
<target state="translated">,</target> <target state="new">,</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">145</context> <context context-type="linenumber">145</context>
@ -5618,7 +5618,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4905798562247431262" datatype="html"> <trans-unit id="4905798562247431262" datatype="html">
<source>per month</source> <source>per month</source>
<target state="translated">por mes</target> <target state="new">per month</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -5866,7 +5866,7 @@
</trans-unit> </trans-unit>
<trans-unit id="858192247408211331" datatype="html"> <trans-unit id="858192247408211331" datatype="html">
<source>here</source> <source>here</source>
<target state="translated">aquí</target> <target state="new">here</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">347</context> <context context-type="linenumber">347</context>
@ -5874,7 +5874,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1600023202562292052" datatype="html"> <trans-unit id="1600023202562292052" datatype="html">
<source>Close Holding</source> <source>Close Holding</source>
<target state="translated">Cerrar posición</target> <target state="new">Close Holding</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">442</context> <context context-type="linenumber">442</context>
@ -6175,7 +6175,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7309206099560156141" datatype="html"> <trans-unit id="7309206099560156141" datatype="html">
<source>{VAR_PLURAL, plural, =1 {activity} other {activities}}</source> <source>{VAR_PLURAL, plural, =1 {activity} other {activities}}</source>
<target state="translated">{VAR_PLURAL, plural, =1 {actividad} other {actividades}}</target> <target state="new">{VAR_PLURAL, plural, =1 {activity} other {activities}}</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">14</context> <context context-type="linenumber">14</context>
@ -6255,7 +6255,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5707368132268957392" datatype="html"> <trans-unit id="5707368132268957392" datatype="html">
<source>Include in</source> <source>Include in</source>
<target state="translated">Incluir en</target> <target state="new">Include in</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">374</context> <context context-type="linenumber">374</context>
@ -6539,7 +6539,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3477953895055172777" datatype="html"> <trans-unit id="3477953895055172777" datatype="html">
<source>View Holding</source> <source>View Holding</source>
<target state="translated">Ver fondos</target> <target state="new">View Holding</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context> <context context-type="sourcefile">libs/ui/src/lib/activities-table/activities-table.component.html</context>
<context context-type="linenumber">450</context> <context context-type="linenumber">450</context>
@ -6683,7 +6683,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4762855117875399861" datatype="html"> <trans-unit id="4762855117875399861" datatype="html">
<source>Oops! Could not update access.</source> <source>Oops! Could not update access.</source>
<target state="translated">Oops! No se pudo actualizar el acceso.</target> <target state="new">Oops! Could not update access.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts</context>
<context context-type="linenumber">178</context> <context context-type="linenumber">178</context>
@ -6691,7 +6691,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1355312194390410495" datatype="html"> <trans-unit id="1355312194390410495" datatype="html">
<source>, based on your total assets of</source> <source>, based on your total assets of</source>
<target state="translated">, basado en tus activos totales de</target> <target state="new">, based on your total assets of</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">96</context> <context context-type="linenumber">96</context>
@ -6763,7 +6763,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7819314041543176992" datatype="html"> <trans-unit id="7819314041543176992" datatype="html">
<source>Close</source> <source>Close</source>
<target state="translated">Cerrar</target> <target state="translated">Cerca</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html</context>
<context context-type="linenumber">594</context> <context context-type="linenumber">594</context>
@ -6807,7 +6807,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4145496584631696119" datatype="html"> <trans-unit id="4145496584631696119" datatype="html">
<source>Role</source> <source>Role</source>
<target state="translated">Rol</target> <target state="new">Role</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">14</context>
@ -6839,7 +6839,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8375528527939577247" datatype="html"> <trans-unit id="8375528527939577247" datatype="html">
<source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source> <source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source>
<target state="translated"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Cambiar con efecto de cambio dedivisa <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Cambiar <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target> <target state="new"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Change with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Change <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">63</context> <context context-type="linenumber">63</context>
@ -6847,7 +6847,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6602358241522477056" datatype="html"> <trans-unit id="6602358241522477056" datatype="html">
<source>If you plan to open an account at</source> <source>If you plan to open an account at</source>
<target state="translated">Si planeas abrir una cuenta en</target> <target state="new">If you plan to open an account at</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">312</context> <context context-type="linenumber">312</context>
@ -6855,7 +6855,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6608617124920241143" datatype="html"> <trans-unit id="6608617124920241143" datatype="html">
<source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source> <source><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></source>
<target state="translated"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Rendimiento con cambio de divisa <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Rendimiento <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target> <target state="new"><x id="START_BLOCK_IF" equiv-text="@if ( SymbolProfile?.currency &amp;&amp; data.baseCurrency !== SymbolProfile?.currency ) {"/> Performance with currency effect <x id="CLOSE_BLOCK_IF" equiv-text="}"/><x id="START_BLOCK_ELSE" equiv-text="@else {"/> Performance <x id="CLOSE_BLOCK_ELSE" equiv-text="}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">83</context>
@ -6879,7 +6879,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6664504469290651320" datatype="html"> <trans-unit id="6664504469290651320" datatype="html">
<source>send an e-mail to</source> <source>send an e-mail to</source>
<target state="translated">enviar un correo electrónico a</target> <target state="new">send an e-mail to</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">87</context> <context context-type="linenumber">87</context>
@ -6951,7 +6951,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2878377610946588870" datatype="html"> <trans-unit id="2878377610946588870" datatype="html">
<source>, assuming a</source> <source>, assuming a</source>
<target state="translated">, asumiendo un</target> <target state="new">, assuming a</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">174</context> <context context-type="linenumber">174</context>
@ -6959,7 +6959,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7522916136412124285" datatype="html"> <trans-unit id="7522916136412124285" datatype="html">
<source>to use our referral link and get a Ghostfolio Premium membership for one year</source> <source>to use our referral link and get a Ghostfolio Premium membership for one year</source>
<target state="translated">para usar nuestro enlace de referido y obtener una membresía Ghostfolio Premium por un año</target> <target state="new">to use our referral link and get a Ghostfolio Premium membership for one year</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/pricing/pricing-page.html</context>
<context context-type="linenumber">340</context> <context context-type="linenumber">340</context>
@ -7039,7 +7039,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3528767106831563012" datatype="html"> <trans-unit id="3528767106831563012" datatype="html">
<source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source> <source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source>
<target state="translated">Ghostfolio es una aplicación de gestión de patrimonio para aquellos individuos que desean realizar un seguimiento de acciones, ETFs o criptomonedas y tomar decisiones de inversión sólidas y basadas en datos.</target> <target state="new">Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@ -7353,7 +7353,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1789421195684815451" datatype="html"> <trans-unit id="1789421195684815451" datatype="html">
<source>Check the system status at</source> <source>Check the system status at</source>
<target state="translated">Verificar el estado del sistema en</target> <target state="new">Check the system status at</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">57</context> <context context-type="linenumber">57</context>
@ -7369,7 +7369,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7825231215382064101" datatype="html"> <trans-unit id="7825231215382064101" datatype="html">
<source>Change with currency effect</source> <source>Change with currency effect</source>
<target state="translated">Cambiar con el efecto del tipo de cambio de divisa</target> <target state="new">Change with currency effect</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">116</context> <context context-type="linenumber">116</context>
@ -7509,7 +7509,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1325095699053123251" datatype="html"> <trans-unit id="1325095699053123251" datatype="html">
<source>The project has been initiated by</source> <source>The project has been initiated by</source>
<target state="translated">El proyecto ha sido iniciado por</target> <target state="new">The project has been initiated by</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">40</context> <context context-type="linenumber">40</context>
@ -7533,7 +7533,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5004550577313573215" datatype="html"> <trans-unit id="5004550577313573215" datatype="html">
<source>Total amount</source> <source>Total amount</source>
<target state="translated">Cantidad total</target> <target state="new">Total amount</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">95</context> <context context-type="linenumber">95</context>
@ -7625,7 +7625,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6752851341939241310" datatype="html"> <trans-unit id="6752851341939241310" datatype="html">
<source>Find account, holding or page...</source> <source>Find account, holding or page...</source>
<target state="translated">Buscar cuenta, posición o página...</target> <target state="new">Find account, holding or page...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context> <context context-type="sourcefile">libs/ui/src/lib/assistant/assistant.component.ts</context>
<context context-type="linenumber">151</context> <context context-type="linenumber">151</context>
@ -8049,7 +8049,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7383756232563820625" datatype="html"> <trans-unit id="7383756232563820625" datatype="html">
<source>Current month</source> <source>Current month</source>
<target state="translated">Mes actual</target> <target state="new">Current month</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context> <context context-type="sourcefile">apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
@ -8234,7 +8234,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5199695670214400859" datatype="html"> <trans-unit id="5199695670214400859" datatype="html">
<source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="translated">Si encuentras un error, deseas sugerir una mejora o una nueva <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>característica<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, por favor únete a la comunidad Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, publica en <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="new">If you encounter a bug, would like to suggest an improvement or a new <x id="START_LINK" ctype="x-a" equiv-text="&lt;a [routerLink]=&quot;routerLinkFeatures&quot;&gt;"/>feature<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/>, please join the Ghostfolio <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg&quot; title=&quot;Join the Ghostfolio Slack community&quot; &gt;"/>Slack<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> community, post to <x id="START_LINK_2" equiv-text="&lt;a href=&quot;https://x.com/ghostfolio_&quot; title=&quot;Post to Ghostfolio on X (formerly Twitter)&quot; &gt;"/>@ghostfolio_<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">69</context> <context context-type="linenumber">69</context>
@ -8266,7 +8266,7 @@
</trans-unit> </trans-unit>
<trans-unit id="187187500641108332" datatype="html"> <trans-unit id="187187500641108332" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></source> <source><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></source>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{ `Expira ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></target> <target state="new"><x id="INTERPOLATION" equiv-text="{{ `Expires ${formatDistanceToNow( element.subscription.expiresAt )} (${ (element.subscription.expiresAt | date: defaultDateFormat) })` }}"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">39</context> <context context-type="linenumber">39</context>
@ -8334,7 +8334,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRisk.category" datatype="html"> <trans-unit id="rule.economicMarketClusterRisk.category" datatype="html">
<source>Economic Market Cluster Risks</source> <source>Economic Market Cluster Risks</source>
<target state="translated">Riesgos del clúster de mercados económicos</target> <target state="new">Economic Market Cluster Risks</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">106</context> <context context-type="linenumber">106</context>
@ -8342,7 +8342,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.emergencyFund.category" datatype="html"> <trans-unit id="rule.emergencyFund.category" datatype="html">
<source>Emergency Fund</source> <source>Emergency Fund</source>
<target state="translated">Fondo de emergencia</target> <target state="new">Emergency Fund</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">144</context> <context context-type="linenumber">144</context>
@ -8350,7 +8350,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.fees.category" datatype="html"> <trans-unit id="rule.fees.category" datatype="html">
<source>Fees</source> <source>Fees</source>
<target state="translated">Comisiones</target> <target state="new">Fees</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">161</context> <context context-type="linenumber">161</context>
@ -8358,7 +8358,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidity.category" datatype="html"> <trans-unit id="rule.liquidity.category" datatype="html">
<source>Liquidity</source> <source>Liquidity</source>
<target state="translated">Liquidez</target> <target state="new">Liquidity</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">70</context> <context context-type="linenumber">70</context>
@ -8366,7 +8366,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower" datatype="html"> <trans-unit id="rule.liquidityBuyingPower" datatype="html">
<source>Buying Power</source> <source>Buying Power</source>
<target state="translated">Poder de compra</target> <target state="new">Buying Power</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -8374,7 +8374,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.false.min" datatype="html">
<source>Your buying power is below ${thresholdMin} ${baseCurrency}</source> <source>Your buying power is below ${thresholdMin} ${baseCurrency}</source>
<target state="translated">Tu poder de compra es inferior a ${thresholdMin} ${baseCurrency}</target> <target state="new">Your buying power is below ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">73</context>
@ -8382,7 +8382,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.false.zero" datatype="html">
<source>Your buying power is 0 ${baseCurrency}</source> <source>Your buying power is 0 ${baseCurrency}</source>
<target state="translated">Tu poder de compra es 0 ${baseCurrency}</target> <target state="new">Your buying power is 0 ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">77</context> <context context-type="linenumber">77</context>
@ -8390,7 +8390,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.true" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.true" datatype="html">
<source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source> <source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source>
<target state="translated">Tu poder de compra excede ${thresholdMin} ${baseCurrency}</target> <target state="new">Your buying power exceeds ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">80</context> <context context-type="linenumber">80</context>
@ -8422,7 +8422,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.max" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.max" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</source> <source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</source>
<target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) supera el ${thresholdMax}%</target> <target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) exceeds ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">112</context>
@ -8430,7 +8430,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.min" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.false.min" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</source> <source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</source>
<target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) es inferior al ${thresholdMin}%</target> <target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is below ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">117</context>
@ -8438,7 +8438,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.true" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets.true" datatype="html">
<source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="translated">La contribución a los mercados desarrollados de tu inversión actual (${developedMarketsValueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target> <target state="new">The developed markets contribution of your current investment (${developedMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">122</context> <context context-type="linenumber">122</context>
@ -8454,7 +8454,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.max" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.max" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</source> <source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</source>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) supera el ${thresholdMax}%</target> <target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) exceeds ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">130</context>
@ -8462,7 +8462,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.min" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.false.min" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</source> <source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</source>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) es inferior al ${thresholdMin}%</target> <target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is below ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">135</context> <context context-type="linenumber">135</context>
@ -8470,7 +8470,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.true" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets.true" datatype="html">
<source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${emergingMarketsValueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target> <target state="new">The emerging markets contribution of your current investment (${emergingMarketsValueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">140</context> <context context-type="linenumber">140</context>
@ -8494,7 +8494,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific" datatype="html">
<source>Asia-Pacific</source> <source>Asia-Pacific</source>
<target state="translated">Asia-Pacífico</target> <target state="new">Asia-Pacific</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">165</context> <context context-type="linenumber">165</context>
@ -8502,7 +8502,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.max" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.max" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source> <source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target> <target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">167</context> <context context-type="linenumber">167</context>
@ -8510,7 +8510,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.min" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.false.min" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source> <source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target> <target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">171</context> <context context-type="linenumber">171</context>
@ -8518,7 +8518,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.true" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskAsiaPacific.true" datatype="html">
<source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="translated">La contribución al mercado de Asia-Pacífico de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target> <target state="new">The Asia-Pacific market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">175</context> <context context-type="linenumber">175</context>
@ -8526,7 +8526,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets" datatype="html">
<source>Emerging Markets</source> <source>Emerging Markets</source>
<target state="translated">Mercados emergentes</target> <target state="new">Emerging Markets</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">180</context> <context context-type="linenumber">180</context>
@ -8534,7 +8534,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.max" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.max" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source> <source>The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target> <target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">183</context>
@ -8542,7 +8542,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.min" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.false.min" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source> <source>The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target> <target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">187</context> <context context-type="linenumber">187</context>
@ -8550,7 +8550,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.true" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEmergingMarkets.true" datatype="html">
<source>The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="translated">La contribución a los mercados emergentes de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target> <target state="new">The Emerging Markets contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">191</context> <context context-type="linenumber">191</context>
@ -8558,7 +8558,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html">
<source>Europe</source> <source>Europe</source>
<target state="translated">Europa</target> <target state="new">Europe</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
@ -8566,7 +8566,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.false.max" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope.false.max" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source> <source>The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</source>
<target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) supera el ${thresholdMax}%</target> <target state="new">The Europe market contribution of your current investment (${valueRatio}%) exceeds ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">197</context> <context context-type="linenumber">197</context>
@ -8574,7 +8574,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.false.min" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope.false.min" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source> <source>The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</source>
<target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) es inferior al ${thresholdMin}%</target> <target state="new">The Europe market contribution of your current investment (${valueRatio}%) is below ${thresholdMin}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">201</context>
@ -8582,7 +8582,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope.true" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope.true" datatype="html">
<source>The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source> <source>The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</source>
<target state="translated">La contribución al mercado europeo de tu inversión actual (${valueRatio}%) está dentro del rango de ${thresholdMin}% y ${thresholdMax}%</target> <target state="new">The Europe market contribution of your current investment (${valueRatio}%) is within the range of ${thresholdMin}% and ${thresholdMax}%</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">205</context> <context context-type="linenumber">205</context>
@ -8694,7 +8694,7 @@
</trans-unit> </trans-unit>
<trans-unit id="339860602695747533" datatype="html"> <trans-unit id="339860602695747533" datatype="html">
<source>Registration Date</source> <source>Registration Date</source>
<target state="translated">Fecha de registro</target> <target state="new">Registration Date</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context> <context context-type="sourcefile">apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html</context>
<context context-type="linenumber">26</context> <context context-type="linenumber">26</context>

1
libs/common/src/lib/interfaces/portfolio-summary.interface.ts

@ -9,6 +9,7 @@ export interface PortfolioSummary extends PortfolioPerformance {
committedFunds: number; committedFunds: number;
dateOfFirstActivity: Date; dateOfFirstActivity: Date;
dividendInBaseCurrency: number; dividendInBaseCurrency: number;
dividendYieldTrailingTwelveMonths: number;
emergencyFund: { emergencyFund: {
assets: number; assets: number;
cash: number; cash: number;

2
libs/common/src/lib/models/portfolio-snapshot.ts

@ -17,6 +17,8 @@ export class PortfolioSnapshot {
@Type(() => Big) @Type(() => Big)
currentValueInBaseCurrency: Big; currentValueInBaseCurrency: Big;
dividendYieldTrailingTwelveMonths: number;
errors: AssetProfileIdentifier[]; errors: AssetProfileIdentifier[];
hasErrors: boolean; hasErrors: boolean;

2
libs/common/src/lib/models/timeline-position.ts

@ -27,6 +27,8 @@ export class TimelinePosition {
@Type(() => Big) @Type(() => Big)
dividendInBaseCurrency: Big; dividendInBaseCurrency: Big;
dividendYieldTrailingTwelveMonths: number;
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })
@Type(() => Big) @Type(() => Big)
fee: Big; fee: Big;

Loading…
Cancel
Save