|  | @ -12,6 +12,7 @@ import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/ap | 
			
		
	
		
		
			
				
					|  |  | import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; |  |  | import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; | 
			
		
	
		
		
			
				
					|  |  | import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; |  |  | import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; | 
			
		
	
		
		
			
				
					|  |  | import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; |  |  | import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.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 { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; |  |  | import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; | 
			
		
	
	
		
		
			
				
					|  | @ -67,14 +68,16 @@ import { | 
			
		
	
		
		
			
				
					|  |  |   isBefore, |  |  |   isBefore, | 
			
		
	
		
		
			
				
					|  |  |   isSameMonth, |  |  |   isSameMonth, | 
			
		
	
		
		
			
				
					|  |  |   isSameYear, |  |  |   isSameYear, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   isValid, | 
			
		
	
		
		
			
				
					|  |  |   max, |  |  |   max, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   min, | 
			
		
	
		
		
			
				
					|  |  |   parseISO, |  |  |   parseISO, | 
			
		
	
		
		
			
				
					|  |  |   set, |  |  |   set, | 
			
		
	
		
		
			
				
					|  |  |   setDayOfYear, |  |  |   setDayOfYear, | 
			
		
	
		
		
			
				
					|  |  |   subDays, |  |  |   subDays, | 
			
		
	
		
		
			
				
					|  |  |   subYears |  |  |   subYears | 
			
		
	
		
		
			
				
					|  |  | } from 'date-fns'; |  |  | } from 'date-fns'; | 
			
		
	
		
		
			
				
					
					|  |  | import { isEmpty, sortBy, uniq, uniqBy } from 'lodash'; |  |  | import { isEmpty, last, sortBy, uniq, uniqBy } from 'lodash'; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | import { |  |  | import { | 
			
		
	
		
		
			
				
					|  |  |   HistoricalDataContainer, |  |  |   HistoricalDataContainer, | 
			
		
	
	
		
		
			
				
					|  | @ -91,6 +94,7 @@ const europeMarkets = require('../../assets/countries/europe-markets.json'); | 
			
		
	
		
		
			
				
					|  |  | @Injectable() |  |  | @Injectable() | 
			
		
	
		
		
			
				
					|  |  | export class PortfolioService { |  |  | export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |   public constructor( |  |  |   public constructor( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     private readonly accountBalanceService: AccountBalanceService, | 
			
		
	
		
		
			
				
					|  |  |     private readonly accountService: AccountService, |  |  |     private readonly accountService: AccountService, | 
			
		
	
		
		
			
				
					|  |  |     private readonly currentRateService: CurrentRateService, |  |  |     private readonly currentRateService: CurrentRateService, | 
			
		
	
		
		
			
				
					|  |  |     private readonly dataProviderService: DataProviderService, |  |  |     private readonly dataProviderService: DataProviderService, | 
			
		
	
	
		
		
			
				
					|  | @ -114,8 +118,12 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |   }): Promise<AccountWithValue[]> { |  |  |   }): Promise<AccountWithValue[]> { | 
			
		
	
		
		
			
				
					|  |  |     const where: Prisma.AccountWhereInput = { userId: userId }; |  |  |     const where: Prisma.AccountWhereInput = { userId: userId }; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     if (filters?.[0].id && filters?.[0].type === 'ACCOUNT') { |  |  |     const accountFilter = filters?.find(({ type }) => { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |       where.id = filters[0].id; |  |  |       return type === 'ACCOUNT'; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (accountFilter) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       where.id = accountFilter.id; | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const [accounts, details] = await Promise.all([ |  |  |     const [accounts, details] = await Promise.all([ | 
			
		
	
	
		
		
			
				
					|  | @ -267,6 +275,13 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |         includeDrafts: true |  |  |         includeDrafts: true | 
			
		
	
		
		
			
				
					|  |  |       }); |  |  |       }); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (transactionPoints.length === 0) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       return { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         investments: [], | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         streaks: { currentStreak: 0, longestStreak: 0 } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const portfolioCalculator = new PortfolioCalculator({ |  |  |     const portfolioCalculator = new PortfolioCalculator({ | 
			
		
	
		
		
			
				
					|  |  |       currency: this.request.user.Settings.settings.baseCurrency, |  |  |       currency: this.request.user.Settings.settings.baseCurrency, | 
			
		
	
		
		
			
				
					|  |  |       currentRateService: this.currentRateService, |  |  |       currentRateService: this.currentRateService, | 
			
		
	
	
		
		
			
				
					|  | @ -274,12 +289,6 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |     }); |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); |  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); | 
			
		
	
		
		
			
				
					|  |  |     if (transactionPoints.length === 0) { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       return { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         investments: [], |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         streaks: { currentStreak: 0, longestStreak: 0 } |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       }; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     let investments: InvestmentItem[]; |  |  |     let investments: InvestmentItem[]; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -367,67 +376,6 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |     }; |  |  |     }; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async getChart({ |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     dateRange = 'max', |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     filters, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     impersonationId, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     userCurrency, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     userId, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     withExcludedAccounts = false |  |  |  | 
			
		
	
		
		
			
				
					|  |  |   }: { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     dateRange?: DateRange; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     filters?: Filter[]; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     impersonationId: string; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     userCurrency: string; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     userId: string; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     withExcludedAccounts?: boolean; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |   }): Promise<HistoricalDataContainer> { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     userId = await this.getUserId(impersonationId, userId); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const { portfolioOrders, transactionPoints } = |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       await this.getTransactionPoints({ |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         filters, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         userId, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         withExcludedAccounts |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       }); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const portfolioCalculator = new PortfolioCalculator({ |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       currency: userCurrency, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       currentRateService: this.currentRateService, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       orders: portfolioOrders |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     }); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if (transactionPoints.length === 0) { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       return { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         isAllTimeHigh: false, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         isAllTimeLow: false, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         items: [] |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       }; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const endDate = new Date(); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const portfolioStart = parseDate(transactionPoints[0].date); |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const startDate = this.getStartDate(dateRange, portfolioStart); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const daysInMarket = differenceInDays(new Date(), startDate); |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const step = Math.round( |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     ); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     const items = await portfolioCalculator.getChartData( |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       startDate, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       endDate, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       step |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     ); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     return { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       items, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       isAllTimeHigh: false, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       isAllTimeLow: false |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     }; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |   public async getDetails({ |  |  |   public async getDetails({ | 
			
		
	
		
		
			
				
					|  |  |     dateRange = 'max', |  |  |     dateRange = 'max', | 
			
		
	
		
		
			
				
					|  |  |     filters, |  |  |     filters, | 
			
		
	
	
		
		
			
				
					|  | @ -1028,12 +976,6 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |         userId |  |  |         userId | 
			
		
	
		
		
			
				
					|  |  |       }); |  |  |       }); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const portfolioCalculator = new PortfolioCalculator({ |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       currency: this.request.user.Settings.settings.baseCurrency, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       currentRateService: this.currentRateService, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       orders: portfolioOrders |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     }); |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if (transactionPoints?.length <= 0) { |  |  |     if (transactionPoints?.length <= 0) { | 
			
		
	
		
		
			
				
					|  |  |       return { |  |  |       return { | 
			
		
	
		
		
			
				
					|  |  |         hasErrors: false, |  |  |         hasErrors: false, | 
			
		
	
	
		
		
			
				
					|  | @ -1041,6 +983,12 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |       }; |  |  |       }; | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const portfolioCalculator = new PortfolioCalculator({ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       currency: this.request.user.Settings.settings.baseCurrency, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       currentRateService: this.currentRateService, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       orders: portfolioOrders | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); |  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const portfolioStart = parseDate(transactionPoints[0].date); |  |  |     const portfolioStart = parseDate(transactionPoints[0].date); | 
			
		
	
	
		
		
			
				
					|  | @ -1126,6 +1074,31 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |     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); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const accountBalances = await this.accountBalanceService.getAccountBalances( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       { filters, user } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     let accountBalanceItems: HistoricalDataItem[] = Object.values( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       // Reduce the array to a map with unique dates as keys
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       accountBalances.balances.reduce( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         ( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           map: { [date: string]: HistoricalDataItem }, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           { date, valueInBaseCurrency } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         ) => { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           const formattedDate = format(date, DATE_FORMAT); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           // Store the item in the map, overwriting if the date already exists
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           map[formattedDate] = { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             date: formattedDate, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             value: valueInBaseCurrency | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           return map; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         }, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         {} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       ) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const { portfolioOrders, transactionPoints } = |  |  |     const { portfolioOrders, transactionPoints } = | 
			
		
	
		
		
			
				
					|  |  |       await this.getTransactionPoints({ |  |  |       await this.getTransactionPoints({ | 
			
		
	
		
		
			
				
					|  |  |         filters, |  |  |         filters, | 
			
		
	
	
		
		
			
				
					|  | @ -1139,7 +1112,7 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |       orders: portfolioOrders |  |  |       orders: portfolioOrders | 
			
		
	
		
		
			
				
					|  |  |     }); |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     if (transactionPoints?.length <= 0) { |  |  |     if (accountBalanceItems?.length <= 0 && transactionPoints?.length <= 0) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |       return { |  |  |       return { | 
			
		
	
		
		
			
				
					|  |  |         chart: [], |  |  |         chart: [], | 
			
		
	
		
		
			
				
					|  |  |         firstOrderDate: undefined, |  |  |         firstOrderDate: undefined, | 
			
		
	
	
		
		
			
				
					|  | @ -1149,6 +1122,7 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |           currentGrossPerformancePercent: 0, |  |  |           currentGrossPerformancePercent: 0, | 
			
		
	
		
		
			
				
					|  |  |           currentNetPerformance: 0, |  |  |           currentNetPerformance: 0, | 
			
		
	
		
		
			
				
					|  |  |           currentNetPerformancePercent: 0, |  |  |           currentNetPerformancePercent: 0, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           currentNetWorth: 0, | 
			
		
	
		
		
			
				
					|  |  |           currentValue: 0, |  |  |           currentValue: 0, | 
			
		
	
		
		
			
				
					|  |  |           totalInvestment: 0 |  |  |           totalInvestment: 0 | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
	
		
		
			
				
					|  | @ -1157,7 +1131,15 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); |  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     const portfolioStart = parseDate(transactionPoints[0].date); |  |  |     const portfolioStart = min( | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |       [ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         parseDate(accountBalanceItems[0]?.date), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         parseDate(transactionPoints[0]?.date) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       ].filter((date) => { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return isValid(date); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       }) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const startDate = this.getStartDate(dateRange, portfolioStart); |  |  |     const startDate = this.getStartDate(dateRange, portfolioStart); | 
			
		
	
		
		
			
				
					|  |  |     const { |  |  |     const { | 
			
		
	
		
		
			
				
					|  |  |       currentValue, |  |  |       currentValue, | 
			
		
	
	
		
		
			
				
					|  | @ -1175,17 +1157,17 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |     let currentNetPerformance = netPerformance; |  |  |     let currentNetPerformance = netPerformance; | 
			
		
	
		
		
			
				
					|  |  |     let currentNetPerformancePercent = netPerformancePercentage; |  |  |     let currentNetPerformancePercent = netPerformancePercentage; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     const historicalDataContainer = await this.getChart({ |  |  |     const { items } = await this.getChart({ | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |       dateRange, |  |  |       dateRange, | 
			
		
	
		
		
			
				
					|  |  |       filters, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       impersonationId, |  |  |       impersonationId, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       portfolioOrders, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       transactionPoints, | 
			
		
	
		
		
			
				
					|  |  |       userCurrency, |  |  |       userCurrency, | 
			
		
	
		
		
			
				
					
					|  |  |       userId, |  |  |       userId | 
			
				
				
			
		
	
		
		
			
				
					|  |  |       withExcludedAccounts |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					|  |  |     }); |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     const itemOfToday = historicalDataContainer.items.find((item) => { |  |  |     const itemOfToday = items.find(({ date }) => { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |       return item.date === format(new Date(), DATE_FORMAT); |  |  |       return date === format(new Date(), DATE_FORMAT); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |     }); |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     if (itemOfToday) { |  |  |     if (itemOfToday) { | 
			
		
	
	
		
		
			
				
					|  | @ -1195,34 +1177,42 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |       ).div(100); |  |  |       ).div(100); | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     accountBalanceItems = accountBalanceItems.filter(({ date }) => { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       return !isBefore(parseDate(date), startDate); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const accountBalanceItemOfToday = accountBalanceItems.find(({ date }) => { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       return date === format(new Date(), DATE_FORMAT); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (!accountBalanceItemOfToday) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       accountBalanceItems.push({ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         date: format(new Date(), DATE_FORMAT), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         value: last(accountBalanceItems)?.value ?? 0 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const mergedHistoricalDataItems = this.mergeHistoricalDataItems( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       accountBalanceItems, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       items | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const currentHistoricalDataItem = last(mergedHistoricalDataItems); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const currentNetWorth = currentHistoricalDataItem?.netWorth ?? 0; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     return { |  |  |     return { | 
			
		
	
		
		
			
				
					|  |  |       errors, |  |  |       errors, | 
			
		
	
		
		
			
				
					|  |  |       hasErrors, |  |  |       hasErrors, | 
			
		
	
		
		
			
				
					
					|  |  |       chart: historicalDataContainer.items.map( |  |  |       chart: mergedHistoricalDataItems, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         ({ |  |  |       firstOrderDate: parseDate(items[0]?.date), | 
			
				
				
			
		
	
		
		
			
				
					|  |  |           date, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |           netPerformance: netPerformanceOfItem, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |           netPerformanceInPercentage, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |           totalInvestment: totalInvestmentOfItem, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |           value |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         }) => { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |           return { |  |  |  | 
			
		
	
		
		
			
				
					|  |  |             date, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |             netPerformanceInPercentage, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |             value, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |             netPerformance: netPerformanceOfItem, |  |  |  | 
			
		
	
		
		
			
				
					|  |  |             totalInvestment: totalInvestmentOfItem |  |  |  | 
			
		
	
		
		
			
				
					|  |  |           }; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       ), |  |  |  | 
			
		
	
		
		
			
				
					|  |  |       firstOrderDate: parseDate(historicalDataContainer.items[0]?.date), |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |       performance: { |  |  |       performance: { | 
			
		
	
		
		
			
				
					
					|  |  |         currentValue: currentValue.toNumber(), |  |  |         currentNetWorth, | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |         currentGrossPerformance: currentGrossPerformance.toNumber(), |  |  |         currentGrossPerformance: currentGrossPerformance.toNumber(), | 
			
		
	
		
		
			
				
					|  |  |         currentGrossPerformancePercent: |  |  |         currentGrossPerformancePercent: | 
			
		
	
		
		
			
				
					|  |  |           currentGrossPerformancePercent.toNumber(), |  |  |           currentGrossPerformancePercent.toNumber(), | 
			
		
	
		
		
			
				
					|  |  |         currentNetPerformance: currentNetPerformance.toNumber(), |  |  |         currentNetPerformance: currentNetPerformance.toNumber(), | 
			
		
	
		
		
			
				
					|  |  |         currentNetPerformancePercent: currentNetPerformancePercent.toNumber(), |  |  |         currentNetPerformancePercent: currentNetPerformancePercent.toNumber(), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         currentValue: currentValue.toNumber(), | 
			
		
	
		
		
			
				
					|  |  |         totalInvestment: totalInvestment.toNumber() |  |  |         totalInvestment: totalInvestment.toNumber() | 
			
		
	
		
		
			
				
					|  |  |       } |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |     }; |  |  |     }; | 
			
		
	
	
		
		
			
				
					|  | @ -1376,6 +1366,62 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  |     return cashPositions; |  |  |     return cashPositions; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   private async getChart({ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     dateRange = 'max', | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     impersonationId, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     portfolioOrders, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     transactionPoints, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     userCurrency, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     userId | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   }: { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     dateRange?: DateRange; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     impersonationId: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     portfolioOrders: PortfolioOrder[]; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     transactionPoints: TransactionPoint[]; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     userCurrency: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     userId: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   }): Promise<HistoricalDataContainer> { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (transactionPoints.length === 0) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       return { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         isAllTimeHigh: false, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         isAllTimeLow: false, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         items: [] | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     userId = await this.getUserId(impersonationId, userId); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const portfolioCalculator = new PortfolioCalculator({ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       currency: userCurrency, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       currentRateService: this.currentRateService, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       orders: portfolioOrders | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     portfolioCalculator.setTransactionPoints(transactionPoints); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const endDate = new Date(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const portfolioStart = parseDate(transactionPoints[0].date); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const startDate = this.getStartDate(dateRange, portfolioStart); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const daysInMarket = differenceInDays(new Date(), startDate); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const step = Math.round( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const items = await portfolioCalculator.getChartData( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       startDate, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       endDate, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       step | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       items, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       isAllTimeHigh: false, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       isAllTimeLow: false | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   private getDividendsByGroup({ |  |  |   private getDividendsByGroup({ | 
			
		
	
		
		
			
				
					|  |  |     dividends, |  |  |     dividends, | 
			
		
	
		
		
			
				
					|  |  |     groupBy |  |  |     groupBy | 
			
		
	
	
		
		
			
				
					|  | @ -1999,4 +2045,44 @@ export class PortfolioService { | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     return { accounts, platforms }; |  |  |     return { accounts, platforms }; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   private mergeHistoricalDataItems( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     accountBalanceItems: HistoricalDataItem[], | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     performanceChartItems: HistoricalDataItem[] | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   ): HistoricalDataItem[] { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const historicalDataItemsMap: { [date: string]: HistoricalDataItem } = {}; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     let latestAccountBalance = 0; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     for (const item of accountBalanceItems.concat(performanceChartItems)) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       const isAccountBalanceItem = accountBalanceItems.includes(item); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       const totalAccountBalance = isAccountBalanceItem | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         ? item.value | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         : latestAccountBalance; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       if (isAccountBalanceItem && performanceChartItems.length > 0) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         latestAccountBalance = item.value; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } else { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         historicalDataItemsMap[item.date] = { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           ...item, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           totalAccountBalance, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           netWorth: | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             (isAccountBalanceItem ? 0 : item.value) + totalAccountBalance | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     // Convert to an array and sort by date in ascending order
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const historicalDataItems = Object.keys(historicalDataItemsMap).map( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       (date) => { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return historicalDataItemsMap[date]; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     historicalDataItems.sort( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime() | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return historicalDataItems; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
	
		
		
			
				
					|  | 
 |