/** * Portfolio Service * Client for connecting to the Portfolio API */ const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3080'; // ============================================================================ // Types // ============================================================================ export type RiskProfile = 'conservative' | 'moderate' | 'aggressive'; export interface Portfolio { id: string; userId: string; name: string; riskProfile: RiskProfile; allocations: PortfolioAllocation[]; totalValue: number; totalCost: number; unrealizedPnl: number; unrealizedPnlPercent: number; realizedPnl: number; lastRebalanced: string | null; createdAt: string; updatedAt: string; } export interface PortfolioAllocation { id: string; portfolioId: string; asset: string; targetPercent: number; currentPercent: number; quantity: number; value: number; cost: number; pnl: number; pnlPercent: number; deviation: number; } export interface PortfolioGoal { id: string; userId: string; name: string; targetAmount: number; currentAmount: number; targetDate: string; monthlyContribution: number; progress: number; projectedCompletion: string | null; status: 'on_track' | 'at_risk' | 'behind'; createdAt: string; updatedAt: string; } export interface RebalanceRecommendation { asset: string; currentPercent: number; targetPercent: number; action: 'buy' | 'sell' | 'hold'; amount: number; amountUSD: number; priority: 'high' | 'medium' | 'low'; } export interface PortfolioStats { totalValue: number; dayChange: number; dayChangePercent: number; weekChange: number; weekChangePercent: number; monthChange: number; monthChangePercent: number; allTimeChange: number; allTimeChangePercent: number; bestPerformer: { asset: string; change: number }; worstPerformer: { asset: string; change: number }; } export interface CreatePortfolioInput { name: string; riskProfile: RiskProfile; initialValue?: number; } export interface CreateGoalInput { name: string; targetAmount: number; targetDate: string; monthlyContribution: number; } // ============================================================================ // Portfolio API Functions // ============================================================================ /** * Get user's portfolios */ export async function getUserPortfolios(): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio`, { credentials: 'include', }); if (!response.ok) throw new Error('Failed to fetch portfolios'); const data = await response.json(); return data.data || data; } /** * Get portfolio by ID */ export async function getPortfolio(portfolioId: string): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/${portfolioId}`, { credentials: 'include', }); if (!response.ok) throw new Error('Failed to fetch portfolio'); const data = await response.json(); return data.data || data; } /** * Create a new portfolio */ export async function createPortfolio(input: CreatePortfolioInput): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }); if (!response.ok) throw new Error('Failed to create portfolio'); const data = await response.json(); return data.data || data; } /** * Update portfolio allocations */ export async function updateAllocations( portfolioId: string, allocations: { asset: string; targetPercent: number }[] ): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/${portfolioId}/allocations`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ allocations }), }); if (!response.ok) throw new Error('Failed to update allocations'); const data = await response.json(); return data.data || data; } /** * Get rebalancing recommendations */ export async function getRebalanceRecommendations( portfolioId: string ): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/${portfolioId}/rebalance`, { credentials: 'include', }); if (!response.ok) throw new Error('Failed to fetch recommendations'); const data = await response.json(); return data.data || data; } /** * Execute rebalancing */ export async function executeRebalance(portfolioId: string): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/${portfolioId}/rebalance`, { method: 'POST', credentials: 'include', }); if (!response.ok) throw new Error('Failed to execute rebalance'); const data = await response.json(); return data.data || data; } /** * Get portfolio statistics */ export async function getPortfolioStats(portfolioId: string): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/${portfolioId}/stats`, { credentials: 'include', }); if (!response.ok) throw new Error('Failed to fetch stats'); const data = await response.json(); return data.data || data; } // ============================================================================ // Goals API Functions // ============================================================================ /** * Get user's goals */ export async function getUserGoals(): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/goals`, { credentials: 'include', }); if (!response.ok) throw new Error('Failed to fetch goals'); const data = await response.json(); return data.data || data; } /** * Create a new goal */ export async function createGoal(input: CreateGoalInput): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/goals`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }); if (!response.ok) throw new Error('Failed to create goal'); const data = await response.json(); return data.data || data; } /** * Update goal progress */ export async function updateGoalProgress( goalId: string, currentAmount: number ): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/goals/${goalId}/progress`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ currentAmount }), }); if (!response.ok) throw new Error('Failed to update goal'); const data = await response.json(); return data.data || data; } /** * Delete a goal */ export async function deleteGoal(goalId: string): Promise { const response = await fetch(`${API_URL}/api/v1/portfolio/goals/${goalId}`, { method: 'DELETE', credentials: 'include', }); if (!response.ok) throw new Error('Failed to delete goal'); }