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)) {
const value = position.valueInBaseCurrency;
if (position.assetProfile.assetClass !== AssetClass.LIQUIDITY) {
if (position.assetProfile.countries.length > 0) {
markets.developedMarkets.valueInBaseCurrency +=
position.markets.developedMarkets * value;
markets.emergingMarkets.valueInBaseCurrency +=
position.markets.emergingMarkets * value;
markets.otherMarkets.valueInBaseCurrency +=
position.markets.otherMarkets * value;
marketsAdvanced.asiaPacific.valueInBaseCurrency +=
position.marketsAdvanced.asiaPacific * value;
marketsAdvanced.emergingMarkets.valueInBaseCurrency +=
position.marketsAdvanced.emergingMarkets * value;
marketsAdvanced.europe.valueInBaseCurrency +=
position.marketsAdvanced.europe * value;
marketsAdvanced.japan.valueInBaseCurrency +=
position.marketsAdvanced.japan * value;
marketsAdvanced.northAmerica.valueInBaseCurrency +=
position.marketsAdvanced.northAmerica * value;
marketsAdvanced.otherMarkets.valueInBaseCurrency +=
position.marketsAdvanced.otherMarkets * value;
} else {
markets[UNKNOWN_KEY].valueInBaseCurrency += value;
marketsAdvanced[UNKNOWN_KEY].valueInBaseCurrency += value;
}
if (position.assetProfile.countries.length > 0) {
markets.developedMarkets.valueInBaseCurrency +=
position.markets.developedMarkets * value;
markets.emergingMarkets.valueInBaseCurrency +=
position.markets.emergingMarkets * value;
markets.otherMarkets.valueInBaseCurrency +=
position.markets.otherMarkets * value;
marketsAdvanced.asiaPacific.valueInBaseCurrency +=
position.marketsAdvanced.asiaPacific * value;
marketsAdvanced.emergingMarkets.valueInBaseCurrency +=
position.marketsAdvanced.emergingMarkets * value;
marketsAdvanced.europe.valueInBaseCurrency +=
position.marketsAdvanced.europe * value;
marketsAdvanced.japan.valueInBaseCurrency +=
position.marketsAdvanced.japan * value;
marketsAdvanced.northAmerica.valueInBaseCurrency +=
position.marketsAdvanced.northAmerica * value;
marketsAdvanced.otherMarkets.valueInBaseCurrency +=
position.marketsAdvanced.otherMarkets * value;
} else {
markets[UNKNOWN_KEY].valueInBaseCurrency += value;
marketsAdvanced[UNKNOWN_KEY].valueInBaseCurrency += value;
}
}
@ -2163,40 +2161,44 @@ export class PortfolioService {
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 }) => {
return accountId === account.id;
return account ? accountId === account.id : !accountId;
});
accounts[account.id] = {
balance: account.balance,
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] = {
if (account) {
accounts[account.id] = {
balance: account.balance,
currency: account.currency,
name: account.platform?.name,
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,
currency: account.currency,
name: account.platform?.name,
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
)
};
}
}
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({
assetSubClass,
name
@ -339,7 +359,7 @@ export class GfAllocationsPageComponent implements OnInit {
position.assetProfile.assetSubClass || (UNKNOWN_KEY as AssetSubClass),
assetSubClassLabel:
position.assetProfile.assetSubClassLabel || UNKNOWN_KEY,
currency: position.assetProfile.currency,
currency: this.extractCurrency(position.assetProfile),
etfProvider: this.extractEtfProvider({
assetSubClass: position.assetProfile.assetSubClass,
name: position.assetProfile.name
@ -348,119 +368,117 @@ export class GfAllocationsPageComponent implements OnInit {
name: position.assetProfile.name
};
if (position.assetProfile.assetClass !== AssetClass.LIQUIDITY) {
// Prepare analysis data by continents, countries, holdings and sectors except for liquidity
if (position.assetProfile.countries.length > 0) {
for (const country of position.assetProfile.countries) {
const { code, continent, weight } = country;
if (this.continents[continent]?.value) {
this.continents[continent].value +=
// Prepare analysis data by continents, countries, holdings and sectors
if (position.assetProfile.countries.length > 0) {
for (const country of position.assetProfile.countries) {
const { code, continent, weight } = country;
if (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 *
(isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency
: position.valueInPercentage);
} else {
this.continents[continent] = {
name: translate(continent),
value:
weight *
(isNumber(position.valueInBaseCurrency)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage)
};
}
if (this.countries[code]?.value) {
this.countries[code].value +=
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage)
};
}
if (this.countries[code]?.value) {
this.countries[code].value +=
weight *
(isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency
: position.valueInPercentage);
} else {
this.countries[code] = {
name: getCountryName({
code,
locale: this.user?.settings?.locale
}),
value:
weight *
(isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency
: position.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)
};
}
? 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) {
for (const {
allocationInPercentage,
name,
valueInBaseCurrency
} of position.assetProfile.holdings) {
const normalizedAssetName = this.normalizeAssetName(name);
if (this.topHoldingsMap[normalizedAssetName]?.value) {
this.topHoldingsMap[normalizedAssetName].value += isNumber(
valueInBaseCurrency
)
if (position.assetProfile.holdings.length > 0) {
for (const {
allocationInPercentage,
name,
valueInBaseCurrency
} of position.assetProfile.holdings) {
const normalizedAssetName = this.normalizeAssetName(name);
if (this.topHoldingsMap[normalizedAssetName]?.value) {
this.topHoldingsMap[normalizedAssetName].value += isNumber(
valueInBaseCurrency
)
? valueInBaseCurrency
: allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage;
} else {
this.topHoldingsMap[normalizedAssetName] = {
name,
value: isNumber(valueInBaseCurrency)
? valueInBaseCurrency
: allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage;
} else {
this.topHoldingsMap[normalizedAssetName] = {
name,
value: isNumber(valueInBaseCurrency)
? valueInBaseCurrency
: allocationInPercentage *
this.portfolioDetails.holdings[symbol].valueInPercentage
};
}
this.portfolioDetails.holdings[symbol].valueInPercentage
};
}
}
}
if (position.assetProfile.sectors.length > 0) {
for (const sector of position.assetProfile.sectors) {
const { name, weight } = sector;
if (this.sectors[name]?.value) {
this.sectors[name].value +=
if (position.assetProfile.sectors.length > 0) {
for (const sector of position.assetProfile.sectors) {
const { name, weight } = sector;
if (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 *
(isNumber(position.valueInBaseCurrency)
? position.valueInBaseCurrency
: position.valueInPercentage);
} else {
this.sectors[name] = {
name: translate(name),
value:
weight *
(isNumber(position.valueInBaseCurrency)
? this.portfolioDetails.holdings[symbol].valueInBaseCurrency
: this.portfolioDetails.holdings[symbol].valueInPercentage)
};
}
? 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') {

Loading…
Cancel
Save