Browse Source

Open portfolio calculator to overwrite calcluations

pull/5027/head
Dan 2 months ago
parent
commit
1e0e318c17
  1. 180
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  2. 3
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-symbolmetrics-helper.ts
  3. 68
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator.ts

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

@ -71,8 +71,8 @@ export abstract class PortfolioCalculator {
private filters: Filter[]; private filters: Filter[];
private portfolioSnapshotService: PortfolioSnapshotService; private portfolioSnapshotService: PortfolioSnapshotService;
private redisCacheService: RedisCacheService; private redisCacheService: RedisCacheService;
private snapshot: PortfolioSnapshot; protected snapshot: PortfolioSnapshot;
private snapshotPromise: Promise<void>; protected snapshotPromise: Promise<void>;
private startDate: Date; private startDate: Date;
private transactionPoints: TransactionPoint[]; private transactionPoints: TransactionPoint[];
protected userId: string; protected userId: string;
@ -557,56 +557,9 @@ export abstract class PortfolioCalculator {
} }
} }
const historicalData: HistoricalDataItem[] = Object.entries( const historicalData: HistoricalDataItem[] = this.getHistoricalDataItems(
accumulatedValuesByDate accumulatedValuesByDate
).map(([date, values]) => { );
const {
investmentValueWithCurrencyEffect,
totalAccountBalanceWithCurrencyEffect,
totalCurrentValue,
totalCurrentValueWithCurrencyEffect,
totalInvestmentValue,
totalInvestmentValueWithCurrencyEffect,
totalNetPerformanceValue,
totalNetPerformanceValueWithCurrencyEffect,
totalTimeWeightedInvestmentValue,
totalTimeWeightedInvestmentValueWithCurrencyEffect
} = values;
const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0)
? 0
: totalNetPerformanceValue
.div(totalTimeWeightedInvestmentValue)
.toNumber();
const netPerformanceInPercentageWithCurrencyEffect =
totalTimeWeightedInvestmentValueWithCurrencyEffect.eq(0)
? 0
: totalNetPerformanceValueWithCurrencyEffect
.div(totalTimeWeightedInvestmentValueWithCurrencyEffect)
.toNumber();
return {
date,
netPerformanceInPercentage,
netPerformanceInPercentageWithCurrencyEffect,
investmentValueWithCurrencyEffect:
investmentValueWithCurrencyEffect.toNumber(),
netPerformance: totalNetPerformanceValue.toNumber(),
netPerformanceWithCurrencyEffect:
totalNetPerformanceValueWithCurrencyEffect.toNumber(),
// TODO: Add valuables
netWorth: totalCurrentValueWithCurrencyEffect
.plus(totalAccountBalanceWithCurrencyEffect)
.toNumber(),
totalAccountBalance: totalAccountBalanceWithCurrencyEffect.toNumber(),
totalInvestment: totalInvestmentValue.toNumber(),
totalInvestmentValueWithCurrencyEffect:
totalInvestmentValueWithCurrencyEffect.toNumber(),
value: totalCurrentValue.toNumber(),
valueWithCurrencyEffect: totalCurrentValueWithCurrencyEffect.toNumber()
};
});
const overall = this.calculateOverallPerformance(positions); const overall = this.calculateOverallPerformance(positions);
@ -865,39 +818,69 @@ export abstract class PortfolioCalculator {
}; };
} }
public getStartDate() { @LogPerformance
let firstAccountBalanceDate: Date; protected getHistoricalDataItems(accumulatedValuesByDate: {
let firstActivityDate: Date; [date: string]: {
investmentValueWithCurrencyEffect: Big;
try { totalAccountBalanceWithCurrencyEffect: Big;
const firstAccountBalanceDateString = this.accountBalanceItems[0]?.date; totalCurrentValue: Big;
firstAccountBalanceDate = firstAccountBalanceDateString totalCurrentValueWithCurrencyEffect: Big;
? parseDate(firstAccountBalanceDateString) totalInvestmentValue: Big;
: new Date(); totalInvestmentValueWithCurrencyEffect: Big;
} catch (error) { totalNetPerformanceValue: Big;
firstAccountBalanceDate = new Date(); totalNetPerformanceValueWithCurrencyEffect: Big;
} totalTimeWeightedInvestmentValue: Big;
totalTimeWeightedInvestmentValueWithCurrencyEffect: Big;
try { };
const firstActivityDateString = this.transactionPoints[0].date; }): HistoricalDataItem[] {
firstActivityDate = firstActivityDateString return Object.entries(accumulatedValuesByDate).map(([date, values]) => {
? parseDate(firstActivityDateString) const {
: new Date(); investmentValueWithCurrencyEffect,
} catch (error) { totalAccountBalanceWithCurrencyEffect,
firstActivityDate = new Date(); totalCurrentValue,
} totalCurrentValueWithCurrencyEffect,
totalInvestmentValue,
return min([firstAccountBalanceDate, firstActivityDate]); totalInvestmentValueWithCurrencyEffect,
} totalNetPerformanceValue,
totalNetPerformanceValueWithCurrencyEffect,
totalTimeWeightedInvestmentValue,
totalTimeWeightedInvestmentValueWithCurrencyEffect
} = values;
public getTransactionPoints() { const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0)
return this.transactionPoints; ? 0
} : totalNetPerformanceValue
.div(totalTimeWeightedInvestmentValue)
.toNumber();
public async getValuablesInBaseCurrency() { const netPerformanceInPercentageWithCurrencyEffect =
await this.snapshotPromise; totalTimeWeightedInvestmentValueWithCurrencyEffect.eq(0)
? 0
: totalNetPerformanceValueWithCurrencyEffect
.div(totalTimeWeightedInvestmentValueWithCurrencyEffect)
.toNumber();
return this.snapshot.totalValuablesWithCurrencyEffect; return {
date,
netPerformanceInPercentage,
netPerformanceInPercentageWithCurrencyEffect,
investmentValueWithCurrencyEffect:
investmentValueWithCurrencyEffect.toNumber(),
netPerformance: totalNetPerformanceValue.toNumber(),
netPerformanceWithCurrencyEffect:
totalNetPerformanceValueWithCurrencyEffect.toNumber(),
// TODO: Add valuables
netWorth: totalCurrentValueWithCurrencyEffect
.plus(totalAccountBalanceWithCurrencyEffect)
.toNumber(),
totalAccountBalance: totalAccountBalanceWithCurrencyEffect.toNumber(),
totalInvestment: totalInvestmentValue.toNumber(),
totalInvestmentValueWithCurrencyEffect:
totalInvestmentValueWithCurrencyEffect.toNumber(),
value: totalCurrentValue.toNumber(),
valueWithCurrencyEffect: totalCurrentValueWithCurrencyEffect.toNumber()
};
});
} }
@LogPerformance @LogPerformance
@ -1281,6 +1264,41 @@ export abstract class PortfolioCalculator {
); );
} }
public getStartDate() {
let firstAccountBalanceDate: Date;
let firstActivityDate: Date;
try {
const firstAccountBalanceDateString = this.accountBalanceItems[0]?.date;
firstAccountBalanceDate = firstAccountBalanceDateString
? parseDate(firstAccountBalanceDateString)
: new Date();
} catch (error) {
firstAccountBalanceDate = new Date();
}
try {
const firstActivityDateString = this.transactionPoints[0].date;
firstActivityDate = firstActivityDateString
? parseDate(firstActivityDateString)
: new Date();
} catch (error) {
firstActivityDate = new Date();
}
return min([firstAccountBalanceDate, firstActivityDate]);
}
public getTransactionPoints() {
return this.transactionPoints;
}
public async getValuablesInBaseCurrency() {
await this.snapshotPromise;
return this.snapshot.totalValuablesWithCurrencyEffect;
}
private calculateHoldings( private calculateHoldings(
investmentByDate: { [date: string]: PortfolioOrder[] }, investmentByDate: { [date: string]: PortfolioOrder[] },
start: Date, start: Date,

3
apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-symbolmetrics-helper.ts

@ -1,3 +1,4 @@
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { SymbolMetrics } from '@ghostfolio/common/interfaces'; import { SymbolMetrics } from '@ghostfolio/common/interfaces';
@ -27,6 +28,7 @@ export class RoiPortfolioCalculatorSymbolMetricsHelper {
this.chartDates = chartDates; this.chartDates = chartDates;
} }
@LogPerformance
public calculateNetPerformanceByDateRange( public calculateNetPerformanceByDateRange(
start: Date, start: Date,
symbolMetricsHelper: PortfolioCalculatorSymbolMetricsHelperObject symbolMetricsHelper: PortfolioCalculatorSymbolMetricsHelperObject
@ -119,6 +121,7 @@ export class RoiPortfolioCalculatorSymbolMetricsHelper {
} }
} }
@LogPerformance
public processOrderMetrics( public processOrderMetrics(
orders: PortfolioOrderItem[], orders: PortfolioOrderItem[],
i: number, i: number,

68
apps/api/src/app/portfolio/calculator/roi/portfolio-calculator.ts

@ -1,6 +1,8 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import { import {
AssetProfileIdentifier, AssetProfileIdentifier,
HistoricalDataItem,
SymbolMetrics SymbolMetrics
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models'; import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models';
@ -15,6 +17,63 @@ import { RoiPortfolioCalculatorSymbolMetricsHelper } from './portfolio-calculato
export class RoiPortfolioCalculator extends PortfolioCalculator { export class RoiPortfolioCalculator extends PortfolioCalculator {
private chartDates: string[]; private chartDates: string[];
//TODO Overwrite historicalData creation for ROI --> Use TimeWeighted as used for chart
@LogPerformance
public override async getPerformance({
end,
start
}: {
end: string | number | Date;
start: string | number | Date;
}): Promise<{
chart: HistoricalDataItem[];
netPerformance: number;
netPerformanceInPercentage: number;
netPerformanceWithCurrencyEffect: number;
netPerformanceInPercentageWithCurrencyEffect: number;
netWorth: number;
totalInvestment: number;
valueWithCurrencyEffect: number;
}> {
await this.snapshotPromise;
const { positions } = this.snapshot;
const { chart } = await super.getPerformance({ start, end });
const last = chart.at(-1);
const netWorth = last.netWorth;
const totalInvestment = last.totalInvestment;
const valueWithCurrencyEffect = last.valueWithCurrencyEffect;
let netPerformance: number;
let netPerformanceInPercentage: number;
let netPerformanceWithCurrencyEffect: number;
let netPerformanceInPercentageWithCurrencyEffect: number;
for (const position of positions) {
netPerformance = netPerformance + position.netPerformance.toNumber();
netPerformanceInPercentage =
netPerformanceInPercentage *
position.valueInBaseCurrency.div(netWorth).toNumber();
//TODO Calculate performance values not using chart
}
return {
chart,
netPerformance,
netPerformanceInPercentage,
netPerformanceWithCurrencyEffect,
netPerformanceInPercentageWithCurrencyEffect,
netWorth,
totalInvestment,
valueWithCurrencyEffect
};
}
@LogPerformance
protected calculateOverallPerformance( protected calculateOverallPerformance(
positions: TimelinePosition[] positions: TimelinePosition[]
): PortfolioSnapshot { ): PortfolioSnapshot {
@ -76,10 +135,7 @@ export class RoiPortfolioCalculator extends PortfolioCalculator {
}; };
} }
protected getPerformanceCalculationType() { @LogPerformance
return PerformanceCalculationType.ROI;
}
protected getSymbolMetrics({ protected getSymbolMetrics({
chartDateMap, chartDateMap,
dataSource, dataSource,
@ -183,6 +239,10 @@ export class RoiPortfolioCalculator extends PortfolioCalculator {
return symbolMetricsHelper.symbolMetrics; return symbolMetricsHelper.symbolMetrics;
} }
protected getPerformanceCalculationType() {
return PerformanceCalculationType.ROI;
}
private calculatePositionMetrics( private calculatePositionMetrics(
currentPosition: TimelinePosition, currentPosition: TimelinePosition,
totalFeesWithCurrencyEffect: Big, totalFeesWithCurrencyEffect: Big,

Loading…
Cancel
Save