Browse Source

Migrate overall performance calculation to time weighted

pull/2778/head
Reto Kaul 2 years ago
committed by Thomas Kaul
parent
commit
53efd4f79a
  1. 1
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy-and-sell.spec.ts
  2. 1
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy.spec.ts
  3. 1
      apps/api/src/app/portfolio/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  4. 1
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  5. 1
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell.spec.ts
  6. 58
      apps/api/src/app/portfolio/portfolio-calculator.ts
  7. 1
      libs/common/src/lib/interfaces/timeline-position.interface.ts

1
apps/api/src/app/portfolio/portfolio-calculator-baln-buy-and-sell.spec.ts

@ -92,6 +92,7 @@ describe('PortfolioCalculator', () => {
marketPrice: 148.9,
quantity: new Big('0'),
symbol: 'BALN.SW',
timeWeightedInvestment: new Big('285.8'),
transactionCount: 2
}
],

1
apps/api/src/app/portfolio/portfolio-calculator-baln-buy.spec.ts

@ -81,6 +81,7 @@ describe('PortfolioCalculator', () => {
marketPrice: 148.9,
quantity: new Big('2'),
symbol: 'BALN.SW',
timeWeightedInvestment: new Big('273.2'),
transactionCount: 1
}
],

1
apps/api/src/app/portfolio/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -92,6 +92,7 @@ describe('PortfolioCalculator', () => {
marketPrice: 13657.2,
quantity: new Big('1'),
symbol: 'BTCUSD',
timeWeightedInvestment: new Big('640.56763686131386861314'),
transactionCount: 2
}
],

1
apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -92,6 +92,7 @@ describe('PortfolioCalculator', () => {
marketPrice: 87.8,
quantity: new Big('1'),
symbol: 'NOVN.SW',
timeWeightedInvestment: new Big('145.10285714285714285714'),
transactionCount: 2
}
],

1
apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -112,6 +112,7 @@ describe('PortfolioCalculator', () => {
marketPrice: 87.8,
quantity: new Big('0'),
symbol: 'NOVN.SW',
timeWeightedInvestment: new Big('151.6'),
transactionCount: 2
}
],

58
apps/api/src/app/portfolio/portfolio-calculator.ts

@ -464,7 +464,6 @@ export class PortfolioCalculator {
if (firstIndex > 0) {
firstIndex--;
}
const initialValues: { [symbol: string]: Big } = {};
const positions: TimelinePosition[] = [];
let hasAnySymbolMetricsErrors = false;
@ -478,9 +477,9 @@ export class PortfolioCalculator {
grossPerformance,
grossPerformancePercentage,
hasErrors,
initialValue,
netPerformance,
netPerformancePercentage
netPerformancePercentage,
timeWeightedInvestment
} = this.getSymbolMetrics({
end,
marketSymbolMap,
@ -489,9 +488,9 @@ export class PortfolioCalculator {
});
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors;
initialValues[item.symbol] = initialValue;
positions.push({
timeWeightedInvestment,
averagePrice: item.quantity.eq(0)
? new Big(0)
: item.investment.div(item.quantity),
@ -526,7 +525,7 @@ export class PortfolioCalculator {
}
}
const overall = this.calculateOverallPerformance(positions, initialValues);
const overall = this.calculateOverallPerformance(positions);
return {
...overall,
@ -749,18 +748,13 @@ export class PortfolioCalculator {
};
}
private calculateOverallPerformance(
positions: TimelinePosition[],
initialValues: { [symbol: string]: Big }
) {
private calculateOverallPerformance(positions: TimelinePosition[]) {
let currentValue = new Big(0);
let grossPerformance = new Big(0);
let grossPerformancePercentage = new Big(0);
let hasErrors = false;
let netPerformance = new Big(0);
let netPerformancePercentage = new Big(0);
let sumOfWeights = new Big(0);
let totalInvestment = new Big(0);
let totalTimeWeightedInvestment = new Big(0);
for (const currentPosition of positions) {
if (currentPosition.marketPrice) {
@ -783,21 +777,9 @@ export class PortfolioCalculator {
hasErrors = true;
}
if (currentPosition.grossPerformancePercentage) {
// Use the average from the initial value and the current investment as
// a weight
const weight = (initialValues[currentPosition.symbol] ?? new Big(0))
.plus(currentPosition.investment)
.div(2);
sumOfWeights = sumOfWeights.plus(weight);
grossPerformancePercentage = grossPerformancePercentage.plus(
currentPosition.grossPerformancePercentage.mul(weight)
);
netPerformancePercentage = netPerformancePercentage.plus(
currentPosition.netPerformancePercentage.mul(weight)
if (currentPosition.timeWeightedInvestment) {
totalTimeWeightedInvestment = totalTimeWeightedInvestment.plus(
currentPosition.timeWeightedInvestment
);
} else if (!currentPosition.quantity.eq(0)) {
Logger.warn(
@ -808,22 +790,18 @@ export class PortfolioCalculator {
}
}
if (sumOfWeights.gt(0)) {
grossPerformancePercentage = grossPerformancePercentage.div(sumOfWeights);
netPerformancePercentage = netPerformancePercentage.div(sumOfWeights);
} else {
grossPerformancePercentage = new Big(0);
netPerformancePercentage = new Big(0);
}
return {
currentValue,
grossPerformance,
grossPerformancePercentage,
hasErrors,
netPerformance,
netPerformancePercentage,
totalInvestment
totalInvestment,
netPerformancePercentage: !totalTimeWeightedInvestment.eq(0)
? netPerformance.div(totalTimeWeightedInvestment)
: new Big(0),
grossPerformancePercentage: !totalTimeWeightedInvestment.eq(0)
? grossPerformance.div(totalTimeWeightedInvestment)
: new Big(0)
};
}
@ -1438,7 +1416,9 @@ export class PortfolioCalculator {
timeWeightedInvestmentValues,
grossPerformance: totalGrossPerformance,
hasErrors: totalUnits.gt(0) && (!initialValue || !unitPriceAtEndDate),
netPerformance: totalNetPerformance
netPerformance: totalNetPerformance,
timeWeightedInvestment:
timeWeightedAverageInvestmentBetweenStartAndEndDate
};
}

1
libs/common/src/lib/interfaces/timeline-position.interface.ts

@ -16,5 +16,6 @@ export interface TimelinePosition {
quantity: Big;
symbol: string;
tags?: Tag[];
timeWeightedInvestment: Big;
transactionCount: number;
}

Loading…
Cancel
Save