Browse Source

Bugfix/fix currency conversion in accounts (#937)

* Fix currency conversion in accounts

* Update changelog
pull/940/head
Thomas Kaul 3 years ago
committed by GitHub
parent
commit
af0863d193
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 20
      apps/api/src/app/portfolio/current-rate.service.mock.ts
  3. 7
      apps/api/src/app/portfolio/current-rate.service.spec.ts
  4. 20
      apps/api/src/app/portfolio/current-rate.service.ts
  5. 2
      apps/api/src/app/portfolio/interfaces/get-value-object.interface.ts
  6. 8
      apps/api/src/app/portfolio/portfolio-calculator.ts
  7. 41
      apps/api/src/app/portfolio/portfolio.service.ts

1
CHANGELOG.md

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### Fixed
- Fixed an issue with the currency conversion in the account calculations
- Fixed an issue with countries in the symbol profile overrides - Fixed an issue with countries in the symbol profile overrides
## 1.149.0 - 16.05.2022 ## 1.149.0 - 16.05.2022

20
apps/api/src/app/portfolio/current-rate.service.mock.ts

@ -1,6 +1,7 @@
import { parseDate, resetHours } from '@ghostfolio/common/helper'; import { parseDate, resetHours } from '@ghostfolio/common/helper';
import { addDays, endOfDay, isBefore, isSameDay } from 'date-fns'; import { addDays, endOfDay, isBefore, isSameDay } from 'date-fns';
import { GetValueObject } from './interfaces/get-value-object.interface';
import { GetValuesParams } from './interfaces/get-values-params.interface'; import { GetValuesParams } from './interfaces/get-values-params.interface';
function mockGetValue(symbol: string, date: Date) { function mockGetValue(symbol: string, date: Date) {
@ -33,8 +34,11 @@ function mockGetValue(symbol: string, date: Date) {
} }
export const CurrentRateServiceMock = { export const CurrentRateServiceMock = {
getValues: ({ dataGatheringItems, dateQuery }: GetValuesParams) => { getValues: ({
const result = []; dataGatheringItems,
dateQuery
}: GetValuesParams): Promise<GetValueObject[]> => {
const result: GetValueObject[] = [];
if (dateQuery.lt) { if (dateQuery.lt) {
for ( for (
let date = resetHours(dateQuery.gte); let date = resetHours(dateQuery.gte);
@ -44,8 +48,10 @@ export const CurrentRateServiceMock = {
for (const dataGatheringItem of dataGatheringItems) { for (const dataGatheringItem of dataGatheringItems) {
result.push({ result.push({
date, date,
marketPrice: mockGetValue(dataGatheringItem.symbol, date) marketPriceInBaseCurrency: mockGetValue(
.marketPrice, dataGatheringItem.symbol,
date
).marketPrice,
symbol: dataGatheringItem.symbol symbol: dataGatheringItem.symbol
}); });
} }
@ -55,8 +61,10 @@ export const CurrentRateServiceMock = {
for (const dataGatheringItem of dataGatheringItems) { for (const dataGatheringItem of dataGatheringItems) {
result.push({ result.push({
date, date,
marketPrice: mockGetValue(dataGatheringItem.symbol, date) marketPriceInBaseCurrency: mockGetValue(
.marketPrice, dataGatheringItem.symbol,
date
).marketPrice,
symbol: dataGatheringItem.symbol symbol: dataGatheringItem.symbol
}); });
} }

7
apps/api/src/app/portfolio/current-rate.service.spec.ts

@ -4,6 +4,7 @@ import { MarketDataService } from '@ghostfolio/api/services/market-data.service'
import { DataSource, MarketData } from '@prisma/client'; import { DataSource, MarketData } from '@prisma/client';
import { CurrentRateService } from './current-rate.service'; import { CurrentRateService } from './current-rate.service';
import { GetValueObject } from './interfaces/get-value-object.interface';
jest.mock('@ghostfolio/api/services/market-data.service', () => { jest.mock('@ghostfolio/api/services/market-data.service', () => {
return { return {
@ -96,15 +97,15 @@ describe('CurrentRateService', () => {
}, },
userCurrency: 'CHF' userCurrency: 'CHF'
}) })
).toMatchObject([ ).toMatchObject<GetValueObject[]>([
{ {
date: undefined, date: undefined,
marketPrice: 1841.823902, marketPriceInBaseCurrency: 1841.823902,
symbol: 'AMZN' symbol: 'AMZN'
}, },
{ {
date: undefined, date: undefined,
marketPrice: 1847.839966, marketPriceInBaseCurrency: 1847.839966,
symbol: 'AMZN' symbol: 'AMZN'
} }
]); ]);

20
apps/api/src/app/portfolio/current-rate.service.ts

@ -28,13 +28,7 @@ export class CurrentRateService {
(!dateQuery.gte || isBefore(dateQuery.gte, new Date())) && (!dateQuery.gte || isBefore(dateQuery.gte, new Date())) &&
(!dateQuery.in || this.containsToday(dateQuery.in)); (!dateQuery.in || this.containsToday(dateQuery.in));
const promises: Promise< const promises: Promise<GetValueObject[]>[] = [];
{
date: Date;
marketPrice: number;
symbol: string;
}[]
>[] = [];
if (includeToday) { if (includeToday) {
const today = resetHours(new Date()); const today = resetHours(new Date());
@ -42,13 +36,14 @@ export class CurrentRateService {
this.dataProviderService this.dataProviderService
.getQuotes(dataGatheringItems) .getQuotes(dataGatheringItems)
.then((dataResultProvider) => { .then((dataResultProvider) => {
const result = []; const result: GetValueObject[] = [];
for (const dataGatheringItem of dataGatheringItems) { for (const dataGatheringItem of dataGatheringItems) {
result.push({ result.push({
date: today, date: today,
marketPrice: this.exchangeRateDataService.toCurrency( marketPriceInBaseCurrency:
dataResultProvider?.[dataGatheringItem.symbol]?.marketPrice ?? this.exchangeRateDataService.toCurrency(
0, dataResultProvider?.[dataGatheringItem.symbol]
?.marketPrice ?? 0,
dataResultProvider?.[dataGatheringItem.symbol]?.currency, dataResultProvider?.[dataGatheringItem.symbol]?.currency,
userCurrency userCurrency
), ),
@ -74,7 +69,8 @@ export class CurrentRateService {
return data.map((marketDataItem) => { return data.map((marketDataItem) => {
return { return {
date: marketDataItem.date, date: marketDataItem.date,
marketPrice: this.exchangeRateDataService.toCurrency( marketPriceInBaseCurrency:
this.exchangeRateDataService.toCurrency(
marketDataItem.marketPrice, marketDataItem.marketPrice,
currencies[marketDataItem.symbol], currencies[marketDataItem.symbol],
userCurrency userCurrency

2
apps/api/src/app/portfolio/interfaces/get-value-object.interface.ts

@ -1,5 +1,5 @@
export interface GetValueObject { export interface GetValueObject {
date: Date; date: Date;
marketPrice: number; marketPriceInBaseCurrency: number;
symbol: string; symbol: string;
} }

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

@ -231,9 +231,9 @@ export class PortfolioCalculator {
if (!marketSymbolMap[date]) { if (!marketSymbolMap[date]) {
marketSymbolMap[date] = {}; marketSymbolMap[date] = {};
} }
if (marketSymbol.marketPrice) { if (marketSymbol.marketPriceInBaseCurrency) {
marketSymbolMap[date][marketSymbol.symbol] = new Big( marketSymbolMap[date][marketSymbol.symbol] = new Big(
marketSymbol.marketPrice marketSymbol.marketPriceInBaseCurrency
); );
} }
} }
@ -548,9 +548,9 @@ export class PortfolioCalculator {
if (!marketSymbolMap[date]) { if (!marketSymbolMap[date]) {
marketSymbolMap[date] = {}; marketSymbolMap[date] = {};
} }
if (marketSymbol.marketPrice) { if (marketSymbol.marketPriceInBaseCurrency) {
marketSymbolMap[date][marketSymbol.symbol] = new Big( marketSymbolMap[date][marketSymbol.symbol] = new Big(
marketSymbol.marketPrice marketSymbol.marketPriceInBaseCurrency
); );
} }
} }

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

@ -462,8 +462,9 @@ export class PortfolioService {
const accounts = await this.getValueOfAccounts({ const accounts = await this.getValueOfAccounts({
orders, orders,
userId,
portfolioItemsNow, portfolioItemsNow,
userCurrency,
userId,
filters: aFilters filters: aFilters
}); });
@ -899,7 +900,8 @@ export class PortfolioService {
const accounts = await this.getValueOfAccounts({ const accounts = await this.getValueOfAccounts({
orders, orders,
portfolioItemsNow, portfolioItemsNow,
userId userId,
userCurrency: currency
}); });
return { return {
rules: { rules: {
@ -1268,11 +1270,13 @@ export class PortfolioService {
filters = [], filters = [],
orders, orders,
portfolioItemsNow, portfolioItemsNow,
userCurrency,
userId userId
}: { }: {
filters?: Filter[]; filters?: Filter[];
orders: OrderWithAccount[]; orders: OrderWithAccount[];
portfolioItemsNow: { [p: string]: TimelinePosition }; portfolioItemsNow: { [p: string]: TimelinePosition };
userCurrency: string;
userId: string; userId: string;
}) { }) {
const accounts: PortfolioDetails['accounts'] = {}; const accounts: PortfolioDetails['accounts'] = {};
@ -1301,34 +1305,47 @@ export class PortfolioService {
accounts[account.id] = { accounts[account.id] = {
balance: account.balance, balance: account.balance,
currency: account.currency, currency: account.currency,
current: account.balance, current: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
),
name: account.name, name: account.name,
original: account.balance original: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
)
}; };
for (const order of ordersByAccount) { for (const order of ordersByAccount) {
let currentValueOfSymbol = let currentValueOfSymbolInBaseCurrency =
order.quantity * order.quantity *
portfolioItemsNow[order.SymbolProfile.symbol].marketPrice; portfolioItemsNow[order.SymbolProfile.symbol].marketPrice;
let originalValueOfSymbol = order.quantity * order.unitPrice; let originalValueOfSymbolInBaseCurrency =
this.exchangeRateDataService.toCurrency(
order.quantity * order.unitPrice,
order.SymbolProfile.currency,
userCurrency
);
if (order.type === 'SELL') { if (order.type === 'SELL') {
currentValueOfSymbol *= -1; currentValueOfSymbolInBaseCurrency *= -1;
originalValueOfSymbol *= -1; originalValueOfSymbolInBaseCurrency *= -1;
} }
if (accounts[order.Account?.id || UNKNOWN_KEY]?.current) { if (accounts[order.Account?.id || UNKNOWN_KEY]?.current) {
accounts[order.Account?.id || UNKNOWN_KEY].current += accounts[order.Account?.id || UNKNOWN_KEY].current +=
currentValueOfSymbol; currentValueOfSymbolInBaseCurrency;
accounts[order.Account?.id || UNKNOWN_KEY].original += accounts[order.Account?.id || UNKNOWN_KEY].original +=
originalValueOfSymbol; originalValueOfSymbolInBaseCurrency;
} else { } else {
accounts[order.Account?.id || UNKNOWN_KEY] = { accounts[order.Account?.id || UNKNOWN_KEY] = {
balance: 0, balance: 0,
currency: order.Account?.currency, currency: order.Account?.currency,
current: currentValueOfSymbol, current: currentValueOfSymbolInBaseCurrency,
name: account.name, name: account.name,
original: originalValueOfSymbol original: originalValueOfSymbolInBaseCurrency
}; };
} }
} }

Loading…
Cancel
Save