271 lines
5.3 KiB
Markdown
271 lines
5.3 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
<ErrorBoundary fallback={<ErrorPage />}>
|
||
|
|
<TradingPage />
|
||
|
|
</ErrorBoundary>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance Optimization
|
||
|
|
|
||
|
|
### Code Splitting
|
||
|
|
|
||
|
|
Use React.lazy for code splitting:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const TradingPage = lazy(() => import('@/pages/TradingPage'));
|
||
|
|
|
||
|
|
<Suspense fallback={<Loading />}>
|
||
|
|
<TradingPage />
|
||
|
|
</Suspense>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Memoization
|
||
|
|
|
||
|
|
Use React.memo, useMemo, and useCallback to prevent unnecessary re-renders:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const MemoizedTable = React.memo(OrdersTable);
|
||
|
|
|
||
|
|
const filteredData = useMemo(() => {
|
||
|
|
return data.filter(/* ... */);
|
||
|
|
}, [data, filter]);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
See [Testing Guide](../developer/testing.md) 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
|