@ -24,6 +24,7 @@ import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.se
import { UNKNOWN_KEY , ghostfolioCashSymbol } from '@ghostfolio/common/config' ;
import { UNKNOWN_KEY , ghostfolioCashSymbol } from '@ghostfolio/common/config' ;
import { DATE_FORMAT , parseDate } from '@ghostfolio/common/helper' ;
import { DATE_FORMAT , parseDate } from '@ghostfolio/common/helper' ;
import {
import {
PortfolioDetails ,
PortfolioPerformance ,
PortfolioPerformance ,
PortfolioPosition ,
PortfolioPosition ,
PortfolioReport ,
PortfolioReport ,
@ -154,10 +155,7 @@ export class PortfolioService {
public async getDetails (
public async getDetails (
aImpersonationId : string ,
aImpersonationId : string ,
aDateRange : DateRange = 'max'
aDateRange : DateRange = 'max'
) : Promise < {
) : Promise < PortfolioDetails & { hasErrors : boolean } > {
details : { [ symbol : string ] : PortfolioPosition } ;
hasErrors : boolean ;
} > {
const userId = await this . getUserId ( aImpersonationId ) ;
const userId = await this . getUserId ( aImpersonationId ) ;
const userCurrency = this . request . user . Settings . currency ;
const userCurrency = this . request . user . Settings . currency ;
@ -171,7 +169,7 @@ export class PortfolioService {
} ) ;
} ) ;
if ( transactionPoints ? . length <= 0 ) {
if ( transactionPoints ? . length <= 0 ) {
return { detail s: { } , hasErrors : false } ;
return { accounts : { } , holding s: { } , hasErrors : false } ;
}
}
portfolioCalculator . setTransactionPoints ( transactionPoints ) ;
portfolioCalculator . setTransactionPoints ( transactionPoints ) ;
@ -187,7 +185,7 @@ export class PortfolioService {
userCurrency
userCurrency
) ;
) ;
const details : { [ symbol : string ] : PortfolioPosition } = { } ;
const holdings : PortfolioDetails [ 'holdings' ] = { } ;
const totalInvestment = currentPositions . totalInvestment . plus (
const totalInvestment = currentPositions . totalInvestment . plus (
cashDetails . balance
cashDetails . balance
) ;
) ;
@ -211,14 +209,12 @@ export class PortfolioService {
for ( const position of currentPositions . positions ) {
for ( const position of currentPositions . positions ) {
portfolioItemsNow [ position . symbol ] = position ;
portfolioItemsNow [ position . symbol ] = position ;
}
}
const accounts = this . getAccounts ( orders , portfolioItemsNow , userCurrency ) ;
for ( const item of currentPositions . positions ) {
for ( const item of currentPositions . positions ) {
const value = item . quantity . mul ( item . marketPrice ) ;
const value = item . quantity . mul ( item . marketPrice ) ;
const symbolProfile = symbolProfileMap [ item . symbol ] ;
const symbolProfile = symbolProfileMap [ item . symbol ] ;
const dataProviderResponse = dataProviderResponses [ item . symbol ] ;
const dataProviderResponse = dataProviderResponses [ item . symbol ] ;
details [ item . symbol ] = {
holdings [ item . symbol ] = {
accounts ,
allocationCurrent : value.div ( totalValue ) . toNumber ( ) ,
allocationCurrent : value.div ( totalValue ) . toNumber ( ) ,
allocationInvestment : item.investment.div ( totalInvestment ) . toNumber ( ) ,
allocationInvestment : item.investment.div ( totalInvestment ) . toNumber ( ) ,
assetClass : symbolProfile.assetClass ,
assetClass : symbolProfile.assetClass ,
@ -241,13 +237,20 @@ export class PortfolioService {
}
}
// TODO: Add a cash position for each currency
// TODO: Add a cash position for each currency
detail s[ ghostfolioCashSymbol ] = await this . getCashPosition ( {
holding s[ ghostfolioCashSymbol ] = await this . getCashPosition ( {
cashDetails ,
cashDetails ,
investment : totalInvestment ,
investment : totalInvestment ,
value : totalValue
value : totalValue
} ) ;
} ) ;
return { details , hasErrors : currentPositions.hasErrors } ;
const accounts = await this . getAccounts (
orders ,
portfolioItemsNow ,
userCurrency ,
userId
) ;
return { accounts , holdings , hasErrors : currentPositions.hasErrors } ;
}
}
public async getPosition (
public async getPosition (
@ -604,7 +607,12 @@ export class PortfolioService {
for ( const position of currentPositions . positions ) {
for ( const position of currentPositions . positions ) {
portfolioItemsNow [ position . symbol ] = position ;
portfolioItemsNow [ position . symbol ] = position ;
}
}
const accounts = this . getAccounts ( orders , portfolioItemsNow , baseCurrency ) ;
const accounts = await this . getAccounts (
orders ,
portfolioItemsNow ,
baseCurrency ,
userId
) ;
return {
return {
rules : {
rules : {
accountClusterRisk : await this . rulesService . evaluate (
accountClusterRisk : await this . rulesService . evaluate (
@ -704,18 +712,9 @@ export class PortfolioService {
investment : Big ;
investment : Big ;
value : Big ;
value : Big ;
} ) {
} ) {
const accounts = { } ;
const cashValue = new Big ( cashDetails . balance ) ;
const cashValue = new Big ( cashDetails . balance ) ;
cashDetails . accounts . forEach ( ( account ) = > {
accounts [ account . name ] = {
current : account.balance ,
original : account.balance
} ;
} ) ;
return {
return {
accounts ,
allocationCurrent : cashValue.div ( value ) . toNumber ( ) ,
allocationCurrent : cashValue.div ( value ) . toNumber ( ) ,
allocationInvestment : cashValue.div ( investment ) . toNumber ( ) ,
allocationInvestment : cashValue.div ( investment ) . toNumber ( ) ,
assetClass : AssetClass.CASH ,
assetClass : AssetClass.CASH ,
@ -797,41 +796,67 @@ export class PortfolioService {
} ;
} ;
}
}
private getAccounts (
private async getAccounts (
orders : OrderWithAccount [ ] ,
orders : OrderWithAccount [ ] ,
portfolioItemsNow : { [ p : string ] : TimelinePosition } ,
portfolioItemsNow : { [ p : string ] : TimelinePosition } ,
userCurrency
userCurrency : Currency ,
userId : string
) {
) {
const accounts : PortfolioPosition [ 'accounts' ] = { } ;
const accounts : PortfolioDetails [ 'accounts' ] = { } ;
for ( const order of orders ) {
let currentValueOfSymbol = this . exchangeRateDataService . toCurrency (
order . quantity * portfolioItemsNow [ order . symbol ] . marketPrice ,
order . currency ,
userCurrency
) ;
let originalValueOfSymbol = this . exchangeRateDataService . toCurrency (
order . quantity * order . unitPrice ,
order . currency ,
userCurrency
) ;
if ( order . type === 'SELL' ) {
const currentAccounts = await this . accountService . getAccounts ( userId ) ;
currentValueOfSymbol *= - 1 ;
originalValueOfSymbol *= - 1 ;
}
if ( accounts [ order . Account ? . name || UNKNOWN_KEY ] ? . current ) {
for ( const account of currentAccounts ) {
accounts [ order . Account ? . name || UNKNOWN_KEY ] . current +=
const ordersByAccount = orders . filter ( ( { accountId } ) = > {
currentValueOfSymbol ;
return accountId === account . id ;
accounts [ order . Account ? . name || UNKNOWN_KEY ] . original +=
} ) ;
originalValueOfSymbol ;
} else {
if ( ordersByAccount . length <= 0 ) {
accounts [ order . Account ? . name || UNKNOWN_KEY ] = {
// Add account without orders
current : currentValueOfSymbol ,
const balance = this . exchangeRateDataService . toCurrency (
original : originalValueOfSymbol
account . balance ,
account . currency ,
userCurrency
) ;
accounts [ account . name ] = {
current : balance ,
original : balance
} ;
} ;
continue ;
}
for ( const order of ordersByAccount ) {
let currentValueOfSymbol = this . exchangeRateDataService . toCurrency (
order . quantity * portfolioItemsNow [ order . symbol ] . marketPrice ,
order . currency ,
userCurrency
) ;
let originalValueOfSymbol = this . exchangeRateDataService . toCurrency (
order . quantity * order . unitPrice ,
order . currency ,
userCurrency
) ;
if ( order . type === 'SELL' ) {
currentValueOfSymbol *= - 1 ;
originalValueOfSymbol *= - 1 ;
}
if ( accounts [ order . Account ? . name || UNKNOWN_KEY ] ? . current ) {
accounts [ order . Account ? . name || UNKNOWN_KEY ] . current +=
currentValueOfSymbol ;
accounts [ order . Account ? . name || UNKNOWN_KEY ] . original +=
originalValueOfSymbol ;
} else {
accounts [ order . Account ? . name || UNKNOWN_KEY ] = {
current : currentValueOfSymbol ,
original : originalValueOfSymbol
} ;
}
}
}
}
}
return accounts ;
return accounts ;
}
}