| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -106,16 +106,19 @@ export class PortfolioCalculator { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public async getCurrentPositions(start: Date): Promise<{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    [symbol: string]: TimelinePosition; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    hasErrors: boolean; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    positions: TimelinePosition[]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  }> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (!this.transactionPoints?.length) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return {}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        hasErrors: false, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        positions: [] | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const lastTransactionPoint = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.transactionPoints[this.transactionPoints.length - 1]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const result: { [symbol: string]: TimelinePosition } = {}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // use Date.now() to use the mock for today
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const today = new Date(Date.now()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -171,6 +174,7 @@ export class PortfolioCalculator { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let hasErrors = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const startString = format(start, DATE_FORMAT); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const holdingPeriodReturns: { [symbol: string]: Big } = {}; | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -178,12 +182,14 @@ export class PortfolioCalculator { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let todayString = format(today, DATE_FORMAT); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // in case no symbols are there for today, use yesterday
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (!marketSymbolMap[todayString]) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      hasErrors = true; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      todayString = format(subDays(today, 1), DATE_FORMAT); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (firstIndex > 0) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      firstIndex--; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const invalidSymbols = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    for (let i = firstIndex; i < this.transactionPoints.length; i++) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const currentDate = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        i === firstIndex ? startString : this.transactionPoints[i].date; | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -198,6 +204,22 @@ export class PortfolioCalculator { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if (!oldHoldingPeriodReturn) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          oldHoldingPeriodReturn = new Big(1); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if (!marketSymbolMap[nextDate]?.[item.symbol]) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          invalidSymbols.push(item.symbol); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          hasErrors = true; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          console.error( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            `Missing value for symbol ${item.symbol} at ${nextDate}` | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          continue; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if (!marketSymbolMap[currentDate]?.[item.symbol]) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          invalidSymbols.push(item.symbol); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          hasErrors = true; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          console.error( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            `Missing value for symbol ${item.symbol} at ${currentDate}` | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          continue; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        holdingPeriodReturns[item.symbol] = oldHoldingPeriodReturn.mul( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          marketSymbolMap[nextDate][item.symbol].div( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            marketSymbolMap[currentDate][item.symbol] | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -215,26 +237,31 @@ export class PortfolioCalculator { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const positions: TimelinePosition[] = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    for (const item of lastTransactionPoint.items) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const marketValue = marketSymbolMap[todayString][item.symbol]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      result[item.symbol] = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const marketValue = marketSymbolMap[todayString]?.[item.symbol]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const isValid = invalidSymbols.indexOf(item.symbol) === -1; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      positions.push({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        averagePrice: item.investment.div(item.quantity), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        currency: item.currency, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        firstBuyDate: item.firstBuyDate, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        grossPerformance: grossPerformance[item.symbol] ?? null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        grossPerformancePercentage: holdingPeriodReturns[item.symbol] | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          ? holdingPeriodReturns[item.symbol].minus(1) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        grossPerformance: isValid | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          ? grossPerformance[item.symbol] ?? null | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          : null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        grossPerformancePercentage: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          isValid && holdingPeriodReturns[item.symbol] | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            ? holdingPeriodReturns[item.symbol].minus(1) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            : null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        investment: item.investment, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        marketPrice: marketValue.toNumber(), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        marketPrice: marketValue?.toNumber() ?? null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        name: item.name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        quantity: item.quantity, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        symbol: item.symbol, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        transactionCount: item.transactionCount | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return result; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return { hasErrors, positions }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public async calculateTimeline( | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
				 | 
				
					
  |