Feature/optimize calculation of allocations by market (#3249)
* Optimize calculation of allocations by market * Update changelog
This commit is contained in:
parent
b51255a543
commit
719bbe156e
@ -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
|
||||
};
|
||||
let markets: PortfolioPosition['markets'];
|
||||
let marketsAdvanced: PortfolioPosition['marketsAdvanced'];
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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…
x
Reference in New Issue
Block a user