Some checks failed
- 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
94 lines
2.7 KiB
TypeScript
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`);
|
|
}
|