Browse Source

Refactoring

pull/3214/head
Thomas Kaul 1 year ago
parent
commit
27147fa551
  1. 4
      apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts
  2. 30
      apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts
  3. 16
      apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts
  4. 22
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  5. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
  6. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts
  7. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts
  8. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  9. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts
  10. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts
  11. 14
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts
  12. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  13. 19
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts
  14. 11
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.spec.ts
  15. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
  16. 2
      apps/api/src/app/portfolio/interfaces/current-positions.interface.ts
  17. 35
      apps/api/src/app/portfolio/portfolio.service.ts

4
apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts

@ -1,3 +1,4 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface'; import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface';
import { import {
TimelinePosition, TimelinePosition,
@ -5,14 +6,13 @@ import {
SymbolMetrics SymbolMetrics
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { PortfolioCalculator } from '../portfolio-calculator';
export class MWRPortfolioCalculator extends PortfolioCalculator { export class MWRPortfolioCalculator extends PortfolioCalculator {
protected calculateOverallPerformance( protected calculateOverallPerformance(
positions: TimelinePosition[] positions: TimelinePosition[]
): CurrentPositions { ): CurrentPositions {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
protected getSymbolMetrics({ protected getSymbolMetrics({
dataSource, dataSource,
end, end,

30
apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts

@ -1,3 +1,18 @@
export const activityDummyData = {
accountId: undefined,
accountUserId: undefined,
comment: undefined,
createdAt: new Date(),
feeInBaseCurrency: undefined,
id: undefined,
isDraft: false,
symbolProfileId: undefined,
updatedAt: new Date(),
userId: undefined,
value: undefined,
valueInBaseCurrency: undefined
};
export const symbolProfileDummyData = { export const symbolProfileDummyData = {
activitiesCount: undefined, activitiesCount: undefined,
assetClass: undefined, assetClass: undefined,
@ -8,18 +23,3 @@ export const symbolProfileDummyData = {
sectors: [], sectors: [],
updatedAt: undefined updatedAt: undefined
}; };
export const activityDummyData = {
value: undefined,
valueInBaseCurrency: undefined,
accountId: undefined,
id: undefined,
userId: undefined,
createdAt: new Date(),
updatedAt: new Date(),
accountUserId: undefined,
comment: undefined,
feeInBaseCurrency: undefined,
isDraft: false,
symbolProfileId: undefined
};

16
apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts

@ -15,30 +15,30 @@ export enum PerformanceCalculationType {
@Injectable() @Injectable()
export class PortfolioCalculatorFactory { export class PortfolioCalculatorFactory {
constructor( public constructor(
private readonly currentRateService: CurrentRateService, private readonly currentRateService: CurrentRateService,
private readonly exchangeRateDataService: ExchangeRateDataService private readonly exchangeRateDataService: ExchangeRateDataService
) {} ) {}
createCalculator({ public createCalculator({
calculationType,
activities, activities,
calculationType,
currency currency
}: { }: {
calculationType: PerformanceCalculationType;
activities: Activity[]; activities: Activity[];
calculationType: PerformanceCalculationType;
currency: string; currency: string;
}): PortfolioCalculator { }): PortfolioCalculator {
switch (calculationType) { switch (calculationType) {
case PerformanceCalculationType.TWR: case PerformanceCalculationType.MWR:
return new TWRPortfolioCalculator({ return new MWRPortfolioCalculator({
activities, activities,
currency, currency,
currentRateService: this.currentRateService, currentRateService: this.currentRateService,
exchangeRateDataService: this.exchangeRateDataService exchangeRateDataService: this.exchangeRateDataService
}); });
case PerformanceCalculationType.MWR: case PerformanceCalculationType.TWR:
return new MWRPortfolioCalculator({ return new TWRPortfolioCalculator({
activities, activities,
currency, currency,
currentRateService: this.currentRateService, currentRateService: this.currentRateService,

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

@ -34,11 +34,12 @@ import { isNumber, last, uniq } from 'lodash';
export abstract class PortfolioCalculator { export abstract class PortfolioCalculator {
protected static readonly ENABLE_LOGGING = false; protected static readonly ENABLE_LOGGING = false;
protected orders: PortfolioOrder[];
private currency: string; private currency: string;
private currentRateService: CurrentRateService; private currentRateService: CurrentRateService;
private dataProviderInfos: DataProviderInfo[]; private dataProviderInfos: DataProviderInfo[];
private exchangeRateDataService: ExchangeRateDataService; private exchangeRateDataService: ExchangeRateDataService;
protected orders: PortfolioOrder[];
private transactionPoints: TransactionPoint[]; private transactionPoints: TransactionPoint[];
public constructor({ public constructor({
@ -75,6 +76,10 @@ export abstract class PortfolioCalculator {
this.computeTransactionPoints(); this.computeTransactionPoints();
} }
protected abstract calculateOverallPerformance(
positions: TimelinePosition[]
): CurrentPositions;
public getAnnualizedPerformancePercent({ public getAnnualizedPerformancePercent({
daysInMarket, daysInMarket,
netPerformancePercent netPerformancePercent
@ -394,7 +399,8 @@ export abstract class PortfolioCalculator {
netPerformancePercentageWithCurrencyEffect: new Big(0), netPerformancePercentageWithCurrencyEffect: new Big(0),
netPerformanceWithCurrencyEffect: new Big(0), netPerformanceWithCurrencyEffect: new Big(0),
positions: [], positions: [],
totalInvestment: new Big(0) totalInvestment: new Big(0),
totalInvestmentWithCurrencyEffect: new Big(0)
}; };
} }
@ -652,20 +658,12 @@ export abstract class PortfolioCalculator {
})); }));
} }
public getTransactionPoints() {
return this.transactionPoints;
}
public getStartDate() { public getStartDate() {
return this.transactionPoints.length > 0 return this.transactionPoints.length > 0
? parseDate(this.transactionPoints[0].date) ? parseDate(this.transactionPoints[0].date)
: new Date(); : new Date();
} }
protected abstract calculateOverallPerformance(
positions: TimelinePosition[]
): CurrentPositions;
protected abstract getSymbolMetrics({ protected abstract getSymbolMetrics({
dataSource, dataSource,
end, end,
@ -686,6 +684,10 @@ export abstract class PortfolioCalculator {
step?: number; step?: number;
} & UniqueAsset): SymbolMetrics; } & UniqueAsset): SymbolMetrics;
public getTransactionPoints() {
return this.transactionPoints;
}
private computeTransactionPoints() { private computeTransactionPoints() {
this.transactionPoints = []; this.transactionPoints = [];
const symbols: { [symbol: string]: TransactionPointSymbol } = {}; const symbols: { [symbol: string]: TransactionPointSymbol } = {};

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts

@ -1,19 +1,18 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { import {
activityDummyData, activityDummyData,
symbolProfileDummyData symbolProfileDummyData
} from '../portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { import {
PortfolioCalculatorFactory, PortfolioCalculatorFactory,
PerformanceCalculationType PerformanceCalculationType
} from '../portfolio-calculator.factory'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
@ -96,8 +95,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts

@ -1,19 +1,18 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { import {
activityDummyData, activityDummyData,
symbolProfileDummyData symbolProfileDummyData
} from '../portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { import {
PerformanceCalculationType, PerformanceCalculationType,
PortfolioCalculatorFactory PortfolioCalculatorFactory
} from '../portfolio-calculator.factory'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
@ -81,8 +80,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts

@ -1,19 +1,18 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { import {
activityDummyData, activityDummyData,
symbolProfileDummyData symbolProfileDummyData
} from '../portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { import {
PortfolioCalculatorFactory, PortfolioCalculatorFactory,
PerformanceCalculationType PerformanceCalculationType
} from '../portfolio-calculator.factory'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
@ -66,8 +65,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -1,4 +1,12 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
symbolProfileDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import {
PortfolioCalculatorFactory,
PerformanceCalculationType
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
@ -7,15 +15,6 @@ import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js'; import { Big } from 'big.js';
import {
activityDummyData,
symbolProfileDummyData
} from '../portfolio-calculator-test-utils';
import {
PortfolioCalculatorFactory,
PerformanceCalculationType
} from '../portfolio-calculator.factory';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -94,8 +93,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts

@ -1,4 +1,12 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
symbolProfileDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import {
PortfolioCalculatorFactory,
PerformanceCalculationType
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
@ -7,15 +15,6 @@ import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js'; import { Big } from 'big.js';
import {
activityDummyData,
symbolProfileDummyData
} from '../portfolio-calculator-test-utils';
import {
PortfolioCalculatorFactory,
PerformanceCalculationType
} from '../portfolio-calculator.factory';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -79,8 +78,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts

@ -1,4 +1,12 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
symbolProfileDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import {
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
@ -7,15 +15,6 @@ import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js'; import { Big } from 'big.js';
import {
activityDummyData,
symbolProfileDummyData
} from '../portfolio-calculator-test-utils';
import {
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '../portfolio-calculator.factory';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -94,8 +93,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'USD' currency: 'USD'
}); });

14
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts

@ -1,3 +1,7 @@
import {
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
@ -6,11 +10,6 @@ import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { subDays } from 'date-fns'; import { subDays } from 'date-fns';
import {
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '../portfolio-calculator.factory';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -44,8 +43,8 @@ describe('PortfolioCalculator', () => {
describe('get current positions', () => { describe('get current positions', () => {
it('with no orders', async () => { it('with no orders', async () => {
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities: [], activities: [],
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });
@ -81,7 +80,8 @@ describe('PortfolioCalculator', () => {
netPerformancePercentageWithCurrencyEffect: new Big(0), netPerformancePercentageWithCurrencyEffect: new Big(0),
netPerformanceWithCurrencyEffect: new Big(0), netPerformanceWithCurrencyEffect: new Big(0),
positions: [], positions: [],
totalInvestment: new Big(0) totalInvestment: new Big(0),
totalInvestmentWithCurrencyEffect: new Big(0)
}); });
expect(investments).toEqual([]); expect(investments).toEqual([]);

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -1,19 +1,18 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { import {
activityDummyData, activityDummyData,
symbolProfileDummyData symbolProfileDummyData
} from '../portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { import {
PerformanceCalculationType, PerformanceCalculationType,
PortfolioCalculatorFactory PortfolioCalculatorFactory
} from '../portfolio-calculator.factory'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
@ -81,8 +80,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });
const spy = jest const spy = jest

19
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -1,19 +1,18 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { import {
activityDummyData, activityDummyData,
symbolProfileDummyData symbolProfileDummyData
} from '../portfolio-calculator-test-utils'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
import { import {
PerformanceCalculationType, PerformanceCalculationType,
PortfolioCalculatorFactory PortfolioCalculatorFactory
} from '../portfolio-calculator.factory'; } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return { return {
@ -81,8 +80,8 @@ describe('PortfolioCalculator', () => {
]; ];
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

11
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.spec.ts

@ -1,13 +1,12 @@
import {
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { Big } from 'big.js'; import { Big } from 'big.js';
import {
PerformanceCalculationType,
PortfolioCalculatorFactory
} from '../portfolio-calculator.factory';
describe('PortfolioCalculator', () => { describe('PortfolioCalculator', () => {
let currentRateService: CurrentRateService; let currentRateService: CurrentRateService;
let exchangeRateDataService: ExchangeRateDataService; let exchangeRateDataService: ExchangeRateDataService;
@ -32,8 +31,8 @@ describe('PortfolioCalculator', () => {
describe('annualized performance percentage', () => { describe('annualized performance percentage', () => {
it('Get annualized performance', async () => { it('Get annualized performance', async () => {
const portfolioCalculator = factory.createCalculator({ const portfolioCalculator = factory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities: [], activities: [],
calculationType: PerformanceCalculationType.TWR,
currency: 'CHF' currency: 'CHF'
}); });

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts

@ -1,3 +1,4 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface'; import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface';
import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface'; import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface';
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
@ -19,8 +20,6 @@ import {
} from 'date-fns'; } from 'date-fns';
import { cloneDeep, first, last, sortBy } from 'lodash'; import { cloneDeep, first, last, sortBy } from 'lodash';
import { PortfolioCalculator } from '../portfolio-calculator';
export class TWRPortfolioCalculator extends PortfolioCalculator { export class TWRPortfolioCalculator extends PortfolioCalculator {
protected calculateOverallPerformance( protected calculateOverallPerformance(
positions: TimelinePosition[] positions: TimelinePosition[]

2
apps/api/src/app/portfolio/interfaces/current-positions.interface.ts

@ -16,5 +16,5 @@ export interface CurrentPositions extends ResponseError {
netPerformancePercentageWithCurrencyEffect: Big; netPerformancePercentageWithCurrencyEffect: Big;
positions: TimelinePosition[]; positions: TimelinePosition[];
totalInvestment: Big; totalInvestment: Big;
totalInvestmentWithCurrencyEffect?: Big; totalInvestmentWithCurrencyEffect: Big;
} }

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

@ -3,7 +3,6 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface'; import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { UserService } from '@ghostfolio/api/app/user/user.service'; import { UserService } from '@ghostfolio/api/app/user/user.service';
import { import {
getFactor, getFactor,
@ -76,16 +75,8 @@ import {
isBefore, isBefore,
isSameMonth, isSameMonth,
isSameYear, isSameYear,
isValid,
max,
min,
parseISO, parseISO,
set, set
startOfWeek,
startOfMonth,
startOfYear,
subDays,
subYears
} from 'date-fns'; } from 'date-fns';
import { isEmpty, last, uniq, uniqBy } from 'lodash'; import { isEmpty, last, uniq, uniqBy } from 'lodash';
@ -110,7 +101,7 @@ export class PortfolioService {
public constructor( public constructor(
private readonly accountBalanceService: AccountBalanceService, private readonly accountBalanceService: AccountBalanceService,
private readonly accountService: AccountService, private readonly accountService: AccountService,
private readonly currentRateService: CurrentRateService, private readonly calculatorFactory: PortfolioCalculatorFactory,
private readonly dataProviderService: DataProviderService, private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService, private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly impersonationService: ImpersonationService, private readonly impersonationService: ImpersonationService,
@ -118,8 +109,7 @@ export class PortfolioService {
@Inject(REQUEST) private readonly request: RequestWithUser, @Inject(REQUEST) private readonly request: RequestWithUser,
private readonly rulesService: RulesService, private readonly rulesService: RulesService,
private readonly symbolProfileService: SymbolProfileService, private readonly symbolProfileService: SymbolProfileService,
private readonly userService: UserService, private readonly userService: UserService
private readonly calculatorFactory: PortfolioCalculatorFactory
) {} ) {}
public async getAccounts({ public async getAccounts({
@ -279,8 +269,8 @@ export class PortfolioService {
} }
const portfolioCalculator = this.calculatorFactory.createCalculator({ const portfolioCalculator = this.calculatorFactory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: this.request.user.Settings.settings.baseCurrency currency: this.request.user.Settings.settings.baseCurrency
}); });
@ -367,8 +357,8 @@ export class PortfolioService {
}); });
const portfolioCalculator = this.calculatorFactory.createCalculator({ const portfolioCalculator = this.calculatorFactory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: userCurrency currency: userCurrency
}); });
@ -731,14 +721,13 @@ export class PortfolioService {
tags = uniqBy(tags, 'id'); tags = uniqBy(tags, 'id');
const filteredActivities = orders.filter((order) => { const portfolioCalculator = this.calculatorFactory.createCalculator({
activities: orders.filter((order) => {
tags = tags.concat(order.tags); tags = tags.concat(order.tags);
return ['BUY', 'DIVIDEND', 'ITEM', 'SELL'].includes(order.type);
});
const portfolioCalculator = this.calculatorFactory.createCalculator({ return ['BUY', 'DIVIDEND', 'ITEM', 'SELL'].includes(order.type);
}),
calculationType: PerformanceCalculationType.TWR, calculationType: PerformanceCalculationType.TWR,
activities: filteredActivities,
currency: userCurrency currency: userCurrency
}); });
@ -974,8 +963,8 @@ export class PortfolioService {
} }
const portfolioCalculator = this.calculatorFactory.createCalculator({ const portfolioCalculator = this.calculatorFactory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: this.request.user.Settings.settings.baseCurrency currency: this.request.user.Settings.settings.baseCurrency
}); });
@ -1161,8 +1150,8 @@ export class PortfolioService {
} }
const portfolioCalculator = this.calculatorFactory.createCalculator({ const portfolioCalculator = this.calculatorFactory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: userCurrency currency: userCurrency
}); });
@ -1278,8 +1267,8 @@ export class PortfolioService {
}); });
const portfolioCalculator = this.calculatorFactory.createCalculator({ const portfolioCalculator = this.calculatorFactory.createCalculator({
calculationType: PerformanceCalculationType.TWR,
activities, activities,
calculationType: PerformanceCalculationType.TWR,
currency: this.request.user.Settings.settings.baseCurrency currency: this.request.user.Settings.settings.baseCurrency
}); });

Loading…
Cancel
Save