@ -23,6 +23,7 @@ import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/ru
import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service' ;
import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service' ;
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service' ;
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service' ;
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service' ;
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service' ;
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service' ;
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service' ;
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service' ;
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service' ;
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service' ;
import {
import {
@ -31,7 +32,7 @@ import {
} from '@ghostfolio/common/calculation-helper' ;
} from '@ghostfolio/common/calculation-helper' ;
import {
import {
DEFAULT_CURRENCY ,
DEFAULT_CURRENCY ,
EMERGENCY_FUND_TAG_I D ,
TAG_ID_ EMERGENCY_FUND,
UNKNOWN_KEY
UNKNOWN_KEY
} from '@ghostfolio/common/config' ;
} from '@ghostfolio/common/config' ;
import {
import {
@ -54,7 +55,6 @@ import {
PortfolioPosition ,
PortfolioPosition ,
PortfolioReportResponse ,
PortfolioReportResponse ,
PortfolioSummary ,
PortfolioSummary ,
Position ,
UserSettings
UserSettings
} from '@ghostfolio/common/interfaces' ;
} from '@ghostfolio/common/interfaces' ;
import { TimelinePosition } from '@ghostfolio/common/models' ;
import { TimelinePosition } from '@ghostfolio/common/models' ;
@ -97,6 +97,8 @@ import { PortfolioCalculator } from './calculator/portfolio-calculator';
import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory' ;
import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory' ;
import { RulesService } from './rules.service' ;
import { RulesService } from './rules.service' ;
const Fuse = require ( 'fuse.js' ) ;
const asiaPacificMarkets = require ( '../../assets/countries/asia-pacific-markets.json' ) ;
const asiaPacificMarkets = require ( '../../assets/countries/asia-pacific-markets.json' ) ;
const developedMarkets = require ( '../../assets/countries/developed-markets.json' ) ;
const developedMarkets = require ( '../../assets/countries/developed-markets.json' ) ;
const emergingMarkets = require ( '../../assets/countries/emerging-markets.json' ) ;
const emergingMarkets = require ( '../../assets/countries/emerging-markets.json' ) ;
@ -111,6 +113,7 @@ export class PortfolioService {
private readonly calculatorFactory : PortfolioCalculatorFactory ,
private readonly calculatorFactory : PortfolioCalculatorFactory ,
private readonly dataProviderService : DataProviderService ,
private readonly dataProviderService : DataProviderService ,
private readonly exchangeRateDataService : ExchangeRateDataService ,
private readonly exchangeRateDataService : ExchangeRateDataService ,
private readonly i18nService : I18nService ,
private readonly impersonationService : ImpersonationService ,
private readonly impersonationService : ImpersonationService ,
private readonly orderService : OrderService ,
private readonly orderService : OrderService ,
@Inject ( REQUEST ) private readonly request : RequestWithUser ,
@Inject ( REQUEST ) private readonly request : RequestWithUser ,
@ -162,7 +165,10 @@ export class PortfolioService {
const [ accounts , details ] = await Promise . all ( [
const [ accounts , details ] = await Promise . all ( [
this . accountService . accounts ( {
this . accountService . accounts ( {
where ,
where ,
include : { activities : true , Platform : true } ,
include : {
activities : true ,
platform : true
} ,
orderBy : { name : 'asc' }
orderBy : { name : 'asc' }
} ) ,
} ) ,
this . getDetails ( {
this . getDetails ( {
@ -270,20 +276,59 @@ export class PortfolioService {
return dividends ;
return dividends ;
}
}
public async getHoldings ( {
dateRange ,
filters ,
impersonationId ,
query ,
userId
} : {
dateRange : DateRange ;
filters? : Filter [ ] ;
impersonationId : string ;
query? : string ;
userId : string ;
} ) {
userId = await this . getUserId ( impersonationId , userId ) ;
const { holdings : holdingsMap } = await this . getDetails ( {
dateRange ,
filters ,
impersonationId ,
userId
} ) ;
let holdings = Object . values ( holdingsMap ) ;
if ( query ) {
const fuse = new Fuse ( holdings , {
keys : [ 'isin' , 'name' , 'symbol' ] ,
threshold : 0.3
} ) ;
holdings = fuse . search ( query ) . map ( ( { item } ) = > {
return item ;
} ) ;
}
return holdings ;
}
public async getInvestments ( {
public async getInvestments ( {
dateRange ,
dateRange ,
filters ,
filters ,
groupBy ,
groupBy ,
impersonationId ,
impersonationId ,
savingsRate
savingsRate ,
userId
} : {
} : {
dateRange : DateRange ;
dateRange : DateRange ;
filters? : Filter [ ] ;
filters? : Filter [ ] ;
groupBy? : GroupBy ;
groupBy? : GroupBy ;
impersonationId : string ;
impersonationId : string ;
savingsRate : number ;
savingsRate : number ;
userId : string ;
} ) : Promise < PortfolioInvestments > {
} ) : Promise < PortfolioInvestments > {
const userId = await this . getUserId ( impersonationId , this . request . user . id ) ;
userId = await this . getUserId ( impersonationId , userI d) ;
const user = await this . userService . user ( { id : userId } ) ;
const user = await this . userService . user ( { id : userId } ) ;
const userCurrency = this . getUserCurrency ( user ) ;
const userCurrency = this . getUserCurrency ( user ) ;
@ -587,7 +632,7 @@ export class PortfolioService {
}
}
if ( filters ? . length === 0 || isFilteredByAccount || isFilteredByCash ) {
if ( filters ? . length === 0 || isFilteredByAccount || isFilteredByCash ) {
const cashPositions = await this . getCashPositions ( {
const cashPositions = this . getCashPositions ( {
cashDetails ,
cashDetails ,
userCurrency ,
userCurrency ,
value : filteredValueInBaseCurrency
value : filteredValueInBaseCurrency
@ -609,10 +654,10 @@ export class PortfolioService {
if (
if (
filters ? . length === 1 &&
filters ? . length === 1 &&
filters [ 0 ] . id === EMERGENCY_FUND_TAG_I D &&
filters [ 0 ] . id === TAG_ID_ EMERGENCY_FUND &&
filters [ 0 ] . type === 'TAG'
filters [ 0 ] . type === 'TAG'
) {
) {
const emergencyFundCashPositions = await this . getCashPositions ( {
const emergencyFundCashPositions = this . getCashPositions ( {
cashDetails ,
cashDetails ,
userCurrency ,
userCurrency ,
value : filteredValueInBaseCurrency
value : filteredValueInBaseCurrency
@ -678,12 +723,18 @@ export class PortfolioService {
} ;
} ;
}
}
public async getHolding (
public async getHolding ( {
aDataSource : DataSource ,
dataSource ,
aImpersonationId : string ,
impersonationId ,
aSymbol : string
symbol ,
) : Promise < PortfolioHoldingResponse > {
userId
const userId = await this . getUserId ( aImpersonationId , this . request . user . id ) ;
} : {
dataSource : DataSource ;
impersonationId : string ;
symbol : string ;
userId : string ;
} ) : Promise < PortfolioHoldingResponse > {
userId = await this . getUserId ( impersonationId , userId ) ;
const user = await this . userService . user ( { id : userId } ) ;
const user = await this . userService . user ( { id : userId } ) ;
const userCurrency = this . getUserCurrency ( user ) ;
const userCurrency = this . getUserCurrency ( user ) ;
@ -708,7 +759,7 @@ export class PortfolioService {
grossPerformancePercentWithCurrencyEffect : undefined ,
grossPerformancePercentWithCurrencyEffect : undefined ,
grossPerformanceWithCurrencyEffect : undefined ,
grossPerformanceWithCurrencyEffect : undefined ,
historicalData : [ ] ,
historicalData : [ ] ,
investment : undefined ,
investmentInBaseCurrencyWithCurrencyEffect : undefined ,
marketPrice : undefined ,
marketPrice : undefined ,
marketPriceMax : undefined ,
marketPriceMax : undefined ,
marketPriceMin : undefined ,
marketPriceMin : undefined ,
@ -726,7 +777,7 @@ export class PortfolioService {
}
}
const [ SymbolProfile ] = await this . symbolProfileService . getSymbolProfiles ( [
const [ SymbolProfile ] = await this . symbolProfileService . getSymbolProfiles ( [
{ dataSource : aDataSource , symbol : aS ymbol }
{ dataSource , symbol }
] ) ;
] ) ;
const portfolioCalculator = this . calculatorFactory . createCalculator ( {
const portfolioCalculator = this . calculatorFactory . createCalculator ( {
@ -741,26 +792,33 @@ export class PortfolioService {
const { positions } = await portfolioCalculator . getSnapshot ( ) ;
const { positions } = await portfolioCalculator . getSnapshot ( ) ;
const position = positions . find ( ( { dataSource , symbol } ) = > {
const holding = positions . find ( ( position ) = > {
return dataSource === aD ataSource && symbol === aS ymbol;
return position . dataSource === d ataSource && position . symbol === s ymbol;
} ) ;
} ) ;
if ( position ) {
if ( holding ) {
const {
const {
averagePrice ,
averagePrice ,
currency ,
currency ,
dataSource ,
dividendInBaseCurrency ,
dividendInBaseCurrency ,
fee ,
fee ,
firstBuyDate ,
firstBuyDate ,
grossPerformance ,
grossPerformancePercentage ,
grossPerformancePercentageWithCurrencyEffect ,
grossPerformanceWithCurrencyEffect ,
investmentWithCurrencyEffect ,
marketPrice ,
marketPrice ,
netPerformance ,
netPerformancePercentage ,
netPerformancePercentageWithCurrencyEffectMap ,
netPerformanceWithCurrencyEffectMap ,
quantity ,
quantity ,
symbol ,
tags ,
tags ,
timeWeightedInvestment ,
timeWeightedInvestment ,
timeWeightedInvestmentWithCurrencyEffect ,
timeWeightedInvestmentWithCurrencyEffect ,
transactionCount
transactionCount
} = position ;
} = holding ;
const activitiesOfHolding = activities . filter ( ( { SymbolProfile } ) = > {
const activitiesOfHolding = activities . filter ( ( { SymbolProfile } ) = > {
return (
return (
@ -789,7 +847,7 @@ export class PortfolioService {
} ) ;
} ) ;
const historicalData = await this . dataProviderService . getHistorical (
const historicalData = await this . dataProviderService . getHistorical (
[ { dataSource , symbol : aSymbol } ] ,
[ { dataSource , symbol } ] ,
'day' ,
'day' ,
parseISO ( firstBuyDate ) ,
parseISO ( firstBuyDate ) ,
new Date ( )
new Date ( )
@ -809,10 +867,10 @@ export class PortfolioService {
marketPrice
marketPrice
) ;
) ;
if ( historicalData [ aS ymbol] ) {
if ( historicalData [ s ymbol] ) {
let j = - 1 ;
let j = - 1 ;
for ( const [ date , { marketPrice } ] of Object . entries (
for ( const [ date , { marketPrice } ] of Object . entries (
historicalData [ aS ymbol]
historicalData [ s ymbol]
) ) {
) ) {
while (
while (
j + 1 < transactionPoints . length &&
j + 1 < transactionPoints . length &&
@ -825,8 +883,8 @@ export class PortfolioService {
let currentQuantity = 0 ;
let currentQuantity = 0 ;
const currentSymbol = transactionPoints [ j ] ? . items . find (
const currentSymbol = transactionPoints [ j ] ? . items . find (
( { symbol } ) = > {
( transactionPointSymbol ) = > {
return symbol === aS ymbol;
return transactionPointSymbol . symbol === s ymbol;
}
}
) ;
) ;
@ -890,23 +948,21 @@ export class PortfolioService {
SymbolProfile . currency ,
SymbolProfile . currency ,
userCurrency
userCurrency
) ,
) ,
grossPerformance : position.grossPerformance?.toNumber ( ) ,
grossPerformance : grossPerformance?.toNumber ( ) ,
grossPerformancePercent :
grossPerformancePercent : grossPerformancePercentage?.toNumber ( ) ,
position . grossPerformancePercentage ? . toNumber ( ) ,
grossPerformancePercentWithCurrencyEffect :
grossPerformancePercentWithCurrencyEffect :
position . grossPerformancePercentageWithCurrencyEffect ? . toNumber ( ) ,
grossPerformancePercentageWithCurrencyEffect ? . toNumber ( ) ,
grossPerformanceWithCurrencyEffect :
grossPerformanceWithCurrencyEffect :
position . grossPerformanceWithCurrencyEffect ? . toNumber ( ) ,
grossPerformanceWithCurrencyEffect ? . toNumber ( ) ,
historicalData : historicalDataArray ,
historicalData : historicalDataArray ,
investment : position.investment?.toNumber ( ) ,
investmentInBaseCurrencyWithCurrencyEffect :
netPerformance : position.netPerformance?.toNumber ( ) ,
investmentWithCurrencyEffect ? . toNumber ( ) ,
netPerformancePercent : position.netPerformancePercentage?.toNumber ( ) ,
netPerformance : netPerformance?.toNumber ( ) ,
netPerformancePercent : netPerformancePercentage?.toNumber ( ) ,
netPerformancePercentWithCurrencyEffect :
netPerformancePercentWithCurrencyEffect :
position . netPerformancePercentageWithCurrencyEffectMap ? . [
netPerformancePercentageWithCurrencyEffectMap ? . [ 'max' ] ? . toNumber ( ) ,
'max'
] ? . toNumber ( ) ,
netPerformanceWithCurrencyEffect :
netPerformanceWithCurrencyEffect :
position . netPerformanceWithCurrencyEffectMap ? . [ 'max' ] ? . toNumber ( ) ,
netPerformanceWithCurrencyEffectMap ? . [ 'max' ] ? . toNumber ( ) ,
performances : {
performances : {
allTimeHigh : {
allTimeHigh : {
performancePercent ,
performancePercent ,
@ -923,12 +979,12 @@ export class PortfolioService {
} else {
} else {
const currentData = await this . dataProviderService . getQuotes ( {
const currentData = await this . dataProviderService . getQuotes ( {
user ,
user ,
items : [ { dataSource : DataSource.YAHOO , symbol : aSymbol } ]
items : [ { symbol , dataSource : DataSource.YAHOO } ]
} ) ;
} ) ;
const marketPrice = currentData [ aS ymbol] ? . marketPrice ;
const marketPrice = currentData [ s ymbol] ? . marketPrice ;
let historicalData = await this . dataProviderService . getHistorical (
let historicalData = await this . dataProviderService . getHistorical (
[ { dataSource : DataSource.YAHOO , symbol : aSymbol } ] ,
[ { symbol , dataSource : DataSource.YAHOO } ] ,
'day' ,
'day' ,
portfolioStart ,
portfolioStart ,
new Date ( )
new Date ( )
@ -937,15 +993,13 @@ export class PortfolioService {
if ( isEmpty ( historicalData ) ) {
if ( isEmpty ( historicalData ) ) {
try {
try {
historicalData = await this . dataProviderService . getHistoricalRaw ( {
historicalData = await this . dataProviderService . getHistoricalRaw ( {
assetProfileIdentifiers : [
assetProfileIdentifiers : [ { symbol , dataSource : DataSource.YAHOO } ] ,
{ dataSource : DataSource.YAHOO , symbol : aSymbol }
] ,
from : portfolioStart ,
from : portfolioStart ,
to : new Date ( )
to : new Date ( )
} ) ;
} ) ;
} catch {
} catch {
historicalData = {
historicalData = {
[ aS ymbol] : { }
[ s ymbol] : { }
} ;
} ;
}
}
}
}
@ -956,7 +1010,7 @@ export class PortfolioService {
let marketPriceMin = marketPrice ;
let marketPriceMin = marketPrice ;
for ( const [ date , { marketPrice } ] of Object . entries (
for ( const [ date , { marketPrice } ] of Object . entries (
historicalData [ aS ymbol]
historicalData [ s ymbol]
) ) {
) ) {
historicalDataArray . push ( {
historicalDataArray . push ( {
date ,
date ,
@ -997,7 +1051,7 @@ export class PortfolioService {
grossPerformancePercentWithCurrencyEffect : undefined ,
grossPerformancePercentWithCurrencyEffect : undefined ,
grossPerformanceWithCurrencyEffect : undefined ,
grossPerformanceWithCurrencyEffect : undefined ,
historicalData : historicalDataArray ,
historicalData : historicalDataArray ,
investment : 0 ,
investmentInBaseCurrencyWithCurrencyEffect : 0 ,
netPerformance : undefined ,
netPerformance : undefined ,
netPerformancePercent : undefined ,
netPerformancePercent : undefined ,
netPerformancePercentWithCurrencyEffect : undefined ,
netPerformancePercentWithCurrencyEffect : undefined ,
@ -1016,155 +1070,6 @@ export class PortfolioService {
}
}
}
}
public async getHoldings ( {
dateRange = 'max' ,
filters ,
impersonationId
} : {
dateRange? : DateRange ;
filters? : Filter [ ] ;
impersonationId : string ;
} ) : Promise < { hasErrors : boolean ; positions : Position [ ] } > {
const searchQuery = filters . find ( ( { type } ) = > {
return type === 'SEARCH_QUERY' ;
} ) ? . id ;
const userId = await this . getUserId ( impersonationId , this . request . user . id ) ;
const user = await this . userService . user ( { id : userId } ) ;
const userCurrency = this . getUserCurrency ( user ) ;
const { activities } =
await this . orderService . getOrdersForPortfolioCalculator ( {
filters ,
userCurrency ,
userId
} ) ;
if ( activities . length === 0 ) {
return {
hasErrors : false ,
positions : [ ]
} ;
}
const portfolioCalculator = this . calculatorFactory . createCalculator ( {
activities ,
filters ,
userId ,
calculationType : this.getUserPerformanceCalculationType ( user ) ,
currency : userCurrency
} ) ;
const portfolioSnapshot = await portfolioCalculator . getSnapshot ( ) ;
const hasErrors = portfolioSnapshot . hasErrors ;
let positions = portfolioSnapshot . positions ;
positions = positions . filter ( ( { quantity } ) = > {
return ! quantity . eq ( 0 ) ;
} ) ;
const assetProfileIdentifiers = positions . map ( ( { dataSource , symbol } ) = > {
return {
dataSource ,
symbol
} ;
} ) ;
const [ dataProviderResponses , symbolProfiles ] = await Promise . all ( [
this . dataProviderService . getQuotes ( {
user ,
items : assetProfileIdentifiers
} ) ,
this . symbolProfileService . getSymbolProfiles (
positions . map ( ( { dataSource , symbol } ) = > {
return { dataSource , symbol } ;
} )
)
] ) ;
const symbolProfileMap : { [ symbol : string ] : EnhancedSymbolProfile } = { } ;
for ( const symbolProfile of symbolProfiles ) {
symbolProfileMap [ symbolProfile . symbol ] = symbolProfile ;
}
if ( searchQuery ) {
positions = positions . filter ( ( { symbol } ) = > {
const enhancedSymbolProfile = symbolProfileMap [ symbol ] ;
return (
enhancedSymbolProfile . isin ? . toLowerCase ( ) . startsWith ( searchQuery ) ||
enhancedSymbolProfile . name ? . toLowerCase ( ) . startsWith ( searchQuery ) ||
enhancedSymbolProfile . symbol ? . toLowerCase ( ) . startsWith ( searchQuery )
) ;
} ) ;
}
return {
hasErrors ,
positions : positions.map (
( {
averagePrice ,
currency ,
dataSource ,
firstBuyDate ,
grossPerformance ,
grossPerformancePercentage ,
grossPerformancePercentageWithCurrencyEffect ,
grossPerformanceWithCurrencyEffect ,
investment ,
investmentWithCurrencyEffect ,
netPerformance ,
netPerformancePercentage ,
netPerformancePercentageWithCurrencyEffectMap ,
netPerformanceWithCurrencyEffectMap ,
quantity ,
symbol ,
timeWeightedInvestment ,
timeWeightedInvestmentWithCurrencyEffect ,
transactionCount
} ) = > {
return {
currency ,
dataSource ,
firstBuyDate ,
symbol ,
transactionCount ,
assetClass : symbolProfileMap [ symbol ] . assetClass ,
assetSubClass : symbolProfileMap [ symbol ] . assetSubClass ,
averagePrice : averagePrice.toNumber ( ) ,
grossPerformance : grossPerformance?.toNumber ( ) ? ? null ,
grossPerformancePercentage :
grossPerformancePercentage ? . toNumber ( ) ? ? null ,
grossPerformancePercentageWithCurrencyEffect :
grossPerformancePercentageWithCurrencyEffect ? . toNumber ( ) ? ? null ,
grossPerformanceWithCurrencyEffect :
grossPerformanceWithCurrencyEffect ? . toNumber ( ) ? ? null ,
investment : investment.toNumber ( ) ,
investmentWithCurrencyEffect :
investmentWithCurrencyEffect ? . toNumber ( ) ,
marketState :
dataProviderResponses [ symbol ] ? . marketState ? ? 'delayed' ,
name : symbolProfileMap [ symbol ] . name ,
netPerformance : netPerformance?.toNumber ( ) ? ? null ,
netPerformancePercentage :
netPerformancePercentage ? . toNumber ( ) ? ? null ,
netPerformancePercentageWithCurrencyEffect :
netPerformancePercentageWithCurrencyEffectMap ? . [
dateRange
] ? . toNumber ( ) ? ? null ,
netPerformanceWithCurrencyEffect :
netPerformanceWithCurrencyEffectMap ? . [ dateRange ] ? . toNumber ( ) ? ?
null ,
quantity : quantity.toNumber ( ) ,
timeWeightedInvestment : timeWeightedInvestment?.toNumber ( ) ,
timeWeightedInvestmentWithCurrencyEffect :
timeWeightedInvestmentWithCurrencyEffect ? . toNumber ( )
} ;
}
)
} ;
}
public async getPerformance ( {
public async getPerformance ( {
dateRange = 'max' ,
dateRange = 'max' ,
filters ,
filters ,
@ -1266,10 +1171,14 @@ export class PortfolioService {
} ;
} ;
}
}
public async getReport (
public async getReport ( {
impersonationId : string
impersonationId ,
) : Promise < PortfolioReportResponse > {
userId
const userId = await this . getUserId ( impersonationId , this . request . user . id ) ;
} : {
impersonationId : string ;
userId : string ;
} ) : Promise < PortfolioReportResponse > {
userId = await this . getUserId ( impersonationId , userId ) ;
const userSettings = this . request . user . Settings . settings as UserSettings ;
const userSettings = this . request . user . Settings . settings as UserSettings ;
const { accounts , holdings , markets , marketsAdvanced , summary } =
const { accounts , holdings , markets , marketsAdvanced , summary } =
@ -1294,15 +1203,19 @@ export class PortfolioService {
const rules : PortfolioReportResponse [ 'rules' ] = {
const rules : PortfolioReportResponse [ 'rules' ] = {
accountClusterRisk :
accountClusterRisk :
summary . orders Count > 0
summary . activity Count > 0
? await this . rulesService . evaluate (
? await this . rulesService . evaluate (
[
[
new AccountClusterRiskCurrentInvestment (
new AccountClusterRiskCurrentInvestment (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
this . i18nService ,
userSettings . language ,
accounts
accounts
) ,
) ,
new AccountClusterRiskSingleAccount (
new AccountClusterRiskSingleAccount (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
this . i18nService ,
userSettings . language ,
accounts
accounts
)
)
] ,
] ,
@ -1310,15 +1223,19 @@ export class PortfolioService {
)
)
: undefined ,
: undefined ,
assetClassClusterRisk :
assetClassClusterRisk :
summary . orders Count > 0
summary . activity Count > 0
? await this . rulesService . evaluate (
? await this . rulesService . evaluate (
[
[
new AssetClassClusterRiskEquity (
new AssetClassClusterRiskEquity (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
this . i18nService ,
userSettings . language ,
Object . values ( holdings )
Object . values ( holdings )
) ,
) ,
new AssetClassClusterRiskFixedIncome (
new AssetClassClusterRiskFixedIncome (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
this . i18nService ,
userSettings . language ,
Object . values ( holdings )
Object . values ( holdings )
)
)
] ,
] ,
@ -1326,23 +1243,27 @@ export class PortfolioService {
)
)
: undefined ,
: undefined ,
currencyClusterRisk :
currencyClusterRisk :
summary . orders Count > 0
summary . activity Count > 0
? await this . rulesService . evaluate (
? await this . rulesService . evaluate (
[
[
new CurrencyClusterRiskBaseCurrencyCurrentInvestment (
new CurrencyClusterRiskBaseCurrencyCurrentInvestment (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
Object . values ( holdings )
this . i18nService ,
Object . values ( holdings ) ,
userSettings . language
) ,
) ,
new CurrencyClusterRiskCurrentInvestment (
new CurrencyClusterRiskCurrentInvestment (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
Object . values ( holdings )
this . i18nService ,
Object . values ( holdings ) ,
userSettings . language
)
)
] ,
] ,
userSettings
userSettings
)
)
: undefined ,
: undefined ,
economicMarketClusterRisk :
economicMarketClusterRisk :
summary . orders Count > 0
summary . activity Count > 0
? await this . rulesService . evaluate (
? await this . rulesService . evaluate (
[
[
new EconomicMarketClusterRiskDevelopedMarkets (
new EconomicMarketClusterRiskDevelopedMarkets (
@ -1363,6 +1284,8 @@ export class PortfolioService {
[
[
new EmergencyFundSetup (
new EmergencyFundSetup (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
this . i18nService ,
userSettings . language ,
this . getTotalEmergencyFund ( {
this . getTotalEmergencyFund ( {
userSettings ,
userSettings ,
emergencyFundHoldingsValueInBaseCurrency :
emergencyFundHoldingsValueInBaseCurrency :
@ -1376,6 +1299,8 @@ export class PortfolioService {
[
[
new FeeRatioInitialInvestment (
new FeeRatioInitialInvestment (
this . exchangeRateDataService ,
this . exchangeRateDataService ,
this . i18nService ,
userSettings . language ,
summary . committedFunds ,
summary . committedFunds ,
summary . fees
summary . fees
)
)
@ -1383,7 +1308,7 @@ export class PortfolioService {
userSettings
userSettings
) ,
) ,
regionalMarketClusterRisk :
regionalMarketClusterRisk :
summary . orders Count > 0
summary . activity Count > 0
? await this . rulesService . evaluate (
? await this . rulesService . evaluate (
[
[
new RegionalMarketClusterRiskAsiaPacific (
new RegionalMarketClusterRiskAsiaPacific (
@ -1577,7 +1502,7 @@ export class PortfolioService {
return { markets , marketsAdvanced } ;
return { markets , marketsAdvanced } ;
}
}
private async getCashPositions ( {
private getCashPositions ( {
cashDetails ,
cashDetails ,
userCurrency ,
userCurrency ,
value
value
@ -1699,7 +1624,7 @@ export class PortfolioService {
const emergencyFundHoldings = Object . values ( holdings ) . filter ( ( { tags } ) = > {
const emergencyFundHoldings = Object . values ( holdings ) . filter ( ( { tags } ) = > {
return (
return (
tags ? . some ( ( { id } ) = > {
tags ? . some ( ( { id } ) = > {
return id === EMERGENCY_FUND_TAG_I D ;
return id === TAG_ID_ EMERGENCY_FUND;
} ) ? ? false
} ) ? ? false
) ;
) ;
} ) ;
} ) ;
@ -1906,7 +1831,7 @@ export class PortfolioService {
const nonExcludedActivities : Activity [ ] = [ ] ;
const nonExcludedActivities : Activity [ ] = [ ] ;
for ( const activity of activities ) {
for ( const activity of activities ) {
if ( activity . A ccount? . isExcluded ) {
if ( activity . a ccount? . isExcluded ) {
excludedActivities . push ( activity ) ;
excludedActivities . push ( activity ) ;
} else {
} else {
nonExcludedActivities . push ( activity ) ;
nonExcludedActivities . push ( activity ) ;
@ -1945,8 +1870,6 @@ export class PortfolioService {
const liabilities =
const liabilities =
await portfolioCalculator . getLiabilitiesInBaseCurrency ( ) ;
await portfolioCalculator . getLiabilitiesInBaseCurrency ( ) ;
const valuables = await portfolioCalculator . getValuablesInBaseCurrency ( ) ;
const totalBuy = this . getSumOfActivityType ( {
const totalBuy = this . getSumOfActivityType ( {
userCurrency ,
userCurrency ,
activities : nonExcludedActivities ,
activities : nonExcludedActivities ,
@ -1995,7 +1918,6 @@ export class PortfolioService {
const netWorth = new Big ( balanceInBaseCurrency )
const netWorth = new Big ( balanceInBaseCurrency )
. plus ( currentValueInBaseCurrency )
. plus ( currentValueInBaseCurrency )
. plus ( valuables )
. plus ( excludedAccountsAndActivities )
. plus ( excludedAccountsAndActivities )
. minus ( liabilities )
. minus ( liabilities )
. toNumber ( ) ;
. toNumber ( ) ;
@ -2026,6 +1948,9 @@ export class PortfolioService {
netPerformanceWithCurrencyEffect ,
netPerformanceWithCurrencyEffect ,
totalBuy ,
totalBuy ,
totalSell ,
totalSell ,
activityCount : activities.filter ( ( { type } ) = > {
return [ 'BUY' , 'SELL' ] . includes ( type ) ;
} ) . length ,
committedFunds : committedFunds.toNumber ( ) ,
committedFunds : committedFunds.toNumber ( ) ,
currentValueInBaseCurrency : currentValueInBaseCurrency.toNumber ( ) ,
currentValueInBaseCurrency : currentValueInBaseCurrency.toNumber ( ) ,
dividendInBaseCurrency : dividendInBaseCurrency.toNumber ( ) ,
dividendInBaseCurrency : dividendInBaseCurrency.toNumber ( ) ,
@ -2051,11 +1976,7 @@ export class PortfolioService {
. plus ( fees )
. plus ( fees )
. toNumber ( ) ,
. toNumber ( ) ,
interest : interest.toNumber ( ) ,
interest : interest.toNumber ( ) ,
items : valuables.toNumber ( ) ,
liabilities : liabilities.toNumber ( ) ,
liabilities : liabilities.toNumber ( ) ,
ordersCount : activities.filter ( ( { type } ) = > {
return [ 'BUY' , 'SELL' ] . includes ( type ) ;
} ) . length ,
totalInvestment : totalInvestment.toNumber ( ) ,
totalInvestment : totalInvestment.toNumber ( ) ,
totalValueInBaseCurrency : netWorth
totalValueInBaseCurrency : netWorth
} ;
} ;
@ -2143,14 +2064,14 @@ export class PortfolioService {
let currentAccounts : ( Account & {
let currentAccounts : ( Account & {
Order? : Order [ ] ;
Order? : Order [ ] ;
P latform? : Platform ;
p latform? : Platform ;
} ) [ ] = [ ] ;
} ) [ ] = [ ] ;
if ( filters . length === 0 ) {
if ( filters . length === 0 ) {
currentAccounts = await this . accountService . getAccounts ( userId ) ;
currentAccounts = await this . accountService . getAccounts ( userId ) ;
} else if ( filters . length === 1 && filters [ 0 ] . type === 'ACCOUNT' ) {
} else if ( filters . length === 1 && filters [ 0 ] . type === 'ACCOUNT' ) {
currentAccounts = await this . accountService . accounts ( {
currentAccounts = await this . accountService . accounts ( {
include : { P latform : true } ,
include : { p latform : true } ,
where : { id : filters [ 0 ] . id }
where : { id : filters [ 0 ] . id }
} ) ;
} ) ;
} else {
} else {
@ -2167,7 +2088,7 @@ export class PortfolioService {
) ;
) ;
currentAccounts = await this . accountService . accounts ( {
currentAccounts = await this . accountService . accounts ( {
include : { P latform : true } ,
include : { p latform : true } ,
where : { id : { in : accountIds } }
where : { id : { in : accountIds } }
} ) ;
} ) ;
}
}
@ -2192,18 +2113,18 @@ export class PortfolioService {
)
)
} ;
} ;
if ( platforms [ account . Platform ? . i d || UNKNOWN_KEY ] ? . valueInBaseCurrency ) {
if ( platforms [ account . platformI d || UNKNOWN_KEY ] ? . valueInBaseCurrency ) {
platforms [ account . Platform ? . i d || UNKNOWN_KEY ] . valueInBaseCurrency +=
platforms [ account . platformI d || UNKNOWN_KEY ] . valueInBaseCurrency +=
this . exchangeRateDataService . toCurrency (
this . exchangeRateDataService . toCurrency (
account . balance ,
account . balance ,
account . currency ,
account . currency ,
userCurrency
userCurrency
) ;
) ;
} else {
} else {
platforms [ account . Platform ? . i d || UNKNOWN_KEY ] = {
platforms [ account . platformI d || UNKNOWN_KEY ] = {
balance : account.balance ,
balance : account.balance ,
currency : account.currency ,
currency : account.currency ,
name : account.P latform?.name ,
name : account.p latform?.name ,
valueInBaseCurrency : this.exchangeRateDataService.toCurrency (
valueInBaseCurrency : this.exchangeRateDataService.toCurrency (
account . balance ,
account . balance ,
account . currency ,
account . currency ,
@ -2213,7 +2134,7 @@ export class PortfolioService {
}
}
for ( const {
for ( const {
A ccount,
a ccount,
quantity ,
quantity ,
SymbolProfile ,
SymbolProfile ,
type
type
@ -2224,28 +2145,28 @@ export class PortfolioService {
( portfolioItemsNow [ SymbolProfile . symbol ] ? . marketPriceInBaseCurrency ? ?
( portfolioItemsNow [ SymbolProfile . symbol ] ? . marketPriceInBaseCurrency ? ?
0 ) ;
0 ) ;
if ( accounts [ A ccount? . id || UNKNOWN_KEY ] ? . valueInBaseCurrency ) {
if ( accounts [ a ccount? . id || UNKNOWN_KEY ] ? . valueInBaseCurrency ) {
accounts [ A ccount? . id || UNKNOWN_KEY ] . valueInBaseCurrency +=
accounts [ a ccount? . id || UNKNOWN_KEY ] . valueInBaseCurrency +=
currentValueOfSymbolInBaseCurrency ;
currentValueOfSymbolInBaseCurrency ;
} else {
} else {
accounts [ A ccount? . id || UNKNOWN_KEY ] = {
accounts [ a ccount? . id || UNKNOWN_KEY ] = {
balance : 0 ,
balance : 0 ,
currency : A ccount?.currency,
currency : a ccount?.currency,
name : account.name ,
name : account.name ,
valueInBaseCurrency : currentValueOfSymbolInBaseCurrency
valueInBaseCurrency : currentValueOfSymbolInBaseCurrency
} ;
} ;
}
}
if (
if (
platforms [ Account ? . Platform ? . i d || UNKNOWN_KEY ] ? . valueInBaseCurrency
platforms [ account ? . platformI d || UNKNOWN_KEY ] ? . valueInBaseCurrency
) {
) {
platforms [ Account ? . Platform ? . i d || UNKNOWN_KEY ] . valueInBaseCurrency +=
platforms [ account ? . platformI d || UNKNOWN_KEY ] . valueInBaseCurrency +=
currentValueOfSymbolInBaseCurrency ;
currentValueOfSymbolInBaseCurrency ;
} else {
} else {
platforms [ Account ? . Platform ? . i d || UNKNOWN_KEY ] = {
platforms [ account ? . platformI d || UNKNOWN_KEY ] = {
balance : 0 ,
balance : 0 ,
currency : A ccount?.currency,
currency : a ccount?.currency,
name : account.P latform?.name ,
name : account.p latform?.name ,
valueInBaseCurrency : currentValueOfSymbolInBaseCurrency
valueInBaseCurrency : currentValueOfSymbolInBaseCurrency
} ;
} ;
}
}