From bac0f0f4f41fcc89ecc0b10cc544d4294767be30 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Wed, 26 Mar 2025 10:16:42 +0100 Subject: [PATCH 01/13] Unit price not converted in the frontend --- apps/api/src/app/order/order.service.ts | 4 +- ...ate-or-update-activity-dialog.component.ts | 88 +++---------------- .../create-or-update-activity-dialog.html | 74 +--------------- .../activities-table.component.html | 2 +- 4 files changed, 20 insertions(+), 148 deletions(-) diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index a26099e9d..fced56950 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -537,7 +537,7 @@ export class OrderService { feeInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( order.fee, - order.SymbolProfile.currency, + order.currency ?? order.SymbolProfile.currency, userCurrency, order.date ), @@ -545,7 +545,7 @@ export class OrderService { valueInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( value, - order.SymbolProfile.currency, + order.currency ?? order.SymbolProfile.currency, userCurrency, order.date ) diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index 555fbc7aa..81cfec651 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -15,7 +15,7 @@ import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AssetClass, AssetSubClass, Tag, Type } from '@prisma/client'; import { isAfter, isToday } from 'date-fns'; -import { EMPTY, Subject, lastValueFrom } from 'rxjs'; +import { EMPTY, Subject } from 'rxjs'; import { catchError, delay, takeUntil } from 'rxjs/operators'; import { DataService } from '../../../../services/data.service'; @@ -101,17 +101,13 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { this.data.activity?.SymbolProfile?.currency, Validators.required ], - currencyOfUnitPrice: [ - this.data.activity?.SymbolProfile?.currency, - Validators.required - ], + currencyOfUnitPrice: [this.data.activity?.currency, Validators.required], dataSource: [ this.data.activity?.SymbolProfile?.dataSource, Validators.required ], date: [this.data.activity?.date, Validators.required], fee: [this.data.activity?.fee, Validators.required], - feeInCustomCurrency: [this.data.activity?.fee, Validators.required], name: [this.data.activity?.SymbolProfile?.name, Validators.required], quantity: [this.data.activity?.quantity, Validators.required], searchSymbol: [ @@ -133,10 +129,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { ], type: [undefined, Validators.required], // Set after value changes subscription unitPrice: [this.data.activity?.unitPrice, Validators.required], - unitPriceInCustomCurrency: [ - this.data.activity?.unitPrice, - Validators.required - ], updateAccountBalance: [false] }); @@ -148,57 +140,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { takeUntil(this.unsubscribeSubject) ) .subscribe(async () => { - let exchangeRateOfUnitPrice = 1; - - this.activityForm.get('feeInCustomCurrency').setErrors(null); - this.activityForm.get('unitPriceInCustomCurrency').setErrors(null); - - const currency = this.activityForm.get('currency').value; - const currencyOfUnitPrice = this.activityForm.get( - 'currencyOfUnitPrice' - ).value; - const date = this.activityForm.get('date').value; - - if ( - currency && - currencyOfUnitPrice && - currency !== currencyOfUnitPrice && - date - ) { - try { - const { marketPrice } = await lastValueFrom( - this.dataService - .fetchExchangeRateForDate({ - date, - symbol: `${currencyOfUnitPrice}-${currency}` - }) - .pipe(takeUntil(this.unsubscribeSubject)) - ); - - exchangeRateOfUnitPrice = marketPrice; - } catch { - this.activityForm.get('unitPriceInCustomCurrency').setErrors({ - invalid: true - }); - } - } - - const feeInCustomCurrency = - this.activityForm.get('feeInCustomCurrency').value * - exchangeRateOfUnitPrice; - - const unitPriceInCustomCurrency = - this.activityForm.get('unitPriceInCustomCurrency').value * - exchangeRateOfUnitPrice; - - this.activityForm.get('fee').setValue(feeInCustomCurrency, { - emitEvent: false - }); - - this.activityForm.get('unitPrice').setValue(unitPriceInCustomCurrency, { - emitEvent: false - }); - if ( this.activityForm.get('type').value === 'BUY' || this.activityForm.get('type').value === 'FEE' || @@ -265,10 +206,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { this.activityForm.get('type').value ) ) { - this.activityForm - .get('dataSource') - .setValue(this.activityForm.get('searchSymbol').value.dataSource); - this.updateSymbol(); } @@ -297,7 +234,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { .get('dataSource') .removeValidators(Validators.required); this.activityForm.get('dataSource').updateValueAndValidity(); - this.activityForm.get('feeInCustomCurrency').reset(); + this.activityForm.get('fee').reset(); this.activityForm.get('name').setValidators(Validators.required); this.activityForm.get('name').updateValueAndValidity(); this.activityForm.get('quantity').setValue(1); @@ -331,12 +268,11 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { this.activityForm.get('dataSource').updateValueAndValidity(); if ( - (type === 'FEE' && - this.activityForm.get('feeInCustomCurrency').value === 0) || + (type === 'FEE' && this.activityForm.get('fee').value === 0) || type === 'INTEREST' || type === 'LIABILITY' ) { - this.activityForm.get('feeInCustomCurrency').reset(); + this.activityForm.get('fee').reset(); } this.activityForm.get('name').setValidators(Validators.required); @@ -354,7 +290,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { this.activityForm.get('searchSymbol').updateValueAndValidity(); if (type === 'FEE') { - this.activityForm.get('unitPriceInCustomCurrency').setValue(0); + this.activityForm.get('unitPrice').setValue(0); } if ( @@ -410,7 +346,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { public applyCurrentMarketPrice() { this.activityForm.patchValue({ currencyOfUnitPrice: this.activityForm.get('currency').value, - unitPriceInCustomCurrency: this.currentMarketPrice + unitPrice: this.currentMarketPrice }); } @@ -496,7 +432,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { this.dataService .fetchSymbolItem({ - dataSource: this.activityForm.get('dataSource').value, + dataSource: this.activityForm.get('searchSymbol').value.dataSource, symbol: this.activityForm.get('searchSymbol').value.symbol }) .pipe( @@ -512,9 +448,11 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { takeUntil(this.unsubscribeSubject) ) .subscribe(({ currency, dataSource, marketPrice }) => { - this.activityForm.get('currency').setValue(currency); - this.activityForm.get('currencyOfUnitPrice').setValue(currency); - this.activityForm.get('dataSource').setValue(dataSource); + if (this.mode === 'create') { + this.activityForm.get('currency').setValue(currency); + this.activityForm.get('currencyOfUnitPrice').setValue(currency); + this.activityForm.get('dataSource').setValue(dataSource); + } this.currentMarketPrice = marketPrice; diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html index 85fcf5a94..b0521530f 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html @@ -214,11 +214,7 @@ } } - +
- @if ( - activityForm.get('unitPriceInCustomCurrency').hasError('invalid') - ) { - Oops! Could not get the historical exchange rate - from - {{ - activityForm.get('date')?.value | date: defaultDateFormat - }} - } @if ( currentMarketPrice && @@ -263,36 +246,6 @@ } -
- - - @switch (activityForm.get('type')?.value) { - @case ('DIVIDEND') { - Dividend - } - @case ('FEE') { - Value - } - @case ('INTEREST') { - Value - } - @case ('ITEM') { - Value - } - @case ('LIABILITY') { - Value - } - @default { - Unit Price - } - } - - - {{ - activityForm.get('currency').value - }} - -
Fee - +
{{ activityForm.get('currencyOfUnitPrice').value }}
- @if (activityForm.get('feeInCustomCurrency').hasError('invalid')) { - Oops! Could not get the historical exchange rate - from - {{ - activityForm.get('date')?.value | date: defaultDateFormat - }} - } - -
-
- - Fee - - {{ - activityForm.get('currency').value - }}
@@ -392,7 +325,8 @@ [isCurrency]="true" [locale]="data.user?.settings?.locale" [unit]=" - activityForm.get('currency')?.value ?? data.user?.settings?.baseCurrency + activityForm.get('currencyOfUnitPrice')?.value ?? + data.user?.settings?.baseCurrency " [value]="total" /> 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 e5b33efd2..76aeea0fe 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -280,7 +280,7 @@ class="d-none d-lg-table-cell px-1" mat-cell > - {{ element.SymbolProfile?.currency }} + {{ element.currency }} From 28a2201679ac9bff3a93520c87af64ed5f048d79 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Thu, 27 Mar 2025 09:36:29 +0100 Subject: [PATCH 02/13] Portfolio calculation updated --- apps/api/src/app/import/import.service.ts | 7 +++++++ apps/api/src/app/order/interfaces/activities.interface.ts | 1 + apps/api/src/app/order/order.service.ts | 7 +++++++ .../src/app/portfolio/calculator/portfolio-calculator.ts | 8 ++++---- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index c72420417..9e0bf1d53 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -128,6 +128,13 @@ export class ImportService { symbolProfileId: assetProfile.id, type: 'DIVIDEND', unitPrice: marketPrice, + unitPriceInBaseCurrency: + await this.exchangeRateDataService.toCurrencyAtDate( + marketPrice, + assetProfile.currency, + userCurrency, + date + ), updatedAt: undefined, userId: Account?.userId, valueInBaseCurrency: diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/apps/api/src/app/order/interfaces/activities.interface.ts index b16d10b7d..09038ea42 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/apps/api/src/app/order/interfaces/activities.interface.ts @@ -14,6 +14,7 @@ export interface Activity extends Order { feeInBaseCurrency: number; SymbolProfile?: EnhancedSymbolProfile; tags?: Tag[]; + unitPriceInBaseCurrency: number; updateAccountBalance?: boolean; value: number; valueInBaseCurrency: number; diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index fced56950..abc7299c6 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -542,6 +542,13 @@ export class OrderService { order.date ), SymbolProfile: assetProfile, + unitPriceInBaseCurrency: + await this.exchangeRateDataService.toCurrencyAtDate( + order.unitPrice, + order.currency ?? order.SymbolProfile.currency, + userCurrency, + order.date + ), valueInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( value, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 52d57230b..64ded194e 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -112,12 +112,12 @@ export abstract class PortfolioCalculator { .map( ({ date, - fee, + feeInBaseCurrency, quantity, SymbolProfile, tags = [], type, - unitPrice + unitPriceInBaseCurrency }) => { if (isBefore(date, dateOfFirstActivity)) { dateOfFirstActivity = date; @@ -134,9 +134,9 @@ export abstract class PortfolioCalculator { tags, type, date: format(date, DATE_FORMAT), - fee: new Big(fee), + fee: new Big(feeInBaseCurrency), quantity: new Big(quantity), - unitPrice: new Big(unitPrice) + unitPrice: new Big(unitPriceInBaseCurrency) }; } ) From 39d3ad4f49b536fbefebd127e2305e1077dc69e4 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Thu, 27 Mar 2025 17:23:05 +0100 Subject: [PATCH 03/13] API tests fixed --- apps/api/src/app/export/export.service.ts | 3 ++- .../calculator/portfolio-calculator-test-utils.ts | 3 +++ ...lator-baln-buy-and-sell-in-two-activities.spec.ts | 12 ++++++------ .../portfolio-calculator-baln-buy-and-sell.spec.ts | 8 ++++---- .../roai/portfolio-calculator-baln-buy.spec.ts | 4 ++-- ...-calculator-btcusd-buy-and-sell-partially.spec.ts | 8 ++++---- .../calculator/roai/portfolio-calculator-fee.spec.ts | 4 ++-- .../roai/portfolio-calculator-googl-buy.spec.ts | 4 ++-- .../roai/portfolio-calculator-item.spec.ts | 4 ++-- .../roai/portfolio-calculator-liability.spec.ts | 4 ++-- ...rtfolio-calculator-msft-buy-with-dividend.spec.ts | 8 ++++---- ...io-calculator-novn-buy-and-sell-partially.spec.ts | 4 +++- .../portfolio-calculator-novn-buy-and-sell.spec.ts | 4 +++- 13 files changed, 39 insertions(+), 31 deletions(-) diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index f0449dc14..5efa429c7 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -120,6 +120,7 @@ export class ExportService { ({ accountId, comment, + currency, date, fee, id, @@ -137,7 +138,7 @@ export class ExportService { quantity, type, unitPrice, - currency: SymbolProfile.currency, + currency: currency ?? SymbolProfile.currency, dataSource: SymbolProfile.dataSource, date: date.toISOString(), symbol: ['FEE', 'INTEREST', 'ITEM', 'LIABILITY'].includes(type) diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts index c5a902c29..72738bf02 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts @@ -6,10 +6,13 @@ export const activityDummyData = { comment: undefined, createdAt: new Date(), currency: undefined, + fee: undefined, feeInBaseCurrency: undefined, id: undefined, isDraft: false, symbolProfileId: undefined, + unitPrice: undefined, + unitPriceInBaseCurrency: undefined, updatedAt: new Date(), userId: undefined, value: undefined, diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index e157e2d26..c5f5a39c8 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-22'), - fee: 1.55, + feeInBaseCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,12 +101,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPrice: 142.9 + unitPriceInBaseCurrency: 142.9 }, { ...activityDummyData, date: new Date('2021-11-30'), - fee: 1.65, + feeInBaseCurrency: 1.65, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -116,12 +116,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPrice: 136.6 + unitPriceInBaseCurrency: 136.6 }, { ...activityDummyData, date: new Date('2021-11-30'), - fee: 0, + feeInBaseCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -131,7 +131,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPrice: 136.6 + unitPriceInBaseCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index a1650ea82..6997e0258 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-22'), - fee: 1.55, + feeInBaseCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,12 +101,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPrice: 142.9 + unitPriceInBaseCurrency: 142.9 }, { ...activityDummyData, date: new Date('2021-11-30'), - fee: 1.65, + feeInBaseCurrency: 1.65, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -116,7 +116,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPrice: 136.6 + unitPriceInBaseCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index 63a4d77b4..be7d3a0a0 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-30'), - fee: 1.55, + feeInBaseCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPrice: 136.6 + unitPriceInBaseCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 2853e3d87..45d446e59 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2015-01-01'), - fee: 0, + feeInBaseCurrency: 0, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -115,12 +115,12 @@ describe('PortfolioCalculator', () => { symbol: 'BTCUSD' }, type: 'BUY', - unitPrice: 320.43 + unitPriceInBaseCurrency: 320.43 }, { ...activityDummyData, date: new Date('2017-12-31'), - fee: 0, + feeInBaseCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -130,7 +130,7 @@ describe('PortfolioCalculator', () => { symbol: 'BTCUSD' }, type: 'SELL', - unitPrice: 14156.4 + unitPriceInBaseCurrency: 14156.4 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index b96e4f540..c15847a59 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-09-01'), - fee: 49, + feeInBaseCurrency: 49, quantity: 0, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: '2c463fb3-af07-486e-adb0-8301b3d72141' }, type: 'FEE', - unitPrice: 0 + unitPriceInBaseCurrency: 0 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index b3793a5b4..b287507b8 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -104,7 +104,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2023-01-03'), - fee: 1, + feeInBaseCurrency: 1, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -114,7 +114,7 @@ describe('PortfolioCalculator', () => { symbol: 'GOOGL' }, type: 'BUY', - unitPrice: 89.12 + unitPriceInBaseCurrency: 89.12 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts index d226fe6f8..408b82dc8 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2022-01-01'), - fee: 0, + feeInBaseCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: 'dac95060-d4f2-4653-a253-2c45e6fb5cde' }, type: 'ITEM', - unitPrice: 500000 + unitPriceInBaseCurrency: 500000 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index 569212b9a..74dfd6673 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2023-01-01'), // Date in future - fee: 0, + feeInBaseCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: '55196015-1365-4560-aa60-8751ae6d18f8' }, type: 'LIABILITY', - unitPrice: 3000 + unitPriceInBaseCurrency: 3000 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index 4c54ba7aa..1cf652a85 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -104,7 +104,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-09-16'), - fee: 19, + feeInBaseCurrency: 19, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -114,12 +114,12 @@ describe('PortfolioCalculator', () => { symbol: 'MSFT' }, type: 'BUY', - unitPrice: 298.58 + unitPriceInBaseCurrency: 298.58 }, { ...activityDummyData, date: new Date('2021-11-16'), - fee: 0, + feeInBaseCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -129,7 +129,7 @@ describe('PortfolioCalculator', () => { symbol: 'MSFT' }, type: 'DIVIDEND', - unitPrice: 0.62 + unitPriceInBaseCurrency: 0.62 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 84bcc5bc1..444de4d51 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -105,13 +105,15 @@ describe('PortfolioCalculator', () => { ...activityDummyData, ...activity, date: parseDate(activity.date), + feeInBaseCurrency: activity.fee, SymbolProfile: { ...symbolProfileDummyData, currency: activity.currency, dataSource: activity.dataSource, name: 'Novartis AG', symbol: activity.symbol - } + }, + unitPriceInBaseCurrency: activity.unitPrice })); const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index 937fd8b48..1d2183553 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -105,13 +105,15 @@ describe('PortfolioCalculator', () => { ...activityDummyData, ...activity, date: parseDate(activity.date), + feeInBaseCurrency: activity.fee, SymbolProfile: { ...symbolProfileDummyData, currency: activity.currency, dataSource: activity.dataSource, name: 'Novartis AG', symbol: activity.symbol - } + }, + unitPriceInBaseCurrency: activity.unitPrice })); const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ From fd4aea0c526ce251122c79d6a1884ab2a1ed4ae2 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Tue, 1 Apr 2025 10:30:34 +0200 Subject: [PATCH 04/13] Database migration created --- .../migration.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 prisma/migrations/20250401084916_set_value_of_currency_to_null_in_order/migration.sql diff --git a/prisma/migrations/20250401084916_set_value_of_currency_to_null_in_order/migration.sql b/prisma/migrations/20250401084916_set_value_of_currency_to_null_in_order/migration.sql new file mode 100644 index 000000000..03a4e7178 --- /dev/null +++ b/prisma/migrations/20250401084916_set_value_of_currency_to_null_in_order/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +UPDATE "Order" SET "currency" = NULL; From 96c9c201e035cab1c7ef3a3694df29bd56bb5d6b Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Tue, 1 Apr 2025 10:48:19 +0200 Subject: [PATCH 05/13] Test cases added --- test/import/ok-btceur.json | 29 +++++++++++++++++++++++++++++ test/import/ok-btcusd.json | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 test/import/ok-btceur.json create mode 100644 test/import/ok-btcusd.json diff --git a/test/import/ok-btceur.json b/test/import/ok-btceur.json new file mode 100644 index 000000000..b2856943d --- /dev/null +++ b/test/import/ok-btceur.json @@ -0,0 +1,29 @@ +{ + "meta": { + "date": "2021-12-12T00:00:00.000Z", + "version": "dev" + }, + "accounts": [], + "platforms": [], + "tags": [], + "activities": [ + { + "accountId": null, + "comment": null, + "fee": 0, + "quantity": 1, + "type": "BUY", + "unitPrice": 39378.5, + "currency": "EUR", + "dataSource": "YAHOO", + "date": "2021-12-12T00:00:00.000Z", + "symbol": "BTCUSD", + "tags": [] + } + ], + "user": { + "settings": { + "currency": "USD" + } + } +} diff --git a/test/import/ok-btcusd.json b/test/import/ok-btcusd.json new file mode 100644 index 000000000..87c936f23 --- /dev/null +++ b/test/import/ok-btcusd.json @@ -0,0 +1,29 @@ +{ + "meta": { + "date": "2021-12-12T00:00:00.000Z", + "version": "dev" + }, + "accounts": [], + "platforms": [], + "tags": [], + "activities": [ + { + "accountId": null, + "comment": null, + "fee": 0, + "quantity": 1, + "type": "BUY", + "unitPrice": 44558.42, + "currency": "USD", + "dataSource": "YAHOO", + "date": "2021-12-12T00:00:00.000Z", + "symbol": "BTCUSD", + "tags": [] + } + ], + "user": { + "settings": { + "currency": "USD" + } + } +} From 3b2086f5538e44b1a2e475c90096a67144cacab7 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Mon, 7 Apr 2025 09:14:30 +0200 Subject: [PATCH 06/13] PR review changes --- apps/api/src/app/import/import.service.ts | 6 ------ .../create-or-update-activity-dialog.component.ts | 6 +++++- .../lib/activities-table/activities-table.component.html | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 9e0bf1d53..4ed5749a7 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -640,12 +640,6 @@ export class ImportService { `activities.${index}.symbol ("${symbol}") is not valid for the specified data source ("${dataSource}")` ); } - - if (assetProfile.currency !== currency) { - throw new Error( - `activities.${index}.currency ("${currency}") does not match with currency of ${assetProfile.symbol} ("${assetProfile.currency}")` - ); - } } assetProfiles[getAssetProfileIdentifier({ dataSource, symbol })] = diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts index 81cfec651..f6ce2a81d 100644 --- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts @@ -101,7 +101,11 @@ export class CreateOrUpdateActivityDialog implements OnDestroy { this.data.activity?.SymbolProfile?.currency, Validators.required ], - currencyOfUnitPrice: [this.data.activity?.currency, Validators.required], + currencyOfUnitPrice: [ + this.data.activity?.currency ?? + this.data.activity?.SymbolProfile?.currency, + Validators.required + ], dataSource: [ this.data.activity?.SymbolProfile?.dataSource, Validators.required 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 76aeea0fe..79a7d3417 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -280,7 +280,7 @@ class="d-none d-lg-table-cell px-1" mat-cell > - {{ element.currency }} + {{ element.currency ?? element.SymbolProfile?.currency }} From 6fd99d12285a34deaf0ce86a51dabfa6daeebff7 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Mon, 7 Apr 2025 11:48:02 +0200 Subject: [PATCH 07/13] Activity fee, unitPrice in symbol curreny added --- apps/api/src/app/import/import.service.ts | 2 ++ .../app/order/interfaces/activities.interface.ts | 2 ++ apps/api/src/app/order/order.service.ts | 14 ++++++++++++++ .../portfolio/calculator/portfolio-calculator.ts | 8 ++++---- ...tor-baln-buy-and-sell-in-two-activities.spec.ts | 12 ++++++------ .../portfolio-calculator-baln-buy-and-sell.spec.ts | 8 ++++---- .../roai/portfolio-calculator-baln-buy.spec.ts | 4 ++-- ...alculator-btcusd-buy-and-sell-partially.spec.ts | 8 ++++---- .../roai/portfolio-calculator-fee.spec.ts | 4 ++-- .../roai/portfolio-calculator-googl-buy.spec.ts | 4 ++-- .../roai/portfolio-calculator-item.spec.ts | 4 ++-- .../roai/portfolio-calculator-liability.spec.ts | 4 ++-- ...folio-calculator-msft-buy-with-dividend.spec.ts | 8 ++++---- ...-calculator-novn-buy-and-sell-partially.spec.ts | 4 ++-- .../portfolio-calculator-novn-buy-and-sell.spec.ts | 4 ++-- 15 files changed, 54 insertions(+), 36 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 4ed5749a7..6ebaa8417 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -122,6 +122,7 @@ export class ImportService { createdAt: undefined, fee: 0, feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, id: assetProfile.id, isDraft: false, SymbolProfile: assetProfile, @@ -135,6 +136,7 @@ export class ImportService { userCurrency, date ), + unitPriceInSymbolCurrency: marketPrice, updatedAt: undefined, userId: Account?.userId, valueInBaseCurrency: diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/apps/api/src/app/order/interfaces/activities.interface.ts index 09038ea42..e085d1f23 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/apps/api/src/app/order/interfaces/activities.interface.ts @@ -12,9 +12,11 @@ export interface Activity extends Order { Account?: AccountWithPlatform; error?: ActivityError; feeInBaseCurrency: number; + feeInSymbolCurrency: number; SymbolProfile?: EnhancedSymbolProfile; tags?: Tag[]; unitPriceInBaseCurrency: number; + unitPriceInSymbolCurrency: number; updateAccountBalance?: boolean; value: number; valueInBaseCurrency: number; diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index abc7299c6..bae7b1ac2 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -541,6 +541,13 @@ export class OrderService { userCurrency, order.date ), + feeInSymbolCurrency: + await this.exchangeRateDataService.toCurrencyAtDate( + order.fee, + order.currency ?? order.SymbolProfile.currency, + order.SymbolProfile.currency, + order.date + ), SymbolProfile: assetProfile, unitPriceInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( @@ -549,6 +556,13 @@ export class OrderService { userCurrency, order.date ), + unitPriceInSymbolCurrency: + await this.exchangeRateDataService.toCurrencyAtDate( + order.unitPrice, + order.currency ?? order.SymbolProfile.currency, + order.SymbolProfile.currency, + order.date + ), valueInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( value, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 64ded194e..1657733ef 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -112,12 +112,12 @@ export abstract class PortfolioCalculator { .map( ({ date, - feeInBaseCurrency, + feeInSymbolCurrency, quantity, SymbolProfile, tags = [], type, - unitPriceInBaseCurrency + unitPriceInSymbolCurrency }) => { if (isBefore(date, dateOfFirstActivity)) { dateOfFirstActivity = date; @@ -134,9 +134,9 @@ export abstract class PortfolioCalculator { tags, type, date: format(date, DATE_FORMAT), - fee: new Big(feeInBaseCurrency), + fee: new Big(feeInSymbolCurrency), quantity: new Big(quantity), - unitPrice: new Big(unitPriceInBaseCurrency) + unitPrice: new Big(unitPriceInSymbolCurrency) }; } ) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index c5f5a39c8..be216239b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-22'), - feeInBaseCurrency: 1.55, + feeInSymbolCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,12 +101,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPriceInBaseCurrency: 142.9 + unitPriceInSymbolCurrency: 142.9 }, { ...activityDummyData, date: new Date('2021-11-30'), - feeInBaseCurrency: 1.65, + feeInSymbolCurrency: 1.65, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -116,12 +116,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPriceInBaseCurrency: 136.6 + unitPriceInSymbolCurrency: 136.6 }, { ...activityDummyData, date: new Date('2021-11-30'), - feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -131,7 +131,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPriceInBaseCurrency: 136.6 + unitPriceInSymbolCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index 6997e0258..ac15f5be0 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-22'), - feeInBaseCurrency: 1.55, + feeInSymbolCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,12 +101,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPriceInBaseCurrency: 142.9 + unitPriceInSymbolCurrency: 142.9 }, { ...activityDummyData, date: new Date('2021-11-30'), - feeInBaseCurrency: 1.65, + feeInSymbolCurrency: 1.65, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -116,7 +116,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPriceInBaseCurrency: 136.6 + unitPriceInSymbolCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index be7d3a0a0..4c3fbd5bf 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-30'), - feeInBaseCurrency: 1.55, + feeInSymbolCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPriceInBaseCurrency: 136.6 + unitPriceInSymbolCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 45d446e59..2f2440ead 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2015-01-01'), - feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -115,12 +115,12 @@ describe('PortfolioCalculator', () => { symbol: 'BTCUSD' }, type: 'BUY', - unitPriceInBaseCurrency: 320.43 + unitPriceInSymbolCurrency: 320.43 }, { ...activityDummyData, date: new Date('2017-12-31'), - feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -130,7 +130,7 @@ describe('PortfolioCalculator', () => { symbol: 'BTCUSD' }, type: 'SELL', - unitPriceInBaseCurrency: 14156.4 + unitPriceInSymbolCurrency: 14156.4 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index c15847a59..022c96621 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-09-01'), - feeInBaseCurrency: 49, + feeInSymbolCurrency: 49, quantity: 0, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: '2c463fb3-af07-486e-adb0-8301b3d72141' }, type: 'FEE', - unitPriceInBaseCurrency: 0 + unitPriceInSymbolCurrency: 0 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index b287507b8..32e4bc6c1 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -104,7 +104,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2023-01-03'), - feeInBaseCurrency: 1, + feeInSymbolCurrency: 1, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -114,7 +114,7 @@ describe('PortfolioCalculator', () => { symbol: 'GOOGL' }, type: 'BUY', - unitPriceInBaseCurrency: 89.12 + unitPriceInSymbolCurrency: 89.12 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts index 408b82dc8..1d89d848d 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2022-01-01'), - feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: 'dac95060-d4f2-4653-a253-2c45e6fb5cde' }, type: 'ITEM', - unitPriceInBaseCurrency: 500000 + unitPriceInSymbolCurrency: 500000 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index 74dfd6673..00c1a3720 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2023-01-01'), // Date in future - feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: '55196015-1365-4560-aa60-8751ae6d18f8' }, type: 'LIABILITY', - unitPriceInBaseCurrency: 3000 + unitPriceInSymbolCurrency: 3000 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index 1cf652a85..f841384c9 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -104,7 +104,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-09-16'), - feeInBaseCurrency: 19, + feeInSymbolCurrency: 19, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -114,12 +114,12 @@ describe('PortfolioCalculator', () => { symbol: 'MSFT' }, type: 'BUY', - unitPriceInBaseCurrency: 298.58 + unitPriceInSymbolCurrency: 298.58 }, { ...activityDummyData, date: new Date('2021-11-16'), - feeInBaseCurrency: 0, + feeInSymbolCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -129,7 +129,7 @@ describe('PortfolioCalculator', () => { symbol: 'MSFT' }, type: 'DIVIDEND', - unitPriceInBaseCurrency: 0.62 + unitPriceInSymbolCurrency: 0.62 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 444de4d51..1620ff258 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { ...activityDummyData, ...activity, date: parseDate(activity.date), - feeInBaseCurrency: activity.fee, + feeInSymbolCurrency: activity.fee, SymbolProfile: { ...symbolProfileDummyData, currency: activity.currency, @@ -113,7 +113,7 @@ describe('PortfolioCalculator', () => { name: 'Novartis AG', symbol: activity.symbol }, - unitPriceInBaseCurrency: activity.unitPrice + unitPriceInSymbolCurrency: activity.unitPrice })); const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index 1d2183553..457248faa 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { ...activityDummyData, ...activity, date: parseDate(activity.date), - feeInBaseCurrency: activity.fee, + feeInSymbolCurrency: activity.fee, SymbolProfile: { ...symbolProfileDummyData, currency: activity.currency, @@ -113,7 +113,7 @@ describe('PortfolioCalculator', () => { name: 'Novartis AG', symbol: activity.symbol }, - unitPriceInBaseCurrency: activity.unitPrice + unitPriceInSymbolCurrency: activity.unitPrice })); const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ From 34e24eba9ab17f7d95eeffa01c6d6053e593f26e Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Mon, 7 Apr 2025 13:12:10 +0200 Subject: [PATCH 08/13] Fee and unit price in base currency removed from activity --- apps/api/src/app/import/import.service.ts | 14 -------------- .../app/order/interfaces/activities.interface.ts | 2 -- apps/api/src/app/order/order.service.ts | 14 -------------- 3 files changed, 30 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 6ebaa8417..7bd8606ee 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -121,7 +121,6 @@ export class ImportService { currency: undefined, createdAt: undefined, fee: 0, - feeInBaseCurrency: 0, feeInSymbolCurrency: 0, id: assetProfile.id, isDraft: false, @@ -129,13 +128,6 @@ export class ImportService { symbolProfileId: assetProfile.id, type: 'DIVIDEND', unitPrice: marketPrice, - unitPriceInBaseCurrency: - await this.exchangeRateDataService.toCurrencyAtDate( - marketPrice, - assetProfile.currency, - userCurrency, - date - ), unitPriceInSymbolCurrency: marketPrice, updatedAt: undefined, userId: Account?.userId, @@ -448,12 +440,6 @@ export class ImportService { ...order, error, value, - feeInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( - fee, - assetProfile.currency, - userCurrency, - date - ), // @ts-ignore SymbolProfile: assetProfile, valueInBaseCurrency: diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/apps/api/src/app/order/interfaces/activities.interface.ts index e085d1f23..c3b487e0c 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/apps/api/src/app/order/interfaces/activities.interface.ts @@ -11,11 +11,9 @@ export interface Activities { export interface Activity extends Order { Account?: AccountWithPlatform; error?: ActivityError; - feeInBaseCurrency: number; feeInSymbolCurrency: number; SymbolProfile?: EnhancedSymbolProfile; tags?: Tag[]; - unitPriceInBaseCurrency: number; unitPriceInSymbolCurrency: number; updateAccountBalance?: boolean; value: number; diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index bae7b1ac2..eaf576220 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -534,13 +534,6 @@ export class OrderService { return { ...order, value, - feeInBaseCurrency: - await this.exchangeRateDataService.toCurrencyAtDate( - order.fee, - order.currency ?? order.SymbolProfile.currency, - userCurrency, - order.date - ), feeInSymbolCurrency: await this.exchangeRateDataService.toCurrencyAtDate( order.fee, @@ -549,13 +542,6 @@ export class OrderService { order.date ), SymbolProfile: assetProfile, - unitPriceInBaseCurrency: - await this.exchangeRateDataService.toCurrencyAtDate( - order.unitPrice, - order.currency ?? order.SymbolProfile.currency, - userCurrency, - order.date - ), unitPriceInSymbolCurrency: await this.exchangeRateDataService.toCurrencyAtDate( order.unitPrice, From 2b479149d23d53dd9a9c7d64aa8b45ec22dc3d60 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Mon, 7 Apr 2025 13:25:31 +0200 Subject: [PATCH 09/13] Test data redundant fields removed --- .../app/portfolio/calculator/portfolio-calculator-test-utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts index 72738bf02..2c9f7b6f3 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts @@ -7,12 +7,10 @@ export const activityDummyData = { createdAt: new Date(), currency: undefined, fee: undefined, - feeInBaseCurrency: undefined, id: undefined, isDraft: false, symbolProfileId: undefined, unitPrice: undefined, - unitPriceInBaseCurrency: undefined, updatedAt: new Date(), userId: undefined, value: undefined, From 9a7d5b5c9cc74c800a22785b3d7a88f6c230fe84 Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Tue, 8 Apr 2025 09:11:42 +0200 Subject: [PATCH 10/13] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd591b934..791ac0749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added the data gathering status column to the historical market data table of the admin control +- Added support to activity in custom currency ### Changed From 98cb2a3bfba85d0267db61f5dd9b3fd569a39298 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Fri, 11 Apr 2025 08:14:14 +0200 Subject: [PATCH 11/13] Code review changes --- apps/api/src/app/import/import.service.ts | 54 +++++-------------- .../order/interfaces/activities.interface.ts | 4 +- apps/api/src/app/order/order.service.ts | 4 +- .../calculator/portfolio-calculator.ts | 8 +-- ...aln-buy-and-sell-in-two-activities.spec.ts | 12 ++--- ...folio-calculator-baln-buy-and-sell.spec.ts | 8 +-- .../portfolio-calculator-baln-buy.spec.ts | 4 +- ...ator-btcusd-buy-and-sell-partially.spec.ts | 8 +-- .../roai/portfolio-calculator-fee.spec.ts | 4 +- .../portfolio-calculator-googl-buy.spec.ts | 4 +- .../roai/portfolio-calculator-item.spec.ts | 4 +- .../portfolio-calculator-liability.spec.ts | 4 +- ...-calculator-msft-buy-with-dividend.spec.ts | 8 +-- ...ulator-novn-buy-and-sell-partially.spec.ts | 4 +- ...folio-calculator-novn-buy-and-sell.spec.ts | 4 +- .../app/services/import-activities.service.ts | 3 +- 16 files changed, 55 insertions(+), 82 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 7bd8606ee..c9e15ce4a 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -15,7 +15,6 @@ import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathe import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DATA_GATHERING_QUEUE_PRIORITY_HIGH } from '@ghostfolio/common/config'; import { - DATE_FORMAT, getAssetProfileIdentifier, parseDate } from '@ghostfolio/common/helper'; @@ -29,8 +28,8 @@ import { import { Injectable } from '@nestjs/common'; import { DataSource, Prisma, SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; -import { endOfToday, format, isAfter, isSameSecond, parseISO } from 'date-fns'; -import { isNumber, uniqBy } from 'lodash'; +import { endOfToday, isAfter, isSameSecond, parseISO } from 'date-fns'; +import { uniqBy } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; @Injectable() @@ -121,14 +120,14 @@ export class ImportService { currency: undefined, createdAt: undefined, fee: 0, - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, id: assetProfile.id, isDraft: false, SymbolProfile: assetProfile, symbolProfileId: assetProfile.id, type: 'DIVIDEND', unitPrice: marketPrice, - unitPriceInSymbolCurrency: marketPrice, + unitPriceInAssetProfileCurrency: marketPrice, updatedAt: undefined, userId: Account?.userId, valueInBaseCurrency: @@ -267,17 +266,17 @@ export class ImportService { const activities: Activity[] = []; - for (const [index, activity] of activitiesExtendedWithErrors.entries()) { + for (const activity of activitiesExtendedWithErrors) { const accountId = activity.accountId; const comment = activity.comment; const currency = activity.currency; const date = activity.date; const error = activity.error; - let fee = activity.fee; + const fee = activity.fee; const quantity = activity.quantity; const SymbolProfile = activity.SymbolProfile; const type = activity.type; - let unitPrice = activity.unitPrice; + const unitPrice = activity.unitPrice; const assetProfile = assetProfiles[ getAssetProfileIdentifier({ @@ -285,7 +284,6 @@ export class ImportService { symbol: SymbolProfile.symbol }) ] ?? { - currency: SymbolProfile.currency, dataSource: SymbolProfile.dataSource, symbol: SymbolProfile.symbol }; @@ -321,35 +319,6 @@ export class ImportService { Account?: { id: string; name: string }; }); - if (SymbolProfile.currency !== assetProfile.currency) { - // Convert the unit price and fee to the asset currency if the imported - // activity is in a different currency - unitPrice = await this.exchangeRateDataService.toCurrencyAtDate( - unitPrice, - SymbolProfile.currency, - assetProfile.currency, - date - ); - - if (!isNumber(unitPrice)) { - throw new Error( - `activities.${index} historical exchange rate at ${format( - date, - DATE_FORMAT - )} is not available from "${SymbolProfile.currency}" to "${ - assetProfile.currency - }"` - ); - } - - fee = await this.exchangeRateDataService.toCurrencyAtDate( - fee, - SymbolProfile.currency, - assetProfile.currency, - date - ); - } - if (isDryRun) { order = { comment, @@ -401,6 +370,7 @@ export class ImportService { order = await this.orderService.createOrder({ comment, + currency, date, fee, quantity, @@ -445,7 +415,7 @@ export class ImportService { valueInBaseCurrency: await this.exchangeRateDataService.toCurrencyAtDate( value, - assetProfile.currency, + currency ?? assetProfile.currency, userCurrency, date ) @@ -515,7 +485,8 @@ export class ImportService { return ( activity.accountId === accountId && activity.comment === comment && - activity.SymbolProfile.currency === currency && + (activity.currency === currency || + activity.SymbolProfile.currency === currency) && activity.SymbolProfile.dataSource === dataSource && isSameSecond(activity.date, date) && activity.fee === fee && @@ -533,6 +504,7 @@ export class ImportService { return { accountId, comment, + currency, date, error, fee, @@ -540,7 +512,7 @@ export class ImportService { type, unitPrice, SymbolProfile: { - currency, + currency: undefined, dataSource, symbol, activitiesCount: undefined, diff --git a/apps/api/src/app/order/interfaces/activities.interface.ts b/apps/api/src/app/order/interfaces/activities.interface.ts index c3b487e0c..3dcde1464 100644 --- a/apps/api/src/app/order/interfaces/activities.interface.ts +++ b/apps/api/src/app/order/interfaces/activities.interface.ts @@ -11,10 +11,10 @@ export interface Activities { export interface Activity extends Order { Account?: AccountWithPlatform; error?: ActivityError; - feeInSymbolCurrency: number; + feeInAssetProfileCurrency: number; SymbolProfile?: EnhancedSymbolProfile; tags?: Tag[]; - unitPriceInSymbolCurrency: number; + unitPriceInAssetProfileCurrency: number; updateAccountBalance?: boolean; value: number; valueInBaseCurrency: number; diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index eaf576220..0745aa3d9 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -534,7 +534,7 @@ export class OrderService { return { ...order, value, - feeInSymbolCurrency: + feeInAssetProfileCurrency: await this.exchangeRateDataService.toCurrencyAtDate( order.fee, order.currency ?? order.SymbolProfile.currency, @@ -542,7 +542,7 @@ export class OrderService { order.date ), SymbolProfile: assetProfile, - unitPriceInSymbolCurrency: + unitPriceInAssetProfileCurrency: await this.exchangeRateDataService.toCurrencyAtDate( order.unitPrice, order.currency ?? order.SymbolProfile.currency, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 1657733ef..9698cb315 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -112,12 +112,12 @@ export abstract class PortfolioCalculator { .map( ({ date, - feeInSymbolCurrency, + feeInAssetProfileCurrency, quantity, SymbolProfile, tags = [], type, - unitPriceInSymbolCurrency + unitPriceInAssetProfileCurrency }) => { if (isBefore(date, dateOfFirstActivity)) { dateOfFirstActivity = date; @@ -134,9 +134,9 @@ export abstract class PortfolioCalculator { tags, type, date: format(date, DATE_FORMAT), - fee: new Big(feeInSymbolCurrency), + fee: new Big(feeInAssetProfileCurrency), quantity: new Big(quantity), - unitPrice: new Big(unitPriceInSymbolCurrency) + unitPrice: new Big(unitPriceInAssetProfileCurrency) }; } ) diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index be216239b..eed5a1e80 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-22'), - feeInSymbolCurrency: 1.55, + feeInAssetProfileCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,12 +101,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPriceInSymbolCurrency: 142.9 + unitPriceInAssetProfileCurrency: 142.9 }, { ...activityDummyData, date: new Date('2021-11-30'), - feeInSymbolCurrency: 1.65, + feeInAssetProfileCurrency: 1.65, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -116,12 +116,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPriceInSymbolCurrency: 136.6 + unitPriceInAssetProfileCurrency: 136.6 }, { ...activityDummyData, date: new Date('2021-11-30'), - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -131,7 +131,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPriceInSymbolCurrency: 136.6 + unitPriceInAssetProfileCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index ac15f5be0..15f3983fe 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-22'), - feeInSymbolCurrency: 1.55, + feeInAssetProfileCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,12 +101,12 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPriceInSymbolCurrency: 142.9 + unitPriceInAssetProfileCurrency: 142.9 }, { ...activityDummyData, date: new Date('2021-11-30'), - feeInSymbolCurrency: 1.65, + feeInAssetProfileCurrency: 1.65, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -116,7 +116,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'SELL', - unitPriceInSymbolCurrency: 136.6 + unitPriceInAssetProfileCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index 4c3fbd5bf..7a34bd114 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-11-30'), - feeInSymbolCurrency: 1.55, + feeInAssetProfileCurrency: 1.55, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: 'BALN.SW' }, type: 'BUY', - unitPriceInSymbolCurrency: 136.6 + unitPriceInAssetProfileCurrency: 136.6 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 2f2440ead..3158076cb 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2015-01-01'), - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, quantity: 2, SymbolProfile: { ...symbolProfileDummyData, @@ -115,12 +115,12 @@ describe('PortfolioCalculator', () => { symbol: 'BTCUSD' }, type: 'BUY', - unitPriceInSymbolCurrency: 320.43 + unitPriceInAssetProfileCurrency: 320.43 }, { ...activityDummyData, date: new Date('2017-12-31'), - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -130,7 +130,7 @@ describe('PortfolioCalculator', () => { symbol: 'BTCUSD' }, type: 'SELL', - unitPriceInSymbolCurrency: 14156.4 + unitPriceInAssetProfileCurrency: 14156.4 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index 022c96621..22a34af24 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-09-01'), - feeInSymbolCurrency: 49, + feeInAssetProfileCurrency: 49, quantity: 0, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: '2c463fb3-af07-486e-adb0-8301b3d72141' }, type: 'FEE', - unitPriceInSymbolCurrency: 0 + unitPriceInAssetProfileCurrency: 0 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index 32e4bc6c1..41299eb40 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -104,7 +104,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2023-01-03'), - feeInSymbolCurrency: 1, + feeInAssetProfileCurrency: 1, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -114,7 +114,7 @@ describe('PortfolioCalculator', () => { symbol: 'GOOGL' }, type: 'BUY', - unitPriceInSymbolCurrency: 89.12 + unitPriceInAssetProfileCurrency: 89.12 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts index 1d89d848d..721c9ae96 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2022-01-01'), - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: 'dac95060-d4f2-4653-a253-2c45e6fb5cde' }, type: 'ITEM', - unitPriceInSymbolCurrency: 500000 + unitPriceInAssetProfileCurrency: 500000 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index 00c1a3720..a82e605d4 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -91,7 +91,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2023-01-01'), // Date in future - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -101,7 +101,7 @@ describe('PortfolioCalculator', () => { symbol: '55196015-1365-4560-aa60-8751ae6d18f8' }, type: 'LIABILITY', - unitPriceInSymbolCurrency: 3000 + unitPriceInAssetProfileCurrency: 3000 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index f841384c9..d8e774639 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -104,7 +104,7 @@ describe('PortfolioCalculator', () => { { ...activityDummyData, date: new Date('2021-09-16'), - feeInSymbolCurrency: 19, + feeInAssetProfileCurrency: 19, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -114,12 +114,12 @@ describe('PortfolioCalculator', () => { symbol: 'MSFT' }, type: 'BUY', - unitPriceInSymbolCurrency: 298.58 + unitPriceInAssetProfileCurrency: 298.58 }, { ...activityDummyData, date: new Date('2021-11-16'), - feeInSymbolCurrency: 0, + feeInAssetProfileCurrency: 0, quantity: 1, SymbolProfile: { ...symbolProfileDummyData, @@ -129,7 +129,7 @@ describe('PortfolioCalculator', () => { symbol: 'MSFT' }, type: 'DIVIDEND', - unitPriceInSymbolCurrency: 0.62 + unitPriceInAssetProfileCurrency: 0.62 } ]; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 1620ff258..ffbfe7345 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { ...activityDummyData, ...activity, date: parseDate(activity.date), - feeInSymbolCurrency: activity.fee, + feeInAssetProfileCurrency: activity.fee, SymbolProfile: { ...symbolProfileDummyData, currency: activity.currency, @@ -113,7 +113,7 @@ describe('PortfolioCalculator', () => { name: 'Novartis AG', symbol: activity.symbol }, - unitPriceInSymbolCurrency: activity.unitPrice + unitPriceInAssetProfileCurrency: activity.unitPrice })); const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index 457248faa..0256a6a1e 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -105,7 +105,7 @@ describe('PortfolioCalculator', () => { ...activityDummyData, ...activity, date: parseDate(activity.date), - feeInSymbolCurrency: activity.fee, + feeInAssetProfileCurrency: activity.fee, SymbolProfile: { ...symbolProfileDummyData, currency: activity.currency, @@ -113,7 +113,7 @@ describe('PortfolioCalculator', () => { name: 'Novartis AG', symbol: activity.symbol }, - unitPriceInSymbolCurrency: activity.unitPrice + unitPriceInAssetProfileCurrency: activity.unitPrice })); const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index c1b2209b3..f334a159f 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -126,6 +126,7 @@ export class ImportActivitiesService { private convertToCreateOrderDto({ accountId, comment, + currency, date, fee, quantity, @@ -137,12 +138,12 @@ export class ImportActivitiesService { return { accountId, comment, + currency, fee, quantity, type, unitPrice, updateAccountBalance, - currency: SymbolProfile.currency, dataSource: SymbolProfile.dataSource, date: date.toString(), symbol: SymbolProfile.symbol From 98a43f1d4ad33d29098df4857e987eddb5da01a5 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 12 Apr 2025 10:55:15 +0200 Subject: [PATCH 12/13] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 791ac0749..36b25f3af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added the data gathering status column to the historical market data table of the admin control -- Added support to activity in custom currency +- Added support for activities in a custom currency ### Changed From d6a9c226384996b8b3db7e1d4e79b137b057b11f Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 12 Apr 2025 11:11:50 +0200 Subject: [PATCH 13/13] Refactoring --- apps/api/src/app/import/import.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index c9e15ce4a..a00e0fb58 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -512,7 +512,6 @@ export class ImportService { type, unitPrice, SymbolProfile: { - currency: undefined, dataSource, symbol, activitiesCount: undefined, @@ -520,6 +519,7 @@ export class ImportService { assetSubClass: undefined, countries: undefined, createdAt: undefined, + currency: undefined, holdings: undefined, id: undefined, isActive: true,