|  |  | @ -1,7 +1,7 @@ | 
			
		
	
		
			
				
					|  |  |  | import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; | 
			
		
	
		
			
				
					|  |  |  | import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; | 
			
		
	
		
			
				
					|  |  |  | import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface'; | 
			
		
	
		
			
				
					|  |  |  | import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface'; | 
			
		
	
		
			
				
					|  |  |  | import { PortfolioSnapshot } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-snapshot.interface'; | 
			
		
	
		
			
				
					|  |  |  | import { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface'; | 
			
		
	
		
			
				
					|  |  |  | import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; | 
			
		
	
		
			
				
					|  |  |  | import { | 
			
		
	
	
		
			
				
					|  |  | @ -11,7 +11,12 @@ import { | 
			
		
	
		
			
				
					|  |  |  | import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; | 
			
		
	
		
			
				
					|  |  |  | import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; | 
			
		
	
		
			
				
					|  |  |  | import { MAX_CHART_ITEMS } from '@ghostfolio/common/config'; | 
			
		
	
		
			
				
					|  |  |  | import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; | 
			
		
	
		
			
				
					|  |  |  | import { | 
			
		
	
		
			
				
					|  |  |  |   DATE_FORMAT, | 
			
		
	
		
			
				
					|  |  |  |   getSum, | 
			
		
	
		
			
				
					|  |  |  |   parseDate, | 
			
		
	
		
			
				
					|  |  |  |   resetHours | 
			
		
	
		
			
				
					|  |  |  | } from '@ghostfolio/common/helper'; | 
			
		
	
		
			
				
					|  |  |  | import { | 
			
		
	
		
			
				
					|  |  |  |   DataProviderInfo, | 
			
		
	
		
			
				
					|  |  |  |   HistoricalDataItem, | 
			
		
	
	
		
			
				
					|  |  | @ -44,18 +49,24 @@ export abstract class PortfolioCalculator { | 
			
		
	
		
			
				
					|  |  |  |   private currency: string; | 
			
		
	
		
			
				
					|  |  |  |   private currentRateService: CurrentRateService; | 
			
		
	
		
			
				
					|  |  |  |   private dataProviderInfos: DataProviderInfo[]; | 
			
		
	
		
			
				
					|  |  |  |   private endDate: Date; | 
			
		
	
		
			
				
					|  |  |  |   private exchangeRateDataService: ExchangeRateDataService; | 
			
		
	
		
			
				
					|  |  |  |   private snapshot: PortfolioSnapshot; | 
			
		
	
		
			
				
					|  |  |  |   private snapshotPromise: Promise<void>; | 
			
		
	
		
			
				
					|  |  |  |   private startDate: Date; | 
			
		
	
		
			
				
					|  |  |  |   private transactionPoints: TransactionPoint[]; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public constructor({ | 
			
		
	
		
			
				
					|  |  |  |     activities, | 
			
		
	
		
			
				
					|  |  |  |     currency, | 
			
		
	
		
			
				
					|  |  |  |     currentRateService, | 
			
		
	
		
			
				
					|  |  |  |     dateRange, | 
			
		
	
		
			
				
					|  |  |  |     exchangeRateDataService | 
			
		
	
		
			
				
					|  |  |  |   }: { | 
			
		
	
		
			
				
					|  |  |  |     activities: Activity[]; | 
			
		
	
		
			
				
					|  |  |  |     currency: string; | 
			
		
	
		
			
				
					|  |  |  |     currentRateService: CurrentRateService; | 
			
		
	
		
			
				
					|  |  |  |     dateRange: DateRange; | 
			
		
	
		
			
				
					|  |  |  |     exchangeRateDataService: ExchangeRateDataService; | 
			
		
	
		
			
				
					|  |  |  |   }) { | 
			
		
	
		
			
				
					|  |  |  |     this.currency = currency; | 
			
		
	
	
		
			
				
					|  |  | @ -79,12 +90,270 @@ export abstract class PortfolioCalculator { | 
			
		
	
		
			
				
					|  |  |  |       return a.date?.localeCompare(b.date); | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const { endDate, startDate } = getInterval(dateRange); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     this.endDate = endDate; | 
			
		
	
		
			
				
					|  |  |  |     this.startDate = startDate; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     this.computeTransactionPoints(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     this.snapshotPromise = this.initialize(); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   protected abstract calculateOverallPerformance( | 
			
		
	
		
			
				
					|  |  |  |     positions: TimelinePosition[] | 
			
		
	
		
			
				
					|  |  |  |   ): CurrentPositions; | 
			
		
	
		
			
				
					|  |  |  |   ): PortfolioSnapshot; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public async computeSnapshot( | 
			
		
	
		
			
				
					|  |  |  |     start: Date, | 
			
		
	
		
			
				
					|  |  |  |     end?: Date | 
			
		
	
		
			
				
					|  |  |  |   ): Promise<PortfolioSnapshot> { | 
			
		
	
		
			
				
					|  |  |  |     const lastTransactionPoint = last(this.transactionPoints); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let endDate = end; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (!endDate) { | 
			
		
	
		
			
				
					|  |  |  |       endDate = new Date(Date.now()); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (lastTransactionPoint) { | 
			
		
	
		
			
				
					|  |  |  |         endDate = max([endDate, parseDate(lastTransactionPoint.date)]); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const transactionPoints = this.transactionPoints?.filter(({ date }) => { | 
			
		
	
		
			
				
					|  |  |  |       return isBefore(parseDate(date), endDate); | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (!transactionPoints.length) { | 
			
		
	
		
			
				
					|  |  |  |       return { | 
			
		
	
		
			
				
					|  |  |  |         currentValueInBaseCurrency: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformance: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentage: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentageWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformanceWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         hasErrors: false, | 
			
		
	
		
			
				
					|  |  |  |         netPerformance: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentage: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentageWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         netPerformanceWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         positions: [], | 
			
		
	
		
			
				
					|  |  |  |         totalFeesWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         totalInterestWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         totalInvestment: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         totalInvestmentWithCurrencyEffect: new Big(0) | 
			
		
	
		
			
				
					|  |  |  |       }; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const currencies: { [symbol: string]: string } = {}; | 
			
		
	
		
			
				
					|  |  |  |     const dataGatheringItems: IDataGatheringItem[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let dates: Date[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let firstIndex = transactionPoints.length; | 
			
		
	
		
			
				
					|  |  |  |     let firstTransactionPoint: TransactionPoint = null; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     dates.push(resetHours(start)); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const { currency, dataSource, symbol } of transactionPoints[ | 
			
		
	
		
			
				
					|  |  |  |       firstIndex - 1 | 
			
		
	
		
			
				
					|  |  |  |     ].items) { | 
			
		
	
		
			
				
					|  |  |  |       dataGatheringItems.push({ | 
			
		
	
		
			
				
					|  |  |  |         dataSource, | 
			
		
	
		
			
				
					|  |  |  |         symbol | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       currencies[symbol] = currency; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (let i = 0; i < transactionPoints.length; i++) { | 
			
		
	
		
			
				
					|  |  |  |       if ( | 
			
		
	
		
			
				
					|  |  |  |         !isBefore(parseDate(transactionPoints[i].date), start) && | 
			
		
	
		
			
				
					|  |  |  |         firstTransactionPoint === null | 
			
		
	
		
			
				
					|  |  |  |       ) { | 
			
		
	
		
			
				
					|  |  |  |         firstTransactionPoint = transactionPoints[i]; | 
			
		
	
		
			
				
					|  |  |  |         firstIndex = i; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (firstTransactionPoint !== null) { | 
			
		
	
		
			
				
					|  |  |  |         dates.push(resetHours(parseDate(transactionPoints[i].date))); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     dates.push(resetHours(endDate)); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     // Add dates of last week for fallback
 | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 7)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 6)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 5)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 4)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 3)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 2)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 1)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(resetHours(new Date())); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     dates = uniq( | 
			
		
	
		
			
				
					|  |  |  |       dates.map((date) => { | 
			
		
	
		
			
				
					|  |  |  |         return date.getTime(); | 
			
		
	
		
			
				
					|  |  |  |       }) | 
			
		
	
		
			
				
					|  |  |  |     ) | 
			
		
	
		
			
				
					|  |  |  |       .map((timestamp) => { | 
			
		
	
		
			
				
					|  |  |  |         return new Date(timestamp); | 
			
		
	
		
			
				
					|  |  |  |       }) | 
			
		
	
		
			
				
					|  |  |  |       .sort((a, b) => { | 
			
		
	
		
			
				
					|  |  |  |         return a.getTime() - b.getTime(); | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let exchangeRatesByCurrency = | 
			
		
	
		
			
				
					|  |  |  |       await this.exchangeRateDataService.getExchangeRatesByCurrency({ | 
			
		
	
		
			
				
					|  |  |  |         currencies: uniq(Object.values(currencies)), | 
			
		
	
		
			
				
					|  |  |  |         endDate: endOfDay(endDate), | 
			
		
	
		
			
				
					|  |  |  |         startDate: this.getStartDate(), | 
			
		
	
		
			
				
					|  |  |  |         targetCurrency: this.currency | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const { | 
			
		
	
		
			
				
					|  |  |  |       dataProviderInfos, | 
			
		
	
		
			
				
					|  |  |  |       errors: currentRateErrors, | 
			
		
	
		
			
				
					|  |  |  |       values: marketSymbols | 
			
		
	
		
			
				
					|  |  |  |     } = await this.currentRateService.getValues({ | 
			
		
	
		
			
				
					|  |  |  |       dataGatheringItems, | 
			
		
	
		
			
				
					|  |  |  |       dateQuery: { | 
			
		
	
		
			
				
					|  |  |  |         in: dates | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     this.dataProviderInfos = dataProviderInfos; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const marketSymbolMap: { | 
			
		
	
		
			
				
					|  |  |  |       [date: string]: { [symbol: string]: Big }; | 
			
		
	
		
			
				
					|  |  |  |     } = {}; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const marketSymbol of marketSymbols) { | 
			
		
	
		
			
				
					|  |  |  |       const date = format(marketSymbol.date, DATE_FORMAT); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (!marketSymbolMap[date]) { | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap[date] = {}; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (marketSymbol.marketPrice) { | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap[date][marketSymbol.symbol] = new Big( | 
			
		
	
		
			
				
					|  |  |  |           marketSymbol.marketPrice | 
			
		
	
		
			
				
					|  |  |  |         ); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const endDateString = format(endDate, DATE_FORMAT); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (firstIndex > 0) { | 
			
		
	
		
			
				
					|  |  |  |       firstIndex--; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const positions: TimelinePosition[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let hasAnySymbolMetricsErrors = false; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const errors: ResponseError['errors'] = []; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const item of lastTransactionPoint.items) { | 
			
		
	
		
			
				
					|  |  |  |       const marketPriceInBaseCurrency = ( | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap[endDateString]?.[item.symbol] ?? item.averagePrice | 
			
		
	
		
			
				
					|  |  |  |       ).mul( | 
			
		
	
		
			
				
					|  |  |  |         exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[ | 
			
		
	
		
			
				
					|  |  |  |           endDateString | 
			
		
	
		
			
				
					|  |  |  |         ] | 
			
		
	
		
			
				
					|  |  |  |       ); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       const { | 
			
		
	
		
			
				
					|  |  |  |         grossPerformance, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentage, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentageWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformanceWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         hasErrors, | 
			
		
	
		
			
				
					|  |  |  |         netPerformance, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentage, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentageWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         netPerformanceWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestment, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestmentWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         totalDividend, | 
			
		
	
		
			
				
					|  |  |  |         totalDividendInBaseCurrency, | 
			
		
	
		
			
				
					|  |  |  |         totalInvestment, | 
			
		
	
		
			
				
					|  |  |  |         totalInvestmentWithCurrencyEffect | 
			
		
	
		
			
				
					|  |  |  |       } = this.getSymbolMetrics({ | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap, | 
			
		
	
		
			
				
					|  |  |  |         start, | 
			
		
	
		
			
				
					|  |  |  |         dataSource: item.dataSource, | 
			
		
	
		
			
				
					|  |  |  |         end: endDate, | 
			
		
	
		
			
				
					|  |  |  |         exchangeRates: | 
			
		
	
		
			
				
					|  |  |  |           exchangeRatesByCurrency[`${item.currency}${this.currency}`], | 
			
		
	
		
			
				
					|  |  |  |         symbol: item.symbol | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       positions.push({ | 
			
		
	
		
			
				
					|  |  |  |         dividend: totalDividend, | 
			
		
	
		
			
				
					|  |  |  |         dividendInBaseCurrency: totalDividendInBaseCurrency, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestment, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestmentWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         averagePrice: item.averagePrice, | 
			
		
	
		
			
				
					|  |  |  |         currency: item.currency, | 
			
		
	
		
			
				
					|  |  |  |         dataSource: item.dataSource, | 
			
		
	
		
			
				
					|  |  |  |         fee: item.fee, | 
			
		
	
		
			
				
					|  |  |  |         firstBuyDate: item.firstBuyDate, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformance: !hasErrors ? grossPerformance ?? null : null, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentage: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? grossPerformancePercentage ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentageWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? grossPerformancePercentageWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformanceWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? grossPerformanceWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         investment: totalInvestment, | 
			
		
	
		
			
				
					|  |  |  |         investmentWithCurrencyEffect: totalInvestmentWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         marketPrice: | 
			
		
	
		
			
				
					|  |  |  |           marketSymbolMap[endDateString]?.[item.symbol]?.toNumber() ?? null, | 
			
		
	
		
			
				
					|  |  |  |         marketPriceInBaseCurrency: | 
			
		
	
		
			
				
					|  |  |  |           marketPriceInBaseCurrency?.toNumber() ?? null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformance: !hasErrors ? netPerformance ?? null : null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentage: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? netPerformancePercentage ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentageWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? netPerformancePercentageWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformanceWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? netPerformanceWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         quantity: item.quantity, | 
			
		
	
		
			
				
					|  |  |  |         symbol: item.symbol, | 
			
		
	
		
			
				
					|  |  |  |         tags: item.tags, | 
			
		
	
		
			
				
					|  |  |  |         transactionCount: item.transactionCount, | 
			
		
	
		
			
				
					|  |  |  |         valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul( | 
			
		
	
		
			
				
					|  |  |  |           item.quantity | 
			
		
	
		
			
				
					|  |  |  |         ) | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if ( | 
			
		
	
		
			
				
					|  |  |  |         (hasErrors || | 
			
		
	
		
			
				
					|  |  |  |           currentRateErrors.find(({ dataSource, symbol }) => { | 
			
		
	
		
			
				
					|  |  |  |             return dataSource === item.dataSource && symbol === item.symbol; | 
			
		
	
		
			
				
					|  |  |  |           })) && | 
			
		
	
		
			
				
					|  |  |  |         item.investment.gt(0) | 
			
		
	
		
			
				
					|  |  |  |       ) { | 
			
		
	
		
			
				
					|  |  |  |         errors.push({ dataSource: item.dataSource, symbol: item.symbol }); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const overall = this.calculateOverallPerformance(positions); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     return { | 
			
		
	
		
			
				
					|  |  |  |       ...overall, | 
			
		
	
		
			
				
					|  |  |  |       errors, | 
			
		
	
		
			
				
					|  |  |  |       positions, | 
			
		
	
		
			
				
					|  |  |  |       hasErrors: hasAnySymbolMetricsErrors || overall.hasErrors, | 
			
		
	
		
			
				
					|  |  |  |       totalInterestWithCurrencyEffect: lastTransactionPoint.interest | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public async getChart({ | 
			
		
	
		
			
				
					|  |  |  |     dateRange = 'max', | 
			
		
	
	
		
			
				
					|  |  | @ -380,256 +649,30 @@ export abstract class PortfolioCalculator { | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public async getCurrentPositions( | 
			
		
	
		
			
				
					|  |  |  |     start: Date, | 
			
		
	
		
			
				
					|  |  |  |     end?: Date | 
			
		
	
		
			
				
					|  |  |  |   ): Promise<CurrentPositions> { | 
			
		
	
		
			
				
					|  |  |  |     const lastTransactionPoint = last(this.transactionPoints); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let endDate = end; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (!endDate) { | 
			
		
	
		
			
				
					|  |  |  |       endDate = new Date(Date.now()); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (lastTransactionPoint) { | 
			
		
	
		
			
				
					|  |  |  |         endDate = max([endDate, parseDate(lastTransactionPoint.date)]); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const transactionPoints = this.transactionPoints?.filter(({ date }) => { | 
			
		
	
		
			
				
					|  |  |  |       return isBefore(parseDate(date), endDate); | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (!transactionPoints.length) { | 
			
		
	
		
			
				
					|  |  |  |       return { | 
			
		
	
		
			
				
					|  |  |  |         currentValueInBaseCurrency: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformance: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentage: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentageWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         grossPerformanceWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         hasErrors: false, | 
			
		
	
		
			
				
					|  |  |  |         netPerformance: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentage: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentageWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         netPerformanceWithCurrencyEffect: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         positions: [], | 
			
		
	
		
			
				
					|  |  |  |         totalInvestment: new Big(0), | 
			
		
	
		
			
				
					|  |  |  |         totalInvestmentWithCurrencyEffect: new Big(0) | 
			
		
	
		
			
				
					|  |  |  |       }; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const currencies: { [symbol: string]: string } = {}; | 
			
		
	
		
			
				
					|  |  |  |     const dataGatheringItems: IDataGatheringItem[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let dates: Date[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let firstIndex = transactionPoints.length; | 
			
		
	
		
			
				
					|  |  |  |     let firstTransactionPoint: TransactionPoint = null; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     dates.push(resetHours(start)); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const { currency, dataSource, symbol } of transactionPoints[ | 
			
		
	
		
			
				
					|  |  |  |       firstIndex - 1 | 
			
		
	
		
			
				
					|  |  |  |     ].items) { | 
			
		
	
		
			
				
					|  |  |  |       dataGatheringItems.push({ | 
			
		
	
		
			
				
					|  |  |  |         dataSource, | 
			
		
	
		
			
				
					|  |  |  |         symbol | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       currencies[symbol] = currency; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (let i = 0; i < transactionPoints.length; i++) { | 
			
		
	
		
			
				
					|  |  |  |       if ( | 
			
		
	
		
			
				
					|  |  |  |         !isBefore(parseDate(transactionPoints[i].date), start) && | 
			
		
	
		
			
				
					|  |  |  |         firstTransactionPoint === null | 
			
		
	
		
			
				
					|  |  |  |       ) { | 
			
		
	
		
			
				
					|  |  |  |         firstTransactionPoint = transactionPoints[i]; | 
			
		
	
		
			
				
					|  |  |  |         firstIndex = i; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (firstTransactionPoint !== null) { | 
			
		
	
		
			
				
					|  |  |  |         dates.push(resetHours(parseDate(transactionPoints[i].date))); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     dates.push(resetHours(endDate)); | 
			
		
	
		
			
				
					|  |  |  |   public getDataProviderInfos() { | 
			
		
	
		
			
				
					|  |  |  |     return this.dataProviderInfos; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     // Add dates of last week for fallback
 | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 7)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 6)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 5)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 4)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 3)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 2)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(subDays(resetHours(new Date()), 1)); | 
			
		
	
		
			
				
					|  |  |  |     dates.push(resetHours(new Date())); | 
			
		
	
		
			
				
					|  |  |  |   public async getDividendInBaseCurrency() { | 
			
		
	
		
			
				
					|  |  |  |     await this.snapshotPromise; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     dates = uniq( | 
			
		
	
		
			
				
					|  |  |  |       dates.map((date) => { | 
			
		
	
		
			
				
					|  |  |  |         return date.getTime(); | 
			
		
	
		
			
				
					|  |  |  |       }) | 
			
		
	
		
			
				
					|  |  |  |     ) | 
			
		
	
		
			
				
					|  |  |  |       .map((timestamp) => { | 
			
		
	
		
			
				
					|  |  |  |         return new Date(timestamp); | 
			
		
	
		
			
				
					|  |  |  |     return getSum( | 
			
		
	
		
			
				
					|  |  |  |       this.snapshot.positions.map(({ dividendInBaseCurrency }) => { | 
			
		
	
		
			
				
					|  |  |  |         return dividendInBaseCurrency; | 
			
		
	
		
			
				
					|  |  |  |       }) | 
			
		
	
		
			
				
					|  |  |  |       .sort((a, b) => { | 
			
		
	
		
			
				
					|  |  |  |         return a.getTime() - b.getTime(); | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let exchangeRatesByCurrency = | 
			
		
	
		
			
				
					|  |  |  |       await this.exchangeRateDataService.getExchangeRatesByCurrency({ | 
			
		
	
		
			
				
					|  |  |  |         currencies: uniq(Object.values(currencies)), | 
			
		
	
		
			
				
					|  |  |  |         endDate: endOfDay(endDate), | 
			
		
	
		
			
				
					|  |  |  |         startDate: this.getStartDate(), | 
			
		
	
		
			
				
					|  |  |  |         targetCurrency: this.currency | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const { | 
			
		
	
		
			
				
					|  |  |  |       dataProviderInfos, | 
			
		
	
		
			
				
					|  |  |  |       errors: currentRateErrors, | 
			
		
	
		
			
				
					|  |  |  |       values: marketSymbols | 
			
		
	
		
			
				
					|  |  |  |     } = await this.currentRateService.getValues({ | 
			
		
	
		
			
				
					|  |  |  |       dataGatheringItems, | 
			
		
	
		
			
				
					|  |  |  |       dateQuery: { | 
			
		
	
		
			
				
					|  |  |  |         in: dates | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     this.dataProviderInfos = dataProviderInfos; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const marketSymbolMap: { | 
			
		
	
		
			
				
					|  |  |  |       [date: string]: { [symbol: string]: Big }; | 
			
		
	
		
			
				
					|  |  |  |     } = {}; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const marketSymbol of marketSymbols) { | 
			
		
	
		
			
				
					|  |  |  |       const date = format(marketSymbol.date, DATE_FORMAT); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (!marketSymbolMap[date]) { | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap[date] = {}; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (marketSymbol.marketPrice) { | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap[date][marketSymbol.symbol] = new Big( | 
			
		
	
		
			
				
					|  |  |  |           marketSymbol.marketPrice | 
			
		
	
		
			
				
					|  |  |  |         ); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const endDateString = format(endDate, DATE_FORMAT); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (firstIndex > 0) { | 
			
		
	
		
			
				
					|  |  |  |       firstIndex--; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const positions: TimelinePosition[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let hasAnySymbolMetricsErrors = false; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const errors: ResponseError['errors'] = []; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     for (const item of lastTransactionPoint.items) { | 
			
		
	
		
			
				
					|  |  |  |       const marketPriceInBaseCurrency = ( | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap[endDateString]?.[item.symbol] ?? item.averagePrice | 
			
		
	
		
			
				
					|  |  |  |       ).mul( | 
			
		
	
		
			
				
					|  |  |  |         exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[ | 
			
		
	
		
			
				
					|  |  |  |           endDateString | 
			
		
	
		
			
				
					|  |  |  |         ] | 
			
		
	
		
			
				
					|  |  |  |       ); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       const { | 
			
		
	
		
			
				
					|  |  |  |         grossPerformance, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentage, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentageWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformanceWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         hasErrors, | 
			
		
	
		
			
				
					|  |  |  |         netPerformance, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentage, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentageWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         netPerformanceWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestment, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestmentWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         totalDividend, | 
			
		
	
		
			
				
					|  |  |  |         totalDividendInBaseCurrency, | 
			
		
	
		
			
				
					|  |  |  |         totalInvestment, | 
			
		
	
		
			
				
					|  |  |  |         totalInvestmentWithCurrencyEffect | 
			
		
	
		
			
				
					|  |  |  |       } = this.getSymbolMetrics({ | 
			
		
	
		
			
				
					|  |  |  |         marketSymbolMap, | 
			
		
	
		
			
				
					|  |  |  |         start, | 
			
		
	
		
			
				
					|  |  |  |         dataSource: item.dataSource, | 
			
		
	
		
			
				
					|  |  |  |         end: endDate, | 
			
		
	
		
			
				
					|  |  |  |         exchangeRates: | 
			
		
	
		
			
				
					|  |  |  |           exchangeRatesByCurrency[`${item.currency}${this.currency}`], | 
			
		
	
		
			
				
					|  |  |  |         symbol: item.symbol | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       positions.push({ | 
			
		
	
		
			
				
					|  |  |  |         dividend: totalDividend, | 
			
		
	
		
			
				
					|  |  |  |         dividendInBaseCurrency: totalDividendInBaseCurrency, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestment, | 
			
		
	
		
			
				
					|  |  |  |         timeWeightedInvestmentWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         averagePrice: item.averagePrice, | 
			
		
	
		
			
				
					|  |  |  |         currency: item.currency, | 
			
		
	
		
			
				
					|  |  |  |         dataSource: item.dataSource, | 
			
		
	
		
			
				
					|  |  |  |         fee: item.fee, | 
			
		
	
		
			
				
					|  |  |  |         firstBuyDate: item.firstBuyDate, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformance: !hasErrors ? grossPerformance ?? null : null, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentage: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? grossPerformancePercentage ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformancePercentageWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? grossPerformancePercentageWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         grossPerformanceWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? grossPerformanceWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         investment: totalInvestment, | 
			
		
	
		
			
				
					|  |  |  |         investmentWithCurrencyEffect: totalInvestmentWithCurrencyEffect, | 
			
		
	
		
			
				
					|  |  |  |         marketPrice: | 
			
		
	
		
			
				
					|  |  |  |           marketSymbolMap[endDateString]?.[item.symbol]?.toNumber() ?? null, | 
			
		
	
		
			
				
					|  |  |  |         marketPriceInBaseCurrency: | 
			
		
	
		
			
				
					|  |  |  |           marketPriceInBaseCurrency?.toNumber() ?? null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformance: !hasErrors ? netPerformance ?? null : null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentage: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? netPerformancePercentage ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformancePercentageWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? netPerformancePercentageWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         netPerformanceWithCurrencyEffect: !hasErrors | 
			
		
	
		
			
				
					|  |  |  |           ? netPerformanceWithCurrencyEffect ?? null | 
			
		
	
		
			
				
					|  |  |  |           : null, | 
			
		
	
		
			
				
					|  |  |  |         quantity: item.quantity, | 
			
		
	
		
			
				
					|  |  |  |         symbol: item.symbol, | 
			
		
	
		
			
				
					|  |  |  |         tags: item.tags, | 
			
		
	
		
			
				
					|  |  |  |         transactionCount: item.transactionCount, | 
			
		
	
		
			
				
					|  |  |  |         valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul( | 
			
		
	
		
			
				
					|  |  |  |           item.quantity | 
			
		
	
		
			
				
					|  |  |  |         ) | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if ( | 
			
		
	
		
			
				
					|  |  |  |         (hasErrors || | 
			
		
	
		
			
				
					|  |  |  |           currentRateErrors.find(({ dataSource, symbol }) => { | 
			
		
	
		
			
				
					|  |  |  |             return dataSource === item.dataSource && symbol === item.symbol; | 
			
		
	
		
			
				
					|  |  |  |           })) && | 
			
		
	
		
			
				
					|  |  |  |         item.investment.gt(0) | 
			
		
	
		
			
				
					|  |  |  |       ) { | 
			
		
	
		
			
				
					|  |  |  |         errors.push({ dataSource: item.dataSource, symbol: item.symbol }); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     const overall = this.calculateOverallPerformance(positions); | 
			
		
	
		
			
				
					|  |  |  |   public async getFeesInBaseCurrency() { | 
			
		
	
		
			
				
					|  |  |  |     await this.snapshotPromise; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     return { | 
			
		
	
		
			
				
					|  |  |  |       ...overall, | 
			
		
	
		
			
				
					|  |  |  |       errors, | 
			
		
	
		
			
				
					|  |  |  |       positions, | 
			
		
	
		
			
				
					|  |  |  |       hasErrors: hasAnySymbolMetricsErrors || overall.hasErrors | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  |     return this.snapshot.totalFeesWithCurrencyEffect; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public getDataProviderInfos() { | 
			
		
	
		
			
				
					|  |  |  |     return this.dataProviderInfos; | 
			
		
	
		
			
				
					|  |  |  |   public async getInterestInBaseCurrency() { | 
			
		
	
		
			
				
					|  |  |  |     await this.snapshotPromise; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     return this.snapshot.totalInterestWithCurrencyEffect; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public getInvestments(): { date: string; investment: Big }[] { | 
			
		
	
	
		
			
				
					|  |  | @ -672,6 +715,12 @@ export abstract class PortfolioCalculator { | 
			
		
	
		
			
				
					|  |  |  |     })); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public async getSnapshot() { | 
			
		
	
		
			
				
					|  |  |  |     await this.snapshotPromise; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     return this.snapshot; | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public getStartDate() { | 
			
		
	
		
			
				
					|  |  |  |     return this.transactionPoints.length > 0 | 
			
		
	
		
			
				
					|  |  |  |       ? parseDate(this.transactionPoints[0].date) | 
			
		
	
	
		
			
				
					|  |  | @ -718,6 +767,13 @@ export abstract class PortfolioCalculator { | 
			
		
	
		
			
				
					|  |  |  |       type, | 
			
		
	
		
			
				
					|  |  |  |       unitPrice | 
			
		
	
		
			
				
					|  |  |  |     } of this.orders) { | 
			
		
	
		
			
				
					|  |  |  |       if ( | 
			
		
	
		
			
				
					|  |  |  |         // TODO
 | 
			
		
	
		
			
				
					|  |  |  |         ['ITEM', 'LIABILITY'].includes(type) | 
			
		
	
		
			
				
					|  |  |  |       ) { | 
			
		
	
		
			
				
					|  |  |  |         continue; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       let currentTransactionPointItem: TransactionPointSymbol; | 
			
		
	
		
			
				
					|  |  |  |       const oldAccumulatedSymbol = symbols[SymbolProfile.symbol]; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -790,18 +846,39 @@ export abstract class PortfolioCalculator { | 
			
		
	
		
			
				
					|  |  |  |         return a.symbol?.localeCompare(b.symbol); | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       let fees = new Big(0); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (type === 'FEE') { | 
			
		
	
		
			
				
					|  |  |  |         fees = fee; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       let interest = new Big(0); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (type === 'INTEREST') { | 
			
		
	
		
			
				
					|  |  |  |         interest = quantity.mul(unitPrice); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       if (lastDate !== date || lastTransactionPoint === null) { | 
			
		
	
		
			
				
					|  |  |  |         lastTransactionPoint = { | 
			
		
	
		
			
				
					|  |  |  |           date, | 
			
		
	
		
			
				
					|  |  |  |           fees, | 
			
		
	
		
			
				
					|  |  |  |           interest, | 
			
		
	
		
			
				
					|  |  |  |           items: newItems | 
			
		
	
		
			
				
					|  |  |  |         }; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         this.transactionPoints.push(lastTransactionPoint); | 
			
		
	
		
			
				
					|  |  |  |       } else { | 
			
		
	
		
			
				
					|  |  |  |         lastTransactionPoint.fees = lastTransactionPoint.fees.plus(fees); | 
			
		
	
		
			
				
					|  |  |  |         lastTransactionPoint.interest = | 
			
		
	
		
			
				
					|  |  |  |           lastTransactionPoint.interest.plus(interest); | 
			
		
	
		
			
				
					|  |  |  |         lastTransactionPoint.items = newItems; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       lastDate = date; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   private async initialize() { | 
			
		
	
		
			
				
					|  |  |  |     this.snapshot = await this.computeSnapshot(this.startDate, this.endDate); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | 
 |