Browse Source

Refactoring

pull/938/head
Thomas 3 years ago
parent
commit
b0983087aa
  1. 4
      apps/api/src/app/portfolio/current-rate.service.mock.ts
  2. 2
      apps/api/src/app/portfolio/current-rate.service.spec.ts
  3. 4
      apps/api/src/app/portfolio/current-rate.service.ts
  4. 1
      apps/api/src/app/portfolio/interfaces/get-value-object.interface.ts
  5. 2
      apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts
  6. 1
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy-and-sell.spec.ts
  7. 1
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy.spec.ts
  8. 1
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  9. 56
      apps/api/src/app/portfolio/portfolio-calculator.ts
  10. 21
      apps/api/src/app/portfolio/portfolio.service.ts
  11. 4
      apps/api/src/models/rule.ts
  12. 9
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
  13. 9
      apps/client/src/app/services/data.service.ts
  14. 1
      libs/common/src/lib/interfaces/timeline-position.interface.ts

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

@ -48,6 +48,8 @@ export const CurrentRateServiceMock = {
for (const dataGatheringItem of dataGatheringItems) {
result.push({
date,
marketPrice: mockGetValue(dataGatheringItem.symbol, date)
.marketPrice,
marketPriceInBaseCurrency: mockGetValue(
dataGatheringItem.symbol,
date
@ -61,6 +63,8 @@ export const CurrentRateServiceMock = {
for (const dataGatheringItem of dataGatheringItems) {
result.push({
date,
marketPrice: mockGetValue(dataGatheringItem.symbol, date)
.marketPrice,
marketPriceInBaseCurrency: mockGetValue(
dataGatheringItem.symbol,
date

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

@ -100,11 +100,13 @@ describe('CurrentRateService', () => {
).toMatchObject<GetValueObject[]>([
{
date: undefined,
marketPrice: 1841.823902,
marketPriceInBaseCurrency: 1841.823902,
symbol: 'AMZN'
},
{
date: undefined,
marketPrice: 1847.839966,
marketPriceInBaseCurrency: 1847.839966,
symbol: 'AMZN'
}

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

@ -40,6 +40,9 @@ export class CurrentRateService {
for (const dataGatheringItem of dataGatheringItems) {
result.push({
date: today,
marketPrice:
dataResultProvider?.[dataGatheringItem.symbol]?.marketPrice ??
0,
marketPriceInBaseCurrency:
this.exchangeRateDataService.toCurrency(
dataResultProvider?.[dataGatheringItem.symbol]
@ -69,6 +72,7 @@ export class CurrentRateService {
return data.map((marketDataItem) => {
return {
date: marketDataItem.date,
marketPrice: marketDataItem.marketPrice,
marketPriceInBaseCurrency:
this.exchangeRateDataService.toCurrency(
marketDataItem.marketPrice,

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

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

2
apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts

@ -20,7 +20,7 @@ export interface PortfolioPositionDetail {
SymbolProfile: EnhancedSymbolProfile;
tags: Tag[];
transactionCount: number;
value: number;
valueInBaseCurrency: number;
}
export interface HistoricalDataContainer {

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

@ -84,6 +84,7 @@ describe('PortfolioCalculator', () => {
netPerformance: new Big('-15.8'),
netPerformancePercentage: new Big('-0.0552834149755073478'),
marketPrice: 148.9,
marketPriceInBaseCurrency: 148.9,
quantity: new Big('0'),
symbol: 'BALN.SW',
transactionCount: 2

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

@ -73,6 +73,7 @@ describe('PortfolioCalculator', () => {
netPerformance: new Big('23.05'),
netPerformancePercentage: new Big('0.08437042459736456808'),
marketPrice: 148.9,
marketPriceInBaseCurrency: 148.9,
quantity: new Big('2'),
symbol: 'BALN.SW',
transactionCount: 1

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

@ -84,6 +84,7 @@ describe('PortfolioCalculator', () => {
netPerformance: new Big('17.68'),
netPerformancePercentage: new Big('0.11662269129287598945'),
marketPrice: 87.8,
marketPriceInBaseCurrency: 87.8,
quantity: new Big('1'),
symbol: 'NOVN.SW',
transactionCount: 2

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

@ -36,7 +36,7 @@ export class PortfolioCalculator {
private static readonly CALCULATE_PERCENTAGE_PERFORMANCE_WITH_MAX_INVESTMENT =
true;
private static readonly ENABLE_LOGGING = false;
private static readonly ENABLE_LOGGING = true;
private currency: string;
private currentRateService: CurrentRateService;
@ -223,7 +223,9 @@ export class PortfolioCalculator {
});
const marketSymbolMap: {
[date: string]: { [symbol: string]: Big };
[date: string]: {
[symbol: string]: { marketPrice: Big; marketPriceInBaseCurrency: Big };
};
} = {};
for (const marketSymbol of marketSymbols) {
@ -231,11 +233,12 @@ export class PortfolioCalculator {
if (!marketSymbolMap[date]) {
marketSymbolMap[date] = {};
}
if (marketSymbol.marketPriceInBaseCurrency) {
marketSymbolMap[date][marketSymbol.symbol] = new Big(
marketSymbolMap[date][marketSymbol.symbol] = {
marketPrice: new Big(marketSymbol.marketPrice),
marketPriceInBaseCurrency: new Big(
marketSymbol.marketPriceInBaseCurrency
);
}
)
};
}
const todayString = format(today, DATE_FORMAT);
@ -251,7 +254,10 @@ export class PortfolioCalculator {
const errors: ResponseError['errors'] = [];
for (const item of lastTransactionPoint.items) {
const marketValue = marketSymbolMap[todayString]?.[item.symbol];
const marketPrice =
marketSymbolMap[todayString]?.[item.symbol].marketPrice;
const marketValue =
marketSymbolMap[todayString]?.[item.symbol].marketPriceInBaseCurrency;
const {
grossPerformance,
@ -281,7 +287,8 @@ export class PortfolioCalculator {
? grossPerformancePercentage ?? null
: null,
investment: item.investment,
marketPrice: marketValue?.toNumber() ?? null,
marketPrice: marketPrice?.toNumber() ?? null,
marketPriceInBaseCurrency: marketValue?.toNumber() ?? null,
netPerformance: !hasErrors ? netPerformance ?? null : null,
netPerformancePercentage: !hasErrors
? netPerformancePercentage ?? null
@ -432,9 +439,28 @@ export class PortfolioCalculator {
let totalInvestment = new Big(0);
for (const currentPosition of positions) {
if (currentPosition.symbol === 'GOOG') {
console.log(
'grossPerformance',
currentPosition.grossPerformance.toNumber()
);
console.log(
'netPerformance',
currentPosition.netPerformance.toNumber()
);
console.log('marketPrice', currentPosition.marketPrice);
console.log(
'marketPriceInBaseCurrency',
currentPosition.marketPriceInBaseCurrency
);
}
// Market price or market price in base currency?
if (currentPosition.marketPrice) {
currentValue = currentValue.plus(
new Big(currentPosition.marketPrice).mul(currentPosition.quantity)
currentPosition.quantity.mul(
currentPosition.marketPriceInBaseCurrency
)
);
} else {
hasErrors = true;
@ -649,7 +675,9 @@ export class PortfolioCalculator {
symbol
}: {
marketSymbolMap: {
[date: string]: { [symbol: string]: Big };
[date: string]: {
[symbol: string]: { marketPrice: Big; marketPriceInBaseCurrency: Big };
};
};
start: Date;
symbol: string;
@ -673,10 +701,10 @@ export class PortfolioCalculator {
const endDate = new Date(Date.now());
const unitPriceAtStartDate =
marketSymbolMap[format(start, DATE_FORMAT)]?.[symbol];
marketSymbolMap[format(start, DATE_FORMAT)]?.[symbol]?.marketPrice;
const unitPriceAtEndDate =
marketSymbolMap[format(endDate, DATE_FORMAT)]?.[symbol];
marketSymbolMap[format(endDate, DATE_FORMAT)]?.[symbol]?.marketPrice;
if (
!unitPriceAtEndDate ||
@ -962,7 +990,9 @@ export class PortfolioCalculator {
)
.minus(1);
if (PortfolioCalculator.ENABLE_LOGGING) {
if (PortfolioCalculator.ENABLE_LOGGING && symbol === 'GOOG') {
console.log(orders);
console.log(
`
${symbol}

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

@ -389,7 +389,7 @@ export class PortfolioService {
continue;
}
const value = item.quantity.mul(item.marketPrice);
const value = item.quantity.mul(item.marketPriceInBaseCurrency);
const symbolProfile = symbolProfileMap[item.symbol];
const dataProviderResponse = dataProviderResponses[item.symbol];
@ -428,7 +428,7 @@ export class PortfolioService {
grossPerformancePercent:
item.grossPerformancePercentage?.toNumber() ?? 0,
investment: item.investment.toNumber(),
marketPrice: item.marketPrice,
marketPrice: item.marketPriceInBaseCurrency,
marketState: dataProviderResponse.marketState,
name: symbolProfile.name,
netPerformance: item.netPerformance?.toNumber() ?? 0,
@ -508,11 +508,10 @@ export class PortfolioService {
quantity: undefined,
SymbolProfile: undefined,
transactionCount: undefined,
value: undefined
valueInBaseCurrency: undefined
};
}
const positionCurrency = orders[0].SymbolProfile.currency;
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
aSymbol
]);
@ -538,7 +537,7 @@ export class PortfolioService {
tags = uniqBy(tags, 'id');
const portfolioCalculator = new PortfolioCalculator({
currency: positionCurrency,
currency: userCurrency,
currentRateService: this.currentRateService,
orders: portfolioOrders
});
@ -562,6 +561,7 @@ export class PortfolioService {
dataSource,
firstBuyDate,
marketPrice,
marketPriceInBaseCurrency,
quantity,
transactionCount
} = position;
@ -653,11 +653,7 @@ export class PortfolioService {
historicalData: historicalDataArray,
netPerformancePercent: position.netPerformancePercentage?.toNumber(),
quantity: quantity.toNumber(),
value: this.exchangeRateDataService.toCurrency(
quantity.mul(marketPrice).toNumber(),
currency,
userCurrency
)
valueInBaseCurrency: quantity.mul(marketPriceInBaseCurrency).toNumber()
};
} else {
const currentData = await this.dataProviderService.getQuotes([
@ -713,7 +709,7 @@ export class PortfolioService {
netPerformancePercent: undefined,
quantity: 0,
transactionCount: undefined,
value: 0
valueInBaseCurrency: 0
};
}
}
@ -1321,7 +1317,8 @@ export class PortfolioService {
for (const order of ordersByAccount) {
let currentValueOfSymbolInBaseCurrency =
order.quantity *
portfolioItemsNow[order.SymbolProfile.symbol].marketPrice;
portfolioItemsNow[order.SymbolProfile.symbol]
.marketPriceInBaseCurrency;
let originalValueOfSymbolInBaseCurrency =
this.exchangeRateDataService.toCurrency(
order.quantity * order.unitPrice,

4
apps/api/src/models/rule.ts

@ -42,7 +42,9 @@ export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
(previousValue, currentValue) =>
previousValue +
this.exchangeRateDataService.toCurrency(
currentValue.quantity.mul(currentValue.marketPrice).toNumber(),
currentValue.quantity
.mul(currentValue.marketPriceInBaseCurrency)
.toNumber(),
currentValue.currency,
baseCurrency
),

9
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts

@ -7,11 +7,12 @@ import {
OnInit
} from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { EnhancedSymbolProfile } from '@ghostfolio/api/services/interfaces/symbol-profile.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
import { OrderWithAccount } from '@ghostfolio/common/types';
import { LineChartItem } from '@ghostfolio/ui/line-chart/interfaces/line-chart.interface';
import { SymbolProfile, Tag } from '@prisma/client';
import { Tag } from '@prisma/client';
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@ -48,7 +49,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
public sectors: {
[name: string]: { name: string; value: number };
};
public SymbolProfile: SymbolProfile;
public SymbolProfile: EnhancedSymbolProfile;
public tags: Tag[];
public transactionCount: number;
public value: number;
@ -87,7 +88,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
SymbolProfile,
tags,
transactionCount,
value
valueInBaseCurrency
}) => {
this.averagePrice = averagePrice;
this.benchmarkDataItems = [];
@ -121,7 +122,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
this.SymbolProfile = SymbolProfile;
this.tags = tags;
this.transactionCount = transactionCount;
this.value = value;
this.value = valueInBaseCurrency;
if (SymbolProfile?.countries?.length > 0) {
for (const country of SymbolProfile.countries) {

9
apps/client/src/app/services/data.service.ts

@ -6,6 +6,7 @@ import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activities } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { PortfolioPositionDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface';
@ -273,13 +274,15 @@ export class DataService {
symbol: string;
}) {
return this.http
.get<any>(`/api/v1/portfolio/position/${dataSource}/${symbol}`)
.get<PortfolioPositionDetail>(
`/api/v1/portfolio/position/${dataSource}/${symbol}`
)
.pipe(
map((data) => {
if (data.orders) {
for (const order of data.orders) {
order.createdAt = parseISO(order.createdAt);
order.date = parseISO(order.date);
order.createdAt = parseISO(<string>(<unknown>order.createdAt));
order.date = parseISO(<string>(<unknown>order.date));
}
}

1
libs/common/src/lib/interfaces/timeline-position.interface.ts

@ -10,6 +10,7 @@ export interface TimelinePosition {
grossPerformancePercentage: Big;
investment: Big;
marketPrice: number;
marketPriceInBaseCurrency: number;
netPerformance: Big;
netPerformancePercentage: Big;
quantity: Big;

Loading…
Cancel
Save