diff --git a/apps/api/src/app/portfolio/portfolio-calculator.spec.ts b/apps/api/src/app/portfolio/portfolio-calculator.spec.ts index ce8320b16..20c3503db 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.spec.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.spec.ts @@ -62,6 +62,19 @@ function mockGetValue(symbol: string, date: Date) { ) .toNumber() }; + case 'BALN.SW': + if (isSameDay(parseDate('2021-11-12'), date)) { + return { marketPrice: 146 }; + } else if (isSameDay(parseDate('2021-11-22'), date)) { + return { marketPrice: 142.9 }; + } else if (isSameDay(parseDate('2021-11-26'), date)) { + return { marketPrice: 139.9 }; + } else if (isSameDay(parseDate('2021-11-30'), date)) { + return { marketPrice: 136.6 }; + } else if (isSameDay(parseDate('2021-12-18'), date)) { + return { marketPrice: 143.9 }; + } + default: return { marketPrice: 0 }; } @@ -1486,6 +1499,126 @@ describe('PortfolioCalculator', () => { }) ); }); + + it('with BALN.SW', async () => { + const portfolioCalculator = new PortfolioCalculator( + currentRateService, + 'CHF' + ); + + // date,type,ticker,currency,units,price,fee + portfolioCalculator.setTransactionPoints([ + // 12.11.2021,BUY,BALN.SW,CHF,2.00,146.00,1.65 + { + date: '2021-11-12', + items: [ + { + quantity: new Big('2'), + symbol: 'BALN.SW', + investment: new Big('292'), + currency: 'CHF', + dataSource: DataSource.YAHOO, + firstBuyDate: '2021-11-12', + fee: new Big('1.65'), + transactionCount: 1 + } + ] + }, + // HWR: (End Value - (Initial Value + Cash Flow)) / (Initial Value + Cash Flow) + // End Value: 142.9 * 2 = 285.8 + // Initial Value: 292 (Investment) + // Cash Flow: 0 + // HWR_n0: (285.8 - 292) / 292 = -0.021232877 + + // 22.11.2021,BUY,BALN.SW,CHF,7.00,142.90,5.75 + { + date: '2021-11-22', + items: [ + { + quantity: new Big('9'), // 7 + 2 + symbol: 'BALN.SW', + investment: new Big('1292.3'), // 142.9 * 7 + 146 * 2 + currency: 'CHF', + dataSource: DataSource.YAHOO, + firstBuyDate: '2021-11-12', + fee: new Big('7.4'), // 1.65 + 5.75 + transactionCount: 2 + } + ] + }, + // HWR: (End Value - (Initial Value + Cash Flow)) / (Initial Value + Cash Flow) + // End Value: 139.9 * 9 = 1259.1 + // Initial Value: 285.8 (End Value n0) + // Cash Flow: 1000.3 + // Initial Value + Cash Flow: 285.8 + 1000.3 = 1286.1 + // HWR_n1: (1259.1 - 1286.1) / 1286.1 = -0.020993702 + + // 26.11.2021,BUY,BALN.SW,CHF,3.00,139.90,2.40 + { + date: '2021-11-26', + items: [ + { + quantity: new Big('12'), // 3 + 7 + 2 + symbol: 'BALN.SW', + investment: new Big('1712'), // 139.9 * 3 + 142.9 * 7 + 146 * 2 + currency: 'CHF', + dataSource: DataSource.YAHOO, + firstBuyDate: '2021-11-12', + fee: new Big('9.8'), // 2.40 + 1.65 + 5.75 + transactionCount: 3 + } + ] + }, + // HWR: (End Value - (Initial Value + Cash Flow)) / (Initial Value + Cash Flow) + // End Value: 136.6 * 12 = 1639.2 + // Initial Value: 1259.1 (End Value n1) + // Cash Flow: 139.9 * 3 = 419.7 + // Initial Value + Cash Flow: 1259.1 + 419.7 = 1678.8 + // HWR_n2: (1639.2 - 1678.8) / 1678.8 = -0.023588277 + + // 30.11.2021,BUY,BALN.SW,CHF,2.00,136.60,1.55 + { + date: '2021-11-30', + items: [ + { + quantity: new Big('14'), // 2 + 3 + 7 + 2 + symbol: 'BALN.SW', + investment: new Big('1985.2'), // 136.6 * 2 + 139.9 * 3 + 142.9 * 7 + 146 * 2 + currency: 'CHF', + dataSource: DataSource.YAHOO, + firstBuyDate: '2021-11-12', + fee: new Big('11.35'), // 1.55 + 2.40 + 1.65 + 5.75 + transactionCount: 4 + } + ] + } + // HWR: (End Value - (Initial Value + Cash Flow)) / (Initial Value + Cash Flow) + // End Value: 143.9 * 14 = 2014.6 + // Initial Value: 1639.2 (End Value n2) + // Cash Flow: 136.6 * 2 = 273.2 + // Initial Value + Cash Flow: 1639.2 + 273.2 = 1912.4 + // HWR_n3: (2014.6 - 1912.4) / 1912.4 = 0.053440703 + ]); + + // HWR_total = 1 - (HWR_n0 + 1) * (HWR_n1 + 1) * (HWR_n2 + 1) * (HWR_n3 + 1) + // HWR_total = 1 - (-0.021232877 + 1) * (-0.020993702 + 1) * (-0.023588277 + 1) * (0.053440703 + 1) = 0.014383561 + + const spy = jest + .spyOn(Date, 'now') + .mockImplementation(() => new Date(Date.UTC(2021, 11, 18)).getTime()); // 2021-12-18 + + const currentPositions = await portfolioCalculator.getCurrentPositions( + parseDate('2021-11-01') + ); + spy.mockRestore(); + + expect(currentPositions).toBeDefined(); + expect(currentPositions.grossPerformance).toEqual(new Big('29.4')); + expect(currentPositions.netPerformance).toEqual(new Big('18.05')); + expect(currentPositions.grossPerformancePercentage).toEqual( + new Big('-0.01438356164383561644') + ); + }); }); describe('calculate timeline', () => {