Feature/optimize calculation of allocations by market (#3249)

* Optimize calculation of allocations by market

* Update changelog
pull/3252/head
Thomas Kaul 2 months ago committed by GitHub
parent b51255a543
commit 719bbe156e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Optimized the calculation of allocations by market
- Improved the url validation in the create and update platform endpoint
- Improved the language localization for German (`de`)

@ -78,9 +78,11 @@ export class PortfolioController {
@Query('assetClasses') filterByAssetClasses?: string,
@Query('range') dateRange: DateRange = 'max',
@Query('tags') filterByTags?: string,
@Query('withLiabilities') withLiabilitiesParam = 'false'
@Query('withLiabilities') withLiabilitiesParam = 'false',
@Query('withMarkets') withMarketsParam = 'false'
): Promise<PortfolioDetails & { hasError: boolean }> {
const withLiabilities = withLiabilitiesParam === 'true';
const withMarkets = withMarketsParam === 'true';
let hasDetails = true;
let hasError = false;
@ -106,6 +108,7 @@ export class PortfolioController {
filters,
impersonationId,
withLiabilities,
withMarkets,
userId: this.request.user.id,
withSummary: true
});

@ -63,7 +63,8 @@ import {
DataSource,
Order,
Platform,
Prisma
Prisma,
SymbolProfile
} from '@prisma/client';
import { Big } from 'big.js';
import { isUUID } from 'class-validator';
@ -337,6 +338,7 @@ export class PortfolioService {
userId,
withExcludedAccounts = false,
withLiabilities = false,
withMarkets = false,
withSummary = false
}: {
dateRange?: DateRange;
@ -345,6 +347,7 @@ export class PortfolioService {
userId: string;
withExcludedAccounts?: boolean;
withLiabilities?: boolean;
withMarkets?: boolean;
withSummary?: boolean;
}): Promise<PortfolioDetails & { hasErrors: boolean }> {
userId = await this.getUserId(impersonationId, userId);
@ -484,77 +487,17 @@ export class PortfolioService {
}
}
const symbolProfile = symbolProfileMap[symbol];
const assetProfile = symbolProfileMap[symbol];
const dataProviderResponse = dataProviderResponses[symbol];
const markets: PortfolioPosition['markets'] = {
[UNKNOWN_KEY]: 0,
developedMarkets: 0,
emergingMarkets: 0,
otherMarkets: 0
};
const marketsAdvanced: PortfolioPosition['marketsAdvanced'] = {
[UNKNOWN_KEY]: 0,
asiaPacific: 0,
emergingMarkets: 0,
europe: 0,
japan: 0,
northAmerica: 0,
otherMarkets: 0
};
if (symbolProfile.countries.length > 0) {
for (const country of symbolProfile.countries) {
if (developedMarkets.includes(country.code)) {
markets.developedMarkets = new Big(markets.developedMarkets)
.plus(country.weight)
.toNumber();
} else if (emergingMarkets.includes(country.code)) {
markets.emergingMarkets = new Big(markets.emergingMarkets)
.plus(country.weight)
.toNumber();
} else {
markets.otherMarkets = new Big(markets.otherMarkets)
.plus(country.weight)
.toNumber();
}
let markets: PortfolioPosition['markets'];
let marketsAdvanced: PortfolioPosition['marketsAdvanced'];
if (country.code === 'JP') {
marketsAdvanced.japan = new Big(marketsAdvanced.japan)
.plus(country.weight)
.toNumber();
} else if (country.code === 'CA' || country.code === 'US') {
marketsAdvanced.northAmerica = new Big(marketsAdvanced.northAmerica)
.plus(country.weight)
.toNumber();
} else if (asiaPacificMarkets.includes(country.code)) {
marketsAdvanced.asiaPacific = new Big(marketsAdvanced.asiaPacific)
.plus(country.weight)
.toNumber();
} else if (emergingMarkets.includes(country.code)) {
marketsAdvanced.emergingMarkets = new Big(
marketsAdvanced.emergingMarkets
)
.plus(country.weight)
.toNumber();
} else if (europeMarkets.includes(country.code)) {
marketsAdvanced.europe = new Big(marketsAdvanced.europe)
.plus(country.weight)
.toNumber();
} else {
marketsAdvanced.otherMarkets = new Big(marketsAdvanced.otherMarkets)
.plus(country.weight)
.toNumber();
}
}
} else {
markets[UNKNOWN_KEY] = new Big(markets[UNKNOWN_KEY])
.plus(valueInBaseCurrency)
.toNumber();
marketsAdvanced[UNKNOWN_KEY] = new Big(marketsAdvanced[UNKNOWN_KEY])
.plus(valueInBaseCurrency)
.toNumber();
if (withMarkets) {
({ markets, marketsAdvanced } = this.getMarkets({
assetProfile,
valueInBaseCurrency
}));
}
holdings[symbol] = {
@ -568,10 +511,10 @@ export class PortfolioService {
allocationInPercentage: filteredValueInBaseCurrency.eq(0)
? 0
: valueInBaseCurrency.div(filteredValueInBaseCurrency).toNumber(),
assetClass: symbolProfile.assetClass,
assetSubClass: symbolProfile.assetSubClass,
countries: symbolProfile.countries,
dataSource: symbolProfile.dataSource,
assetClass: assetProfile.assetClass,
assetSubClass: assetProfile.assetSubClass,
countries: assetProfile.countries,
dataSource: assetProfile.dataSource,
dateOfFirstActivity: parseDate(firstBuyDate),
dividend: dividend?.toNumber() ?? 0,
grossPerformance: grossPerformance?.toNumber() ?? 0,
@ -582,7 +525,7 @@ export class PortfolioService {
grossPerformanceWithCurrencyEffect?.toNumber() ?? 0,
investment: investment.toNumber(),
marketState: dataProviderResponse?.marketState ?? 'delayed',
name: symbolProfile.name,
name: assetProfile.name,
netPerformance: netPerformance?.toNumber() ?? 0,
netPerformancePercent: netPerformancePercentage?.toNumber() ?? 0,
netPerformancePercentWithCurrencyEffect:
@ -590,8 +533,8 @@ export class PortfolioService {
netPerformanceWithCurrencyEffect:
netPerformanceWithCurrencyEffect?.toNumber() ?? 0,
quantity: quantity.toNumber(),
sectors: symbolProfile.sectors,
url: symbolProfile.url,
sectors: assetProfile.sectors,
url: assetProfile.url,
valueInBaseCurrency: valueInBaseCurrency.toNumber()
};
}
@ -1630,6 +1573,86 @@ export class PortfolioService {
};
}
private getMarkets({
assetProfile,
valueInBaseCurrency
}: {
assetProfile: EnhancedSymbolProfile;
valueInBaseCurrency: Big;
}) {
const markets = {
[UNKNOWN_KEY]: 0,
developedMarkets: 0,
emergingMarkets: 0,
otherMarkets: 0
};
const marketsAdvanced = {
[UNKNOWN_KEY]: 0,
asiaPacific: 0,
emergingMarkets: 0,
europe: 0,
japan: 0,
northAmerica: 0,
otherMarkets: 0
};
if (assetProfile.countries.length > 0) {
for (const country of assetProfile.countries) {
if (developedMarkets.includes(country.code)) {
markets.developedMarkets = new Big(markets.developedMarkets)
.plus(country.weight)
.toNumber();
} else if (emergingMarkets.includes(country.code)) {
markets.emergingMarkets = new Big(markets.emergingMarkets)
.plus(country.weight)
.toNumber();
} else {
markets.otherMarkets = new Big(markets.otherMarkets)
.plus(country.weight)
.toNumber();
}
if (country.code === 'JP') {
marketsAdvanced.japan = new Big(marketsAdvanced.japan)
.plus(country.weight)
.toNumber();
} else if (country.code === 'CA' || country.code === 'US') {
marketsAdvanced.northAmerica = new Big(marketsAdvanced.northAmerica)
.plus(country.weight)
.toNumber();
} else if (asiaPacificMarkets.includes(country.code)) {
marketsAdvanced.asiaPacific = new Big(marketsAdvanced.asiaPacific)
.plus(country.weight)
.toNumber();
} else if (emergingMarkets.includes(country.code)) {
marketsAdvanced.emergingMarkets = new Big(
marketsAdvanced.emergingMarkets
)
.plus(country.weight)
.toNumber();
} else if (europeMarkets.includes(country.code)) {
marketsAdvanced.europe = new Big(marketsAdvanced.europe)
.plus(country.weight)
.toNumber();
} else {
marketsAdvanced.otherMarkets = new Big(marketsAdvanced.otherMarkets)
.plus(country.weight)
.toNumber();
}
}
} else {
markets[UNKNOWN_KEY] = new Big(markets[UNKNOWN_KEY])
.plus(valueInBaseCurrency)
.toNumber();
marketsAdvanced[UNKNOWN_KEY] = new Big(marketsAdvanced[UNKNOWN_KEY])
.plus(valueInBaseCurrency)
.toNumber();
}
return { markets, marketsAdvanced };
}
private getStreaks({
investments,
savingsRate

@ -205,7 +205,8 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
private fetchPortfolioDetails() {
return this.dataService.fetchPortfolioDetails({
filters: this.userService.getFilters()
filters: this.userService.getFilters(),
withMarkets: true
});
}

@ -411,10 +411,12 @@ export class DataService {
public fetchPortfolioDetails({
filters,
withLiabilities = false
withLiabilities = false,
withMarkets = false
}: {
filters?: Filter[];
withLiabilities?: boolean;
withMarkets?: boolean;
} = {}): Observable<PortfolioDetails> {
let params = this.buildFiltersAsQueryParams({ filters });
@ -422,6 +424,10 @@ export class DataService {
params = params.append('withLiabilities', withLiabilities);
}
if (withMarkets) {
params = params.append('withMarkets', withMarkets);
}
return this.http
.get<any>('/api/v1/portfolio/details', {
params

Loading…
Cancel
Save