Add comprehensive documentation for energy trading system: - Backend: FastAPI architecture, API routes, services, WebSocket - Frontend: React structure, components, state management - ML: Feature engineering, XGBoost price prediction, RL battery optimization
19 KiB
19 KiB
Frontend Implementation Strategy
Overview
This document outlines the React frontend for the energy trading system UI. The frontend provides real-time monitoring, backtesting tools, ML model insights, and trading controls.
Backend API: http://localhost:8000
Architecture
┌──────────────────────────────────────────────────────────────┐
│ React Application │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Pages Layer │ │
│ │ Dashboard │ Backtest │ Models │ Trading │ Settings │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Components Layer │ │
│ │ Charts │ Forms │ Alerts │ Tables │ Controls │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Services Layer │ │
│ │ API Client │ WebSocket │ State Management │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ FastAPI Backend │
│ REST API + WebSocket │
└──────────────────────────────────────────────────────────────┘
Project Structure
frontend/
├── public/
│ ├── favicon.ico
│ └── index.html
│
├── src/
│ ├── App.tsx # Main app component
│ ├── main.tsx # Entry point
│ ├── index.css # Global styles
│ │
│ ├── components/
│ │ ├── common/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── Loading.tsx
│ │ │ └── Error.tsx
│ │ │
│ │ ├── charts/
│ │ │ ├── PriceChart.tsx
│ │ │ ├── BatteryChart.tsx
│ │ │ ├── PnLChart.tsx
│ │ │ ├── GenerationChart.tsx
│ │ │ └── ModelMetricsChart.tsx
│ │ │
│ │ ├── alerts/
│ │ │ ├── AlertPanel.tsx
│ │ │ └── AlertItem.tsx
│ │ │
│ │ ├── tables/
│ │ │ ├── ArbitrageTable.tsx
│ │ │ ├── TradeLogTable.tsx
│ │ │ └── ModelListTable.tsx
│ │ │
│ │ └── forms/
│ │ ├── BacktestForm.tsx
│ │ ├── TrainingForm.tsx
│ │ └── SettingsForm.tsx
│ │
│ ├── pages/
│ │ ├── Dashboard.tsx
│ │ ├── Backtest.tsx
│ │ ├── Models.tsx
│ │ ├── Trading.tsx
│ │ └── Settings.tsx
│ │
│ ├── hooks/
│ │ ├── useWebSocket.ts
│ │ ├── useApi.ts
│ │ ├── useBacktest.ts
│ │ ├── useModels.ts
│ │ └── useTrading.ts
│ │
│ ├── services/
│ │ ├── api.ts # REST API client
│ │ ├── websocket.ts # WebSocket client
│ │ └── types.ts # TypeScript types
│ │
│ ├── store/
│ │ ├── index.ts # Zustand store setup
│ │ ├── dashboardSlice.ts
│ │ ├── backtestSlice.ts
│ │ ├── modelsSlice.ts
│ │ └── tradingSlice.ts
│ │
│ └── lib/
│ ├── utils.ts
│ ├── constants.ts
│ └── formatters.ts
│
├── tests/
├── .env.example
├── .env.local
├── package.json
├── tsconfig.json
├── vite.config.ts
└── tailwind.config.js
Configuration
vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
},
'/ws': {
target: 'ws://localhost:8000',
ws: true,
},
},
},
});
.env.example
VITE_API_URL=http://localhost:8000
VITE_WS_URL=ws://localhost:8000/ws/real-time
TypeScript Types
services/types.ts
Enums
export enum Region {
FR = "FR",
BE = "BE",
DE = "DE",
NL = "NL",
UK = "UK",
}
export enum Strategy {
FUNDAMENTAL = "fundamental",
TECHNICAL = "technical",
ML = "ml",
MINING = "mining",
}
export enum TradeType {
BUY = "buy",
SELL = "sell",
CHARGE = "charge",
DISCHARGE = "discharge",
}
export enum BacktestStatus {
PENDING = "pending",
RUNNING = "running",
COMPLETED = "completed",
FAILED = "failed",
CANCELLED = "cancelled",
}
export enum ModelType {
PRICE_PREDICTION = "price_prediction",
RL_BATTERY = "rl_battery",
}
Key Interfaces
export interface PriceData {
timestamp: string;
region: Region;
day_ahead_price: number;
real_time_price: number;
volume_mw: number;
}
export interface BatteryState {
battery_id: string;
timestamp: string;
capacity_mwh: number;
charge_level_mwh: number;
charge_level_pct: number;
charge_rate_mw: number;
discharge_rate_mw: number;
efficiency: number;
}
export interface BacktestConfig {
start_date: string;
end_date: string;
strategies: Strategy[];
use_ml: boolean;
battery_min_reserve?: number;
battery_max_charge?: number;
arbitrage_min_spread?: number;
}
export interface BacktestMetrics {
total_revenue: number;
arbitrage_profit: number;
battery_revenue: number;
mining_profit: number;
battery_utilization: number;
price_capture_rate: number;
win_rate: number;
sharpe_ratio: number;
max_drawdown: number;
total_trades: number;
}
export interface BacktestStatus {
id: string;
status: BacktestStatus;
progress: number;
current_step: string;
started_at?: string;
completed_at?: string;
error?: string;
}
export interface ModelInfo {
id: string;
type: ModelType;
name: string;
horizon?: number;
created_at: string;
metrics: Record<string, number>;
status: string;
}
export interface TrainingRequest {
model_type: ModelType;
horizon?: number;
start_date: string;
end_date: string;
hyperparameters: Record<string, unknown>;
}
export interface TrainingStatus {
id: string;
status: BacktestStatus;
progress: number;
current_epoch?: number;
total_epochs?: number;
metrics: Record<string, number>;
started_at?: string;
completed_at?: string;
error?: string;
}
export interface PredictionRequest {
model_id: string;
timestamp: string;
features?: Record<string, unknown>;
}
export interface PredictionResponse {
model_id: string;
timestamp: string;
prediction: number;
confidence?: number;
features_used: string[];
}
export interface StrategyStatus {
strategy: Strategy;
running: boolean;
last_updated?: string;
}
export interface TradingPosition {
region: Region;
position_mw: number;
battery_charge_pct: number;
pnl: number;
}
export interface Alert {
id: string;
timestamp: string;
type: AlertType;
severity: "info" | "warning" | "error";
message: string;
data: Record<string, unknown>;
acknowledged: boolean;
}
API Client Interface
services/api.ts
Dashboard API
export const dashboardApi = {
getSummary: async (): Promise<DashboardSummary> => { },
getPrices: async (): Promise<Record<string, PriceData>> => { },
getPriceHistory: async (
region: string,
start?: string,
end?: string,
limit?: number
): Promise<{ region: string; data: PriceData[] }> => { },
getBatteryStates: async (): Promise<BatteryState[]> => { },
getArbitrage: async (minSpread?: number): Promise<{
opportunities: ArbitrageOpportunity[];
count: number;
}> => { },
};
Backtest API
export const backtestApi = {
start: async (request: BacktestRequest): Promise<{
backtest_id: string;
status: BacktestStatus;
}> => { },
get: async (backtestId: string): Promise<{
status: BacktestStatus;
results?: BacktestResult;
}> => { },
getResults: async (backtestId: string): Promise<BacktestResult> => { },
getTrades: async (backtestId: string, limit?: number): Promise<{
backtest_id: string;
trades: Trade[];
total: number;
}> => { },
list: async (): Promise<{
backtests: BacktestStatus[];
total: number;
}> => { },
delete: async (backtestId: string): Promise<{ message: string }> => { },
};
Models API
export const modelsApi = {
list: async (): Promise<{ models: ModelInfo[]; total: number }> => { },
train: async (request: TrainingRequest): Promise<{
training_id: string;
status: TrainingStatus;
}> => { },
getStatus: async (modelId: string): Promise<TrainingStatus> => { },
getMetrics: async (modelId: string): Promise<{
model_id: string;
metrics: Record<string, number>;
}> => { },
predict: async (request: PredictionRequest): Promise<PredictionResponse> => { },
};
Trading API
export const tradingApi = {
getStrategies: async (): Promise<{ strategies: StrategyStatus[] }> => { },
toggleStrategy: async (control: {
strategy: Strategy;
action: "start" | "stop";
}): Promise<{ status: StrategyStatus }> => { },
getPositions: async (): Promise<{ positions: TradingPosition[] }> => { },
};
Settings API
export const settingsApi = {
get: async (): Promise<AppSettings> => { },
update: async (settings: Partial<AppSettings>): Promise<{
message: string;
updated_fields: string[];
}> => { },
};
WebSocket Client Interface
services/websocket.ts
class WebSocketService {
private ws: WebSocket | null = null;
private url: string;
private eventHandlers: Map<WebSocketEventType, Set<EventHandler>> = new Map();
private isConnected = false;
constructor(url: string = import.meta.env.VITE_WS_URL);
connect(): void;
disconnect(): void;
subscribe<T = unknown>(
eventType: WebSocketEventType,
handler: (data: T) => void
): () => void; // Returns unsubscribe function
getConnectionStatus(): boolean;
private handleMessage(message: WebSocketMessage): void;
private attemptReconnect(): void;
}
export const webSocketService = new WebSocketService();
WebSocket Event Types
export type WebSocketEventType =
| "price_update"
| "battery_update"
| "arbitrage_opportunity"
| "trade_executed"
| "alert_triggered"
| "backtest_progress"
| "model_training_progress";
export interface WebSocketMessage<T = unknown> {
type: WebSocketEventType;
timestamp: string;
data: T;
}
State Management (Zustand)
store/index.ts
interface DashboardState {
summary: DashboardSummary | null;
prices: Record<string, PriceData>;
batteryStates: BatteryState[];
arbitrageOpportunities: ArbitrageOpportunity[];
alerts: Alert[];
updateSummary: (summary: DashboardSummary) => void;
updatePrices: (prices: Record<string, PriceData>) => void;
addAlert: (alert: Alert) => void;
// ...
}
interface BacktestState {
backtests: Record<string, BacktestStatus>;
currentBacktest: string | null;
isRunning: boolean;
updateBacktest: (status: BacktestStatus) => void;
setCurrentBacktest: (backtestId: string | null) => void;
// ...
}
interface ModelsState {
models: ModelInfo[];
trainingJobs: Record<string, TrainingStatus>;
selectedModel: string | null;
setModels: (models: ModelInfo[]) => void;
updateTrainingJob: (job: TrainingStatus) => void;
// ...
}
interface TradingState {
strategies: Record<string, StrategyStatus>;
positions: TradingPosition[];
pnl: number;
updateStrategy: (strategy: StrategyStatus) => void;
updatePositions: (positions: TradingPosition[]) => void;
// ...
}
export const useStore = create<AppStore>((set) => ({
// ... state and actions
}));
Custom Hooks
hooks/useWebSocket.ts
export function useWebSocket() {
const subscribe = <T = unknown>(
eventType: WebSocketEventType,
handler: (data: T) => void
): (() => void) => {
return webSocketService.subscribe<T>(eventType, handler);
};
const isConnected = webSocketService.getConnectionStatus();
return { subscribe, isConnected };
}
hooks/useApi.ts
// Dashboard hooks
export function useDashboardSummary();
export function usePrices();
export function useBatteryStates();
export function useArbitrageOpportunities(minSpread?: number);
// Backtest hooks
export function useStartBacktest();
export function useBacktest(backtestId: string);
export function useBacktestList();
// Models hooks
export function useModels();
export function useTrainModel();
// Trading hooks
export function useStrategies();
export function useToggleStrategy();
export function usePositions();
// Settings hooks
export function useSettings();
export function useUpdateSettings();
Pages Interface
Dashboard.tsx
export default function Dashboard() {
const { subscribe } = useWebSocket();
const { prices, batteryStates, arbitrageOpportunities, alerts } = useStore();
const { data: pricesData } = usePrices();
// Subscribe to real-time updates
useEffect(() => {
const unsubscribePrice = subscribe('price_update', (data) => { });
const unsubscribeBattery = subscribe('battery_update', (data) => { });
const unsubscribeAlert = subscribe('alert_triggered', (data) => { });
return () => {
unsubscribePrice();
unsubscribeBattery();
unsubscribeAlert();
};
}, [subscribe]);
// Render stats cards, charts, tables
}
Backtest.tsx
export default function Backtest() {
const [selectedBacktestId, setSelectedBacktestId] = useState<string | null>(null);
const { mutate: startBacktest, isPending } = useStartBacktest();
const { data: backtest } = useBacktest(selectedBacktestId || '');
const { data: backtestList } = useBacktestList();
const handleStartBacktest = (config: any) => {
startBacktest({ config, name: config.name }, {
onSuccess: (data) => {
setSelectedBacktestId(data.backtest_id);
},
});
};
// Render form, results, progress
}
Models.tsx
export default function Models() {
const [selectedModel, setSelectedModel] = useState<string | null>(null);
const [showTrainingForm, setShowTrainingForm] = useState(false);
const { data: modelsData } = useModels();
const { mutate: trainModel, isPending } = useTrainModel();
const handleTrainModel = (config: any) => {
trainModel(config, {
onSuccess: () => {
setShowTrainingForm(false);
},
});
};
// Render model list, training form, model details
}
Trading.tsx
export default function Trading() {
const { data: strategiesData } = useStrategies();
const { data: positionsData } = usePositions();
const { mutate: toggleStrategy } = useToggleStrategy();
const handleToggleStrategy = (strategyName: string, running: boolean) => {
toggleStrategy({
strategy: strategyName as any,
action: running ? 'stop' : 'start',
});
};
// Render strategy status, positions, controls
}
Dependencies
package.json
{
"name": "energy-trading-ui",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest",
"type-check": "tsc --noEmit"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"recharts": "^2.10.0",
"zustand": "^4.4.0",
"@tanstack/react-query": "^5.0.0",
"axios": "^1.6.0",
"date-fns": "^2.30.0",
"lucide-react": "^0.292.0"
},
"devDependencies": {
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@vitejs/plugin-react": "^4.2.0",
"typescript": "^5.2.2",
"vite": "^5.0.0",
"vitest": "^1.0.0",
"tailwindcss": "^3.3.5"
}
}
Data Flow
API Calls
Component → useApi Hook → API Client → Axios → Backend
↓
React Query Cache
↓
Automatic Refetch
WebSocket Updates
WebSocket Event → WebSocketService → Event Handler → Store Update → Component Re-render
State Synchronization
API Response → React Query Cache → Component Props → Zustand Store (optional)
WebSocket Event → Zustand Store → Component Re-render
Key Integration Points
Backend API Integration
- REST API endpoints are fully typed in
services/types.ts - API client methods in
services/api.tsmatch backend routes - WebSocket events match backend event types
Error Handling
- API errors handled by React Query error states
- WebSocket reconnection automatic with exponential backoff
- Component-level error boundaries for unhandled errors
Performance
- React Query caching with automatic refetch intervals
- WebSocket connection pooling
- Component memoization where appropriate
- Virtual scrolling for large data tables