From 5d06b131418a0d650f8436d19f9eb54f3afc4b48 Mon Sep 17 00:00:00 2001 From: Daniel Devaud Date: Wed, 12 Jul 2023 15:59:50 +0200 Subject: [PATCH] Added STAKE order type - Calculation handling - Displaying - DB & Controller update --- .../portfolio-position-detail.interface.ts | 1 + .../src/app/portfolio/portfolio-calculator.ts | 19 +++++++++++++-- .../src/app/portfolio/portfolio.service.ts | 23 ++++++++++++++++--- .../position-detail-dialog.component.ts | 3 +++ .../position-detail-dialog.html | 16 ++++++++++++- ...ate-or-update-activity-dialog.component.ts | 6 ++++- .../create-or-update-activity-dialog.html | 8 ++++++- .../app/services/import-activities.service.ts | 2 ++ .../activities-table.component.html | 9 ++++++-- .../activities-table.component.scss | 4 ++++ prisma/schema.prisma | 1 + 11 files changed, 82 insertions(+), 10 deletions(-) diff --git a/apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts b/apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts index 827aa25fe..df4760bb0 100644 --- a/apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts +++ b/apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts @@ -10,6 +10,7 @@ export interface PortfolioPositionDetail { averagePrice: number; dataProviderInfo: DataProviderInfo; dividendInBaseCurrency: number; + stakeRewards: number; feeInBaseCurrency: number; firstBuyDate: string; grossPerformance: number; diff --git a/apps/api/src/app/portfolio/portfolio-calculator.ts b/apps/api/src/app/portfolio/portfolio-calculator.ts index 9addb29dd..07f7339eb 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.ts @@ -92,7 +92,7 @@ export class PortfolioCalculator { let investment = new Big(0); if (newQuantity.gt(0)) { - if (order.type === 'BUY') { + if (order.type === 'BUY' || order.type === 'STAKE') { investment = oldAccumulatedSymbol.investment.plus( order.quantity.mul(unitPrice) ); @@ -931,6 +931,7 @@ export class PortfolioCalculator { switch (type) { case 'BUY': + case 'STAKE': factor = 1; break; case 'SELL': @@ -1087,6 +1088,20 @@ export class PortfolioCalculator { marketSymbolMap[format(day, DATE_FORMAT)]?.[symbol] ?? lastUnitPrice }); + } else { + let orderIndex = orders.findIndex( + (o) => o.date === format(day, DATE_FORMAT) && o.type === 'STAKE' + ); + if (orderIndex >= 0) { + let order = orders[orderIndex]; + orders.splice(orderIndex, 1); + orders.push({ + ...order, + unitPrice: + marketSymbolMap[format(day, DATE_FORMAT)]?.[symbol] ?? + lastUnitPrice + }); + } } lastUnitPrice = last(orders).unitPrice; @@ -1156,7 +1171,7 @@ export class PortfolioCalculator { } const transactionInvestment = - order.type === 'BUY' + order.type === 'BUY' || order.type === 'STAKE' ? order.quantity.mul(order.unitPrice).mul(this.getFactor(order.type)) : totalUnits.gt(0) ? totalInvestment diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 66f3841a4..2d0e0ac23 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -702,6 +702,7 @@ export class PortfolioService { averagePrice: undefined, dataProviderInfo: undefined, dividendInBaseCurrency: undefined, + stakeRewards: undefined, feeInBaseCurrency: undefined, firstBuyDate: undefined, grossPerformance: undefined, @@ -730,7 +731,11 @@ export class PortfolioService { .filter((order) => { tags = tags.concat(order.tags); - return order.type === 'BUY' || order.type === 'SELL'; + return ( + order.type === 'BUY' || + order.type === 'SELL' || + order.type === 'STAKE' + ); }) .map((order) => ({ currency: order.SymbolProfile.currency, @@ -786,6 +791,16 @@ export class PortfolioService { }) ); + const stakeRewards = getSum( + orders + .filter(({ type }) => { + return type === 'STAKE'; + }) + .map(({ quantity }) => { + return new Big(quantity); + }) + ); + // Convert investment, gross and net performance to currency of user const investment = this.exchangeRateDataService.toCurrency( position.investment?.toNumber(), @@ -880,6 +895,7 @@ export class PortfolioService { averagePrice: averagePrice.toNumber(), dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), + stakeRewards: stakeRewards.toNumber(), feeInBaseCurrency: this.exchangeRateDataService.toCurrency( fee.toNumber(), SymbolProfile.currency, @@ -943,6 +959,7 @@ export class PortfolioService { averagePrice: 0, dataProviderInfo: undefined, dividendInBaseCurrency: 0, + stakeRewards: 0, feeInBaseCurrency: 0, firstBuyDate: undefined, grossPerformance: undefined, @@ -1403,7 +1420,7 @@ export class PortfolioService { let valueInBaseCurrencyOfEmergencyFundPositions = new Big(0); for (const order of emergencyFundOrders) { - if (order.type === 'BUY') { + if (order.type === 'BUY' || order.type === 'STAKE') { valueInBaseCurrencyOfEmergencyFundPositions = valueInBaseCurrencyOfEmergencyFundPositions.plus( order.valueInBaseCurrency @@ -1714,7 +1731,7 @@ export class PortfolioService { userCurrency, userId, withExcludedAccounts, - types: ['BUY', 'SELL'] + types: ['BUY', 'SELL', 'STAKE'] }); if (orders.length <= 0) { diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts index bc68cf231..ffa167a00 100644 --- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts +++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts @@ -40,6 +40,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit { }; public dataProviderInfo: DataProviderInfo; public dividendInBaseCurrency: number; + public stakeRewards: number; public feeInBaseCurrency: number; public firstBuyDate: string; public grossPerformance: number; @@ -84,6 +85,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit { averagePrice, dataProviderInfo, dividendInBaseCurrency, + stakeRewards, feeInBaseCurrency, firstBuyDate, grossPerformance, @@ -107,6 +109,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit { this.countries = {}; this.dataProviderInfo = dataProviderInfo; this.dividendInBaseCurrency = dividendInBaseCurrency; + this.stakeRewards = stakeRewards; this.feeInBaseCurrency = feeInBaseCurrency; this.firstBuyDate = firstBuyDate; this.grossPerformance = grossPerformance; diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html index 379250fcd..d6ebc8e86 100644 --- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html +++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html @@ -126,7 +126,10 @@ >Investment -
+
Dividend
+ +
+ Stake Rewards + +
Buy Dividend + Stake reward / Stockdividend Item Liability Sell @@ -102,7 +105,10 @@
-
+
diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 02eeb7e03..5235080f7 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -346,6 +346,8 @@ export class ImportActivitiesService { return Type.LIABILITY; case 'sell': return Type.SELL; + case 'stake': + return Type.STAKE; default: break; } diff --git a/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html index 472a46f10..b7406f3e2 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -163,11 +163,16 @@ dividend: element.type === 'DIVIDEND', item: element.type === 'ITEM', liability: element.type === 'LIABILITY', - sell: element.type === 'SELL' + sell: element.type === 'SELL', + stake: element.type === 'STAKE' }" >