5.3 KiB
UI Architecture
This document describes the user interface architecture and how the frontend integrates with the backend API.
Overview
The UI is built with React and TypeScript, using Material-UI for components. The frontend communicates with the FastAPI backend through REST API endpoints and WebSocket connections for real-time updates.
Architecture
React Frontend → REST API / WebSocket → FastAPI Backend → Python Services → Database
Frontend Structure
frontend/src/
├── pages/ # Page components (Trading, Portfolio, Strategies, etc.)
├── components/ # Reusable UI components
├── api/ # API client functions
├── hooks/ # Custom React hooks
└── types/ # TypeScript type definitions
Page Components
DashboardPage
- Overview of trading activity
- Key metrics and charts
- Quick actions
TradingPage
- Order placement form
- Positions table
- Order history
- Real-time price updates
PortfolioPage
- Portfolio summary
- Holdings table
- Risk metrics
- Performance charts
StrategiesPage
- Strategy list and management
- Strategy configuration
- Start/stop controls
- Performance metrics
BacktestPage
- Backtest configuration
- Results display
- Export functionality
SettingsPage
- Exchange management
- Configuration settings
- API key management
Real-Time Updates
WebSocket Connection
The frontend connects to the backend WebSocket endpoint (/ws/) for real-time updates:
- Price updates
- Order status changes
- Position updates
- Strategy status changes
Implementation
// Using WebSocket hook
import { useWebSocket } from '@/hooks/useWebSocket';
function TradingPage() {
const { data, connected } = useWebSocket('/ws/');
// Handle real-time price updates
useEffect(() => {
if (data?.type === 'price_update') {
// Update UI with new price
}
}, [data]);
}
API Integration
API Client
All API calls go through the API client which handles:
- Request/response serialization
- Error handling
- Authentication (if added)
// Example API call
import { tradingApi } from '@/api/trading';
const placeOrder = async () => {
const order = await tradingApi.placeOrder({
exchangeId: 1,
symbol: 'BTC/USD',
side: 'buy',
type: 'market',
quantity: 0.1,
paperTrading: true
});
};
React Query
The frontend uses React Query for:
- Data fetching
- Caching
- Automatic refetching
- Optimistic updates
import { useQuery, useMutation } from '@tanstack/react-query';
import { portfolioApi } from '@/api/portfolio';
function PortfolioPage() {
const { data, isLoading } = useQuery({
queryKey: ['portfolio'],
queryFn: () => portfolioApi.getCurrent()
});
const updateMutation = useMutation({
mutationFn: portfolioApi.update,
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ['portfolio'] });
}
});
}
State Management
Component State
- Local component state with
useState - Form state management
- UI-only state (modals, tabs, etc.)
Server State
- React Query for server state
- Automatic caching and synchronization
- Optimistic updates
Global State
- Context API for theme, auth (if needed)
- Minimal global state - prefer server state
Component Patterns
Container/Presentational Pattern
// Container component (handles data fetching)
function TradingPageContainer() {
const { data } = useQuery({ queryKey: ['orders'], queryFn: fetchOrders });
return <TradingPage orders={data} />;
}
// Presentational component (pure UI)
function TradingPage({ orders }) {
return (
<Box>
<OrdersTable orders={orders} />
</Box>
);
}
Custom Hooks
Extract reusable logic into custom hooks:
function useOrders() {
const { data, isLoading, error } = useQuery({
queryKey: ['orders'],
queryFn: () => tradingApi.getOrders()
});
const placeOrder = useMutation({
mutationFn: tradingApi.placeOrder
});
return { orders: data, isLoading, error, placeOrder };
}
Error Handling
API Errors
try {
await tradingApi.placeOrder(order);
} catch (error) {
if (error.response?.status === 400) {
// Handle validation error
} else if (error.response?.status === 500) {
// Handle server error
}
}
Error Boundaries
Use React Error Boundaries to catch and display errors gracefully:
<ErrorBoundary fallback={<ErrorPage />}>
<TradingPage />
</ErrorBoundary>
Performance Optimization
Code Splitting
Use React.lazy for code splitting:
const TradingPage = lazy(() => import('@/pages/TradingPage'));
<Suspense fallback={<Loading />}>
<TradingPage />
</Suspense>
Memoization
Use React.memo, useMemo, and useCallback to prevent unnecessary re-renders:
const MemoizedTable = React.memo(OrdersTable);
const filteredData = useMemo(() => {
return data.filter(/* ... */);
}, [data, filter]);
Testing
See Testing Guide for frontend testing strategies.
Styling
- Material-UI components and theming
- Consistent design system
- Dark/light theme support
- Responsive design
Accessibility
- Semantic HTML
- ARIA labels where needed
- Keyboard navigation
- Screen reader support