From 49f46e1a1ece668cad4f9ec38f659c07e2d50a51 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 2 Dec 2021 21:53:38 +0100 Subject: [PATCH] Bugfix/improve allocations by currency with cash balances (#508) * Improve allocations by currency in combination with cash balances * Update changelog --- CHANGELOG.md | 6 ++ .../src/app/portfolio/portfolio.service.ts | 93 ++++++++++++++----- .../interfaces/portfolio-details.interface.ts | 7 +- 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10b80ec7f..3a69ce88a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Improved the allocations by currency in combination with cash balances + ## 1.85.0 - 01.12.2021 ### Fixed diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 76bf34f14..887b1cbe5 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -347,13 +347,17 @@ export class PortfolioService { }; } - // TODO: Add a cash position for each currency - holdings[ghostfolioCashSymbol] = await this.getCashPosition({ + const cashPositions = await this.getCashPositions({ cashDetails, + userCurrency, investment: totalInvestment, value: totalValue }); + for (const symbol of Object.keys(cashPositions)) { + holdings[symbol] = cashPositions[symbol]; + } + const accounts = await this.getValueOfAccounts( orders, portfolioItemsNow, @@ -872,38 +876,73 @@ export class PortfolioService { }; } - private async getCashPosition({ + private async getCashPositions({ cashDetails, investment, + userCurrency, value }: { cashDetails: CashDetails; investment: Big; value: Big; + userCurrency: string; }) { - const cashValue = new Big(cashDetails.balance); + const cashPositions = {}; - return { - allocationCurrent: cashValue.div(value).toNumber(), - allocationInvestment: cashValue.div(investment).toNumber(), - assetClass: AssetClass.CASH, - assetSubClass: AssetClass.CASH, - countries: [], - currency: 'CHF', - grossPerformance: 0, - grossPerformancePercent: 0, - investment: cashValue.toNumber(), - marketPrice: 0, - marketState: MarketState.open, - name: 'Cash', - netPerformance: 0, - netPerformancePercent: 0, - quantity: 0, - sectors: [], - symbol: ghostfolioCashSymbol, - transactionCount: 0, - value: cashValue.toNumber() - }; + for (const account of cashDetails.accounts) { + const convertedBalance = this.exchangeRateDataService.toCurrency( + account.balance, + account.currency, + userCurrency + ); + + if (convertedBalance === 0) { + continue; + } + + if (cashPositions[account.currency]) { + cashPositions[account.currency].investment += convertedBalance; + cashPositions[account.currency].value += convertedBalance; + } else { + cashPositions[account.currency] = { + allocationCurrent: 0, + allocationInvestment: 0, + assetClass: AssetClass.CASH, + assetSubClass: AssetClass.CASH, + countries: [], + currency: account.currency, + grossPerformance: 0, + grossPerformancePercent: 0, + investment: convertedBalance, + marketPrice: 0, + marketState: MarketState.open, + name: account.currency, + netPerformance: 0, + netPerformancePercent: 0, + quantity: 0, + sectors: [], + symbol: account.currency, + transactionCount: 0, + value: convertedBalance + }; + } + } + + for (const symbol of Object.keys(cashPositions)) { + // Calculate allocations for each currency + cashPositions[symbol].allocationCurrent = new Big( + cashPositions[symbol].value + ) + .div(value) + .toNumber(); + cashPositions[symbol].allocationInvestment = new Big( + cashPositions[symbol].investment + ) + .div(investment) + .toNumber(); + } + + return cashPositions; } private getStartDate(aDateRange: DateRange, portfolioStart: Date) { @@ -997,6 +1036,8 @@ export class PortfolioService { userCurrency ); accounts[account.name] = { + balance: convertedBalance, + currency: account.currency, current: convertedBalance, original: convertedBalance }; @@ -1018,6 +1059,8 @@ export class PortfolioService { originalValueOfSymbol; } else { accounts[order.Account?.name || UNKNOWN_KEY] = { + balance: 0, + currency: order.Account?.currency, current: currentValueOfSymbol, original: originalValueOfSymbol }; diff --git a/libs/common/src/lib/interfaces/portfolio-details.interface.ts b/libs/common/src/lib/interfaces/portfolio-details.interface.ts index 3a0d2bb09..2cbf1c6fa 100644 --- a/libs/common/src/lib/interfaces/portfolio-details.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-details.interface.ts @@ -2,7 +2,12 @@ import { PortfolioPosition } from '@ghostfolio/common/interfaces'; export interface PortfolioDetails { accounts: { - [name: string]: { current: number; original: number }; + [name: string]: { + balance: number; + currency: string; + current: number; + original: number; + }; }; holdings: { [symbol: string]: PortfolioPosition }; }