|
|
|
@ -39,7 +39,6 @@ import { GroupBy } from '@ghostfolio/common/types'; |
|
|
|
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; |
|
|
|
|
|
|
|
import { Logger } from '@nestjs/common'; |
|
|
|
import { AssetSubClass } from '@prisma/client'; |
|
|
|
import { Big } from 'big.js'; |
|
|
|
import { plainToClass } from 'class-transformer'; |
|
|
|
import { |
|
|
|
@ -194,6 +193,7 @@ export abstract class PortfolioCalculator { |
|
|
|
hasErrors: false, |
|
|
|
historicalData: [], |
|
|
|
positions: [], |
|
|
|
totalCashInBaseCurrency: new Big(0), |
|
|
|
totalFeesWithCurrencyEffect: new Big(0), |
|
|
|
totalInterestWithCurrencyEffect: new Big(0), |
|
|
|
totalInvestment: new Big(0), |
|
|
|
@ -202,10 +202,12 @@ export abstract class PortfolioCalculator { |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const cashSymbols = new Set<string>(); |
|
|
|
const currencies: { [symbol: string]: string } = {}; |
|
|
|
const dataGatheringItems: DataGatheringItem[] = []; |
|
|
|
let firstIndex = transactionPoints.length; |
|
|
|
let firstTransactionPoint: TransactionPoint = null; |
|
|
|
let totalCashInBaseCurrency = new Big(0); |
|
|
|
let totalInterestWithCurrencyEffect = new Big(0); |
|
|
|
let totalLiabilitiesWithCurrencyEffect = new Big(0); |
|
|
|
|
|
|
|
@ -314,7 +316,7 @@ export abstract class PortfolioCalculator { |
|
|
|
const accumulatedValuesByDate: { |
|
|
|
[date: string]: { |
|
|
|
investmentValueWithCurrencyEffect: Big; |
|
|
|
totalAccountBalanceWithCurrencyEffect: Big; |
|
|
|
totalCashValueWithCurrencyEffect: Big; |
|
|
|
totalCurrentValue: Big; |
|
|
|
totalCurrentValueWithCurrencyEffect: Big; |
|
|
|
totalInvestmentValue: Big; |
|
|
|
@ -349,6 +351,8 @@ export abstract class PortfolioCalculator { |
|
|
|
] ?? 1 |
|
|
|
); |
|
|
|
|
|
|
|
const valueInBaseCurrency = marketPriceInBaseCurrency.mul(item.quantity); |
|
|
|
|
|
|
|
const { |
|
|
|
currentValues, |
|
|
|
currentValuesWithCurrencyEffect, |
|
|
|
@ -389,25 +393,19 @@ export abstract class PortfolioCalculator { |
|
|
|
|
|
|
|
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; |
|
|
|
|
|
|
|
const includeInTotalAssetValue = |
|
|
|
item.assetSubClass !== AssetSubClass.CASH; |
|
|
|
|
|
|
|
if (includeInTotalAssetValue) { |
|
|
|
valuesBySymbol[item.symbol] = { |
|
|
|
currentValues, |
|
|
|
currentValuesWithCurrencyEffect, |
|
|
|
investmentValuesAccumulated, |
|
|
|
investmentValuesAccumulatedWithCurrencyEffect, |
|
|
|
investmentValuesWithCurrencyEffect, |
|
|
|
netPerformanceValues, |
|
|
|
netPerformanceValuesWithCurrencyEffect, |
|
|
|
timeWeightedInvestmentValues, |
|
|
|
timeWeightedInvestmentValuesWithCurrencyEffect |
|
|
|
}; |
|
|
|
} |
|
|
|
valuesBySymbol[item.symbol] = { |
|
|
|
currentValues, |
|
|
|
currentValuesWithCurrencyEffect, |
|
|
|
investmentValuesAccumulated, |
|
|
|
investmentValuesAccumulatedWithCurrencyEffect, |
|
|
|
investmentValuesWithCurrencyEffect, |
|
|
|
netPerformanceValues, |
|
|
|
netPerformanceValuesWithCurrencyEffect, |
|
|
|
timeWeightedInvestmentValues, |
|
|
|
timeWeightedInvestmentValuesWithCurrencyEffect |
|
|
|
}; |
|
|
|
|
|
|
|
positions.push({ |
|
|
|
includeInTotalAssetValue, |
|
|
|
timeWeightedInvestment, |
|
|
|
timeWeightedInvestmentWithCurrencyEffect, |
|
|
|
activitiesCount: item.activitiesCount, |
|
|
|
@ -448,11 +446,16 @@ export abstract class PortfolioCalculator { |
|
|
|
quantity: item.quantity, |
|
|
|
symbol: item.symbol, |
|
|
|
tags: item.tags, |
|
|
|
valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul( |
|
|
|
item.quantity |
|
|
|
) |
|
|
|
valueInBaseCurrency |
|
|
|
}); |
|
|
|
|
|
|
|
if (item.assetSubClass === 'CASH') { |
|
|
|
cashSymbols.add(item.symbol); |
|
|
|
|
|
|
|
totalCashInBaseCurrency = |
|
|
|
totalCashInBaseCurrency.plus(valueInBaseCurrency); |
|
|
|
} |
|
|
|
|
|
|
|
totalInterestWithCurrencyEffect = totalInterestWithCurrencyEffect.plus( |
|
|
|
totalInterestInBaseCurrency |
|
|
|
); |
|
|
|
@ -472,28 +475,7 @@ export abstract class PortfolioCalculator { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const accountBalanceItemsMap = this.accountBalanceItems.reduce( |
|
|
|
(map, { date, value }) => { |
|
|
|
map[date] = new Big(value); |
|
|
|
|
|
|
|
return map; |
|
|
|
}, |
|
|
|
{} as { [date: string]: Big } |
|
|
|
); |
|
|
|
|
|
|
|
const accountBalanceMap: { [date: string]: Big } = {}; |
|
|
|
|
|
|
|
let lastKnownBalance = new Big(0); |
|
|
|
|
|
|
|
for (const dateString of chartDates) { |
|
|
|
if (accountBalanceItemsMap[dateString] !== undefined) { |
|
|
|
// If there's an exact balance for this date, update lastKnownBalance
|
|
|
|
lastKnownBalance = accountBalanceItemsMap[dateString]; |
|
|
|
} |
|
|
|
|
|
|
|
// Add the most recent balance to the accountBalanceMap
|
|
|
|
accountBalanceMap[dateString] = lastKnownBalance; |
|
|
|
|
|
|
|
for (const symbol of Object.keys(valuesBySymbol)) { |
|
|
|
const symbolValues = valuesBySymbol[symbol]; |
|
|
|
|
|
|
|
@ -536,7 +518,14 @@ export abstract class PortfolioCalculator { |
|
|
|
accumulatedValuesByDate[dateString] |
|
|
|
?.investmentValueWithCurrencyEffect ?? new Big(0) |
|
|
|
).add(investmentValueWithCurrencyEffect), |
|
|
|
totalAccountBalanceWithCurrencyEffect: accountBalanceMap[dateString], |
|
|
|
totalCashValueWithCurrencyEffect: ( |
|
|
|
accumulatedValuesByDate[dateString] |
|
|
|
?.totalCashValueWithCurrencyEffect ?? new Big(0) |
|
|
|
).add( |
|
|
|
cashSymbols.has(symbol) |
|
|
|
? currentValueWithCurrencyEffect |
|
|
|
: new Big(0) |
|
|
|
), |
|
|
|
totalCurrentValue: ( |
|
|
|
accumulatedValuesByDate[dateString]?.totalCurrentValue ?? new Big(0) |
|
|
|
).add(currentValue), |
|
|
|
@ -577,7 +566,7 @@ export abstract class PortfolioCalculator { |
|
|
|
).map(([date, values]) => { |
|
|
|
const { |
|
|
|
investmentValueWithCurrencyEffect, |
|
|
|
totalAccountBalanceWithCurrencyEffect, |
|
|
|
totalCashValueWithCurrencyEffect, |
|
|
|
totalCurrentValue, |
|
|
|
totalCurrentValueWithCurrencyEffect, |
|
|
|
totalInvestmentValue, |
|
|
|
@ -610,10 +599,8 @@ export abstract class PortfolioCalculator { |
|
|
|
netPerformance: totalNetPerformanceValue.toNumber(), |
|
|
|
netPerformanceWithCurrencyEffect: |
|
|
|
totalNetPerformanceValueWithCurrencyEffect.toNumber(), |
|
|
|
netWorth: totalCurrentValueWithCurrencyEffect |
|
|
|
.plus(totalAccountBalanceWithCurrencyEffect) |
|
|
|
.toNumber(), |
|
|
|
totalAccountBalance: totalAccountBalanceWithCurrencyEffect.toNumber(), |
|
|
|
netWorth: totalCurrentValueWithCurrencyEffect.toNumber(), |
|
|
|
totalCashInBaseCurrency: totalCashValueWithCurrencyEffect.toNumber(), |
|
|
|
totalInvestment: totalInvestmentValue.toNumber(), |
|
|
|
totalInvestmentValueWithCurrencyEffect: |
|
|
|
totalInvestmentValueWithCurrencyEffect.toNumber(), |
|
|
|
@ -637,6 +624,7 @@ export abstract class PortfolioCalculator { |
|
|
|
...overall, |
|
|
|
errors, |
|
|
|
historicalData, |
|
|
|
totalCashInBaseCurrency, |
|
|
|
totalInterestWithCurrencyEffect, |
|
|
|
totalLiabilitiesWithCurrencyEffect, |
|
|
|
hasErrors: hasAnySymbolMetricsErrors || overall.hasErrors, |
|
|
|
@ -774,11 +762,6 @@ export abstract class PortfolioCalculator { |
|
|
|
? 0 |
|
|
|
: netPerformanceWithCurrencyEffectSinceStartDate / |
|
|
|
timeWeightedInvestmentValue |
|
|
|
// TODO: Add net worth
|
|
|
|
// netWorth: totalCurrentValueWithCurrencyEffect
|
|
|
|
// .plus(totalAccountBalanceWithCurrencyEffect)
|
|
|
|
// .toNumber()
|
|
|
|
// netWorth: 0
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|