--- name: architecture-reference description: "Quick reference for Portfolio Buddy 2 project structure. Use when: adding new features, modifying existing components, understanding data flow, or onboarding to the codebase. Contains component hierarchy, hook patterns, and utility functions." --- # Portfolio Buddy 2 - Architecture Reference ## Component Hierarchy ``` App.tsx (351 lines) ├── Header │ └── App title and branding ├── UploadSection │ ├── File upload to Supabase │ ├── CSV parsing and validation │ └── Error handling ├── ErrorList │ └── Display parsing/validation errors ├── UploadedFilesList │ └── List of successfully uploaded files ├── AnalyticsControls │ ├── Toggle Metrics view │ ├── Toggle Portfolio view │ └── Toggle Correlation view ├── PortfolioSection (591 lines - NEEDS REFACTOR!) │ ├── usePortfolio hook (date filtering) │ ├── useContractMultipliers hook │ ├── Chart.js equity curves │ ├── Portfolio statistics │ ├── ContractInput components │ ├── MasterContractControl │ └── MetricsTable integration ├── CorrelationSection │ ├── CorrelationHeatmap (Chart.js) │ ├── Spearman correlation │ └── Pearson correlation ├── MetricsTable (242 lines) │ ├── useMetrics hook │ ├── useSorting hook (advanced multi-column) │ ├── SortableHeader components │ └── Selection state management └── SessionComplete └── Completion UI/messaging ``` ## Key Hooks ### useMetrics **Location**: [src/hooks/useMetrics.ts](src/hooks/useMetrics.ts) **Purpose**: Calculate trading metrics from uploaded portfolio data **Features**: - Calculates Sharpe Ratio, Sortino Ratio, Max Drawdown, CAGR, Win Rate, etc. - Memoized calculations for performance - Handles empty/invalid data gracefully **Usage**: ```typescript const { metrics, isCalculating } = useMetrics(portfolioData, riskFreeRate) ``` **Returns**: - `metrics`: Array of calculated metrics per strategy - `isCalculating`: Boolean loading state **Note**: Contains 4 TypeScript `any` violations in sort comparisons (tech debt) ### usePortfolio **Location**: [src/hooks/usePortfolio.ts](src/hooks/usePortfolio.ts) **Purpose**: Manage portfolio data with date range filtering **Features**: - Parses CSV trade data - Filters by date range (start/end date) - Builds equity curves - Aggregates daily returns **Usage**: ```typescript const { portfolioData, filteredData, dateRange, setDateRange } = usePortfolio(uploadedFiles) ``` **Recent Addition**: Date range filtering (commit 258ba3a) **Note**: Contains 11 TypeScript `any` violations in trade/metrics types (tech debt) ### useContractMultipliers **Location**: [src/hooks/useContractMultipliers.ts](src/hooks/useContractMultipliers.ts) **Purpose**: Manage contract multipliers for futures trading **Features**: - Per-strategy contract size tracking - Apply multipliers to metrics - Master control to set all contracts at once **Usage**: ```typescript const { multipliers, setMultiplier, setAllMultipliers, getAdjustedMetrics } = useContractMultipliers(strategies) ``` ### useSorting **Location**: [src/hooks/useSorting.ts](src/hooks/useSorting.ts) **Purpose**: Advanced multi-column sorting for MetricsTable **Features**: - Sort by multiple columns with priority - Toggle ascending/descending - Custom comparison logic per data type **Usage**: ```typescript const { sortedData, sortColumn, sortDirection, handleSort } = useSorting(data, defaultColumn) ``` ## Utility Functions ### dataUtils.ts **Location**: [src/utils/dataUtils.ts](src/utils/dataUtils.ts) **Contains Core Functions**: **CSV & Data Processing**: - `parseCSV(file)` - Parse CSV file with PapaParse - `processCurrencyColumns(data)` - Clean currency values ($, commas) - `parseFilenameComponents(filename)` - Extract symbol/direction/strategy from filename - `getDisplayName(symbol, direction, strategy)` - Format display names - `normalizeDate(date)` - Normalize dates to midnight UTC - `getDateKey(date)` - Convert date to YYYY-MM-DD string key **Metric Calculations**: - `calculateMetrics(data, filename)` - Calculate trade-level metrics for a strategy - Net Profit, Gross Profit/Loss - Profit Factor, Win Rate - Average Win/Loss, Expected Value - Max Drawdown (from equity curve) - CAGR equivalent (annualGrowthRate) - Total trades, winning/losing counts - **Note**: Does NOT calculate Sharpe or Sortino (those are in PortfolioSection.tsx) - `getAdjustedMetrics(metrics, multiplier)` - Apply contract multiplier to metrics **Risk-Adjusted Metrics** (PortfolioSection.tsx): - **Sharpe Ratio** (line 533): Calculated inline as `(annualGrowthRate / 100) / (maxDrawdown / startingCapital)` - **Sortino Ratio** (lines 133-158): Calculated inline with downside deviation, uses risk-free rate state **Correlation Analysis**: - `buildCorrelationMatrix(strategies)` - Build Spearman correlation matrix - `calculatePearsonCorrelation(returns1, returns2)` - Pearson correlation coefficient - `calculateRanks(values)` - Rank calculation for Spearman correlation **Trading Calculations**: - `getMarginRate(symbol)` - Get margin requirements by symbol - `calculateEquityCurve(trades)` - Build cumulative equity curve - `calculateDailyReturns(equity)` - Calculate daily returns from equity curve **Formatting**: - `formatNumber(value, decimals)` - Format numbers with decimals - `formatCurrency(value)` - Format as currency ($X,XXX.XX) - `formatPercent(value)` - Format as percentage (X.XX%) **Note**: Contains 1 TypeScript `any` violation in Metrics interface (tech debt) ## Data Flow ### Upload & Processing Flow ``` 1. User uploads CSV via UploadSection ↓ 2. parseCSV() extracts trade data ↓ 3. processCurrencyColumns() cleans data ↓ 4. File uploaded to Supabase storage ↓ 5. usePortfolio hook fetches and aggregates data ↓ 6. Date range filter applied (if set) ↓ 7. useMetrics calculates all metrics ↓ 8. MetricsTable displays results ``` ### Contract Multiplier Flow ``` 1. User inputs contract size in ContractInput ↓ 2. useContractMultipliers stores value ↓ 3. getAdjustedMetrics() applies multiplier ↓ 4. Adjusted metrics shown in MetricsTable ↓ 5. Portfolio charts update with adjusted values ``` ### Sorting Flow ``` 1. User clicks SortableHeader ↓ 2. useSorting updates sort column/direction ↓ 3. Custom comparison logic applied ↓ 4. MetricsTable re-renders with sorted data ``` ### Correlation Flow ``` 1. User selects assets in MetricsTable ↓ 2. Selection state passed to CorrelationSection ↓ 3. buildCorrelationMatrix() calculates correlations ↓ 4. CorrelationHeatmap renders Chart.js heatmap ↓ 5. Spearman & Pearson correlations both shown ``` ## State Management ### Plain React Hooks (No Zustand/TanStack Query) - **Local component state** → `useState` - **Derived state** → `useMemo` - **Stable callbacks** → `useCallback` - **Refs for values** → `useRef` **Example Pattern**: ```typescript const [data, setData] = useState([]) const metrics = useMemo(() => calculateMetrics(data), [data]) const handleUpload = useCallback((file: File) => { // upload logic }, []) ``` ### No Global State Library - Props passed down component tree - Custom hooks encapsulate shared logic - No Redux, Zustand, or Jotai ## Adding New Features ### New Metric Calculation 1. Add calculation logic to `dataUtils.calculateMetrics()` 2. Update return type in `calculateMetrics()` 3. Add column to `MetricsTable.tsx` 4. Update sort logic in `useSorting.ts` if needed 5. Test with sample data **Example**: Sortino Ratio was added in commits 258ba3a & 9f25040 ### New Chart Component 1. Create component in `src/components/` 2. Use Chart.js (NOT Recharts - it's unused) 3. Import chart type and plugins needed: ```typescript import { Line } from 'react-chartjs-2' import { Chart, registerables } from 'chart.js' import zoomPlugin from 'chartjs-plugin-zoom' ``` 4. Hook into `useMetrics` or `usePortfolio` for data 5. Add to appropriate section in `App.tsx` ### New Hook 1. Create in `src/hooks/use[Feature].ts` 2. Follow naming convention: `use` prefix, camelCase 3. Return object with clear property names 4. Use TypeScript for all types (avoid `any`) 5. Add JSDoc comments for complex logic ## Chart.js Architecture ### Current Setup - **Library**: Chart.js 4.x (NOT Recharts) - **React Wrapper**: react-chartjs-2 - **Plugins Used**: - chartjs-plugin-zoom (pan & zoom) - chartjs-plugin-annotation (trend lines, markers) - chartjs-adapter-date-fns (time scales) ### Where Charts Are Used 1. **PortfolioSection**: Equity curve line charts 2. **CorrelationHeatmap**: Correlation matrix heatmap 3. **CustomTooltip**: Shared tooltip component for charts ### Recharts Note ⚠️ **Recharts is installed but NEVER imported** - should be removed (11.5KB waste) ## Component Size Guidelines ### Target: 200 Lines Max **Current Violations**: - ❌ PortfolioSection.tsx: 591 lines (295% of limit) - **HIGH PRIORITY REFACTOR** - ❌ App.tsx: 351 lines (175% of limit) - ❌ MetricsTable.tsx: 242 lines (121% of limit) - improved from 350 ### Refactoring Strategy **For PortfolioSection (591 lines)**: 1. Extract equity chart into `EquityChartSection.tsx` 2. Extract statistics into `PortfolioStats.tsx` 3. Extract contract controls into `ContractControls.tsx` 4. Keep only orchestration logic in main component ## TypeScript Patterns ### Interfaces for Data Structures ```typescript interface Trade { date: Date symbol: string pnl: number // ... } interface Metric { name: string sharpe: number sortino: number // ... } ``` ### Avoid `any` Types **Current violations (15 total)** - see portfolio-context skill for details **Preferred approach**: ```typescript // Bad const data: any = parseData() // Good interface ParsedData { trades: Trade[] errors: string[] } const data: ParsedData = parseData() ``` ## Performance Patterns ### Memoization with useMemo ```typescript // Expensive correlation calculation const correlationMatrix = useMemo( () => buildCorrelationMatrix(selectedStrategies), [selectedStrategies] ) ``` ### Stable Callbacks with useCallback ```typescript const handleSort = useCallback((column: string) => { setSortColumn(column) setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc') }, []) ``` ### Avoid Premature Optimization - Build features first - Profile if performance issues arise - Optimize based on data, not assumptions