Browse Source

Improve unknown bucket grouping

pull/7011/head
Thomas Kaul 3 months ago
parent
commit
00d47e539a
  1. 98
      apps/api/src/app/portfolio/portfolio.service.ts
  2. 214
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

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

@ -1453,31 +1453,29 @@ export class PortfolioService {
for (const [, position] of Object.entries(holdings)) { for (const [, position] of Object.entries(holdings)) {
const value = position.valueInBaseCurrency; const value = position.valueInBaseCurrency;
if (position.assetProfile.assetClass !== AssetClass.LIQUIDITY) { if (position.assetProfile.countries.length > 0) {
if (position.assetProfile.countries.length > 0) { markets.developedMarkets.valueInBaseCurrency +=
markets.developedMarkets.valueInBaseCurrency += position.markets.developedMarkets * value;
position.markets.developedMarkets * value; markets.emergingMarkets.valueInBaseCurrency +=
markets.emergingMarkets.valueInBaseCurrency += position.markets.emergingMarkets * value;
position.markets.emergingMarkets * value; markets.otherMarkets.valueInBaseCurrency +=
markets.otherMarkets.valueInBaseCurrency += position.markets.otherMarkets * value;
position.markets.otherMarkets * value;
marketsAdvanced.asiaPacific.valueInBaseCurrency +=
marketsAdvanced.asiaPacific.valueInBaseCurrency += position.marketsAdvanced.asiaPacific * value;
position.marketsAdvanced.asiaPacific * value; marketsAdvanced.emergingMarkets.valueInBaseCurrency +=
marketsAdvanced.emergingMarkets.valueInBaseCurrency += position.marketsAdvanced.emergingMarkets * value;
position.marketsAdvanced.emergingMarkets * value; marketsAdvanced.europe.valueInBaseCurrency +=
marketsAdvanced.europe.valueInBaseCurrency += position.marketsAdvanced.europe * value;
position.marketsAdvanced.europe * value; marketsAdvanced.japan.valueInBaseCurrency +=
marketsAdvanced.japan.valueInBaseCurrency += position.marketsAdvanced.japan * value;
position.marketsAdvanced.japan * value; marketsAdvanced.northAmerica.valueInBaseCurrency +=
marketsAdvanced.northAmerica.valueInBaseCurrency += position.marketsAdvanced.northAmerica * value;
position.marketsAdvanced.northAmerica * value; marketsAdvanced.otherMarkets.valueInBaseCurrency +=
marketsAdvanced.otherMarkets.valueInBaseCurrency += position.marketsAdvanced.otherMarkets * value;
position.marketsAdvanced.otherMarkets * value; } else {
} else { markets[UNKNOWN_KEY].valueInBaseCurrency += value;
markets[UNKNOWN_KEY].valueInBaseCurrency += value; marketsAdvanced[UNKNOWN_KEY].valueInBaseCurrency += value;
marketsAdvanced[UNKNOWN_KEY].valueInBaseCurrency += value;
}
} }
} }
@ -2163,40 +2161,44 @@ export class PortfolioService {
return withExcludedAccounts || account.isExcluded === false; return withExcludedAccounts || account.isExcluded === false;
}); });
for (const account of currentAccounts) { // Iterate over the accounts plus a null entry to group activities without
// an account into the unknown bucket
for (const account of [...currentAccounts, null]) {
const ordersByAccount = activities.filter(({ accountId }) => { const ordersByAccount = activities.filter(({ accountId }) => {
return accountId === account.id; return account ? accountId === account.id : !accountId;
}); });
accounts[account.id] = { if (account) {
balance: account.balance, accounts[account.id] = {
currency: account.currency,
name: account.name,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
)
};
if (platforms[account.platformId || UNKNOWN_KEY]?.valueInBaseCurrency) {
platforms[account.platformId || UNKNOWN_KEY].valueInBaseCurrency +=
this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
);
} else {
platforms[account.platformId || UNKNOWN_KEY] = {
balance: account.balance, balance: account.balance,
currency: account.currency, currency: account.currency,
name: account.platform?.name, name: account.name,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency( valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
account.balance, account.balance,
account.currency, account.currency,
userCurrency userCurrency
) )
}; };
if (platforms[account.platformId || UNKNOWN_KEY]?.valueInBaseCurrency) {
platforms[account.platformId || UNKNOWN_KEY].valueInBaseCurrency +=
this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
);
} else {
platforms[account.platformId || UNKNOWN_KEY] = {
balance: account.balance,
currency: account.currency,
name: account.platform?.name,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
)
};
}
} }
for (const { for (const {

214
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -201,6 +201,26 @@ export class GfAllocationsPageComponent implements OnInit {
} }
} }
private extractCurrency({
assetClass,
assetSubClass,
currency
}: {
assetClass: PortfolioPosition['assetProfile']['assetClass'];
assetSubClass: PortfolioPosition['assetProfile']['assetSubClass'];
currency?: PortfolioPosition['assetProfile']['currency'];
}) {
if (
assetClass === AssetClass.COMMODITY ||
assetSubClass === AssetSubClass.CRYPTOCURRENCY
) {
// Commodities and cryptocurrencies have no meaningful currency exposure
return UNKNOWN_KEY;
}
return currency;
}
private extractEtfProvider({ private extractEtfProvider({
assetSubClass, assetSubClass,
name name
@ -339,7 +359,7 @@ export class GfAllocationsPageComponent implements OnInit {
position.assetProfile.assetSubClass || (UNKNOWN_KEY as AssetSubClass), position.assetProfile.assetSubClass || (UNKNOWN_KEY as AssetSubClass),
assetSubClassLabel: assetSubClassLabel:
position.assetProfile.assetSubClassLabel || UNKNOWN_KEY, position.assetProfile.assetSubClassLabel || UNKNOWN_KEY,
currency: position.assetProfile.currency, currency: this.extractCurrency(position.assetProfile),
etfProvider: this.extractEtfProvider({ etfProvider: this.extractEtfProvider({
assetSubClass: position.assetProfile.assetSubClass, assetSubClass: position.assetProfile.assetSubClass,
name: position.assetProfile.name name: position.assetProfile.name
@ -348,119 +368,117 @@ export class GfAllocationsPageComponent implements OnInit {
name: position.assetProfile.name name: position.assetProfile.name
}; };
if (position.assetProfile.assetClass !== AssetClass.LIQUIDITY) { // Prepare analysis data by continents, countries, holdings and sectors
// Prepare analysis data by continents, countries, holdings and sectors except for liquidity
if (position.assetProfile.countries.length > 0) {
if (position.assetProfile.countries.length > 0) { for (const country of position.assetProfile.countries) {
for (const country of position.assetProfile.countries) { const { code, continent, weight } = country;
const { code, continent, weight } = country;
if (this.continents[continent]?.value) {
if (this.continents[continent]?.value) { this.continents[continent].value +=
this.continents[continent].value += weight *
(isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency
: position.valueInPercentage);
} else {
this.continents[continent] = {
name: translate(continent),
value:
weight * weight *
(isNumber(position.valueInBaseCurrency) (isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: position.valueInPercentage); : this.portfolioDetails.holdings[symbol].valueInPercentage)
} else { };
this.continents[continent] = { }
name: translate(continent),
value: if (this.countries[code]?.value) {
weight * this.countries[code].value +=
(isNumber(position.valueInBaseCurrency) weight *
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency (isNumber(position.valueInBaseCurrency)
: this.portfolioDetails.holdings[symbol].valueInPercentage) ? position.valueInBaseCurrency
}; : position.valueInPercentage);
} } else {
this.countries[code] = {
if (this.countries[code]?.value) { name: getCountryName({
this.countries[code].value += code,
locale: this.user?.settings?.locale
}),
value:
weight * weight *
(isNumber(position.valueInBaseCurrency) (isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: position.valueInPercentage); : this.portfolioDetails.holdings[symbol].valueInPercentage)
} else { };
this.countries[code] = {
name: getCountryName({
code,
locale: this.user?.settings?.locale
}),
value:
weight *
(isNumber(position.valueInBaseCurrency)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage)
};
}
} }
} else {
this.continents[UNKNOWN_KEY].value += isNumber(
position.valueInBaseCurrency
)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage;
this.countries[UNKNOWN_KEY].value += isNumber(
position.valueInBaseCurrency
)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage;
} }
} else {
this.continents[UNKNOWN_KEY].value += isNumber(
position.valueInBaseCurrency
)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage;
this.countries[UNKNOWN_KEY].value += isNumber(
position.valueInBaseCurrency
)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage;
}
if (position.assetProfile.holdings.length > 0) { if (position.assetProfile.holdings.length > 0) {
for (const { for (const {
allocationInPercentage, allocationInPercentage,
name, name,
valueInBaseCurrency valueInBaseCurrency
} of position.assetProfile.holdings) { } of position.assetProfile.holdings) {
const normalizedAssetName = this.normalizeAssetName(name); const normalizedAssetName = this.normalizeAssetName(name);
if (this.topHoldingsMap[normalizedAssetName]?.value) { if (this.topHoldingsMap[normalizedAssetName]?.value) {
this.topHoldingsMap[normalizedAssetName].value += isNumber( this.topHoldingsMap[normalizedAssetName].value += isNumber(
valueInBaseCurrency valueInBaseCurrency
) )
? valueInBaseCurrency
: allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage;
} else {
this.topHoldingsMap[normalizedAssetName] = {
name,
value: isNumber(valueInBaseCurrency)
? valueInBaseCurrency ? valueInBaseCurrency
: allocationInPercentage * : allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage; this.portfolioDetails.holdings[symbol].valueInPercentage
} else { };
this.topHoldingsMap[normalizedAssetName] = {
name,
value: isNumber(valueInBaseCurrency)
? valueInBaseCurrency
: allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage
};
}
} }
} }
}
if (position.assetProfile.sectors.length > 0) { if (position.assetProfile.sectors.length > 0) {
for (const sector of position.assetProfile.sectors) { for (const sector of position.assetProfile.sectors) {
const { name, weight } = sector; const { name, weight } = sector;
if (this.sectors[name]?.value) { if (this.sectors[name]?.value) {
this.sectors[name].value += this.sectors[name].value +=
weight *
(isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency
: position.valueInPercentage);
} else {
this.sectors[name] = {
name: translate(name),
value:
weight * weight *
(isNumber(position.valueInBaseCurrency) (isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: position.valueInPercentage); : this.portfolioDetails.holdings[symbol].valueInPercentage)
} else { };
this.sectors[name] = {
name: translate(name),
value:
weight *
(isNumber(position.valueInBaseCurrency)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage)
};
}
} }
} else {
this.sectors[UNKNOWN_KEY].value += isNumber(
position.valueInBaseCurrency
)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage;
} }
} else {
this.sectors[UNKNOWN_KEY].value += isNumber(
position.valueInBaseCurrency
)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage;
} }
if (this.holdings[symbol].assetSubClass === 'ETF') { if (this.holdings[symbol].assetSubClass === 'ETF') {

Loading…
Cancel
Save