|  | @ -2,6 +2,17 @@ import { Currency } from '@prisma/client'; | 
			
		
	
		
		
			
				
					|  |  | import { CurrentRateService } from '@ghostfolio/api/app/core/current-rate.service'; |  |  | import { CurrentRateService } from '@ghostfolio/api/app/core/current-rate.service'; | 
			
		
	
		
		
			
				
					|  |  | import { OrderType } from '@ghostfolio/api/models/order-type'; |  |  | import { OrderType } from '@ghostfolio/api/models/order-type'; | 
			
		
	
		
		
			
				
					|  |  | import Big from 'big.js'; |  |  | import Big from 'big.js'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   addDays, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   addMonths, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   addYears, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   format, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   isAfter, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   isBefore, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   parse | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } from 'date-fns'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | const DATE_FORMAT = 'yyyy-MM-dd'; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | export class PortfolioCalculator { |  |  | export class PortfolioCalculator { | 
			
		
	
		
		
			
				
					|  |  |   private transactionPoints: TransactionPoint[]; |  |  |   private transactionPoints: TransactionPoint[]; | 
			
		
	
	
		
		
			
				
					|  | @ -11,10 +22,6 @@ export class PortfolioCalculator { | 
			
		
	
		
		
			
				
					|  |  |     private currency: Currency |  |  |     private currency: Currency | 
			
		
	
		
		
			
				
					|  |  |   ) {} |  |  |   ) {} | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   addOrder(order: PortfolioOrder): void {} |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |   deleteOrder(order: PortfolioOrder): void {} |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |   computeTransactionPoints(orders: PortfolioOrder[]) { |  |  |   computeTransactionPoints(orders: PortfolioOrder[]) { | 
			
		
	
		
		
			
				
					|  |  |     orders.sort((a, b) => a.date.localeCompare(b.date)); |  |  |     orders.sort((a, b) => a.date.localeCompare(b.date)); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -121,9 +128,38 @@ export class PortfolioCalculator { | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   calculateTimeline( |  |  |   calculateTimeline( | 
			
		
	
		
		
			
				
					|  |  |     timelineSpecification: TimelineSpecification[], |  |  |     timelineSpecification: TimelineSpecification[], | 
			
		
	
		
		
			
				
					
					|  |  |     endDate: Date |  |  |     endDate: string | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |   ): TimelinePeriod[] { |  |  |   ): TimelinePeriod[] { | 
			
		
	
		
		
			
				
					
					|  |  |     return null; |  |  |     if (timelineSpecification.length === 0) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |       return []; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const startDate = timelineSpecification[0].start; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const start = parse(startDate, DATE_FORMAT, new Date()); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const end = parse(endDate, DATE_FORMAT, new Date()); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const timelinePeriod: TimelinePeriod[] = []; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     let i = 0; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     for ( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       let currentDate = start; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       !isAfter(currentDate, end); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       currentDate = this.addToDate( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         currentDate, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         timelineSpecification[i].accuracy | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       ) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       if (this.isNextItemActive(timelineSpecification, currentDate, i)) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         i++; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       timelinePeriod.push({ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         date: format(currentDate, DATE_FORMAT), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         grossPerformance: 0, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         investment: 0, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         value: 0 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       }); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return timelinePeriod; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   private getFactor(type: OrderType) { |  |  |   private getFactor(type: OrderType) { | 
			
		
	
	
		
		
			
				
					|  | @ -141,6 +177,31 @@ export class PortfolioCalculator { | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |     return factor; |  |  |     return factor; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   private addToDate(date: Date, accurany: Accuracy): Date { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     switch (accurany) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       case 'day': | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return addDays(date, 1); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       case 'month': | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return addMonths(date, 1); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       case 'year': | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return addYears(date, 1); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   private isNextItemActive( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     timelineSpecification: TimelineSpecification[], | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     currentDate: Date, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     i: number | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   ) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return ( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       i + 1 < timelineSpecification.length && | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       !isBefore( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         currentDate, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         parse(timelineSpecification[i + 1].start, DATE_FORMAT, new Date()) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       ) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | interface TransactionPoint { |  |  | interface TransactionPoint { | 
			
		
	
	
		
		
			
				
					|  | @ -169,13 +230,13 @@ interface TimelinePosition { | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | type Accuracy = 'year' | 'month' | 'day'; |  |  | type Accuracy = 'year' | 'month' | 'day'; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  | interface TimelineSpecification { |  |  | export interface TimelineSpecification { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |   start: Date; |  |  |   start: string; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |   accuracy: Accuracy; |  |  |   accuracy: Accuracy; | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  | interface TimelinePeriod { |  |  | export interface TimelinePeriod { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |   date: Date; |  |  |   date: string; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |   grossPerformance: number; |  |  |   grossPerformance: number; | 
			
		
	
		
		
			
				
					|  |  |   investment: number; |  |  |   investment: number; | 
			
		
	
		
		
			
				
					|  |  |   value: number; |  |  |   value: number; | 
			
		
	
	
		
		
			
				
					|  | 
 |