Browse Source

Task/add asset profile to portfolio position interface (#6473)

* Add asset profile

* Update changelog
pull/6493/head
Thomas Kaul 3 days ago
committed by GitHub
parent
commit
b7d976d2ff
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 1
      apps/api/src/app/endpoints/public/public.controller.ts
  3. 31
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 1
      apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts
  5. 45
      libs/common/src/lib/interfaces/portfolio-position.interface.ts
  6. 15
      libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts
  7. 155
      libs/ui/src/lib/mocks/holdings.ts

2
CHANGELOG.md

@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Included asset profile data in the endpoint `GET api/v1/portfolio/holdings`
- Included asset profile data in the holdings of the public page
- Reused the value component in the platform management of the admin control panel
- Reused the value component in the tag management of the admin control panel
- Upgraded `jsonpath` from version `1.1.1` to `1.2.1`

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

@ -167,6 +167,7 @@ export class PublicController {
allocationInPercentage:
portfolioPosition.valueInBaseCurrency / totalValue,
assetClass: hasDetails ? portfolioPosition.assetClass : undefined,
assetProfile: hasDetails ? portfolioPosition.assetProfile : undefined,
countries: hasDetails ? portfolioPosition.countries : [],
currency: hasDetails ? portfolioPosition.currency : undefined,
dataSource: portfolioPosition.dataSource,

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

@ -623,6 +623,27 @@ export class PortfolioService {
? 0
: valueInBaseCurrency.div(filteredValueInBaseCurrency).toNumber(),
assetClass: assetProfile.assetClass,
assetProfile: {
assetClass: assetProfile.assetClass,
assetSubClass: assetProfile.assetSubClass,
countries: assetProfile.countries,
dataSource: assetProfile.dataSource,
holdings: assetProfile.holdings.map(
({ allocationInPercentage, name }) => {
return {
allocationInPercentage,
name,
valueInBaseCurrency: valueInBaseCurrency
.mul(allocationInPercentage)
.toNumber()
};
}
),
name: assetProfile.name,
sectors: assetProfile.sectors,
symbol: assetProfile.symbol,
url: assetProfile.url
},
assetSubClass: assetProfile.assetSubClass,
countries: assetProfile.countries,
dataSource: assetProfile.dataSource,
@ -1672,6 +1693,16 @@ export class PortfolioService {
allocationInPercentage: 0,
assetClass: AssetClass.LIQUIDITY,
assetSubClass: AssetSubClass.CASH,
assetProfile: {
assetClass: AssetClass.LIQUIDITY,
assetSubClass: AssetSubClass.CASH,
countries: [],
dataSource: undefined,
holdings: [],
name: currency,
sectors: [],
symbol: currency
},
countries: [],
dataSource: undefined,
dateOfFirstActivity: undefined,

1
apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts

@ -68,6 +68,7 @@ export class TransformDataSourceInResponseInterceptor<
'errors[*].dataSource',
'fearAndGreedIndex.CRYPTOCURRENCIES.dataSource',
'fearAndGreedIndex.STOCKS.dataSource',
'holdings[*].assetProfile.dataSource',
'holdings[*].dataSource',
'items[*].dataSource',
'SymbolProfile.dataSource',

45
libs/common/src/lib/interfaces/portfolio-position.interface.ts

@ -3,19 +3,50 @@ import { Market, MarketAdvanced } from '@ghostfolio/common/types';
import { AssetClass, AssetSubClass, DataSource, Tag } from '@prisma/client';
import { Country } from './country.interface';
import { EnhancedSymbolProfile } from './enhanced-symbol-profile.interface';
import { Holding } from './holding.interface';
import { Sector } from './sector.interface';
export interface PortfolioPosition {
activitiesCount: number;
allocationInPercentage: number;
/** @deprecated */
assetClass?: AssetClass;
/** @deprecated */
assetClassLabel?: string;
assetProfile: Pick<
EnhancedSymbolProfile,
| 'assetClass'
| 'assetSubClass'
| 'countries'
| 'dataSource'
| 'holdings'
| 'name'
| 'sectors'
| 'symbol'
| 'url'
> & {
assetClassLabel?: string;
assetSubClassLabel?: string;
};
/** @deprecated */
assetSubClass?: AssetSubClass;
/** @deprecated */
assetSubClassLabel?: string;
/** @deprecated */
countries: Country[];
currency: string;
/** @deprecated */
dataSource: DataSource;
dateOfFirstActivity: Date;
dividend: number;
exchange?: string;
@ -23,24 +54,38 @@ export interface PortfolioPosition {
grossPerformancePercent: number;
grossPerformancePercentWithCurrencyEffect: number;
grossPerformanceWithCurrencyEffect: number;
/** @deprecated */
holdings: Holding[];
investment: number;
marketChange?: number;
marketChangePercent?: number;
marketPrice: number;
markets?: { [key in Market]: number };
marketsAdvanced?: { [key in MarketAdvanced]: number };
/** @deprecated */
name: string;
netPerformance: number;
netPerformancePercent: number;
netPerformancePercentWithCurrencyEffect: number;
netPerformanceWithCurrencyEffect: number;
quantity: number;
/** @deprecated */
sectors: Sector[];
/** @deprecated */
symbol: string;
tags?: Tag[];
type?: string;
/** @deprecated */
url?: string;
valueInBaseCurrency?: number;
valueInPercentage?: number;
}

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

@ -14,16 +14,31 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
[symbol: string]: Pick<
PortfolioPosition,
| 'allocationInPercentage'
/** @deprecated */
| 'assetClass'
| 'assetProfile'
/** @deprecated */
| 'countries'
| 'currency'
/** @deprecated */
| 'dataSource'
| 'dateOfFirstActivity'
| 'markets'
/** @deprecated */
| 'name'
| 'netPerformancePercentWithCurrencyEffect'
/** @deprecated */
| 'sectors'
/** @deprecated */
| 'symbol'
/** @deprecated */
| 'url'
| 'valueInBaseCurrency'
| 'valueInPercentage'

155
libs/ui/src/lib/mocks/holdings.ts

@ -6,14 +6,35 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.042990776363386086,
assetClass: 'EQUITY',
assetClassLabel: 'Equity',
assetProfile: {
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
countries: [
{
code: 'US',
continent: 'North America',
name: 'United States',
weight: 1
}
],
dataSource: 'YAHOO',
holdings: [],
sectors: [
{
name: 'Technology',
weight: 1
}
],
symbol: 'AAPL'
},
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
countries: [
{
code: 'US',
weight: 1,
continent: 'North America',
name: 'United States'
name: 'United States',
weight: 1
}
],
currency: 'USD',
@ -49,14 +70,35 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.02377401948293552,
assetClass: 'EQUITY',
assetClassLabel: 'Equity',
assetProfile: {
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
countries: [
{
code: 'DE',
continent: 'Europe',
name: 'Germany',
weight: 1
}
],
dataSource: 'YAHOO',
holdings: [],
sectors: [
{
name: 'Financial Services',
weight: 1
}
],
symbol: 'ALV.DE'
},
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
countries: [
{
code: 'DE',
weight: 1,
continent: 'Europe',
name: 'Germany'
name: 'Germany',
weight: 1
}
],
currency: 'EUR',
@ -92,14 +134,35 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.08038536990007467,
assetClass: 'EQUITY',
assetClassLabel: 'Equity',
assetProfile: {
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
countries: [
{
code: 'US',
continent: 'North America',
name: 'United States',
weight: 1
}
],
dataSource: 'YAHOO',
holdings: [],
sectors: [
{
name: 'Consumer Discretionary',
weight: 1
}
],
symbol: 'AMZN'
},
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
countries: [
{
code: 'US',
weight: 1,
continent: 'North America',
name: 'United States'
name: 'United States',
weight: 1
}
],
currency: 'USD',
@ -135,6 +198,15 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.19216416482928922,
assetClass: 'LIQUIDITY',
assetClassLabel: 'Liquidity',
assetProfile: {
assetClass: 'LIQUIDITY',
assetSubClass: 'CASH',
countries: [],
dataSource: 'COINGECKO',
holdings: [],
sectors: [],
symbol: 'bitcoin'
},
assetSubClass: 'CRYPTOCURRENCY',
assetSubClassLabel: 'Cryptocurrency',
countries: [],
@ -166,14 +238,35 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.04307127421937313,
assetClass: 'EQUITY',
assetClassLabel: 'Equity',
assetProfile: {
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
countries: [
{
code: 'US',
continent: 'North America',
name: 'United States',
weight: 1
}
],
dataSource: 'YAHOO',
holdings: [],
sectors: [
{
name: 'Technology',
weight: 1
}
],
symbol: 'MSFT'
},
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
countries: [
{
code: 'US',
weight: 1,
continent: 'North America',
name: 'United States'
name: 'United States',
weight: 1
}
],
currency: 'USD',
@ -209,14 +302,35 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.18762679306394897,
assetClass: 'EQUITY',
assetClassLabel: 'Equity',
assetProfile: {
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
countries: [
{
code: 'US',
continent: 'North America',
name: 'United States',
weight: 1
}
],
dataSource: 'YAHOO',
holdings: [],
sectors: [
{
name: 'Consumer Discretionary',
weight: 1
}
],
symbol: 'TSLA'
},
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
countries: [
{
code: 'US',
weight: 1,
continent: 'North America',
name: 'United States'
name: 'United States',
weight: 1
}
],
currency: 'USD',
@ -252,6 +366,27 @@ export const holdings: PortfolioPosition[] = [
allocationInPercentage: 0.053051250766657634,
assetClass: 'EQUITY',
assetClassLabel: 'Equity',
assetProfile: {
assetClass: 'EQUITY',
assetSubClass: 'ETF',
countries: [
{
code: 'US',
continent: 'North America',
name: 'United States',
weight: 1
}
],
dataSource: 'YAHOO',
holdings: [],
sectors: [
{
name: 'Equity',
weight: 1
}
],
symbol: 'VTI'
},
assetSubClass: 'ETF',
assetSubClassLabel: 'ETF',
countries: [

Loading…
Cancel
Save