create investment endpoint for analysis timeline
Co-authored-by: Thomas <dotsilver@gmail.com>
This commit is contained in:
parent
4a0695613e
commit
de83dc7b84
@ -311,6 +311,23 @@ export class PortfolioCalculator {
|
||||
};
|
||||
}
|
||||
|
||||
public getInvestments(): { date: string; investment: Big }[] {
|
||||
if (this.transactionPoints.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.transactionPoints.map((transactionPoint) => {
|
||||
return {
|
||||
date: transactionPoint.date,
|
||||
investment: transactionPoint.items.reduce(
|
||||
(investment, transactionPointSymbol) =>
|
||||
investment.add(transactionPointSymbol.investment),
|
||||
new Big(0)
|
||||
)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public async calculateTimeline(
|
||||
timelineSpecification: TimelineSpecification[],
|
||||
endDate: string
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
} from './interfaces/portfolio-position-detail.interface';
|
||||
import { PortfolioPositions } from './interfaces/portfolio-positions.interface';
|
||||
import { PortfolioService } from './portfolio.service';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
|
||||
@Controller('portfolio')
|
||||
export class PortfolioController {
|
||||
@ -49,12 +50,14 @@ export class PortfolioController {
|
||||
@Inject(REQUEST) private readonly request: RequestWithUser
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@Get('/investments')
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
public async findAll(
|
||||
@Headers('impersonation-id') impersonationId
|
||||
): Promise<PortfolioItem[]> {
|
||||
let portfolio = await this.portfolioService.findAll(impersonationId);
|
||||
): Promise<InvestmentItem[]> {
|
||||
let investments = await this.portfolioService.getInvestments(
|
||||
impersonationId
|
||||
);
|
||||
|
||||
if (
|
||||
impersonationId &&
|
||||
@ -63,25 +66,18 @@ export class PortfolioController {
|
||||
permissions.readForeignPortfolio
|
||||
)
|
||||
) {
|
||||
portfolio = portfolio.map((portfolioItem) => {
|
||||
Object.keys(portfolioItem.positions).forEach((symbol) => {
|
||||
portfolioItem.positions[symbol].investment =
|
||||
portfolioItem.positions[symbol].investment > 0 ? 1 : 0;
|
||||
portfolioItem.positions[symbol].investmentInOriginalCurrency =
|
||||
portfolioItem.positions[symbol].investmentInOriginalCurrency > 0
|
||||
? 1
|
||||
: 0;
|
||||
portfolioItem.positions[symbol].quantity =
|
||||
portfolioItem.positions[symbol].quantity > 0 ? 1 : 0;
|
||||
});
|
||||
const maxInvestment = investments.reduce(
|
||||
(investment, item) => Math.max(investment, item.investment),
|
||||
1
|
||||
);
|
||||
|
||||
portfolioItem.investment = null;
|
||||
|
||||
return portfolioItem;
|
||||
});
|
||||
investments = investments.map((item) => ({
|
||||
date: item.date,
|
||||
investment: item.investment / maxInvestment
|
||||
}));
|
||||
}
|
||||
|
||||
return portfolio;
|
||||
return investments;
|
||||
}
|
||||
|
||||
@Get('chart')
|
||||
|
@ -63,6 +63,7 @@ import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.se
|
||||
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||
import { EnhancedSymbolProfile } from '@ghostfolio/api/services/interfaces/symbol-profile.interface';
|
||||
import { TransactionPoint } from '@ghostfolio/api/app/core/interfaces/transaction-point.interface';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
|
||||
@Injectable()
|
||||
export class PortfolioService {
|
||||
@ -136,34 +137,35 @@ export class PortfolioService {
|
||||
return portfolio;
|
||||
}
|
||||
|
||||
public async findAll(aImpersonationId: string): Promise<PortfolioItem[]> {
|
||||
try {
|
||||
const impersonationUserId =
|
||||
await this.impersonationService.validateImpersonationId(
|
||||
aImpersonationId,
|
||||
this.request.user.id
|
||||
);
|
||||
public async getInvestments(
|
||||
aImpersonationId: string
|
||||
): Promise<InvestmentItem[]> {
|
||||
const userId = await this.getUserId(aImpersonationId);
|
||||
|
||||
const portfolio = await this.createPortfolio(
|
||||
impersonationUserId || this.request.user.id
|
||||
);
|
||||
return portfolio.get();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
this.currentRateService,
|
||||
this.request.user.Settings.currency
|
||||
);
|
||||
|
||||
const { transactionPoints } = await this.getTransactionPoints(userId);
|
||||
portfolioCalculator.setTransactionPoints(transactionPoints);
|
||||
if (transactionPoints.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return portfolioCalculator.getInvestments().map((item) => {
|
||||
return {
|
||||
date: item.date,
|
||||
investment: item.investment.toNumber()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public async getChart(
|
||||
aImpersonationId: string,
|
||||
aDateRange: DateRange = 'max'
|
||||
): Promise<HistoricalDataItem[]> {
|
||||
const impersonationUserId =
|
||||
await this.impersonationService.validateImpersonationId(
|
||||
aImpersonationId,
|
||||
this.request.user.id
|
||||
);
|
||||
|
||||
const userId = impersonationUserId || this.request.user.id;
|
||||
const userId = await this.getUserId(aImpersonationId);
|
||||
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
this.currentRateService,
|
||||
|
@ -10,16 +10,16 @@ import {
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { primaryColorRgb } from '@ghostfolio/common/config';
|
||||
import { PortfolioItem } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
Chart,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
TimeScale
|
||||
} from 'chart.js';
|
||||
import { Chart } from 'chart.js';
|
||||
import { addMonths, isAfter, parseISO, subMonths } from 'date-fns';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-investment-chart',
|
||||
@ -28,7 +28,7 @@ import { addMonths, isAfter, parseISO, subMonths } from 'date-fns';
|
||||
styleUrls: ['./investment-chart.component.scss']
|
||||
})
|
||||
export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
@Input() portfolioItems: PortfolioItem[];
|
||||
@Input() investments: InvestmentItem[];
|
||||
|
||||
@ViewChild('chartCanvas') chartCanvas;
|
||||
|
||||
@ -48,7 +48,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
public ngOnInit() {}
|
||||
|
||||
public ngOnChanges() {
|
||||
if (this.portfolioItems) {
|
||||
if (this.investments) {
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
@ -60,32 +60,32 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
private initialize() {
|
||||
this.isLoading = true;
|
||||
|
||||
if (this.portfolioItems?.length > 0) {
|
||||
if (this.investments?.length > 0) {
|
||||
// Extend chart by three months (before)
|
||||
const firstItem = this.portfolioItems[0];
|
||||
this.portfolioItems.unshift({
|
||||
const firstItem = this.investments[0];
|
||||
this.investments.unshift({
|
||||
...firstItem,
|
||||
date: subMonths(parseISO(firstItem.date), 3).toISOString(),
|
||||
investment: 0
|
||||
});
|
||||
|
||||
// Extend chart by three months (after)
|
||||
const lastItem = this.portfolioItems[this.portfolioItems.length - 1];
|
||||
this.portfolioItems.push({
|
||||
const lastItem = this.investments[this.investments.length - 1];
|
||||
this.investments.push({
|
||||
...lastItem,
|
||||
date: addMonths(parseISO(lastItem.date), 3).toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
const data = {
|
||||
labels: this.portfolioItems.map((position) => {
|
||||
labels: this.investments.map((position) => {
|
||||
return position.date;
|
||||
}),
|
||||
datasets: [
|
||||
{
|
||||
borderColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
|
||||
borderWidth: 2,
|
||||
data: this.portfolioItems.map((position) => {
|
||||
data: this.investments.map((position) => {
|
||||
return position.investment;
|
||||
}),
|
||||
segment: {
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-analysis-page',
|
||||
@ -35,7 +36,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
{ label: 'Initial', value: 'original' },
|
||||
{ label: 'Current', value: 'current' }
|
||||
];
|
||||
public portfolioItems: PortfolioItem[];
|
||||
public investments: InvestmentItem[];
|
||||
public portfolioPositions: { [symbol: string]: PortfolioPosition };
|
||||
public positions: { [symbol: string]: any };
|
||||
public positionsArray: PortfolioPosition[];
|
||||
@ -71,10 +72,10 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
});
|
||||
|
||||
this.dataService
|
||||
.fetchPortfolio()
|
||||
.fetchInvestments()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((response) => {
|
||||
this.portfolioItems = response;
|
||||
this.investments = response;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
@ -202,7 +202,7 @@
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<gf-investment-chart
|
||||
[portfolioItems]="portfolioItems"
|
||||
[investments]='investments'
|
||||
></gf-investment-chart>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
@ -36,6 +36,7 @@ import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { SettingsStorageService } from './settings-storage.service';
|
||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -143,8 +144,8 @@ export class DataService {
|
||||
);
|
||||
}
|
||||
|
||||
public fetchPortfolio() {
|
||||
return this.http.get<PortfolioItem[]>('/api/portfolio');
|
||||
public fetchInvestments() {
|
||||
return this.http.get<InvestmentItem[]>('/api/portfolio/investments');
|
||||
}
|
||||
|
||||
public fetchPortfolioOverview() {
|
||||
|
@ -0,0 +1,4 @@
|
||||
export interface InvestmentItem {
|
||||
date: string;
|
||||
investment: number;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user