Browse Source

Include markets in details

pull/3869/head
Thomas Kaul 11 months ago
parent
commit
ade2e4f80a
  1. 3
      apps/api/src/app/endpoints/public/public.controller.ts
  2. 7
      apps/api/src/app/portfolio/portfolio.controller.ts
  3. 71
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 7
      libs/common/src/lib/interfaces/portfolio-details.interface.ts
  5. 8
      libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts

3
apps/api/src/app/endpoints/public/public.controller.ts

@ -57,7 +57,7 @@ export class PublicController {
} }
const [ const [
{ holdings }, { holdings, markets },
{ performance: performance1d }, { performance: performance1d },
{ performance: performanceMax }, { performance: performanceMax },
{ performance: performanceYtd } { performance: performanceYtd }
@ -80,6 +80,7 @@ export class PublicController {
hasDetails, hasDetails,
alias: access.alias, alias: access.alias,
holdings: {}, holdings: {},
markets: hasDetails ? markets : undefined,
performance: { performance: {
'1d': { '1d': {
relativeChange: relativeChange:

7
apps/api/src/app/portfolio/portfolio.controller.ts

@ -1,4 +1,3 @@
import { AccessService } from '@ghostfolio/api/app/access/access.service';
import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
@ -61,7 +60,6 @@ import { UpdateHoldingTagsDto } from './update-holding-tags.dto';
@Controller('portfolio') @Controller('portfolio')
export class PortfolioController { export class PortfolioController {
public constructor( public constructor(
private readonly accessService: AccessService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService,
private readonly impersonationService: ImpersonationService, private readonly impersonationService: ImpersonationService,
@ -97,7 +95,7 @@ export class PortfolioController {
filterByTags filterByTags
}); });
const { accounts, hasErrors, holdings, platforms, summary } = const { accounts, hasErrors, holdings, markets, platforms, summary } =
await this.portfolioService.getDetails({ await this.portfolioService.getDetails({
dateRange, dateRange,
filters, filters,
@ -216,7 +214,8 @@ export class PortfolioController {
hasError, hasError,
holdings, holdings,
platforms, platforms,
summary: portfolioSummary summary: portfolioSummary,
markets: hasDetails ? markets : undefined
}; };
} }

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

@ -45,6 +45,7 @@ import type {
AccountWithValue, AccountWithValue,
DateRange, DateRange,
GroupBy, GroupBy,
Market,
RequestWithUser, RequestWithUser,
UserWithSettings UserWithSettings
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
@ -73,7 +74,7 @@ import {
parseISO, parseISO,
set set
} from 'date-fns'; } from 'date-fns';
import { isEmpty, last, uniq } from 'lodash'; import { isEmpty, isNumber, last, uniq } from 'lodash';
import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { PortfolioCalculator } from './calculator/portfolio-calculator';
import { import {
@ -581,6 +582,17 @@ export class PortfolioService {
}; };
} }
let markets: {
[key in Market]: {
name: string;
value: number;
};
};
if (withMarkets) {
markets = this.getAggregatedMarkets(holdings);
}
let summary: PortfolioSummary; let summary: PortfolioSummary;
if (withSummary) { if (withSummary) {
@ -602,6 +614,7 @@ export class PortfolioService {
accounts, accounts,
hasErrors, hasErrors,
holdings, holdings,
markets,
platforms, platforms,
summary summary
}; };
@ -1232,6 +1245,62 @@ export class PortfolioService {
await this.orderService.assignTags({ dataSource, symbol, tags, userId }); await this.orderService.assignTags({ dataSource, symbol, tags, userId });
} }
private getAggregatedMarkets(holdings: {
[symbol: string]: PortfolioPosition;
}): {
[key in Market]: { name: string; value: number };
} {
const markets = {
[UNKNOWN_KEY]: {
name: UNKNOWN_KEY,
value: 0
},
developedMarkets: {
name: 'developedMarkets',
value: 0
},
emergingMarkets: {
name: 'emergingMarkets',
value: 0
},
otherMarkets: {
name: 'otherMarkets',
value: 0
}
};
for (const [symbol, position] of Object.entries(holdings)) {
const value = position.valueInBaseCurrency;
if (position.assetClass !== AssetClass.LIQUIDITY) {
if (position.countries.length > 0) {
markets.developedMarkets.value +=
position.markets.developedMarkets * value;
markets.emergingMarkets.value +=
position.markets.emergingMarkets * value;
markets.otherMarkets.value += position.markets.otherMarkets * value;
} else {
markets[UNKNOWN_KEY].value += value;
}
}
}
const marketsTotal =
markets.developedMarkets.value +
markets.emergingMarkets.value +
markets.otherMarkets.value +
markets[UNKNOWN_KEY].value;
markets.developedMarkets.value =
markets.developedMarkets.value / marketsTotal;
markets.emergingMarkets.value =
markets.emergingMarkets.value / marketsTotal;
markets.otherMarkets.value = markets.otherMarkets.value / marketsTotal;
markets[UNKNOWN_KEY].value = markets[UNKNOWN_KEY].value / marketsTotal;
return markets;
}
private async getCashPositions({ private async getCashPositions({
cashDetails, cashDetails,
userCurrency, userCurrency,

7
libs/common/src/lib/interfaces/portfolio-details.interface.ts

@ -2,6 +2,7 @@ import {
PortfolioPosition, PortfolioPosition,
PortfolioSummary PortfolioSummary
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { Market } from '@ghostfolio/common/types';
export interface PortfolioDetails { export interface PortfolioDetails {
accounts: { accounts: {
@ -14,6 +15,12 @@ export interface PortfolioDetails {
}; };
}; };
holdings: { [symbol: string]: PortfolioPosition }; holdings: { [symbol: string]: PortfolioPosition };
markets?: {
[key in Market]: {
name: string;
value: number;
};
};
platforms: { platforms: {
[id: string]: { [id: string]: {
balance: number; balance: number;

8
libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts

@ -1,3 +1,5 @@
import { Market } from '@ghostfolio/common/types';
import { PortfolioPosition } from '../portfolio-position.interface'; import { PortfolioPosition } from '../portfolio-position.interface';
export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
@ -22,6 +24,12 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
| 'valueInPercentage' | 'valueInPercentage'
>; >;
}; };
markets?: {
[key in Market]: {
name: string;
value: number;
};
};
} }
interface PublicPortfolioResponseV1 { interface PublicPortfolioResponseV1 {

Loading…
Cancel
Save