@ -8,11 +8,12 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import { MAX_TOP_HOLDINGS , UNKNOWN_KEY } from '@ghostfolio/common/config' ;
import {
canOpenHoldingDetail ,
convertValuesToPercentagesOfTotal ,
getAssetProfileIdentifier ,
getCountryName
getCountryName ,
isCashPosition
} from '@ghostfolio/common/helper' ;
import {
AssetProfileIdentifier ,
HoldingWithParents ,
PortfolioDetails ,
PortfolioPosition ,
@ -22,7 +23,10 @@ import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { hasScope , scopes } from '@ghostfolio/common/scopes' ;
import { MarketAdvanced } from '@ghostfolio/common/types' ;
import { translate } from '@ghostfolio/ui/i18n' ;
import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart' ;
import {
GfPortfolioProportionChartComponent ,
PortfolioProportionChartClickEvent
} from '@ghostfolio/ui/portfolio-proportion-chart' ;
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator' ;
import { DataService } from '@ghostfolio/ui/services' ;
import { GfTopHoldingsComponent } from '@ghostfolio/ui/top-holdings' ;
@ -206,7 +210,9 @@ export class GfAllocationsPageComponent implements OnInit {
this . initialize ( ) ;
}
protected onAccountChartClicked ( { accountId } : { accountId : string } ) {
protected onAccountChartClicked ( event : PortfolioProportionChartClickEvent ) {
const accountId = 'accountId' in event ? event.accountId : undefined ;
if ( accountId && accountId !== UNKNOWN_KEY ) {
void this . router . navigate ( [ ] , {
queryParams : { accountId , accountDetailDialog : true }
@ -214,10 +220,9 @@ export class GfAllocationsPageComponent implements OnInit {
}
}
protected onSymbolChartClicked ( {
dataSource ,
symbol
} : AssetProfileIdentifier ) {
protected onSymbolChartClicked ( event : PortfolioProportionChartClickEvent ) {
const { dataSource , symbol } = 'symbol' in event ? event : { } ;
if ( dataSource && symbol ) {
void this . router . navigate ( [ ] , {
queryParams : { dataSource , symbol , holdingDetailDialog : true }
@ -267,6 +272,20 @@ export class GfAllocationsPageComponent implements OnInit {
return UNKNOWN_KEY ;
}
private extractValue ( {
valueInBaseCurrency ,
valueInPercentage
} : {
valueInBaseCurrency? : PortfolioPosition [ 'valueInBaseCurrency' ] ;
valueInPercentage? : PortfolioPosition [ 'valueInPercentage' ] ;
} ) {
return (
( isNumber ( valueInBaseCurrency )
? valueInBaseCurrency
: valueInPercentage ) ? ? 0
) ;
}
private fetchPortfolioDetails() {
return this . dataService . fetchPortfolioDetails ( {
filters : this.userService.getFilters ( ) ,
@ -370,6 +389,8 @@ export class GfAllocationsPageComponent implements OnInit {
} ;
}
let totalValueExcludingCashPositions = 0 ;
for ( const position of this . portfolioDetails . holdings ) {
const assetProfileIdentifier = getAssetProfileIdentifier (
position . assetProfile
@ -394,111 +415,104 @@ export class GfAllocationsPageComponent implements OnInit {
: ( position . valueInBaseCurrency ? ? 0 )
} ;
// 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 ;
const value =
( isNumber ( position . valueInBaseCurrency )
? position . valueInBaseCurrency
: position . valueInPercentage ) ? ? 0 ;
if ( ! isCashPosition ( position . assetProfile ) ) {
// Prepare analysis data by continents, countries, holdings and sectors
// except for cash
totalValueExcludingCashPositions += this . extractValue ( position ) ;
if ( position . assetProfile . countries . length > 0 ) {
for ( const country of position . assetProfile . countries ) {
const { code , continent , weight } = country ;
const value = this . extractValue ( position ) ;
const continentData = this . continents [ continent ] ;
if ( continentData ) {
continentData . value += weight * value ;
} else {
this . continents [ continent ] = {
name : translate ( continent ) ,
value : weight * value
} ;
}
const countryData = this . countries [ code ] ;
if ( countryData ) {
countryData . value += weight * value ;
} else {
this . countries [ code ] = {
name : getCountryName ( { code } ) ,
value : weight * value
} ;
}
}
} else {
const value = this . extractValue ( position ) ;
const continentData = this . continents [ continent ] ;
const continentData = this . continents [ UNKNOWN_KEY ] ;
if ( continentData ) {
continentData . value += weight * value ;
} else {
this . continents [ continent ] = {
name : translate ( continent ) ,
value : weight * value
} ;
continentData . value += value ;
}
const countryData = this . countries [ code ] ;
const countryData = this . countries [ UNKNOWN_KEY ] ;
if ( countryData ) {
countryData . value += weight * value ;
} else {
this . countries [ code ] = {
name : getCountryName ( { code } ) ,
value : weight * value
} ;
countryData . value += value ;
}
}
} else {
const value =
( isNumber ( position . valueInBaseCurrency )
? position . valueInBaseCurrency
: position . valueInPercentage ) ? ? 0 ;
const continentData = this . continents [ UNKNOWN_KEY ] ;
if ( continentData ) {
continentData . value += value ;
}
const countryData = this . countries [ UNKNOWN_KEY ] ;
if ( countryData ) {
countryData . value += value ;
}
}
if ( position . assetProfile . holdings . length > 0 ) {
for ( const {
allocationInPercentage ,
name ,
valueInBaseCurrency
} of position . assetProfile . holdings ) {
const normalizedAssetName = this . normalizeAssetName ( name ) ;
const value = isNumber ( valueInBaseCurrency )
? valueInBaseCurrency
: allocationInPercentage * ( position . valueInPercentage ? ? 0 ) ;
const holdingData = this . topHoldingsMap [ normalizedAssetName ] ;
if ( holdingData ) {
holdingData . value += value ;
} else {
this . topHoldingsMap [ normalizedAssetName ] = {
name ,
value
} ;
if ( position . assetProfile . holdings . length > 0 ) {
for ( const {
allocationInPercentage ,
name ,
valueInBaseCurrency
} of position . assetProfile . holdings ) {
const normalizedAssetName = this . normalizeAssetName ( name ) ;
const value = isNumber ( valueInBaseCurrency )
? valueInBaseCurrency
: allocationInPercentage * ( position . valueInPercentage ? ? 0 ) ;
const holdingData = this . topHoldingsMap [ normalizedAssetName ] ;
if ( holdingData ) {
holdingData . value += value ;
} else {
this . topHoldingsMap [ normalizedAssetName ] = {
name ,
value
} ;
}
}
}
}
if ( position . assetProfile . sectors . length > 0 ) {
for ( const sector of position . assetProfile . sectors ) {
const { name , weight } = sector ;
const value =
( isNumber ( position . valueInBaseCurrency )
? position . valueInBaseCurrency
: position . valueInPercentage ) ? ? 0 ;
if ( position . assetProfile . sectors . length > 0 ) {
for ( const sector of position . assetProfile . sectors ) {
const { name , weight } = sector ;
const value = this . extractValue ( position ) ;
const sectorData = this . sectors [ name ] ;
if ( sectorData ) {
sectorData . value += weight * value ;
} else {
this . sectors [ name ] = {
name : translate ( name ) ,
value : weight * value
} ;
}
}
} else {
const value = this . extractValue ( position ) ;
const sectorData = this . sectors [ name ] ;
const sectorData = this . sectors [ UNKNOWN_KEY ] ;
if ( sectorData ) {
sectorData . value += weight * value ;
} else {
this . sectors [ name ] = {
name : translate ( name ) ,
value : weight * value
} ;
sectorData . value += value ;
}
}
} else {
const value =
( isNumber ( position . valueInBaseCurrency )
? position . valueInBaseCurrency
: position . valueInPercentage ) ? ? 0 ;
const sectorData = this . sectors [ UNKNOWN_KEY ] ;
if ( sectorData ) {
sectorData . value += value ;
}
}
if ( this . holdings [ assetProfileIdentifier ] . assetSubClass === 'ETF' ) {
@ -507,10 +521,7 @@ export class GfAllocationsPageComponent implements OnInit {
const symbol = position . assetProfile . symbol ;
const value =
( isNumber ( position . valueInBaseCurrency )
? position . valueInBaseCurrency
: position . valueInPercentage ) ? ? 0 ;
const value = this . extractValue ( position ) ;
const symbolData = this . symbols [ symbol ] ;
@ -530,6 +541,17 @@ export class GfAllocationsPageComponent implements OnInit {
}
}
if ( this . showValuesInPercentage ( ) ) {
// The values are percentages of the whole portfolio, but the analysis
// data does not contain the cash positions
for ( const values of [ this . continents , this . countries , this . sectors ] ) {
convertValuesToPercentagesOfTotal ( {
values ,
total : totalValueExcludingCashPositions
} ) ;
}
}
this . markets = this . portfolioDetails . markets ;
if ( this . portfolioDetails . marketsAdvanced ) {