Browse Source

Fix time weighted Performance

pull/5027/head
Dan 1 year ago
parent
commit
872d1b3d43
  1. 259
      apps/api/src/app/portfolio/portfolio-calculator.ts

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

@ -390,13 +390,11 @@ export class PortfolioCalculator {
? format(previousDate, DATE_FORMAT) ? format(previousDate, DATE_FORMAT)
: null; : null;
let totalCurrentValue = new Big(0); let totalCurrentValue = new Big(0);
let maxTotalInvestmentValue = new Big(0);
let previousTotalInvestmentValue = new Big(0); let previousTotalInvestmentValue = new Big(0);
let timeWeightedPerformance = new Big(0);
if (calculateTimeWeightedPerformance && previousDateString) { if (calculateTimeWeightedPerformance && previousDateString) {
previousTotalInvestmentValue = previousTotalInvestmentValue =
accumulatedValuesByDate[previousDateString].totalInvestmentValue; accumulatedValuesByDate[previousDateString].totalCurrentValue;
} }
for (const symbol of Object.keys(valuesBySymbol)) { for (const symbol of Object.keys(valuesBySymbol)) {
@ -406,6 +404,8 @@ export class PortfolioCalculator {
totalCurrentValue = totalCurrentValue.plus(symbolCurrentValues); totalCurrentValue = totalCurrentValue.plus(symbolCurrentValues);
let timeWeightedPerformanceContribution = new Big(0);
if ( if (
previousTotalInvestmentValue.toNumber() && previousTotalInvestmentValue.toNumber() &&
symbolValues.netPerformanceValuesPercentage && symbolValues.netPerformanceValuesPercentage &&
@ -418,26 +418,16 @@ export class PortfolioCalculator {
const netPerformance = const netPerformance =
symbolValues.netPerformanceValuesPercentage?.[dateString] ?? symbolValues.netPerformanceValuesPercentage?.[dateString] ??
new Big(0); new Big(0);
const timeWeightedPerformanceContribution = previousValue timeWeightedPerformanceContribution = previousValue
.div(previousTotalInvestmentValue) .div(previousTotalInvestmentValue)
.mul(netPerformance) .mul(netPerformance);
.mul(100);
timeWeightedPerformance = timeWeightedPerformance.plus(
timeWeightedPerformanceContribution
);
} }
let totalTimeWeightedPerformance = timeWeightedPerformance.plus(
accumulatedValuesByDate[previousDateString]
?.totalTimeWeightedPerformance ?? new Big(0)
);
accumulatedValuesByDate = this.accumulatedValuesByDate( accumulatedValuesByDate = this.accumulatedValuesByDate(
valuesBySymbol, valuesBySymbol,
symbol, symbol,
dateString, dateString,
accumulatedValuesByDate, accumulatedValuesByDate,
totalTimeWeightedPerformance timeWeightedPerformanceContribution
); );
} }
@ -453,6 +443,19 @@ export class PortfolioCalculator {
totalNetPerformanceValue totalNetPerformanceValue
} = accumulatedValuesByDate[dateString]; } = accumulatedValuesByDate[dateString];
let totalNetTimeWeightedPerformance = new Big(0);
if (previousDateString) {
totalNetTimeWeightedPerformance = (
accumulatedValuesByDate[previousDateString]
?.totalTimeWeightedPerformance ?? new Big(0)
)
.plus(1)
.mul(totalTimeWeightedPerformance.plus(1))
.minus(1)
.mul(100);
}
const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0) const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0)
? 0 ? 0
: totalNetPerformanceValue : totalNetPerformanceValue
@ -476,7 +479,7 @@ export class PortfolioCalculator {
totalInvestment: totalInvestmentValue.toNumber(), totalInvestment: totalInvestmentValue.toNumber(),
value: totalCurrentValue.toNumber(), value: totalCurrentValue.toNumber(),
valueWithCurrencyEffect: totalCurrentValueWithCurrencyEffect.toNumber(), valueWithCurrencyEffect: totalCurrentValueWithCurrencyEffect.toNumber(),
timeWeightedPerformance: totalTimeWeightedPerformance.toNumber(), timeWeightedPerformance: totalNetTimeWeightedPerformance.toNumber(),
investmentValueWithCurrencyEffect: investmentValueWithCurrencyEffect:
investmentValueWithCurrencyEffect.toNumber(), investmentValueWithCurrencyEffect.toNumber(),
netPerformanceWithCurrencyEffect: netPerformanceWithCurrencyEffect:
@ -589,7 +592,10 @@ export class PortfolioCalculator {
accumulatedValuesByDate[dateString] accumulatedValuesByDate[dateString]
?.totalTimeWeightedInvestmentValueWithCurrencyEffect ?? new Big(0) ?.totalTimeWeightedInvestmentValueWithCurrencyEffect ?? new Big(0)
).add(timeWeightedInvestmentValueWithCurrencyEffect), ).add(timeWeightedInvestmentValueWithCurrencyEffect),
totalTimeWeightedPerformance: timeWeightedPerformance totalTimeWeightedPerformance: (
accumulatedValuesByDate[dateString]?.totalTimeWeightedPerformance ??
new Big(0)
).add(timeWeightedPerformance)
}; };
return accumulatedValuesByDate; return accumulatedValuesByDate;
@ -1162,78 +1168,55 @@ export class PortfolioCalculator {
symbol: string; symbol: string;
calculatePerformance?: boolean; calculatePerformance?: boolean;
}): SymbolMetrics { }): SymbolMetrics {
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; let {
const currentValues: WithCurrencyEffect<{ [date: string]: Big }> = { averagePriceAtStartDate,
Value: {}, totalUnits,
WithCurrencyEffect: {} totalInvestment,
}; investmentAtStartDate,
let fees: WithCurrencyEffect<Big> = { valueAtStartDate,
Value: new Big(0), maxTotalInvestment,
WithCurrencyEffect: new Big(0) averagePriceAtEndDate,
}; initialValue,
let feesAtStartDate: WithCurrencyEffect<Big> = { fees,
Value: new Big(0), feesAtStartDate,
WithCurrencyEffect: new Big(0) lastAveragePrice,
}; grossPerformanceFromSells,
let grossPerformance: WithCurrencyEffect<Big> = { totalInvestmentWithGrossPerformanceFromSell,
Value: new Big(0), grossPerformance,
WithCurrencyEffect: new Big(0) grossPerformanceAtStartDate,
}; currentValues,
let grossPerformanceAtStartDate: WithCurrencyEffect<Big> = { netPerformanceValues,
Value: new Big(0), netPerformanceValuesPercentage,
WithCurrencyEffect: new Big(0) investmentValues,
}; investmentValuesAccumulated,
let grossPerformanceFromSells: WithCurrencyEffect<Big> = { maxInvestmentValues,
Value: new Big(0), timeWeightedInvestmentValues
WithCurrencyEffect: new Big(0) }: {
}; averagePriceAtStartDate: Big;
let averagePriceAtEndDate = new Big(0); totalUnits: Big;
let averagePriceAtStartDate = new Big(0); totalInvestment: WithCurrencyEffect<Big>;
const investmentValues: WithCurrencyEffect<{ [date: string]: Big }> = { investmentAtStartDate: any;
Value: {}, valueAtStartDate: WithCurrencyEffect<Big>;
WithCurrencyEffect: {} maxTotalInvestment: Big;
}; averagePriceAtEndDate: Big;
const maxInvestmentValues: { [date: string]: Big } = {}; initialValue: any;
let maxTotalInvestment = new Big(0); fees: WithCurrencyEffect<Big>;
const netPerformanceValuesPercentage: { [date: string]: Big } = {}; feesAtStartDate: WithCurrencyEffect<Big>;
let initialValue; lastAveragePrice: WithCurrencyEffect<Big>;
let investmentAtStartDate; grossPerformanceFromSells: WithCurrencyEffect<Big>;
const investmentValuesAccumulated: WithCurrencyEffect<{ totalInvestmentWithGrossPerformanceFromSell: WithCurrencyEffect<Big>;
[date: string]: Big; grossPerformance: WithCurrencyEffect<Big>;
}> = { grossPerformanceAtStartDate: WithCurrencyEffect<Big>;
Value: {}, currentValues: WithCurrencyEffect<{ [date: string]: Big }>;
WithCurrencyEffect: {} netPerformanceValues: WithCurrencyEffect<{ [date: string]: Big }>;
}; netPerformanceValuesPercentage: { [date: string]: Big };
let lastAveragePrice: WithCurrencyEffect<Big> = { investmentValues: WithCurrencyEffect<{ [date: string]: Big }>;
Value: new Big(0), investmentValuesAccumulated: WithCurrencyEffect<{ [date: string]: Big }>;
WithCurrencyEffect: new Big(0) maxInvestmentValues: { [date: string]: Big };
}; timeWeightedInvestmentValues: WithCurrencyEffect<{ [date: string]: Big }>;
const netPerformanceValues: WithCurrencyEffect<{ [date: string]: Big }> = { } = this.InitializeSymbolMetricValues();
Value: {},
WithCurrencyEffect: {}
};
const timeWeightedInvestmentValues: WithCurrencyEffect<{
[date: string]: Big;
}> = {
Value: {},
WithCurrencyEffect: {}
};
let totalInvestment: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let totalInvestmentWithGrossPerformanceFromSell: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let totalUnits = new Big(0); const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)];
let valueAtStartDate: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
// Clone orders to keep the original values in this.orders // Clone orders to keep the original values in this.orders
let orders: PortfolioOrderItem[] = cloneDeep(this.orders).filter( let orders: PortfolioOrderItem[] = cloneDeep(this.orders).filter(
@ -1426,6 +1409,104 @@ export class PortfolioCalculator {
}; };
} }
private InitializeSymbolMetricValues() {
const currentValues: WithCurrencyEffect<{ [date: string]: Big }> = {
Value: {},
WithCurrencyEffect: {}
};
let fees: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let feesAtStartDate: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let grossPerformance: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let grossPerformanceAtStartDate: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let grossPerformanceFromSells: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let averagePriceAtEndDate = new Big(0);
let averagePriceAtStartDate = new Big(0);
const investmentValues: WithCurrencyEffect<{ [date: string]: Big }> = {
Value: {},
WithCurrencyEffect: {}
};
const maxInvestmentValues: { [date: string]: Big } = {};
let maxTotalInvestment = new Big(0);
const netPerformanceValuesPercentage: { [date: string]: Big } = {};
let initialValue;
let investmentAtStartDate;
const investmentValuesAccumulated: WithCurrencyEffect<{
[date: string]: Big;
}> = {
Value: {},
WithCurrencyEffect: {}
};
let lastAveragePrice: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
const netPerformanceValues: WithCurrencyEffect<{ [date: string]: Big }> = {
Value: {},
WithCurrencyEffect: {}
};
const timeWeightedInvestmentValues: WithCurrencyEffect<{
[date: string]: Big;
}> = {
Value: {},
WithCurrencyEffect: {}
};
let totalInvestment: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let totalInvestmentWithGrossPerformanceFromSell: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
let totalUnits = new Big(0);
let valueAtStartDate: WithCurrencyEffect<Big> = {
Value: new Big(0),
WithCurrencyEffect: new Big(0)
};
return {
averagePriceAtStartDate,
totalUnits,
totalInvestment,
investmentAtStartDate,
valueAtStartDate,
maxTotalInvestment,
averagePriceAtEndDate,
initialValue,
fees,
feesAtStartDate,
lastAveragePrice,
grossPerformanceFromSells,
totalInvestmentWithGrossPerformanceFromSell,
grossPerformance,
grossPerformanceAtStartDate,
currentValues,
netPerformanceValues,
netPerformanceValuesPercentage,
investmentValues,
investmentValuesAccumulated,
maxInvestmentValues,
timeWeightedInvestmentValues
};
}
@LogPerformance @LogPerformance
private calculatePerformanceOfSymbol( private calculatePerformanceOfSymbol(
orders: PortfolioOrderItem[], orders: PortfolioOrderItem[],

Loading…
Cancel
Save