moex-vibe/apps/frontend/src/api/portfolio.ts
Sergey Krylov 96f003852d
Some checks failed
CI / lint (pull_request) Failing after 1m48s
CI / test (pull_request) Successful in 1m47s
CI / build (pull_request) Successful in 1m53s
CI / lint (push) Failing after 1m59s
CI / test (push) Successful in 1m56s
CI / build (push) Successful in 1m50s
feat: implement portfolio analytics, PnL calculation, and security screener
- Add buyPrice and buyDate to positions for PnL tracking
- Implement backend analytics service for real-time portfolio performance
- Add server-side security screener with filtering, sorting, and pagination
- Update frontend UI with analytics summaries and sortable screener table
- Optimize MOEX API calls with batch fetching and portfolio-specific caching
- Add unit tests for analytics and screener services
2026-06-14 15:59:25 +03:00

94 lines
2.7 KiB
TypeScript

import { request } from './client';
import type { AnalyticsResponse, Portfolio, PortfolioDetail, Position } from './responses';
export function getPortfolios(): Promise<{
data: Portfolio[];
meta: { cachedAt: string | null; fromCache: boolean };
}> {
return request<Portfolio[]>('/api/v1/portfolios');
}
export function getPortfolio(
id: number,
): Promise<{ data: PortfolioDetail; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<PortfolioDetail>(`/api/v1/portfolios/${id}`);
}
export function createPortfolio(data: {
name: string;
description?: string;
currency?: string;
}): Promise<{ data: Portfolio; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<Portfolio>('/api/v1/portfolios', undefined, {
method: 'POST',
body: data,
});
}
export function updatePortfolio(
id: number,
data: { name?: string; description?: string; currency?: string },
): Promise<{ data: Portfolio; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<Portfolio>(`/api/v1/portfolios/${id}`, undefined, {
method: 'PATCH',
body: data,
});
}
export function deletePortfolio(
id: number,
): Promise<{ data: null; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<null>(`/api/v1/portfolios/${id}`, undefined, {
method: 'DELETE',
});
}
export function addPosition(
portfolioId: number,
data: {
secid: string;
quantity: number;
buyPrice?: number;
buyDate?: string;
notes?: string;
tags?: string[];
},
): Promise<{ data: Position; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<Position>(`/api/v1/portfolios/${portfolioId}/positions`, undefined, {
method: 'POST',
body: data,
});
}
export function updatePosition(
portfolioId: number,
positionId: number,
data: {
quantity?: number;
buyPrice?: number;
buyDate?: string;
notes?: string;
tags?: string[];
},
): Promise<{ data: Position; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<Position>(`/api/v1/portfolios/${portfolioId}/positions/${positionId}`, undefined, {
method: 'PATCH',
body: data,
});
}
export function removePosition(
portfolioId: number,
positionId: number,
): Promise<{ data: null; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<null>(`/api/v1/portfolios/${portfolioId}/positions/${positionId}`, undefined, {
method: 'DELETE',
});
}
export function getPortfolioAnalytics(
portfolioId: number,
): Promise<{ data: AnalyticsResponse; meta: { cachedAt: string | null; fromCache: boolean } }> {
return request<AnalyticsResponse>(`/api/v1/portfolios/${portfolioId}/analytics`);
}