From 4ab3f813848c7696ee6d15383c4ed54fc7789484 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 3 Mar 2024 20:04:49 +0100 Subject: [PATCH] Extract getFactor() (#3089) * Extract getFactor() * Refactoring --- apps/api/src/app/order/order.service.ts | 4 +- .../interfaces/portfolio-order.interface.ts | 4 +- .../src/app/portfolio/portfolio-calculator.ts | 45 +++++-------------- .../src/app/portfolio/portfolio.service.ts | 3 +- apps/api/src/helper/portfolio.helper.ts | 21 +++++++++ apps/api/src/models/order.ts | 4 +- .../api/src/services/interfaces/interfaces.ts | 4 +- .../app/services/import-activities.service.ts | 18 ++++---- 8 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 apps/api/src/helper/portfolio.helper.ts diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 79738c27e..d200ee024 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -19,7 +19,7 @@ import { Order, Prisma, Tag, - Type as TypeOfOrder + Type as ActivityType } from '@prisma/client'; import Big from 'big.js'; import { endOfToday, isAfter } from 'date-fns'; @@ -229,7 +229,7 @@ export class OrderService { sortColumn?: string; sortDirection?: Prisma.SortOrder; take?: number; - types?: TypeOfOrder[]; + types?: ActivityType[]; userCurrency: string; userId: string; withExcludedAccounts?: boolean; diff --git a/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts b/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts index cc3a97752..161fbbecb 100644 --- a/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts +++ b/apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts @@ -1,4 +1,4 @@ -import { DataSource, Tag, Type as TypeOfOrder } from '@prisma/client'; +import { DataSource, Tag, Type as ActivityType } from '@prisma/client'; import Big from 'big.js'; export interface PortfolioOrder { @@ -10,6 +10,6 @@ export interface PortfolioOrder { quantity: Big; symbol: string; tags?: Tag[]; - type: TypeOfOrder; + type: ActivityType; unitPrice: Big; } diff --git a/apps/api/src/app/portfolio/portfolio-calculator.ts b/apps/api/src/app/portfolio/portfolio-calculator.ts index 9b76aa735..27a5a278b 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.ts @@ -1,3 +1,4 @@ +import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; @@ -12,7 +13,6 @@ import { import { GroupBy } from '@ghostfolio/common/types'; import { Logger } from '@nestjs/common'; -import { Type as TypeOfOrder } from '@prisma/client'; import Big from 'big.js'; import { addDays, @@ -76,7 +76,7 @@ export class PortfolioCalculator { let currentTransactionPointItem: TransactionPointSymbol; const oldAccumulatedSymbol = symbols[order.symbol]; - const factor = this.getFactor(order.type); + const factor = getFactor(order.type); const unitPrice = new Big(order.unitPrice); if (oldAccumulatedSymbol) { const newQuantity = order.quantity @@ -820,25 +820,6 @@ export class PortfolioCalculator { }; } - private getFactor(type: TypeOfOrder) { - let factor: number; - - switch (type) { - case 'BUY': - case 'ITEM': - factor = 1; - break; - case 'SELL': - factor = -1; - break; - default: - factor = 0; - break; - } - - return factor; - } - private getSymbolMetrics({ end, exchangeRates, @@ -989,7 +970,7 @@ export class PortfolioCalculator { itemType: 'start', name: '', quantity: new Big(0), - type: TypeOfOrder.BUY, + type: 'BUY', unitPrice: unitPriceAtStartDate }); @@ -1003,7 +984,7 @@ export class PortfolioCalculator { itemType: 'end', name: '', quantity: new Big(0), - type: TypeOfOrder.BUY, + type: 'BUY', unitPrice: unitPriceAtEndDate }); @@ -1030,7 +1011,7 @@ export class PortfolioCalculator { feeInBaseCurrency: new Big(0), name: '', quantity: new Big(0), - type: TypeOfOrder.BUY, + type: 'BUY', unitPrice: marketSymbolMap[format(day, DATE_FORMAT)]?.[symbol] ?? lastUnitPrice @@ -1131,24 +1112,24 @@ export class PortfolioCalculator { order.type === 'BUY' ? order.quantity .mul(order.unitPriceInBaseCurrency) - .mul(this.getFactor(order.type)) + .mul(getFactor(order.type)) : totalUnits.gt(0) ? totalInvestment .div(totalUnits) .mul(order.quantity) - .mul(this.getFactor(order.type)) + .mul(getFactor(order.type)) : new Big(0); const transactionInvestmentWithCurrencyEffect = order.type === 'BUY' ? order.quantity .mul(order.unitPriceInBaseCurrencyWithCurrencyEffect) - .mul(this.getFactor(order.type)) + .mul(getFactor(order.type)) : totalUnits.gt(0) ? totalInvestmentWithCurrencyEffect .div(totalUnits) .mul(order.quantity) - .mul(this.getFactor(order.type)) + .mul(getFactor(order.type)) : new Big(0); if (PortfolioCalculator.ENABLE_LOGGING) { @@ -1203,9 +1184,7 @@ export class PortfolioCalculator { order.feeInBaseCurrencyWithCurrencyEffect ?? 0 ); - totalUnits = totalUnits.plus( - order.quantity.mul(this.getFactor(order.type)) - ); + totalUnits = totalUnits.plus(order.quantity.mul(getFactor(order.type))); const valueOfInvestment = totalUnits.mul(order.unitPriceInBaseCurrency); @@ -1214,14 +1193,14 @@ export class PortfolioCalculator { ); const grossPerformanceFromSell = - order.type === TypeOfOrder.SELL + order.type === 'SELL' ? order.unitPriceInBaseCurrency .minus(lastAveragePrice) .mul(order.quantity) : new Big(0); const grossPerformanceFromSellWithCurrencyEffect = - order.type === TypeOfOrder.SELL + order.type === 'SELL' ? order.unitPriceInBaseCurrencyWithCurrencyEffect .minus(lastAveragePriceWithCurrencyEffect) .mul(order.quantity) diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index f268349c3..d07389049 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -7,6 +7,7 @@ import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.s import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface'; import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; import { UserService } from '@ghostfolio/api/app/user/user.service'; +import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment'; @@ -2131,7 +2132,7 @@ export class PortfolioService { ?.marketPriceInBaseCurrency ?? 0; if (['LIABILITY', 'SELL'].includes(type)) { - currentValueOfSymbolInBaseCurrency *= -1; + currentValueOfSymbolInBaseCurrency *= getFactor(type); } if (accounts[Account?.id || UNKNOWN_KEY]?.valueInBaseCurrency) { diff --git a/apps/api/src/helper/portfolio.helper.ts b/apps/api/src/helper/portfolio.helper.ts new file mode 100644 index 000000000..01b532cbf --- /dev/null +++ b/apps/api/src/helper/portfolio.helper.ts @@ -0,0 +1,21 @@ +import { Type as ActivityType } from '@prisma/client'; + +export function getFactor(activityType: ActivityType) { + let factor: number; + + switch (activityType) { + case 'BUY': + case 'ITEM': + factor = 1; + break; + case 'LIABILITY': + case 'SELL': + factor = -1; + break; + default: + factor = 0; + break; + } + + return factor; +} diff --git a/apps/api/src/models/order.ts b/apps/api/src/models/order.ts index 214f7e56a..6e6762101 100644 --- a/apps/api/src/models/order.ts +++ b/apps/api/src/models/order.ts @@ -1,6 +1,6 @@ import { IOrder } from '@ghostfolio/api/services/interfaces/interfaces'; -import { Account, SymbolProfile, Type as TypeOfOrder } from '@prisma/client'; +import { Account, SymbolProfile, Type as ActivityType } from '@prisma/client'; import { v4 as uuidv4 } from 'uuid'; export class Order { @@ -14,7 +14,7 @@ export class Order { private symbol: string; private symbolProfile: SymbolProfile; private total: number; - private type: TypeOfOrder; + private type: ActivityType; private unitPrice: number; public constructor(data: IOrder) { diff --git a/apps/api/src/services/interfaces/interfaces.ts b/apps/api/src/services/interfaces/interfaces.ts index bfd29d991..b945d0945 100644 --- a/apps/api/src/services/interfaces/interfaces.ts +++ b/apps/api/src/services/interfaces/interfaces.ts @@ -5,7 +5,7 @@ import { Account, DataSource, SymbolProfile, - Type as TypeOfOrder + Type as ActivityType } from '@prisma/client'; export interface IOrder { @@ -18,7 +18,7 @@ export interface IOrder { quantity: number; symbol: string; symbolProfile: SymbolProfile; - type: TypeOfOrder; + type: ActivityType; unitPrice: number; } diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 5375c32aa..c1b2209b3 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -5,7 +5,7 @@ import { parseDate as parseDateHelper } from '@ghostfolio/common/helper'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Account, DataSource, Type } from '@prisma/client'; +import { Account, DataSource, Type as ActivityType } from '@prisma/client'; import { isFinite } from 'lodash'; import { parse as csvToJson } from 'papaparse'; import { EMPTY } from 'rxjs'; @@ -328,26 +328,26 @@ export class ImportActivitiesService { content: any[]; index: number; item: any; - }) { + }): ActivityType { item = this.lowercaseKeys(item); for (const key of ImportActivitiesService.TYPE_KEYS) { if (item[key]) { switch (item[key].toLowerCase()) { case 'buy': - return Type.BUY; + return 'BUY'; case 'dividend': - return Type.DIVIDEND; + return 'DIVIDEND'; case 'fee': - return Type.FEE; + return 'FEE'; case 'interest': - return Type.INTEREST; + return 'INTEREST'; case 'item': - return Type.ITEM; + return 'ITEM'; case 'liability': - return Type.LIABILITY; + return 'LIABILITY'; case 'sell': - return Type.SELL; + return 'SELL'; default: break; }