ghostfolio/apps/api/src/app/portfolio/portfolio.controller.ts

301 lines
7.8 KiB
TypeScript
Raw Normal View History

import { UserService } from '@ghostfolio/api/app/user/user.service';
import {
hasNotDefinedValuesInObject,
nullifyValuesInObject
} from '@ghostfolio/api/helper/object.helper';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import {
PortfolioDetails,
PortfolioPerformance,
PortfolioReport,
PortfolioSummary
} from '@ghostfolio/common/interfaces';
2021-08-01 09:41:44 +02:00
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import type { RequestWithUser } from '@ghostfolio/common/types';
2021-04-13 21:53:58 +02:00
import {
Controller,
Get,
Headers,
HttpException,
Inject,
Param,
Query,
Res,
UseGuards
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express';
2021-07-28 15:15:31 +02:00
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
2021-04-13 21:53:58 +02:00
import {
HistoricalDataItem,
PortfolioPositionDetail
} from './interfaces/portfolio-position-detail.interface';
2021-07-20 22:52:50 +02:00
import { PortfolioPositions } from './interfaces/portfolio-positions.interface';
2021-04-13 21:53:58 +02:00
import { PortfolioService } from './portfolio.service';
@Controller('portfolio')
export class PortfolioController {
public constructor(
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly portfolioService: PortfolioService,
@Inject(REQUEST) private readonly request: RequestWithUser,
private readonly userService: UserService
2021-04-13 21:53:58 +02:00
) {}
@Get('investments')
2021-04-13 21:53:58 +02:00
@UseGuards(AuthGuard('jwt'))
public async findAll(
@Headers('impersonation-id') impersonationId
): Promise<InvestmentItem[]> {
let investments = await this.portfolioService.getInvestments(
impersonationId
);
2021-04-13 21:53:58 +02:00
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
const maxInvestment = investments.reduce(
(investment, item) => Math.max(investment, item.investment),
1
);
2021-04-13 21:53:58 +02:00
investments = investments.map((item) => ({
date: item.date,
investment: item.investment / maxInvestment
}));
2021-04-13 21:53:58 +02:00
}
return investments;
2021-04-13 21:53:58 +02:00
}
@Get('chart')
@UseGuards(AuthGuard('jwt'))
public async getChart(
@Headers('impersonation-id') impersonationId,
@Query('range') range,
@Res() res: Response
): Promise<HistoricalDataItem[]> {
let chartData = await this.portfolioService.getChart(
impersonationId,
range
);
let hasNullValue = false;
chartData.forEach((chartDataItem) => {
if (hasNotDefinedValuesInObject(chartDataItem)) {
hasNullValue = true;
}
});
if (hasNullValue) {
res.status(StatusCodes.ACCEPTED);
}
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
2021-04-13 21:53:58 +02:00
let maxValue = 0;
chartData.forEach((portfolioItem) => {
if (portfolioItem.value > maxValue) {
maxValue = portfolioItem.value;
}
});
chartData = chartData.map((historicalDataItem) => {
return {
...historicalDataItem,
marketPrice: Number((historicalDataItem.value / maxValue).toFixed(2))
};
});
}
return <any>res.json(chartData);
}
@Get('details')
@UseGuards(AuthGuard('jwt'))
public async getDetails(
@Headers('impersonation-id') impersonationId,
@Query('range') range,
@Res() res: Response
): Promise<PortfolioDetails> {
const { accounts, holdings, hasErrors } =
await this.portfolioService.getDetails(impersonationId, range);
2021-04-13 21:53:58 +02:00
if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
2021-04-13 21:53:58 +02:00
res.status(StatusCodes.ACCEPTED);
}
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
const totalInvestment = Object.values(holdings)
2021-04-13 21:53:58 +02:00
.map((portfolioPosition) => {
return portfolioPosition.investment;
})
.reduce((a, b) => a + b, 0);
const totalValue = Object.values(holdings)
2021-04-13 21:53:58 +02:00
.map((portfolioPosition) => {
return this.exchangeRateDataService.toCurrency(
portfolioPosition.quantity * portfolioPosition.marketPrice,
portfolioPosition.currency,
this.request.user.Settings.currency
);
})
.reduce((a, b) => a + b, 0);
for (const [symbol, portfolioPosition] of Object.entries(holdings)) {
2021-04-13 21:53:58 +02:00
portfolioPosition.grossPerformance = null;
portfolioPosition.investment =
portfolioPosition.investment / totalInvestment;
portfolioPosition.quantity = null;
}
for (const [name, { current, original }] of Object.entries(accounts)) {
accounts[name].current = current / totalValue;
accounts[name].original = original / totalInvestment;
}
2021-04-13 21:53:58 +02:00
}
return <any>res.json({ accounts, holdings });
2021-04-13 21:53:58 +02:00
}
@Get('performance')
@UseGuards(AuthGuard('jwt'))
public async getPerformance(
@Headers('impersonation-id') impersonationId,
@Query('range') range,
@Res() res: Response
): Promise<PortfolioPerformance> {
const performanceInformation = await this.portfolioService.getPerformance(
impersonationId,
range
2021-04-13 21:53:58 +02:00
);
2021-07-27 22:54:00 +02:00
if (performanceInformation?.hasErrors) {
res.status(StatusCodes.ACCEPTED);
}
let performance = performanceInformation.performance;
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
2021-04-13 21:53:58 +02:00
performance = nullifyValuesInObject(performance, [
'currentGrossPerformance',
'currentValue'
]);
}
return <any>res.json(performance);
}
2021-07-20 22:52:50 +02:00
@Get('positions')
@UseGuards(AuthGuard('jwt'))
public async getPositions(
2021-07-24 10:53:15 +02:00
@Headers('impersonation-id') impersonationId,
2021-07-27 12:01:35 +02:00
@Query('range') range,
@Res() res: Response
2021-07-20 22:52:50 +02:00
): Promise<PortfolioPositions> {
2021-07-27 12:01:35 +02:00
const result = await this.portfolioService.getPositions(
impersonationId,
range
);
if (result?.hasErrors) {
res.status(StatusCodes.ACCEPTED);
}
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
result.positions = result.positions.map((position) => {
return nullifyValuesInObject(position, [
'grossPerformance',
'investment',
'quantity'
]);
});
}
2021-07-27 12:01:35 +02:00
return <any>res.json(result);
2021-07-20 22:52:50 +02:00
}
@Get('summary')
@UseGuards(AuthGuard('jwt'))
public async getSummary(
@Headers('impersonation-id') impersonationId
): Promise<PortfolioSummary> {
let summary = await this.portfolioService.getSummary(impersonationId);
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
summary = nullifyValuesInObject(summary, [
'cash',
'committedFunds',
'currentGrossPerformance',
'currentValue',
'fees',
'netWorth',
'totalBuy',
'totalSell'
]);
}
return summary;
}
2021-04-13 21:53:58 +02:00
@Get('position/:symbol')
@UseGuards(AuthGuard('jwt'))
public async getPosition(
@Headers('impersonation-id') impersonationId,
@Param('symbol') symbol
): Promise<PortfolioPositionDetail> {
let position = await this.portfolioService.getPosition(
impersonationId,
symbol
);
if (position) {
if (
impersonationId ||
this.userService.isRestrictedView(this.request.user)
) {
position = nullifyValuesInObject(position, [
'grossPerformance',
'investment',
'netPerformance',
'quantity'
]);
2021-04-13 21:53:58 +02:00
}
return position;
}
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
@Get('report')
@UseGuards(AuthGuard('jwt'))
public async getReport(
@Headers('impersonation-id') impersonationId
): Promise<PortfolioReport> {
return await this.portfolioService.getReport(impersonationId);
2021-04-13 21:53:58 +02:00
}
}