Browse Source

Refactoring

pull/2948/head
Thomas Kaul 2 years ago
parent
commit
7d76f5f7b8
  1. 13
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy-and-sell.spec.ts
  2. 13
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy.spec.ts
  3. 83
      apps/api/src/app/portfolio/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  4. 36
      apps/api/src/app/portfolio/portfolio-calculator-googl-buy.spec.ts
  5. 10
      apps/api/src/app/portfolio/portfolio-calculator-no-orders.spec.ts
  6. 14
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  7. 10
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell.spec.ts
  8. 113
      apps/api/src/app/portfolio/portfolio-calculator.ts
  9. 28
      apps/api/src/app/portfolio/portfolio.service.ts
  10. 2
      libs/common/src/lib/interfaces/index.ts

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

@ -68,14 +68,20 @@ describe('PortfolioCalculator', () => {
.spyOn(Date, 'now') .spyOn(Date, 'now')
.mockImplementation(() => parseDate('2021-12-18').getTime()); .mockImplementation(() => parseDate('2021-12-18').getTime());
const chartData = await portfolioCalculator.getChartData({
start: parseDate('2021-11-22')
});
const currentPositions = await portfolioCalculator.getCurrentPositions( const currentPositions = await portfolioCalculator.getCurrentPositions(
parseDate('2021-11-22') parseDate('2021-11-22')
); );
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();
@ -135,7 +141,8 @@ describe('PortfolioCalculator', () => {
]); ]);
expect(investmentsByMonth).toEqual([ expect(investmentsByMonth).toEqual([
{ date: '2021-11-01', investment: new Big('12.6') } { date: '2021-11-01', investment: 0 },
{ date: '2021-12-01', investment: 0 }
]); ]);
}); });
}); });

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

@ -57,14 +57,20 @@ describe('PortfolioCalculator', () => {
.spyOn(Date, 'now') .spyOn(Date, 'now')
.mockImplementation(() => parseDate('2021-12-18').getTime()); .mockImplementation(() => parseDate('2021-12-18').getTime());
const chartData = await portfolioCalculator.getChartData({
start: parseDate('2021-11-30')
});
const currentPositions = await portfolioCalculator.getCurrentPositions( const currentPositions = await portfolioCalculator.getCurrentPositions(
parseDate('2021-11-30') parseDate('2021-11-30')
); );
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();
@ -123,7 +129,8 @@ describe('PortfolioCalculator', () => {
]); ]);
expect(investmentsByMonth).toEqual([ expect(investmentsByMonth).toEqual([
{ date: '2021-11-01', investment: new Big('273.2') } { date: '2021-11-01', investment: 273.2 },
{ date: '2021-12-01', investment: 0 }
]); ]);
}); });
}); });

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

@ -81,14 +81,20 @@ describe('PortfolioCalculator', () => {
.spyOn(Date, 'now') .spyOn(Date, 'now')
.mockImplementation(() => parseDate('2018-01-01').getTime()); .mockImplementation(() => parseDate('2018-01-01').getTime());
const chartData = await portfolioCalculator.getChartData({
start: parseDate('2015-01-01')
});
const currentPositions = await portfolioCalculator.getCurrentPositions( const currentPositions = await portfolioCalculator.getCurrentPositions(
parseDate('2015-01-01') parseDate('2015-01-01')
); );
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();
@ -155,42 +161,43 @@ describe('PortfolioCalculator', () => {
]); ]);
expect(investmentsByMonth).toEqual([ expect(investmentsByMonth).toEqual([
{ date: '2015-01-01', investment: new Big('640.86') }, { date: '2015-01-01', investment: 637.0853345999999 },
{ date: '2015-02-01', investment: new Big('0') }, { date: '2015-02-01', investment: 0 },
{ date: '2015-03-01', investment: new Big('0') }, { date: '2015-03-01', investment: 0 },
{ date: '2015-04-01', investment: new Big('0') }, { date: '2015-04-01', investment: 0 },
{ date: '2015-05-01', investment: new Big('0') }, { date: '2015-05-01', investment: 0 },
{ date: '2015-06-01', investment: new Big('0') }, { date: '2015-06-01', investment: 0 },
{ date: '2015-07-01', investment: new Big('0') }, { date: '2015-07-01', investment: 0 },
{ date: '2015-08-01', investment: new Big('0') }, { date: '2015-08-01', investment: 0 },
{ date: '2015-09-01', investment: new Big('0') }, { date: '2015-09-01', investment: 0 },
{ date: '2015-10-01', investment: new Big('0') }, { date: '2015-10-01', investment: 0 },
{ date: '2015-11-01', investment: new Big('0') }, { date: '2015-11-01', investment: 0 },
{ date: '2015-12-01', investment: new Big('0') }, { date: '2015-12-01', investment: 0 },
{ date: '2016-01-01', investment: new Big('0') }, { date: '2016-01-01', investment: 0 },
{ date: '2016-02-01', investment: new Big('0') }, { date: '2016-02-01', investment: 0 },
{ date: '2016-03-01', investment: new Big('0') }, { date: '2016-03-01', investment: 0 },
{ date: '2016-04-01', investment: new Big('0') }, { date: '2016-04-01', investment: 0 },
{ date: '2016-05-01', investment: new Big('0') }, { date: '2016-05-01', investment: 0 },
{ date: '2016-06-01', investment: new Big('0') }, { date: '2016-06-01', investment: 0 },
{ date: '2016-07-01', investment: new Big('0') }, { date: '2016-07-01', investment: 0 },
{ date: '2016-08-01', investment: new Big('0') }, { date: '2016-08-01', investment: 0 },
{ date: '2016-09-01', investment: new Big('0') }, { date: '2016-09-01', investment: 0 },
{ date: '2016-10-01', investment: new Big('0') }, { date: '2016-10-01', investment: 0 },
{ date: '2016-11-01', investment: new Big('0') }, { date: '2016-11-01', investment: 0 },
{ date: '2016-12-01', investment: new Big('0') }, { date: '2016-12-01', investment: 0 },
{ date: '2017-01-01', investment: new Big('0') }, { date: '2017-01-01', investment: 0 },
{ date: '2017-02-01', investment: new Big('0') }, { date: '2017-02-01', investment: 0 },
{ date: '2017-03-01', investment: new Big('0') }, { date: '2017-03-01', investment: 0 },
{ date: '2017-04-01', investment: new Big('0') }, { date: '2017-04-01', investment: 0 },
{ date: '2017-05-01', investment: new Big('0') }, { date: '2017-05-01', investment: 0 },
{ date: '2017-06-01', investment: new Big('0') }, { date: '2017-06-01', investment: 0 },
{ date: '2017-07-01', investment: new Big('0') }, { date: '2017-07-01', investment: 0 },
{ date: '2017-08-01', investment: new Big('0') }, { date: '2017-08-01', investment: 0 },
{ date: '2017-09-01', investment: new Big('0') }, { date: '2017-09-01', investment: 0 },
{ date: '2017-10-01', investment: new Big('0') }, { date: '2017-10-01', investment: 0 },
{ date: '2017-11-01', investment: new Big('0') }, { date: '2017-11-01', investment: 0 },
{ date: '2017-12-01', investment: new Big('-14156.4') } { date: '2017-12-01', investment: -318.54266729999995 },
{ date: '2018-01-01', investment: 0 }
]); ]);
}); });
}); });

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

@ -70,14 +70,20 @@ describe('PortfolioCalculator', () => {
.spyOn(Date, 'now') .spyOn(Date, 'now')
.mockImplementation(() => parseDate('2023-07-10').getTime()); .mockImplementation(() => parseDate('2023-07-10').getTime());
const chartData = await portfolioCalculator.getChartData({
start: parseDate('2023-01-03')
});
const currentPositions = await portfolioCalculator.getCurrentPositions( const currentPositions = await portfolioCalculator.getCurrentPositions(
parseDate('2023-01-03') parseDate('2023-01-03')
); );
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();
@ -137,7 +143,31 @@ describe('PortfolioCalculator', () => {
]); ]);
expect(investmentsByMonth).toEqual([ expect(investmentsByMonth).toEqual([
{ date: '2023-01-01', investment: new Big('89.12') } { date: '2023-01-01', investment: 82.329056 },
{
date: '2023-02-01',
investment: 0
},
{
date: '2023-03-01',
investment: 0
},
{
date: '2023-04-01',
investment: 0
},
{
date: '2023-05-01',
investment: 0
},
{
date: '2023-06-01',
investment: 0
},
{
date: '2023-07-01',
investment: 0
}
]); ]);
}); });
}); });

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

@ -45,14 +45,20 @@ describe('PortfolioCalculator', () => {
.spyOn(Date, 'now') .spyOn(Date, 'now')
.mockImplementation(() => parseDate('2021-12-18').getTime()); .mockImplementation(() => parseDate('2021-12-18').getTime());
const chartData = await portfolioCalculator.getChartData({
start: new Date()
});
const currentPositions = await portfolioCalculator.getCurrentPositions( const currentPositions = await portfolioCalculator.getCurrentPositions(
new Date() new Date()
); );
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();

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

@ -68,14 +68,20 @@ describe('PortfolioCalculator', () => {
.spyOn(Date, 'now') .spyOn(Date, 'now')
.mockImplementation(() => parseDate('2022-04-11').getTime()); .mockImplementation(() => parseDate('2022-04-11').getTime());
const chartData = await portfolioCalculator.getChartData({
start: parseDate('2022-03-07')
});
const currentPositions = await portfolioCalculator.getCurrentPositions( const currentPositions = await portfolioCalculator.getCurrentPositions(
parseDate('2022-03-07') parseDate('2022-03-07')
); );
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();
@ -137,8 +143,8 @@ describe('PortfolioCalculator', () => {
]); ]);
expect(investmentsByMonth).toEqual([ expect(investmentsByMonth).toEqual([
{ date: '2022-03-01', investment: new Big('151.6') }, { date: '2022-03-01', investment: 151.6 },
{ date: '2022-04-01', investment: new Big('-85.73') } { date: '2022-04-01', investment: -75.8 }
]); ]);
}); });
}); });

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

@ -78,8 +78,10 @@ describe('PortfolioCalculator', () => {
const investments = portfolioCalculator.getInvestments(); const investments = portfolioCalculator.getInvestments();
const investmentsByMonth = const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({
portfolioCalculator.getInvestmentsByGroup('month'); data: chartData,
groupBy: 'month'
});
spy.mockRestore(); spy.mockRestore();
@ -165,8 +167,8 @@ describe('PortfolioCalculator', () => {
]); ]);
expect(investmentsByMonth).toEqual([ expect(investmentsByMonth).toEqual([
{ date: '2022-03-01', investment: new Big('151.6') }, { date: '2022-03-01', investment: 151.6 },
{ date: '2022-04-01', investment: new Big('-171.46') } { date: '2022-04-01', investment: -151.6 }
]); ]);
}); });
}); });

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

@ -4,6 +4,7 @@ import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper';
import { import {
DataProviderInfo, DataProviderInfo,
HistoricalDataItem, HistoricalDataItem,
InvestmentItem,
ResponseError, ResponseError,
SymbolMetrics, SymbolMetrics,
TimelinePosition TimelinePosition
@ -20,9 +21,6 @@ import {
format, format,
isBefore, isBefore,
isSameDay, isSameDay,
isSameMonth,
isSameYear,
set,
subDays subDays
} from 'date-fns'; } from 'date-fns';
import { cloneDeep, first, isNumber, last, sortBy, uniq } from 'lodash'; import { cloneDeep, first, isNumber, last, sortBy, uniq } from 'lodash';
@ -206,6 +204,7 @@ export class PortfolioCalculator {
dates.push(resetHours(end)); dates.push(resetHours(end));
} }
if (transactionPointsBeforeEndDate.length > 0) {
for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) { for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) {
dataGatheringItems.push({ dataGatheringItems.push({
dataSource: item.dataSource, dataSource: item.dataSource,
@ -214,6 +213,7 @@ export class PortfolioCalculator {
currencies[item.symbol] = item.currency; currencies[item.symbol] = item.currency;
symbols[item.symbol] = true; symbols[item.symbol] = true;
} }
}
const { dataProviderInfos, values: marketSymbols } = const { dataProviderInfos, values: marketSymbols } =
await this.currentRateService.getValues({ await this.currentRateService.getValues({
@ -690,98 +690,27 @@ export class PortfolioCalculator {
}); });
} }
/** public getInvestmentsByGroup({
* @deprecated data,
*/ groupBy
public getInvestmentsByGroup( }: {
groupBy: GroupBy data: HistoricalDataItem[];
): { date: string; investment: Big }[] { groupBy: GroupBy;
if (this.orders.length === 0) { }): InvestmentItem[] {
return []; const groupedData: { [dateGroup: string]: Big } = {};
}
for (const { date, investmentValueWithCurrencyEffect } of data) {
const investments: { date: string; investment: Big }[] = []; const dateGroup =
let currentDate: Date; groupBy === 'month' ? date.substring(0, 7) : date.substring(0, 4);
let investmentByGroup = new Big(0); groupedData[dateGroup] = (groupedData[dateGroup] ?? new Big(0)).plus(
investmentValueWithCurrencyEffect
for (const [index, order] of this.orders.entries()) {
if (
isSameYear(parseDate(order.date), currentDate) &&
(groupBy === 'year' || isSameMonth(parseDate(order.date), currentDate))
) {
// Same group: Add up investments
investmentByGroup = investmentByGroup.plus(
order.quantity.mul(order.unitPrice).mul(this.getFactor(order.type))
);
} else {
// New group: Store previous group and reset
if (currentDate) {
investments.push({
date: format(
set(currentDate, {
date: 1,
month: groupBy === 'year' ? 0 : currentDate.getMonth()
}),
DATE_FORMAT
),
investment: investmentByGroup
});
}
currentDate = parseDate(order.date);
investmentByGroup = order.quantity
.mul(order.unitPrice)
.mul(this.getFactor(order.type));
}
if (index === this.orders.length - 1) {
// Store current group (latest order)
investments.push({
date: format(
set(currentDate, {
date: 1,
month: groupBy === 'year' ? 0 : currentDate.getMonth()
}),
DATE_FORMAT
),
investment: investmentByGroup
});
}
}
// Fill in the missing dates with investment = 0
const startDate = parseDate(first(this.orders).date);
const endDate = parseDate(last(this.orders).date);
const allDates: string[] = [];
currentDate = startDate;
while (currentDate <= endDate) {
allDates.push(
format(
set(currentDate, {
date: 1,
month: groupBy === 'year' ? 0 : currentDate.getMonth()
}),
DATE_FORMAT
)
); );
currentDate.setMonth(currentDate.getMonth() + 1);
} }
for (const date of allDates) { return Object.keys(groupedData).map((dateGroup) => ({
const existingInvestment = investments.find((investment) => { date: groupBy === 'month' ? `${dateGroup}-01` : `${dateGroup}-01-01`,
return investment.date === date; investment: groupedData[dateGroup].toNumber()
}); }));
if (!existingInvestment) {
investments.push({ date, investment: new Big(0) });
}
}
return sortBy(investments, ({ date }) => {
return date;
});
} }
private calculateOverallPerformance(positions: TimelinePosition[]) { private calculateOverallPerformance(positions: TimelinePosition[]) {

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

@ -306,7 +306,10 @@ export class PortfolioService {
let investments: InvestmentItem[]; let investments: InvestmentItem[];
if (groupBy) { if (groupBy) {
investments = this.getInvestmentsByGroup({ groupBy, data: items }); investments = portfolioCalculator.getInvestmentsByGroup({
groupBy,
data: items
});
} else { } else {
investments = items.map(({ date, investmentValueWithCurrencyEffect }) => { investments = items.map(({ date, investmentValueWithCurrencyEffect }) => {
return { return {
@ -1599,29 +1602,6 @@ export class PortfolioService {
}; };
} }
private getInvestmentsByGroup({
data,
groupBy
}: {
data: HistoricalDataItem[];
groupBy: GroupBy;
}): InvestmentItem[] {
const groupedData: { [dateGroup: string]: Big } = {};
for (const { date, investmentValueWithCurrencyEffect } of data) {
const dateGroup =
groupBy === 'month' ? date.substring(0, 7) : date.substring(0, 4);
groupedData[dateGroup] = (groupedData[dateGroup] ?? new Big(0)).plus(
investmentValueWithCurrencyEffect
);
}
return Object.keys(groupedData).map((dateGroup) => ({
date: groupBy === 'month' ? `${dateGroup}-01` : `${dateGroup}-01-01`,
investment: groupedData[dateGroup].toNumber()
}));
}
private getStartDate(aDateRange: DateRange, portfolioStart: Date) { private getStartDate(aDateRange: DateRange, portfolioStart: Date) {
switch (aDateRange) { switch (aDateRange) {
case '1d': case '1d':

2
libs/common/src/lib/interfaces/index.ts

@ -19,6 +19,7 @@ import type { FilterGroup } from './filter-group.interface';
import type { Filter } from './filter.interface'; import type { Filter } from './filter.interface';
import type { HistoricalDataItem } from './historical-data-item.interface'; import type { HistoricalDataItem } from './historical-data-item.interface';
import type { InfoItem } from './info-item.interface'; import type { InfoItem } from './info-item.interface';
import type { InvestmentItem } from './investment-item.interface';
import type { LineChartItem } from './line-chart-item.interface'; import type { LineChartItem } from './line-chart-item.interface';
import type { PortfolioChart } from './portfolio-chart.interface'; import type { PortfolioChart } from './portfolio-chart.interface';
import type { PortfolioDetails } from './portfolio-details.interface'; import type { PortfolioDetails } from './portfolio-details.interface';
@ -74,6 +75,7 @@ export {
HistoricalDataItem, HistoricalDataItem,
ImportResponse, ImportResponse,
InfoItem, InfoItem,
InvestmentItem,
LineChartItem, LineChartItem,
OAuthResponse, OAuthResponse,
PortfolioChart, PortfolioChart,

Loading…
Cancel
Save