Browse Source

Task/rename symbol metrics to holding performance (#7912)

Rename types of portfolio calculator to HoldingPerformance, PortfolioCalculatorHolding and PortfolioSnapshotHolding
pull/7914/head
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
afbb37aed7
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 23
      apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts
  2. 59
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  3. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  4. 54
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts
  5. 23
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator.ts
  6. 23
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
  7. 8
      apps/api/src/app/portfolio/interfaces/holding-performance.interface.ts
  8. 6
      apps/api/src/app/portfolio/interfaces/portfolio-calculator-holding.interface.ts
  9. 6
      apps/api/src/app/portfolio/interfaces/portfolio-calculator-position.interface.ts
  10. 6
      apps/api/src/app/portfolio/portfolio.service.ts
  11. 2
      libs/common/src/lib/interfaces/index.ts
  12. 4
      libs/common/src/lib/models/index.ts
  13. 2
      libs/common/src/lib/models/portfolio-snapshot-holding.ts
  14. 6
      libs/common/src/lib/models/portfolio-snapshot.ts

23
apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts

@ -1,29 +1,28 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface';
AssetProfileIdentifier, import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface';
SymbolMetrics import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
} from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot } from '@ghostfolio/common/models'; import { PortfolioSnapshot } from '@ghostfolio/common/models';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
export class MwrPortfolioCalculator extends PortfolioCalculator { export class MwrPortfolioCalculator extends PortfolioCalculator {
protected calculateOverallPerformance(): PortfolioSnapshot { protected calculateOverallPerformance([]: PortfolioCalculatorHolding[]): PortfolioSnapshot {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getPerformanceCalculationType() { protected getHoldingPerformance({}: {
return PerformanceCalculationType.MWR; chartDateMap: { [date: string]: boolean };
}
protected getSymbolMetrics({}: {
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: { [dateString: string]: number };
marketSymbolMap: { marketSymbolMap: {
[date: string]: { [assetProfileIdentifier: string]: Big }; [date: string]: { [assetProfileIdentifier: string]: Big };
}; };
start: Date; start: Date;
step?: number; } & AssetProfileIdentifier): HoldingPerformance {
} & AssetProfileIdentifier): SymbolMetrics {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getPerformanceCalculationType() {
return PerformanceCalculationType.MWR;
}
} }

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

@ -1,6 +1,7 @@
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { PortfolioSnapshotComputationError } from '@ghostfolio/api/app/portfolio/errors/portfolio-snapshot-computation.error'; import { PortfolioSnapshotComputationError } from '@ghostfolio/api/app/portfolio/errors/portfolio-snapshot-computation.error';
import { PortfolioCalculatorPosition } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-position.interface'; import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface';
import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface';
import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface'; import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface';
import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface';
import { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface'; import { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface';
@ -35,8 +36,7 @@ import {
Filter, Filter,
HistoricalDataItem, HistoricalDataItem,
InvestmentItem, InvestmentItem,
ResponseError, ResponseError
SymbolMetrics
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot } from '@ghostfolio/common/models'; import { PortfolioSnapshot } from '@ghostfolio/common/models';
import { GroupBy } from '@ghostfolio/common/types'; import { GroupBy } from '@ghostfolio/common/types';
@ -209,7 +209,7 @@ export abstract class PortfolioCalculator {
} }
protected abstract calculateOverallPerformance( protected abstract calculateOverallPerformance(
positions: PortfolioCalculatorPosition[] positions: PortfolioCalculatorHolding[]
): PortfolioSnapshot; ): PortfolioSnapshot;
@LogPerformance @LogPerformance
@ -343,9 +343,9 @@ export abstract class PortfolioCalculator {
} }
const errors: ResponseError['errors'] = []; const errors: ResponseError['errors'] = [];
let hasAnySymbolMetricsErrors = false; let hasAnyHoldingPerformanceErrors = false;
const positions: PortfolioCalculatorPosition[] = []; const positions: PortfolioCalculatorHolding[] = [];
const accumulatedValuesByDate: { const accumulatedValuesByDate: {
[date: string]: { [date: string]: {
@ -424,7 +424,7 @@ export abstract class PortfolioCalculator {
totalInvestment, totalInvestment,
totalInvestmentWithCurrencyEffect, totalInvestmentWithCurrencyEffect,
totalLiabilitiesInBaseCurrency totalLiabilitiesInBaseCurrency
} = this.getSymbolMetrics({ } = this.getHoldingPerformance({
chartDateMap, chartDateMap,
marketSymbolMap, marketSymbolMap,
dataSource: item.dataSource, dataSource: item.dataSource,
@ -435,7 +435,8 @@ export abstract class PortfolioCalculator {
symbol: item.symbol symbol: item.symbol
}); });
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; hasAnyHoldingPerformanceErrors =
hasAnyHoldingPerformanceErrors || hasErrors;
// Cash in the base currency cannot generate a currency effect and thus // Cash in the base currency cannot generate a currency effect and thus
// contributes nothing but its balance to the performance calculation. It // contributes nothing but its balance to the performance calculation. It
@ -708,13 +709,11 @@ export abstract class PortfolioCalculator {
totalCashInBaseCurrency, totalCashInBaseCurrency,
totalInterestWithCurrencyEffect, totalInterestWithCurrencyEffect,
totalLiabilitiesWithCurrencyEffect, totalLiabilitiesWithCurrencyEffect,
hasErrors: hasAnySymbolMetricsErrors || overall.hasErrors, hasErrors: hasAnyHoldingPerformanceErrors || overall.hasErrors,
positions: positionsIncludedInHoldings positions: positionsIncludedInHoldings
}; };
} }
protected abstract getPerformanceCalculationType(): PerformanceCalculationType;
public getDataProviderInfos() { public getDataProviderInfos() {
return this.dataProviderInfos; return this.dataProviderInfos;
} }
@ -735,6 +734,24 @@ export abstract class PortfolioCalculator {
return this.snapshot.totalFeesWithCurrencyEffect; return this.snapshot.totalFeesWithCurrencyEffect;
} }
protected abstract getHoldingPerformance({
chartDateMap,
dataSource,
end,
exchangeRates,
marketSymbolMap,
start,
symbol
}: {
chartDateMap: { [date: string]: boolean };
end: Date;
exchangeRates: { [dateString: string]: number };
marketSymbolMap: {
[date: string]: { [assetProfileIdentifier: string]: Big };
};
start: Date;
} & AssetProfileIdentifier): HoldingPerformance;
public async getInterestInBaseCurrency() { public async getInterestInBaseCurrency() {
await this.snapshotPromise; await this.snapshotPromise;
@ -875,6 +892,8 @@ export abstract class PortfolioCalculator {
return { chart }; return { chart };
} }
protected abstract getPerformanceCalculationType(): PerformanceCalculationType;
public async getSnapshot() { public async getSnapshot() {
await this.snapshotPromise; await this.snapshotPromise;
@ -920,24 +939,6 @@ export abstract class PortfolioCalculator {
return min(dates); return min(dates);
} }
protected abstract getSymbolMetrics({
chartDateMap,
dataSource,
end,
exchangeRates,
marketSymbolMap,
start,
symbol
}: {
chartDateMap: { [date: string]: boolean };
end: Date;
exchangeRates: { [dateString: string]: number };
marketSymbolMap: {
[date: string]: { [assetProfileIdentifier: string]: Big };
};
start: Date;
} & AssetProfileIdentifier): SymbolMetrics;
public getTransactionPoints() { public getTransactionPoints() {
return this.transactionPoints; return this.transactionPoints;
} }

6
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts

@ -19,7 +19,7 @@ import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-r
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper';
import { TimelinePosition } from '@ghostfolio/common/models'; import { PortfolioSnapshotHolding } from '@ghostfolio/common/models';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
import { DataSource } from '@prisma/client'; import { DataSource } from '@prisma/client';
@ -250,7 +250,7 @@ describe('PortfolioCalculator', () => {
* Total account balance: 2000 USD * 0.85 = 1700 CHF (using the exchange rate on 2024-12-31) * Total account balance: 2000 USD * 0.85 = 1700 CHF (using the exchange rate on 2024-12-31)
* Value in base currency: 2000 USD * 0.91 = 1820 CHF * Value in base currency: 2000 USD * 0.91 = 1820 CHF
*/ */
expect(position).toMatchObject<TimelinePosition>({ expect(position).toMatchObject<PortfolioSnapshotHolding>({
activitiesCount: 2, activitiesCount: 2,
averagePrice: new Big(1), averagePrice: new Big(1),
currency: 'USD', currency: 'USD',
@ -422,7 +422,7 @@ describe('PortfolioCalculator', () => {
* The holding itself keeps its investment and value so that it remains * The holding itself keeps its investment and value so that it remains
* visible in the holdings table * visible in the holdings table
*/ */
expect(position).toMatchObject<Partial<TimelinePosition>>({ expect(position).toMatchObject<Partial<PortfolioSnapshotHolding>>({
currency: 'CHF', currency: 'CHF',
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformanceWithCurrencyEffect: new Big(0), grossPerformanceWithCurrencyEffect: new Big(0),

54
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts

@ -1,5 +1,6 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { PortfolioCalculatorPosition } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-position.interface'; import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface';
import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface';
import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface'; import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface';
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
@ -8,10 +9,7 @@ import {
getAssetProfileIdentifier, getAssetProfileIdentifier,
parseDate parseDate
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
import { import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
AssetProfileIdentifier,
SymbolMetrics
} from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot } from '@ghostfolio/common/models'; import { PortfolioSnapshot } from '@ghostfolio/common/models';
import { DateRange } from '@ghostfolio/common/types'; import { DateRange } from '@ghostfolio/common/types';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
@ -31,7 +29,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
private chartDates: string[]; private chartDates: string[];
protected calculateOverallPerformance( protected calculateOverallPerformance(
positions: PortfolioCalculatorPosition[] positions: PortfolioCalculatorHolding[]
): PortfolioSnapshot { ): PortfolioSnapshot {
let currentValueInBaseCurrency = new Big(0); let currentValueInBaseCurrency = new Big(0);
let grossPerformance = new Big(0); let grossPerformance = new Big(0);
@ -127,11 +125,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
}; };
} }
protected getPerformanceCalculationType() { protected getHoldingPerformance({
return PerformanceCalculationType.ROAI;
}
protected getSymbolMetrics({
chartDateMap, chartDateMap,
dataSource, dataSource,
end, end,
@ -140,14 +134,14 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
start, start,
symbol symbol
}: { }: {
chartDateMap?: { [date: string]: boolean }; chartDateMap: { [date: string]: boolean };
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: { [dateString: string]: number };
marketSymbolMap: { marketSymbolMap: {
[date: string]: { [assetProfileIdentifier: string]: Big }; [date: string]: { [assetProfileIdentifier: string]: Big };
}; };
start: Date; start: Date;
} & AssetProfileIdentifier): SymbolMetrics { } & AssetProfileIdentifier): HoldingPerformance {
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)];
const currentValues: { [date: string]: Big } = {}; const currentValues: { [date: string]: Big } = {};
const currentValuesWithCurrencyEffect: { [date: string]: Big } = {}; const currentValuesWithCurrencyEffect: { [date: string]: Big } = {};
@ -162,7 +156,6 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
let grossPerformanceFromSells = new Big(0); let grossPerformanceFromSells = new Big(0);
let grossPerformanceFromSellsWithCurrencyEffect = new Big(0); let grossPerformanceFromSellsWithCurrencyEffect = new Big(0);
let initialValue: Big; let initialValue: Big;
let initialValueWithCurrencyEffect: Big;
let investmentAtStartDate: Big; let investmentAtStartDate: Big;
let investmentAtStartDateWithCurrencyEffect: Big; let investmentAtStartDateWithCurrencyEffect: Big;
const investmentValuesAccumulated: { [date: string]: Big } = {}; const investmentValuesAccumulated: { [date: string]: Big } = {};
@ -180,16 +173,13 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
[date: string]: Big; [date: string]: Big;
} = {}; } = {};
const totalAccountBalanceInBaseCurrency = new Big(0);
let totalDividend = new Big(0); let totalDividend = new Big(0);
let totalDividendInBaseCurrency = new Big(0); let totalDividendInBaseCurrency = new Big(0);
let totalInterest = new Big(0);
let totalInterestInBaseCurrency = new Big(0); let totalInterestInBaseCurrency = new Big(0);
let totalInvestment = new Big(0); let totalInvestment = new Big(0);
let totalInvestmentFromBuyTransactions = new Big(0); let totalInvestmentFromBuyTransactions = new Big(0);
let totalInvestmentFromBuyTransactionsWithCurrencyEffect = new Big(0); let totalInvestmentFromBuyTransactionsWithCurrencyEffect = new Big(0);
let totalInvestmentWithCurrencyEffect = new Big(0); let totalInvestmentWithCurrencyEffect = new Big(0);
let totalLiabilities = new Big(0);
let totalLiabilitiesInBaseCurrency = new Big(0); let totalLiabilitiesInBaseCurrency = new Big(0);
let totalQuantity = new Big(0); let totalQuantity = new Big(0);
let totalQuantityFromBuyTransactions = new Big(0); let totalQuantityFromBuyTransactions = new Big(0);
@ -215,14 +205,11 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
return { return {
currentValues: {}, currentValues: {},
currentValuesWithCurrencyEffect: {}, currentValuesWithCurrencyEffect: {},
feesWithCurrencyEffect: new Big(0),
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
grossPerformancePercentageWithCurrencyEffect: new Big(0), grossPerformancePercentageWithCurrencyEffect: new Big(0),
grossPerformanceWithCurrencyEffect: new Big(0), grossPerformanceWithCurrencyEffect: new Big(0),
hasErrors: false, hasErrors: false,
initialValue: new Big(0),
initialValueWithCurrencyEffect: new Big(0),
investmentValuesAccumulated: {}, investmentValuesAccumulated: {},
investmentValuesAccumulatedWithCurrencyEffect: {}, investmentValuesAccumulatedWithCurrencyEffect: {},
investmentValuesWithCurrencyEffect: {}, investmentValuesWithCurrencyEffect: {},
@ -236,14 +223,11 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
timeWeightedInvestmentValues: {}, timeWeightedInvestmentValues: {},
timeWeightedInvestmentValuesWithCurrencyEffect: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {},
timeWeightedInvestmentWithCurrencyEffect: new Big(0), timeWeightedInvestmentWithCurrencyEffect: new Big(0),
totalAccountBalanceInBaseCurrency: new Big(0),
totalDividend: new Big(0), totalDividend: new Big(0),
totalDividendInBaseCurrency: new Big(0), totalDividendInBaseCurrency: new Big(0),
totalInterest: new Big(0),
totalInterestInBaseCurrency: new Big(0), totalInterestInBaseCurrency: new Big(0),
totalInvestment: new Big(0), totalInvestment: new Big(0),
totalInvestmentWithCurrencyEffect: new Big(0), totalInvestmentWithCurrencyEffect: new Big(0),
totalLiabilities: new Big(0),
totalLiabilitiesInBaseCurrency: new Big(0) totalLiabilitiesInBaseCurrency: new Big(0)
}; };
} }
@ -264,14 +248,12 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
} else if (order.type === 'INTEREST') { } else if (order.type === 'INTEREST') {
const interest = order.quantity.mul(order.unitPrice); const interest = order.quantity.mul(order.unitPrice);
totalInterest = totalInterest.plus(interest);
totalInterestInBaseCurrency = totalInterestInBaseCurrency.plus( totalInterestInBaseCurrency = totalInterestInBaseCurrency.plus(
interest.mul(exchangeRateAtOrderDate ?? 1) interest.mul(exchangeRateAtOrderDate ?? 1)
); );
} else if (order.type === 'LIABILITY') { } else if (order.type === 'LIABILITY') {
const liabilities = order.quantity.mul(order.unitPrice); const liabilities = order.quantity.mul(order.unitPrice);
totalLiabilities = totalLiabilities.plus(liabilities);
totalLiabilitiesInBaseCurrency = totalLiabilitiesInBaseCurrency.plus( totalLiabilitiesInBaseCurrency = totalLiabilitiesInBaseCurrency.plus(
liabilities.mul(exchangeRateAtOrderDate ?? 1) liabilities.mul(exchangeRateAtOrderDate ?? 1)
); );
@ -319,20 +301,15 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
return { return {
totalDividend, totalDividend,
totalDividendInBaseCurrency, totalDividendInBaseCurrency,
totalInterest,
totalInterestInBaseCurrency, totalInterestInBaseCurrency,
totalLiabilities,
totalLiabilitiesInBaseCurrency, totalLiabilitiesInBaseCurrency,
currentValues: {}, currentValues: {},
currentValuesWithCurrencyEffect: {}, currentValuesWithCurrencyEffect: {},
feesWithCurrencyEffect: new Big(0),
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
grossPerformancePercentageWithCurrencyEffect: new Big(0), grossPerformancePercentageWithCurrencyEffect: new Big(0),
grossPerformanceWithCurrencyEffect: new Big(0), grossPerformanceWithCurrencyEffect: new Big(0),
hasErrors: hasActivitiesWithQuantity, hasErrors: hasActivitiesWithQuantity,
initialValue: new Big(0),
initialValueWithCurrencyEffect: new Big(0),
investmentValuesAccumulated: {}, investmentValuesAccumulated: {},
investmentValuesAccumulatedWithCurrencyEffect: {}, investmentValuesAccumulatedWithCurrencyEffect: {},
investmentValuesWithCurrencyEffect: {}, investmentValuesWithCurrencyEffect: {},
@ -346,7 +323,6 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
timeWeightedInvestmentValues: {}, timeWeightedInvestmentValues: {},
timeWeightedInvestmentValuesWithCurrencyEffect: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {},
timeWeightedInvestmentWithCurrencyEffect: new Big(0), timeWeightedInvestmentWithCurrencyEffect: new Big(0),
totalAccountBalanceInBaseCurrency: new Big(0),
totalInvestment: new Big(0), totalInvestment: new Big(0),
totalInvestmentWithCurrencyEffect: new Big(0) totalInvestmentWithCurrencyEffect: new Big(0)
}; };
@ -607,14 +583,8 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
!valueOfInvestmentBeforeTransaction.eq(0) !valueOfInvestmentBeforeTransaction.eq(0)
) { ) {
initialValue = valueOfInvestmentBeforeTransaction; initialValue = valueOfInvestmentBeforeTransaction;
initialValueWithCurrencyEffect =
valueOfInvestmentBeforeTransactionWithCurrencyEffect;
} else if (transactionInvestment.gt(0)) { } else if (transactionInvestment.gt(0)) {
initialValue = transactionInvestment; initialValue = transactionInvestment;
initialValueWithCurrencyEffect =
transactionInvestmentWithCurrencyEffect;
} }
} }
@ -1016,11 +986,8 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
return { return {
currentValues, currentValues,
currentValuesWithCurrencyEffect, currentValuesWithCurrencyEffect,
feesWithCurrencyEffect,
grossPerformancePercentage, grossPerformancePercentage,
grossPerformancePercentageWithCurrencyEffect, grossPerformancePercentageWithCurrencyEffect,
initialValue,
initialValueWithCurrencyEffect,
investmentValuesAccumulated, investmentValuesAccumulated,
investmentValuesAccumulatedWithCurrencyEffect, investmentValuesAccumulatedWithCurrencyEffect,
investmentValuesWithCurrencyEffect, investmentValuesWithCurrencyEffect,
@ -1031,14 +998,11 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
netPerformanceWithCurrencyEffectMap, netPerformanceWithCurrencyEffectMap,
timeWeightedInvestmentValues, timeWeightedInvestmentValues,
timeWeightedInvestmentValuesWithCurrencyEffect, timeWeightedInvestmentValuesWithCurrencyEffect,
totalAccountBalanceInBaseCurrency,
totalDividend, totalDividend,
totalDividendInBaseCurrency, totalDividendInBaseCurrency,
totalInterest,
totalInterestInBaseCurrency, totalInterestInBaseCurrency,
totalInvestment, totalInvestment,
totalInvestmentWithCurrencyEffect, totalInvestmentWithCurrencyEffect,
totalLiabilities,
totalLiabilitiesInBaseCurrency, totalLiabilitiesInBaseCurrency,
grossPerformance: totalGrossPerformance, grossPerformance: totalGrossPerformance,
grossPerformanceWithCurrencyEffect: grossPerformanceWithCurrencyEffect:
@ -1051,4 +1015,8 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator {
timeWeightedAverageInvestmentBetweenStartAndEndDateWithCurrencyEffect timeWeightedAverageInvestmentBetweenStartAndEndDateWithCurrencyEffect
}; };
} }
protected getPerformanceCalculationType() {
return PerformanceCalculationType.ROAI;
}
} }

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

@ -1,29 +1,28 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface';
AssetProfileIdentifier, import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface';
SymbolMetrics import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
} from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot } from '@ghostfolio/common/models'; import { PortfolioSnapshot } from '@ghostfolio/common/models';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
export class RoiPortfolioCalculator extends PortfolioCalculator { export class RoiPortfolioCalculator extends PortfolioCalculator {
protected calculateOverallPerformance(): PortfolioSnapshot { protected calculateOverallPerformance([]: PortfolioCalculatorHolding[]): PortfolioSnapshot {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getPerformanceCalculationType() { protected getHoldingPerformance({}: {
return PerformanceCalculationType.ROI; chartDateMap: { [date: string]: boolean };
}
protected getSymbolMetrics({}: {
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: { [dateString: string]: number };
marketSymbolMap: { marketSymbolMap: {
[date: string]: { [assetProfileIdentifier: string]: Big }; [date: string]: { [assetProfileIdentifier: string]: Big };
}; };
start: Date; start: Date;
step?: number; } & AssetProfileIdentifier): HoldingPerformance {
} & AssetProfileIdentifier): SymbolMetrics {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getPerformanceCalculationType() {
return PerformanceCalculationType.ROI;
}
} }

23
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts

@ -1,29 +1,28 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface';
AssetProfileIdentifier, import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface';
SymbolMetrics import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
} from '@ghostfolio/common/interfaces';
import { PortfolioSnapshot } from '@ghostfolio/common/models'; import { PortfolioSnapshot } from '@ghostfolio/common/models';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
export class TwrPortfolioCalculator extends PortfolioCalculator { export class TwrPortfolioCalculator extends PortfolioCalculator {
protected calculateOverallPerformance(): PortfolioSnapshot { protected calculateOverallPerformance([]: PortfolioCalculatorHolding[]): PortfolioSnapshot {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getPerformanceCalculationType() { protected getHoldingPerformance({}: {
return PerformanceCalculationType.TWR; chartDateMap: { [date: string]: boolean };
}
protected getSymbolMetrics({}: {
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: { [dateString: string]: number };
marketSymbolMap: { marketSymbolMap: {
[date: string]: { [assetProfileIdentifier: string]: Big }; [date: string]: { [assetProfileIdentifier: string]: Big };
}; };
start: Date; start: Date;
step?: number; } & AssetProfileIdentifier): HoldingPerformance {
} & AssetProfileIdentifier): SymbolMetrics {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getPerformanceCalculationType() {
return PerformanceCalculationType.TWR;
}
} }

8
libs/common/src/lib/interfaces/symbol-metrics.interface.ts → apps/api/src/app/portfolio/interfaces/holding-performance.interface.ts

@ -2,21 +2,18 @@ import { DateRange } from '@ghostfolio/common/types';
import { Big } from 'big.js'; import { Big } from 'big.js';
export interface SymbolMetrics { export interface HoldingPerformance {
currentValues: { currentValues: {
[date: string]: Big; [date: string]: Big;
}; };
currentValuesWithCurrencyEffect: { currentValuesWithCurrencyEffect: {
[date: string]: Big; [date: string]: Big;
}; };
feesWithCurrencyEffect: Big;
grossPerformance: Big; grossPerformance: Big;
grossPerformancePercentage: Big; grossPerformancePercentage: Big;
grossPerformancePercentageWithCurrencyEffect: Big; grossPerformancePercentageWithCurrencyEffect: Big;
grossPerformanceWithCurrencyEffect: Big; grossPerformanceWithCurrencyEffect: Big;
hasErrors: boolean; hasErrors: boolean;
initialValue: Big;
initialValueWithCurrencyEffect: Big;
investmentValuesAccumulated: { investmentValuesAccumulated: {
[date: string]: Big; [date: string]: Big;
}; };
@ -42,13 +39,10 @@ export interface SymbolMetrics {
[date: string]: Big; [date: string]: Big;
}; };
timeWeightedInvestmentWithCurrencyEffect: Big; timeWeightedInvestmentWithCurrencyEffect: Big;
totalAccountBalanceInBaseCurrency: Big;
totalDividend: Big; totalDividend: Big;
totalDividendInBaseCurrency: Big; totalDividendInBaseCurrency: Big;
totalInterest: Big;
totalInterestInBaseCurrency: Big; totalInterestInBaseCurrency: Big;
totalInvestment: Big; totalInvestment: Big;
totalInvestmentWithCurrencyEffect: Big; totalInvestmentWithCurrencyEffect: Big;
totalLiabilities: Big;
totalLiabilitiesInBaseCurrency: Big; totalLiabilitiesInBaseCurrency: Big;
} }

6
apps/api/src/app/portfolio/interfaces/portfolio-calculator-holding.interface.ts

@ -0,0 +1,6 @@
import { PortfolioSnapshotHolding } from '@ghostfolio/common/models';
export interface PortfolioCalculatorHolding extends PortfolioSnapshotHolding {
includeInHoldings: boolean;
includeInPerformance: boolean;
}

6
apps/api/src/app/portfolio/interfaces/portfolio-calculator-position.interface.ts

@ -1,6 +0,0 @@
import { TimelinePosition } from '@ghostfolio/common/models';
export interface PortfolioCalculatorPosition extends TimelinePosition {
includeInHoldings: boolean;
includeInPerformance: boolean;
}

6
apps/api/src/app/portfolio/portfolio.service.ts

@ -66,7 +66,7 @@ import {
PortfolioSummary, PortfolioSummary,
UserSettings UserSettings
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { TimelinePosition } from '@ghostfolio/common/models'; import { PortfolioSnapshotHolding } from '@ghostfolio/common/models';
import { import {
AccountWithBalance, AccountWithBalance,
AccountWithValue, AccountWithValue,
@ -689,7 +689,7 @@ export class PortfolioService {
} }
const portfolioItemsNow: { const portfolioItemsNow: {
[assetProfileIdentifier: string]: TimelinePosition; [assetProfileIdentifier: string]: PortfolioSnapshotHolding;
} = {}; } = {};
for (const position of positions) { for (const position of positions) {
@ -2316,7 +2316,7 @@ export class PortfolioService {
}: { }: {
activities: Activity[]; activities: Activity[];
filters?: Filter[]; filters?: Filter[];
portfolioItemsNow: Record<string, TimelinePosition>; portfolioItemsNow: Record<string, PortfolioSnapshotHolding>;
userCurrency: string; userCurrency: string;
userId: string; userId: string;
withExcludedAccounts?: boolean; withExcludedAccounts?: boolean;

2
libs/common/src/lib/interfaces/index.ts

@ -92,7 +92,6 @@ import type {
import type { Statistics } from './statistics.interface'; import type { Statistics } from './statistics.interface';
import type { SubscriptionOffer } from './subscription-offer.interface'; import type { SubscriptionOffer } from './subscription-offer.interface';
import type { SymbolItem } from './symbol-item.interface'; import type { SymbolItem } from './symbol-item.interface';
import type { SymbolMetrics } from './symbol-metrics.interface';
import type { SystemMessage } from './system-message.interface'; import type { SystemMessage } from './system-message.interface';
import type { UserItem } from './user-item.interface'; import type { UserItem } from './user-item.interface';
import type { UserSettings } from './user-settings.interface'; import type { UserSettings } from './user-settings.interface';
@ -188,7 +187,6 @@ export {
Statistics, Statistics,
SubscriptionOffer, SubscriptionOffer,
SymbolItem, SymbolItem,
SymbolMetrics,
SystemMessage, SystemMessage,
User, User,
UserItem, UserItem,

4
libs/common/src/lib/models/index.ts

@ -1,4 +1,4 @@
import { PortfolioSnapshot } from './portfolio-snapshot'; import { PortfolioSnapshot } from './portfolio-snapshot';
import { TimelinePosition } from './timeline-position'; import { PortfolioSnapshotHolding } from './portfolio-snapshot-holding';
export { PortfolioSnapshot, TimelinePosition }; export { PortfolioSnapshot, PortfolioSnapshotHolding };

2
libs/common/src/lib/models/timeline-position.ts → libs/common/src/lib/models/portfolio-snapshot-holding.ts

@ -8,7 +8,7 @@ import { DataSource, Tag } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { Transform, Type } from 'class-transformer'; import { Transform, Type } from 'class-transformer';
export class TimelinePosition { export class PortfolioSnapshotHolding {
activitiesCount: number; activitiesCount: number;
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })

6
libs/common/src/lib/models/portfolio-snapshot.ts

@ -3,7 +3,7 @@ import {
AssetProfileIdentifier, AssetProfileIdentifier,
HistoricalDataItem HistoricalDataItem
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { TimelinePosition } from '@ghostfolio/common/models'; import { PortfolioSnapshotHolding } from '@ghostfolio/common/models';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { Transform, Type } from 'class-transformer'; import { Transform, Type } from 'class-transformer';
@ -23,8 +23,8 @@ export class PortfolioSnapshot {
historicalData: HistoricalDataItem[]; historicalData: HistoricalDataItem[];
@Type(() => TimelinePosition) @Type(() => PortfolioSnapshotHolding)
positions: TimelinePosition[]; positions: PortfolioSnapshotHolding[];
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })
@Type(() => Big) @Type(() => Big)

Loading…
Cancel
Save