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 portfolioSnapshotService: PortfolioSnapshotService;
private redisCacheService: RedisCacheService;
private snapshot: PortfolioSnapshot;
private snapshotPromise: Promise<void>;
protected snapshot: PortfolioSnapshot;
protected snapshotPromise: Promise<void>;
private startDate: Date;
private transactionPoints: TransactionPoint[];
protected userId: string;
@ -557,56 +557,9 @@ export abstract class PortfolioCalculator {
}
}
const historicalData: HistoricalDataItem[] = Object.entries(
const historicalData: HistoricalDataItem[] = this.getHistoricalDataItems(
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);
@ -865,39 +818,69 @@ 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]);
}
@LogPerformance
protected getHistoricalDataItems(accumulatedValuesByDate: {
[date: string]: {
investmentValueWithCurrencyEffect: Big;
totalAccountBalanceWithCurrencyEffect: Big;
totalCurrentValue: Big;
totalCurrentValueWithCurrencyEffect: Big;
totalInvestmentValue: Big;
totalInvestmentValueWithCurrencyEffect: Big;
totalNetPerformanceValue: Big;
totalNetPerformanceValueWithCurrencyEffect: Big;
totalTimeWeightedInvestmentValue: Big;
totalTimeWeightedInvestmentValueWithCurrencyEffect: Big;
};
}): HistoricalDataItem[] {
return Object.entries(accumulatedValuesByDate).map(([date, values]) => {
const {
investmentValueWithCurrencyEffect,
totalAccountBalanceWithCurrencyEffect,
totalCurrentValue,
totalCurrentValueWithCurrencyEffect,
totalInvestmentValue,
totalInvestmentValueWithCurrencyEffect,
totalNetPerformanceValue,
totalNetPerformanceValueWithCurrencyEffect,
totalTimeWeightedInvestmentValue,
totalTimeWeightedInvestmentValueWithCurrencyEffect
} = values;
public getTransactionPoints() {
return this.transactionPoints;
}
const netPerformanceInPercentage = totalTimeWeightedInvestmentValue.eq(0)
? 0
: totalNetPerformanceValue
.div(totalTimeWeightedInvestmentValue)
.toNumber();
public async getValuablesInBaseCurrency() {
await this.snapshotPromise;
const netPerformanceInPercentageWithCurrencyEffect =
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
@ -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(
investmentByDate: { [date: string]: PortfolioOrder[] },
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 { DATE_FORMAT } from '@ghostfolio/common/helper';
import { SymbolMetrics } from '@ghostfolio/common/interfaces';
@ -27,6 +28,7 @@ export class RoiPortfolioCalculatorSymbolMetricsHelper {
this.chartDates = chartDates;
}
@LogPerformance
public calculateNetPerformanceByDateRange(
start: Date,
symbolMetricsHelper: PortfolioCalculatorSymbolMetricsHelperObject
@ -119,6 +121,7 @@ export class RoiPortfolioCalculatorSymbolMetricsHelper {
}
}
@LogPerformance
public processOrderMetrics(
orders: PortfolioOrderItem[],
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 { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import {
AssetProfileIdentifier,
HistoricalDataItem,
SymbolMetrics
} from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models';
@ -15,6 +17,63 @@ import { RoiPortfolioCalculatorSymbolMetricsHelper } from './portfolio-calculato
export class RoiPortfolioCalculator extends PortfolioCalculator {
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(
positions: TimelinePosition[]
): PortfolioSnapshot {
@ -76,10 +135,7 @@ export class RoiPortfolioCalculator extends PortfolioCalculator {
};
}
protected getPerformanceCalculationType() {
return PerformanceCalculationType.ROI;
}
@LogPerformance
protected getSymbolMetrics({
chartDateMap,
dataSource,
@ -183,6 +239,10 @@ export class RoiPortfolioCalculator extends PortfolioCalculator {
return symbolMetricsHelper.symbolMetrics;
}
protected getPerformanceCalculationType() {
return PerformanceCalculationType.ROI;
}
private calculatePositionMetrics(
currentPosition: TimelinePosition,
totalFeesWithCurrencyEffect: Big,

Loading…
Cancel
Save