From 88ce9cdd2dd3ff95ea1de9b9182501420db8f5ea Mon Sep 17 00:00:00 2001 From: Han Lap Date: Fri, 3 Jul 2026 13:28:20 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20th=C3=AAm=20chi=20ti=E1=BA=BFt=20backen?= =?UTF-8?q?d-core,=20backend-API,=20frontend=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/backend-api-doc.md | 624 ++++++++++++++++++++++++++ docs/backend-core-doc.md | 921 +++++++++++++++++++++++++++++++++++++++ docs/frontend-doc.md | 638 +++++++++++++++++++++++++++ 3 files changed, 2183 insertions(+) create mode 100644 docs/backend-api-doc.md create mode 100644 docs/backend-core-doc.md create mode 100644 docs/frontend-doc.md diff --git a/docs/backend-api-doc.md b/docs/backend-api-doc.md new file mode 100644 index 0000000..c8eef4a --- /dev/null +++ b/docs/backend-api-doc.md @@ -0,0 +1,624 @@ +# Tài Liệu API & Dịch Vụ Backend Trading Portal + +> **Dự án:** trading-portal +> **Backend:** FastAPI (Python 3.13) +> **Base URL:** `/api/v1` +> **WebSocket:** `/ws/v1/candles` +> **Ngày tạo:** 03/07/2026 + +--- + +# MỤC LỤC + +1. [API Routes (REST)](#1-api-routes-rest) + - [1.1 Auth (`/auth`)](#11-auth-auth) + - [1.2 Users (`/users`)](#12-users-users) + - [1.3 Admin (`/admin`)](#13-admin-admin) + - [1.4 Exchanges (`/exchanges`)](#14-exchanges-exchanges) + - [1.5 Credentials (`/credentials`)](#15-credentials-credentials) + - [1.6 Symbols (`/symbols`)](#16-symbols-symbols) + - [1.7 Signals (`/signals`)](#17-signals-signals) + - [1.8 Strategies (`/strategies`)](#18-strategies-strategies) + - [1.9 Backtest (`/backtest`)](#19-backtest-backtest) + - [1.10 Orders (`/orders`)](#110-orders-orders) + - [1.11 Real Trades (`/real-trades`)](#111-real-trades-real-trades) + - [1.12 Watchlist (`/watchlist`)](#112-watchlist-watchlist) + - [1.13 Analytics (`/analytics`)](#113-analytics-analytics) + - [1.14 Alerts (`/alerts`)](#114-alerts-alerts) + - [1.15 Audit (`/audit`)](#115-audit-audit) +2. [WebSocket Handler](#2-websocket-handler) +3. [Services](#3-services) +4. [Exchange Adapters](#4-exchange-adapters) +5. [Background Tasks](#5-background-tasks) + +--- + +# 1. API ROUTES (REST) + +## 1.1 Auth (`/auth`) + +**Prefix:** `/api/v1/auth` +**Tags:** `auth` +**File:** `api/v1/auth.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `POST` | `/register` | Body: `RegisterRequest` (username, email, password) | Đăng ký tài khoản mới. Kiểm tra username/email đã tồn tại, hash mật khẩu, tạo User. Trả về `UserResponse`. | +| `POST` | `/login` | Body: `LoginRequest` (username, password); Header: `User-Agent`, IP | Xác thực username/password, tạo cặp access token + refresh token. Lưu refresh token hash vào DB. Trả về `TokenResponse`. | +| `POST` | `/refresh` | Body: `RefreshRequest` (refresh_token) | Làm mới access token bằng refresh token (rotation). Revoke token cũ, cấp cặp mới. Trả về `TokenResponse`. | +| `POST` | `/logout` | Body: `LogoutRequest` (refresh_token) | Revoke refresh token, đánh dấu không còn dùng được. | +| `GET` | `/me` | Depends: `get_current_user` | Lấy thông tin profile của user đang đăng nhập. Trả về `UserResponse`. | +| `PUT` | `/me` | Body: `UserUpdateRequest` (email, display_name, preferences) | Cập nhật profile user hiện tại. | +| `GET` | `/sessions` | Depends: `get_current_user` | Lấy danh sách tất cả session đang active (refresh token chưa hết hạn, chưa bị revoke). | +| `DELETE` | `/sessions/{token_hash}` | Path: `token_hash` | Revoke một session cụ thể theo token hash. | +| `POST` | `/change-password` | Body: `ChangePasswordRequest` (old_password, new_password) | Đổi mật khẩu. Xác thực mật khẩu cũ, validate độ mạnh mật khẩu mới, revoke tất cả session. | + +--- + +## 1.2 Users (`/users`) + +**Prefix:** `/api/v1/users` +**Tags:** *(nằm trong api_router)* +**File:** `api/v1/router.py` (định nghĩa trực tiếp trên `api_router`) + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `/users/me` | Depends: `get_current_user` | Lấy thông tin user hiện tại (id, username, email, display_name, is_active, is_admin). | + +--- + +## 1.3 Admin (`/admin`) + +**Prefix:** `/api/v1/admin` +**Tags:** `admin` +**Auth:** Admin only (`get_current_admin_user`) +**File:** `api/v1/admin.py` + +### Quản lý Users + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `/users` | — | Liệt kê tất cả users (admin only). Trả về `list[UserResponse]`. | +| `POST` | `/users` | Body: `AdminCreateUserRequest` (username, email, password, display_name, is_admin, role) | Tạo user mới (admin bypass). | +| `PUT` | `/users/{user_id}` | Path: `user_id`; Body: `AdminUserUpdateRequest` | Cập nhật user (active/deactive, promote/demote admin, email, display_name). | +| `DELETE` | `/users/{user_id}` | Path: `user_id` | Xóa user (cascade: watchlists, credentials, refresh tokens). | +| `POST` | `/users/{user_id}/reset-password` | Path: `user_id`; Body: `AdminResetPasswordRequest` (new_password) | Admin reset mật khẩu user (không cần mật khẩu cũ). Revoke tất cả session. | + +### Quản lý Exchanges + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `/exchanges` | — | Liệt kê tất cả exchanges (kể cả inactive). | +| `POST` | `/exchanges` | Body: `ExchangeCreateRequest` (name, display_name, base_url, ws_url) | Thêm exchange mới. | +| `PUT` | `/exchanges/{exchange_id}` | Path: `exchange_id`; Body: `ExchangeUpdateRequest` | Cập nhật cấu hình exchange. | + +### Health Check + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `/health/detailed` | — | Health check nâng cao: trạng thái DB (connected, latency, pool_size), trạng thái kết nối từng exchange, uptime, version. Trả về `DetailedHealthResponse`. | + +--- + +## 1.4 Exchanges (`/exchanges`) + +**Prefix:** `/api/v1/exchanges` +**Tags:** `exchanges` +**File:** `api/v1/exchanges.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | Query: `active_only` (bool, default=True) | Liệt kê tất cả exchanges, có thể lọc active. | +| `GET` | `/{exchange_id}/symbols` | Path: `exchange_id` | Lấy danh sách symbol của một exchange. | +| `POST` | `/{exchange_id}/sync` | Path: `exchange_id`; Auth: Admin | Đồng bộ symbols từ exchange về DB (admin only). | +| `POST` | `/{exchange_id}/fetch_candles` | Path: `exchange_id`; Body: `symbol`, `timeframe` (default "1h"), `limit` (default 500); Auth: `get_current_active_user` | Fetch và lưu candles cho một symbol trên exchange. | + +--- + +## 1.5 Credentials (`/credentials`) + +**Prefix:** `/api/v1/credentials` +**Tags:** `credentials` +**Auth:** `get_current_user` +**File:** `api/v1/credentials.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | — | Liệt kê tất cả API keys của user (key bị masked). | +| `POST` | `` | Body: `CredentialCreateRequest` (exchange_id, api_key, api_secret, passphrase, is_testnet) | Thêm API key mới. Secret được mã hóa trước khi lưu. | +| `PUT` | `/{credential_id}` | Path: `credential_id`; Body: `CredentialUpdateRequest` | Cập nhật API key/secret/passphrase hoặc deactivate. | +| `DELETE` | `/{credential_id}` | Path: `credential_id` | Xóa API key. | +| `POST` | `/{credential_id}/test` | Path: `credential_id` | Kiểm tra API key bằng cách gọi `load_markets()` từ exchange. | +| `GET` | `/{credential_id}/summary` | Path: `credential_id` | Lấy tổng quan tài khoản: balances, open orders, positions từ exchange. | +| `GET` | `/{credential_id}/balance` | Path: `credential_id` | Lấy số dư token chi tiết. | +| `GET` | `/{credential_id}/orders` | Path: `credential_id` | Lấy danh sách open orders. | +| `GET` | `/{credential_id}/positions` | Path: `credential_id` | Lấy danh sách open positions (futures). | + +--- + +## 1.6 Symbols (`/symbols`) + +**Prefix:** `/api/v1/symbols` +**Tags:** `symbols` +**File:** `api/v1/symbols.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | Query: `exchange`, `active_only` (default True), `limit` (50), `offset` (0) | Liệt kê symbols với phân trang, có thể lọc theo exchange và active status. | +| `GET` | `/search` | Query: `q` (required), `exchange`, `limit` (50) | Tìm kiếm symbol theo tên (case-insensitive partial match). | +| `GET` | `/candles` | Query: `symbol` (required), `exchange` (default "binance"), `timeframe` (default "1h"), `cursor` (ISO datetime), `limit` (500) | Lấy candles với cursor-based pagination. | +| `GET` | `/{base}/{quote}/candles` | Path: `base`, `quote`; Query: `exchange`, `timeframe`, `cursor`, `limit` | Lấy candles qua path (VD: `BTC/USDT/candles`). | +| `GET` | `/indicators` | Query: `symbol` (required), `exchange` (default "binance"), `timeframe` (default "1h") | Tính toán và trả về technical indicators cho symbol. | +| `GET` | `/{base}/{quote}/indicators` | Path: `base`, `quote`; Query: `exchange`, `timeframe` | Tính indicators qua path. | + +--- + +## 1.7 Signals (`/signals`) + +**Prefix:** `/api/v1/signals` +**Tags:** `signals` +**Auth:** `get_current_user` +**File:** `api/v1/signals.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | Query: `symbol` (optional), `limit` (50, max 200) | Lấy danh sách tín hiệu giao dịch gần nhất. | +| `GET` | `/trades` | Query: `symbol`, `status` (OPEN/CLOSED), `limit` (100, max 500) | Lấy lịch sử hypothetical trades của user hiện tại, kèm total_pnl và win_rate. | +| `GET` | `/review` | Query: `period` (weekly/monthly) | Lấy báo cáo hiệu suất theo tuần hoặc tháng. | + +--- + +## 1.8 Strategies (`/strategies`) + +**Prefix:** `/api/v1/strategies` +**Tags:** `strategies` +**Auth:** `get_current_active_user` +**File:** `api/v1/strategies.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | — | Lấy danh sách tất cả chiến lược, trạng thái enabled/disabled theo user preferences, và thresholds. | +| `POST` | `` | Body: `StrategyConfigRequest` (enabled_strategies, thresholds) | Cập nhật cấu hình chiến lược (alias cho PUT). | +| `PUT` | `` | Body: `StrategyConfigRequest` | Cập nhật `enabled_strategies` và/hoặc `thresholds` trong preferences JSON. | + +--- + +## 1.9 Backtest (`/backtest`) + +**Prefix:** `/api/v1/backtest` +**Tags:** `backtest` và `backtest_history` +**File:** `api/v1/backtest.py` và `api/v1/backtest_history.py` + +### Chạy Backtest (backtest.py) + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | — | Redirect: hướng dẫn dùng `/backtest/run`. | +| `GET` | `/symbols` | Query: `exchange` (optional) | Lấy danh sách symbols có đủ candles (>=30 ở 30m/1h/4h/1d) để backtest. | +| `GET` | `/run` | Query: `symbol` (default "BTC/USDT"), `exchange` (default "mexc"), `timeframe` (default "30m"), `days` (7), `trade_size` (10.0) | **Chạy backtest** — mô phỏng giao dịch dựa trên tín hiệu từ 13 thuật toán. Trả về kết quả PnL, win rate, profit factor, danh sách trades. | +| `POST` | `/run` | Query: giống GET `/run` | Alias POST cho `/run`. | + +### Lịch Sử Backtest (backtest_history.py) + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `POST` | `/save` | Query: `symbol`, `exchange`, `timeframe`, `days`, `trade_size`, `total_trades`, `wins`, `losses`, `win_rate`, `total_pnl`, `profit_factor`, `avg_win`, `avg_loss` | Lưu kết quả backtest vào bảng `user_backtests`. | +| `GET` | `/history` | Query: `limit` (50, max 200) | Lấy lịch sử backtest của user hiện tại. | +| `DELETE` | `/{bt_id}` | Path: `bt_id` (UUID) | Xóa một kết quả backtest. | +| `POST` | `/compare` | Body: `CompareRequest` (ids: list[UUID]) | So sánh nhiều kết quả backtest, tính rank, equity curve, prediction. | + +--- + +## 1.10 Orders (`/orders`) + +**Prefix:** `/api/v1/orders` +**Tags:** `orders` +**Auth:** `get_current_user` +**File:** `api/v1/orders.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `POST` | `/place` | Body: `OrderRequest` (symbol, side, order_type, amount, price, reduce_only, position_side) | Đặt lệnh (market/limit) trên exchange thông qua API key đã lưu. Tự động lưu vào `real_trades`. | +| `GET` | `/balance` | Query: `exchange_name` (default "mexc") | Lấy số dư tài khoản từ exchange được kết nối. | + +--- + +## 1.11 Real Trades (`/real-trades`) + +**Prefix:** `/api/v1/real-trades` +**Tags:** `real-trades` +**Auth:** `get_current_viewer_user` (GET), `get_current_trader_user` (POST) +**File:** `api/v1/real_trades.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | Query: `symbol`, `status`, `limit` (100, max 500) | Lấy danh sách real trades của user. Kèm total_pnl và win_rate. | +| `POST` | `` | Body: `RealTradeCreateRequest` (exchange, symbol, side, order_type, amount, price, filled_amount, status, order_id) | Lưu bản ghi real trade (gọi sau khi đặt lệnh). | +| `GET` | `/win-rate` | — | Phân tích win-rate theo daily/weekly/monthly. | + +--- + +## 1.12 Watchlist (`/watchlist`) + +**Prefix:** `/api/v1/watchlist` +**Tags:** `watchlist` +**Auth:** `get_current_active_user` +**File:** `api/v1/watchlist.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | — | Liệt kê tất cả symbols trong watchlist của user. | +| `POST` | `` | Body: `WatchlistCreateRequest` (symbol_id, label, sort_order) | Thêm symbol vào watchlist (kiểm tra trùng lặp). | +| `DELETE` | `/{watchlist_id}` | Path: `watchlist_id` (UUID) | Xóa symbol khỏi watchlist. | +| `GET` | `/all-symbols` | Query: `exchange` (default "mexc"), `q` (search), `limit` (100) | Liệt kê tất cả symbols khả dụng để thêm vào watchlist, hỗ trợ tìm kiếm. | + +--- + +## 1.13 Analytics (`/analytics`) + +**Prefix:** `/api/v1/analytics` +**Tags:** `analytics` +**Auth:** `get_current_active_user` +**File:** `api/v1/analytics.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | — | Redirect: liệt kê các endpoints analytics. | +| `GET` | `/performance` | — | Tổng quan hiệu suất: win rate, PnL, profit factor từ `hypothetical_trades`. | +| `GET` | `/dashboard` | Query: `days` (90, max 365) | **Dashboard tổng hợp:** PnL history (daily), equity curve, drawdown, win rate by period (daily/weekly/monthly), best/worst trade, aggregate stats, open trades count. | + +--- + +## 1.14 Alerts (`/alerts`) + +**Prefix:** `/api/v1/alerts` +**Tags:** `alerts` +**Auth:** `get_current_active_user` +**File:** `api/v1/alerts.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | — | Liệt kê tất cả alerts của user. | +| `POST` | `` | Body: `AlertCreateRequest` (name, conditions: list[ConditionObject], notify_platform) | Tạo alert đa điều kiện mới. Validate indicator và operator. | +| `PUT` | `/{alert_id}` | Path: `alert_id`; Body: `AlertUpdateRequest` | Cập nhật alert. | +| `DELETE` | `/{alert_id}` | Path: `alert_id` | Xóa alert. | + +**ConditionObject schema:** +- `indicator`: `rsi`, `macd`, `bb_width`, `volume`, `price`, `sma`, `ema`, `momentum` +- `operator`: `>`, `<`, `>=`, `<=`, `==`, `cross_above`, `cross_below` +- `value`: float (ngưỡng) +- `timeframe`: optional (VD: "1h", "15m") +- `type`: optional (VD: "avg_multiplier", "absolute") +- `period`: optional (lookback, VD: 20) + +--- + +## 1.15 Audit (`/audit`) + +**Prefix:** `/api/v1/audit` +**Tags:** `audit` +**File:** `api/v1/audit.py` + +| Method | Path | Params | Mô tả | +|--------|------|--------|-------| +| `GET` | `` | Query: `limit` (100), `offset` (0), `action` (optional); Auth: Admin | Liệt kê audit logs (admin only). Hỗ trợ phân trang và lọc theo action. | +| `POST` | `` | Body: `AuditLogCreateRequest` (action, resource, details); Auth: `get_current_active_user` | Ghi log thủ công (bất kỳ user đã xác thực). | +| `GET` | `/logs` | Query: `action`, `limit` (50), `offset` (0) | Alias của GET `/audit` (không yêu cầu admin — public alias). | + +--- + +# 2. WEBSOCKET HANDLER + +**File:** `api/ws/candle_handler.py` +**Path:** `/ws/v1/candles` + +### Chi tiết WebSocket + +| Thuộc tính | Giá trị | +|-----------|--------| +| **Endpoint** | `ws://host/ws/v1/candles?token=JWT_TOKEN` | +| **Auth** | JWT token qua query param `?token=...` hoặc Sec-WebSocket-Protocol header | +| **Handler** | `candle_websocket(websocket: WebSocket)` | + +### Message Flow + +**Server → Client:** + +| Type | Status | Mô tả | +|------|--------|-------| +| `connection` | `connected` | WebSocket đã accept | +| `connection` | `authenticated` | JWT token hợp lệ, sẵn sàng subscribe | +| `candle` | `{data}` | Dữ liệu candle real-time được push | +| `ticker` | `{data}` | Dữ liệu ticker real-time | +| `signal` | `{data}` | Tín hiệu giao dịch mới | +| `error` | `{message}` | Lỗi (token thiếu, JSON không hợp lệ, action không rõ) | + +**Client → Server:** + +```json +{"action": "subscribe", "symbol": "BTC/USDT", "timeframe": "1h", "exchange": "mexc"} +{"action": "unsubscribe", "symbol": "BTC/USDT", "timeframe": "1h", "exchange": "mexc"} +``` + +**Mã lỗi đóng:** `4001` — lỗi xác thực + +--- + +# 3. SERVICES + +## 3.1 Auth Service (`auth_service.py`) + +| Function | Params | Return | Mô tả | +|----------|--------|--------|-------| +| `_validate_password_strength(password)` | `password: str` | `None` (raises `ConflictException`) | Validate mật khẩu: >=8 ký tự, có chữ thường, chữ hoa, số, ký tự đặc biệt. | +| `register(db, req)` | `db: AsyncSession`, `req: RegisterRequest` | `UserResponse` | Đăng ký user mới. Kiểm tra uniqueness, validate password, hash, tạo user. | +| `login(db, req, user_agent, ip_address)` | `db`, `req: LoginRequest`, `user_agent`, `ip_address` | `TokenResponse` | Đăng nhập: xác thực credentials, tạo access + refresh JWT, lưu refresh token hash. | +| `refresh_token(db, refresh_token_str)` | `db`, `refresh_token_str: str` | `TokenResponse` | Rotation refresh token: verify, revoke cũ, cấp mới. | +| `logout(db, refresh_token_str)` | `db`, `refresh_token_str: str` | `None` | Revoke refresh token. | +| `get_user_sessions(db, user_id)` | `db`, `user_id: UUID` | `list[UserSessionResponse]` | Lấy session active (không revoked, chưa hết hạn). | +| `revoke_session(db, token_hash, user_id)` | `db`, `token_hash: str`, `user_id: UUID` | `None` | Revoke session cụ thể, xác minh thuộc về user. | +| `change_password(db, user_id, old_password, new_password)` | `db`, `user_id: UUID`, `old_password`, `new_password` | `None` | Đổi mật khẩu: verify cũ, validate mới, hash, revoke tất cả session. | + +--- + +## 3.2 Signal Service (`signal_service.py`) + +**File lớn nhất:** ~2328 dòng, logic phát hiện tín hiệu cốt lõi. + +### Constants + +- `STRONG_BUY`, `BUY`, `STRONG_SELL`, `SELL`, `CAUTION_LONG`, `CAUTION_SHORT`, `SQUEEZE_ALERT` + +### Core Functions + +| Function | Mô tả | +|----------|-------| +| `_get_bb_values(indicators)` | Trích xuất Bollinger Bands từ indicators dict. | +| `_get_rsi_values(indicators)` | Trích xuất RSI values. | +| `_get_sma_values(indicators)` | Trích xuất SMA values. | +| `_detect_squeeze(bb, lookback=10)` | Phát hiện BB Squeeze (biên độ thu hẹp) — báo hiệu sắp breakout. | +| `_classify_signal_bb(close_price, bb, rsi, sma)` | Phân loại tín hiệu dựa trên Double BB + RSI. Trả về `(signal_type, strength)`. | +| `_classify_signal_combined(...)` | **Hệ thống bỏ phiếu 13 thuật toán:** ①Double BB+RSI ②MACD Crossover ③SuperTrend ④Volume Breakout ⑤Ichimoku Cloud ⑥Divergence Detection ⑦Market Structure (SMC) ⑧Multi-Timeframe (15m/1h/4h) ⑨OBV Crossover ⑩Stochastic RSI ⑪MFI ⑫FVG ⑬Candlestick Patterns. Có boosting theo historical win rate, correlation dampening, dynamic threshold normalization. | +| `_calculate_pnl(entry_price, exit_price, direction, quantity)` | Tính PnL tuyệt đối và %. | +| `analyse_and_generate_signals(exchange, symbol, timeframe, candle_data)` | **Entry point chính:** Lấy indicators → phân tích → lưu signal → push WS → gửi notification → check alerts → execute trades. | +| `get_recent_signals(db, symbol, limit)` | Lấy danh sách signals gần nhất. | +| `get_trade_history(db, symbol, status, limit, user_id)` | Lấy lịch sử hypothetical trades kèm aggregate stats. | +| `get_review(db, period, user_id)` | Tạo báo cáo hiệu suất weekly/monthly. | +| `expire_old_signals(max_age_days=7)` | Đánh dấu EXPIRED cho signals cũ. | + +--- + +## 3.3 Candle Service (`candle_service.py`) + +| Function | Params | Return | Mô tả | +|----------|--------|--------|-------| +| `fetch_and_store_candles(db, exchange_name, symbol, timeframe, limit)` | `db`, `exchange_name`, `symbol`, `timeframe`, `limit=500` | `list[CandleData]` | Fetch candles từ exchange, upsert vào DB (ON CONFLICT DO NOTHING), update cache, gọi after-fetch callbacks. | +| `get_candles(db, symbol, exchange_name, timeframe, cursor, limit)` | `db`, `symbol`, `exchange_name`, `timeframe`, `cursor=None`, `limit=500` | `CandleListResponse` | Lấy candles với cursor-based pagination. Check TTLCache trước, fallback ra DB. Nếu DB không có thì fetch on-demand từ exchange. | +| `get_latest_candle(db, symbol, exchange_name, timeframe)` | `db`, `symbol`, `exchange_name`, `timeframe` | `Optional[CandleData]` | Lấy candle mới nhất. | +| `get_indicators(db, symbol, exchange_name, timeframe)` | `db`, `symbol`, `exchange_name`, `timeframe` | `dict` | Tính toán 250 candles gần nhất và compute tất cả indicators: SMA 20/50, EMA 12/26, RSI 14, Stoch RSI, MACD, Bollinger Bands, VWAP, SuperTrend, Volume Breakout, OBV, Ichimoku, RSI Divergence, MACD Divergence, Market Structure (SMC), ADX, Market Regime, MFI, FVG, Candlestick Score. Kết quả được cache với TTL theo timeframe. | + +--- + +## 3.4 Indicator Service (`indicator_service.py`) + +**~1479 dòng** — thư viện tính toán chỉ báo kỹ thuật thuần toán học. Hỗ trợ NumPy (vectorized) nếu có, fallback về pure Python. + +| Function | Mô tả | +|----------|-------| +| `sma(prices, period)` | Simple Moving Average. | +| `ema(prices, period)` | Exponential Moving Average. | +| `rsi(prices, period=14)` | Relative Strength Index (Wilder's smoothing). | +| `stoch_rsi(prices, rsi_period, stoch_period, k_smoothing, d_smoothing)` | Stochastic RSI. Returns `{k, d}`. | +| `macd(prices, fast=12, slow=26, signal=9)` | MACD. Returns `{macd_line, signal_line, histogram}`. | +| `bollinger_bands(prices, period=20, std_dev=2.0)` | Bollinger Bands. Returns `{upper, middle, lower, upper_1, lower_1}`. | +| `volume_profile(candles, num_bins=10)` | Volume Profile cơ bản. | +| `vwap(candles)` | Volume-Weighted Average Price (cumulative). | +| `atr(candles, period=14)` | Average True Range. | +| `supertrend(candles, period=10, multiplier=3.0)` | SuperTrend. Returns `{trend, supertrend}`. | +| `volume_breakout(candles, period=20, multiplier=2.5)` | Phát hiện volume breakout. | +| `ichimoku(candles)` | Ichimoku Cloud. Returns `{tenkan, kijun, senkou_a, senkou_b, chikou}`. | +| `detect_divergence(prices, indicator, pivot_lookback=5)` | Phát hiện divergence (regular + hidden) trên RSI hoặc MACD. | +| `market_structure(candle_dicts, pivot_lookback=3)` | Smart Money Concepts: BOS (Break of Structure), CHoCH (Change of Character), Order Blocks. | +| `detect_market_regime(adx_data, bb_data, atr_pct, vol_breakout, prices, highs, lows)` | Phân loại chế độ thị trường: trending/sideways/volatile/breakout/choppy/neutral. | +| `detect_fvg(candle_dicts, lookback=30)` | Fair Value Gap detection. | +| `detect_candlestick_patterns(candle_dicts)` | Nhận diện 30+ mẫu nến Nhật. | +| `obv(candle_dicts)` | On-Balance Volume. | +| `obv_signal(obv_vals, period=20)` | OBV crossover signal. | +| `mfi(candle_dicts, period=14)` | Money Flow Index. | +| `adx(candle_dicts, period=14)` | Average Directional Index. | + +--- + +## 3.5 Alert Service (`alert_service.py`) + +| Function | Params | Mô tả | +|----------|--------|-------| +| `evaluate_single_condition(condition, symbol_data)` | `condition: dict`, `symbol_data: dict` | Đánh giá một điều kiện alert duy nhất. | +| `check_alert_conditions(db, symbol_data_map, user_id)` | `db`, `symbol_data_map: dict[str, dict]`, `user_id` | Kiểm tra tất cả alerts active của user, trả về list các alert đã kích hoạt (AND logic). | +| `check_and_notify_alerts(db, symbol, symbol_data, user_id)` | `db`, `symbol`, `symbol_data`, `user_id` | Convenience function: kiểm tra + log alerts được trigger. | + +**Indicators hỗ trợ:** `rsi`, `macd`, `bb_width`, `volume`, `price`, `sma`, `ema`, `momentum` +**Operators hỗ trợ:** `>`, `<`, `>=`, `<=`, `==`, `cross_above`, `cross_below` + +--- + +## 3.6 Audit Service (`audit_service.py`) + +| Function | Params | Return | Mô tả | +|----------|--------|--------|-------| +| `log_action(db, user_id, action, resource, details)` | `db`, `user_id`, `action`, `resource`, `details=None` | `AuditLog` | Ghi audit log entry. | +| `get_audit_logs(db, limit, offset, action)` | `db`, `limit=100`, `offset=0`, `action=None` | `(list[AuditLog], int)` | Lấy audit logs với phân trang và lọc action. | + +--- + +## 3.7 Notification Service (`notification_service.py`) + +| Function | Params | Mô tả | +|----------|--------|-------| +| `send_telegram_notification(chat_id, message)` | `chat_id: str`, `message: str` | Gửi tin nhắn Telegram qua Bot API. Token từ env `TELEGRAM_BOT_TOKEN`. | +| `send_discord_notification(webhook_url, message)` | `webhook_url: str`, `message: str` | Gửi tin nhắn Discord qua webhook URL. | +| `notify_user(user, signal_type, symbol, price, exchange_name)` | `user: User`, `signal_type`, `symbol`, `price`, `exchange_name` | Gửi thông báo tín hiệu cho user dựa trên preferences (`notif_signal`, `notification_channels`). | +| `notify_trade(user, trade_direction, symbol, price, action)` | `user: User`, `trade_direction`, `symbol`, `price`, `action` | Gửi thông báo giao dịch tự động dựa trên preferences (`notif_trade`, `notification_channels`). | + +**Kênh hỗ trợ:** Telegram (qua Bot API), Discord (qua Webhook) + +--- + +## 3.8 WebSocket Push Service (`ws_push_service.py`) + +| Function | Mô tả | +|----------|-------| +| `push_new_candle(symbol, exchange, timeframe, candle_data)` | Broadcast candle data qua WS manager đến tất cả clients subscribed. | +| `setup_push_listener(app)` | Đăng ký callback với candle-fetch scheduler để tự động push candle mới. Idempotent. | + +--- + +## 3.9 Trade Executor (`trade_executor.py`) + +**Tách biệt khỏi pipeline tín hiệu.** Signals để giám sát; chỉ STRONG signals mới execute trades. + +| Function | Mô tả | +|----------|-------| +| `_calculate_pnl(entry, current, direction, quantity)` | Tính unrealized PnL và PnL%. | +| `_determine_winning_strategy(signal)` | Xác định strategy có score cao nhất từ indicators_snapshot. | +| `execute_signal_trade(db, signal, symbol, exchange_name, timeframe, current_price)` | **Thực thi giao dịch:** Chỉ STRONG_BUY/STRONG_SELL mở trade mới. Rules: đóng trade đối nghịch (REVERSAL), hybrid eviction (worst PnL first, then FIFO, max 10 open), Kelly sizing + volatility filter + trailing stop. Chỉ user có `auto_trade` enabled mới được giao dịch. | +| `sync_real_trades()` | Đồng bộ real trades: đóng stale trades (hold >24h), tính PnL cho closed trades thiếu PnL. Gọi định kỳ mỗi 5 phút. | + +--- + +## 3.10 Signal Booster (`signal_booster.py`) + +| Function | Mô tả | +|----------|-------| +| `compute_strategy_win_rates(db)` | Query `hypothetical_trades`, tính win rate theo strategy + direction với exponential decay (half-life 14 ngày, min 15 trades). Cache 6 giờ. | +| `get_cached_rates()` | Lấy cache win-rate hiện tại (sync). | +| `get_booster_multiplier(strategy, rates)` | Tính multiplier = win_rate × 2.0 cho mỗi strategy. | +| `boost_score(score, strategy, rates)` | Áp dụng win-rate multiplier vào score của strategy. | +| `get_confidence(strategy_scores, rates)` | Tính confidence score (0-1) từ weighted average của absolute vote strengths. | +| `get_pnl_stats()` | Trả về avg_win/avg_loss từ cache (dùng cho Kelly sizing). | + +--- + +## 3.11 Risk Manager (`risk_manager.py`) + +### `DynamicKellySizer` + +| Method | Mô tả | +|--------|-------| +| `compute_kelly_pct(win_rate, avg_win, avg_loss, confidence)` | Tính tỉ lệ vốn phân bổ theo Fractional Kelly (25% default). | +| `compute_volatility_adjusted_size(base_size, atr_pct, max_risk_pct, regime)` | Điều chỉnh size theo volatility và market regime. | + +### `AdaptiveSLTPOptimizer` + +| Method | Mô tả | +|--------|-------| +| `compute_sl_tp(atr, entry_price, regime, direction)` | Tính SL/TP dựa trên ATR và regime. Returns `{stop_loss, take_profit, risk_reward, acceptable}`. | +| `compute_partial_tp_levels(atr, entry_price, regime, direction)` | Tính multi-level partial take-profit levels. | + +**Regimes:** trending, sideways, volatile, breakout, choppy, neutral — mỗi regime có SL/TP multiplier riêng. + +--- + +# 4. EXCHANGE ADAPTERS + +## 4.1 Base (`exchange/base.py`) + +**Class:** `AbstractExchange(ABC)` + +| Property/Method | Mô tả | +|----------------|--------| +| `client` | Lazy init CCXT client (gọi `_init_ccxt()`) | +| `rate_limiter` | Singleton `GlobalRateLimiter` per exchange | +| `get_name()` | Abstract: tên exchange | +| `get_base_url()` | Abstract: REST API base URL | +| `get_ws_url()` | Abstract: WebSocket URL | +| `fetch_ohlcv(symbol, timeframe, limit)` | Fetch OHLCV candles qua CCXT (thread pool). Validate OHLC consistency. | +| `fetch_ticker(symbol)` | Fetch ticker data. | +| `fetch_symbols()` | Fetch tất cả markets từ exchange. | +| `create_order(req)` | Đặt lệnh (market/limit) qua CCXT. | +| `fetch_balance()` | Lấy số dư tài khoản. | +| `fetch_open_orders(symbol)` | Lấy danh sách open orders. | +| `fetch_my_trades(symbol, limit)` | Lấy lịch sử trades đã filled. | +| `fetch_positions(symbols)` | Lấy open positions (futures). | + +## 4.2 Implementations + +| Adapter | File | CCXT Class | Rate Limit | +|---------|------|-----------|------------| +| `BinanceAdapter` | `binance.py` | `ccxt.binance` | 10 rps | +| `MEXCAdapter` | `mexc.py` | `ccxt.mexc` | 20 rps | +| `BybitAdapter` | `bybit.py` | `ccxt.bybit` | 10 rps | +| `GateAdapter` | `gate.py` | `ccxt.gate` | 10 rps (default) | +| `BingXAdapter` | `bingx.py` | `ccxt.bingx` | 10 rps (default) | + +## 4.3 Factory (`exchange/factory.py`) + +**Class:** `ExchangeFactory` (Singleton qua global instance `factory`) + +| Method | Mô tả | +|--------|-------| +| `register(name, adapter_cls)` | Đăng ký adapter class mới. | +| `create(name, api_key, api_secret, testnet)` | Tạo/lấy cached adapter instance (TTL 5 phút). | +| `get_available_exchanges()` | Trả về list tên exchanges đã đăng ký. | + +## 4.4 Rate Limiter (`exchange/rate_limiter.py`) + +**Class:** `RateLimiter` — Token bucket rate limiter với async lock. + +| Function | Mô tả | +|----------|-------| +| `GlobalRateLimiter(exchange_name)` | Singleton rate limiter per exchange: Binance 10rps, Bybit 10rps, MEXC 20rps, default 10rps. | + +## 4.5 Types (`exchange/types.py`) + +Các Pydantic models: `CandleData`, `SymbolInfo`, `TickerData`, `CandleValidationError`, `OrderRequest`, `OrderData`, `BalanceData`, `BalanceResponse`, `OpenOrderData`, `PositionData`, `TradeData`, `CredentialSummaryResponse`. + +--- + +# 5. BACKGROUND TASKS + +## 5.1 Candle Fetcher (`tasks/candle_fetcher.py`) + +### Functions + +| Function | Mô tả | +|----------|-------| +| `register_after_fetch_callback(callback)` | Đăng ký async callback được gọi sau khi candles được lưu. | +| `fetch_recent_candles(app, fetch_limit, timeframes, max_symbols)` | **Core scheduler job:** Quét symbols đang trading, fetch candles từ CCXT (batch 25 symbols × 7 timeframes), upsert vào DB, gọi callbacks, invalidate cache, chạy batch signal analysis (chỉ 1h timeframe, semaphore 3). | +| `setup_candle_scheduler(app)` | Tạo và cấu hình `AsyncIOScheduler`. Job: mỗi 5 phút fetch 7 timeframes (15m,30m,1h,4h,1d,1w,1M) với 25 symbols/batch. | +| `force_full_sync(app)` | Chạy sync một lần với `fetch_limit=500` khi khởi động. | + +### Cấu hình +- **Semaphore:** 250 concurrent fetches +- **Signal semaphore:** 3 concurrent analyses +- **Batch:** 25 symbols × 7 TFs = 175 API calls mỗi 5 phút +- **Full cycle:** ~100 phút cho ~476 symbols + +## 5.2 Exchange Sync (`tasks/exchange_sync.py`) + +| Function | Mô tả | +|----------|-------| +| `sync_exchange_symbols(db, exchange_name)` | Fetch tất cả symbols từ exchange và upsert vào DB (ON CONFLICT DO UPDATE). | +| `sync_all_exchanges(app)` | Đồng bộ symbols cho tất cả exchanges active. Trả về `dict[exchange_name, count]`. | + +## 5.3 Stale Data Monitor (`tasks/stale_data_monitor.py`) + +| Function | Mô tả | +|----------|-------| +| `check_stale_candles(db, app)` | Quét tất cả symbols active, kiểm tra candle mới nhất cho mỗi timeframe. Phát hiện stale nếu `latest_timestamp + 2×timeframe_duration < now`. Trả về list stale entries. | + +**Timeframes kiểm tra:** 1m, 5m, 15m, 30m, 1h, 4h, 1d + +--- + +# TỔNG KẾT + +| Danh mục | Số lượng | +|----------|---------| +| **REST Endpoints** | ~65 endpoints | +| **WebSocket Handlers** | 1 (`/ws/v1/candles`) | +| **Service Modules** | 11 | +| **Exchange Adapters** | 5 (Binance, MEXC, Bybit, Gate, BingX) | +| **Background Tasks** | 3 modules (candle_fetcher, exchange_sync, stale_data_monitor) | +| **Technical Indicators** | 20+ (SMA, EMA, RSI, StochRSI, MACD, BB, VWAP, ATR, SuperTrend, Volume Breakout, Ichimoku, Divergence, Market Structure, ADX, Market Regime, MFI, FVG, Candlestick Patterns, OBV, Volume Profile) | +| **Trading Algorithms** | 13 thuật toán bỏ phiếu (Double BB+RSI, MACD, SuperTrend, Volume Breakout, Ichimoku, Divergence, SMC, MTF, OBV, Stoch RSI, MFI, FVG, Candlestick) | diff --git a/docs/backend-core-doc.md b/docs/backend-core-doc.md new file mode 100644 index 0000000..2f5e2ca --- /dev/null +++ b/docs/backend-core-doc.md @@ -0,0 +1,921 @@ +# Tài Liệu Backend Core — Trading Portal + +> **Dự án:** trading-portal +> **Stack:** FastAPI + SQLAlchemy async + PostgreSQL +> **Đường dẫn:** `/opt/data/trading-portal/backend/app/` +> **Ngày tạo:** 03/07/2026 + +--- + +## Mục Lục + +1. [config.py](#1-configpy) +2. [database.py](#2-databasepy) +3. [models/ — ORM Models](#3-models--orm-models) + - [3.1 \_\_init\_\_.py](#31-__init__py) + - [3.2 user.py](#32-userpy) + - [3.3 exchange.py](#33-exchangepy) + - [3.4 symbol.py](#34-symbolpy) + - [3.5 candle.py](#35-candlepy) + - [3.6 credential.py](#36-credentialpy) + - [3.7 refresh_token.py](#37-refresh_tokenpy) + - [3.8 watchlist.py](#38-watchlistpy) + - [3.9 signal.py](#39-signalpy) + - [3.10 alert.py](#310-alertpy) + - [3.11 audit_log.py](#311-audit_logpy) + - [3.12 real_trade.py](#312-real_tradepy) +4. [core/ — Module lõi](#4-core--module-lõi) + - [4.1 security.py](#41-securitypy) + - [4.2 deps.py](#42-depspy) + - [4.3 middleware.py](#43-middlewarepy) + - [4.4 exceptions.py](#44-exceptionspy) +5. [schemas/ — Pydantic Schemas](#5-schemas--pydantic-schemas) + - [5.1 auth.py](#51-authpy) + - [5.2 user.py](#52-userpy) + - [5.3 symbol.py](#53-symbolpy) + - [5.4 exchange.py](#54-exchangepy) + - [5.5 candle.py](#55-candlepy) + - [5.6 signal.py](#56-signalpy) + - [5.7 strategy.py](#57-strategypy) + - [5.8 real_trade.py](#58-real_tradepy) + - [5.9 credential.py](#59-credentialpy) + - [5.10 ws_message.py](#510-ws_messagepy) + - [5.11 health.py](#511-healthpy) + +--- + +## 1. config.py + +**Đường dẫn:** `app/config.py` (43 dòng) + +### Mục đích +File cấu hình tập trung của toàn bộ ứng dụng, sử dụng `pydantic_settings.BaseSettings` để đọc biến môi trường và file `.env`. + +### Class chính + +#### `Settings(BaseSettings)` +Kế thừa từ `pydantic_settings.BaseSettings`, tự động nạp cấu hình từ: +- Biến môi trường +- File `.env` (encoding UTF-8) +- `extra="allow"` cho phép thêm biến không khai báo trước + +**Các thuộc tính cấu hình:** + +| Nhóm | Biến | Giá trị mặc định | Mô tả | +|------|------|-----------------|-------| +| **Database** | `DATABASE_URL` | `postgresql+asyncpg://trading:trading_secret@db:5432/trading_portal` | URL kết nối PostgreSQL async | +| **JWT** | `JWT_PRIVATE_KEY_PATH` | `/run/secrets/jwt_private.pem` | Đường dẫn private key RSA | +| **JWT** | `JWT_PUBLIC_KEY_PATH` | `/run/secrets/jwt_public_key.pem` | Đường dẫn public key RSA | +| **JWT** | `JWT_PUBLIC_KEYS_DIR` | `/run/secrets/jwt_public_keys` | Thư mục chứa tất cả public keys hợp lệ (hỗ trợ key rotation) | +| **JWT** | `JWT_ACCESS_TOKEN_EXPIRE_MINUTES` | `15` | Thời hạn access token (phút) | +| **JWT** | `JWT_REFRESH_TOKEN_EXPIRE_DAYS` | `7` | Thời hạn refresh token (ngày) | +| **Encryption** | `ENCRYPTION_KEY` | `""` | Khóa AES-256-CBC (hex 64 ký tự) để mã hóa API key | +| **Server** | `HOST` | `0.0.0.0` | Host binding | +| **Server** | `PORT` | `8000` | Cổng HTTP | +| **Logging** | `LOG_LEVEL` | `INFO` | Mức log (DEBUG/INFO/WARNING/ERROR) | +| **CORS** | `CORS_ORIGINS` | `""` | Danh sách origin CORS | +| **Demo** | `demo_user` | `demo` | Tài khoản demo | +| **Demo** | `demo_pass` | `demo1234` | Mật khẩu demo | + +### Dependencies +- `pydantic_settings.BaseSettings`, `SettingsConfigDict` + +### Singleton +```python +settings = Settings() # Instance toàn cục, import từ app.config +``` + +--- + +## 2. database.py + +**Đường dẫn:** `app/database.py` (65 dòng) + +### Mục đích +Khởi tạo SQLAlchemy async engine, session factory, base class cho ORM models, và dependency `get_db()` cho FastAPI. + +### Class/Function chính + +#### `engine` (global) +- `create_async_engine()` với PostgreSQL async driver (`asyncpg`) +- **Pool settings:** `pool_size=60`, `max_overflow=20`, `pool_timeout=5`, `pool_recycle=600` +- **Connection settings:** `pool_pre_ping=True`, `idle_in_transaction_session_timeout=60000ms`, `statement_timeout=10000ms` +- Echo mode chỉ bật khi `LOG_LEVEL == "DEBUG"` + +#### `async_session_factory` (global) +- `async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)` + +#### `Base(DeclarativeBase)` +- Base class cho tất cả ORM models, kế thừa từ `sqlalchemy.orm.DeclarativeBase` + +#### `async def get_db() -> AsyncGenerator[AsyncSession, None]` +- Generator cung cấp async DB session cho FastAPI dependency injection +- Tự động commit khi thành công, rollback khi lỗi +- Xử lý `InterfaceError` gracefully (kết nối đã đóng) để tránh CPU spike 500%+ +- Luôn gọi `session.close()` trong finally + +### Dependencies +- `sqlalchemy.ext.asyncio` (AsyncSession, async_sessionmaker, create_async_engine) +- `sqlalchemy.orm.DeclarativeBase` +- `app.config.settings` + +--- + +## 3. models/ — ORM Models + +### 3.1 \_\_init\_\_.py + +**Đường dẫn:** `app/models/__init__.py` (24 dòng) + +#### Mục đích +Re-export tất cả ORM models để import gọn: `from app.models import User, Exchange, ...` + +#### Models được export (10 models): +`User`, `Exchange`, `Symbol`, `Candle`, `Watchlist`, `ExchangeCredential`, `RefreshToken`, `Signal`, `HypotheticalTrade`, `RealTrade`, `AuditLog` + +--- + +### 3.2 user.py + +**Đường dẫn:** `app/models/user.py` (72 dòng) +**Bảng:** `users` + +#### Mục đích +Lưu thông tin người dùng hệ thống, hỗ trợ nhiều role và phân quyền. + +#### Class: `User(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `UUID` | PK, gen_random_uuid() | Định danh duy nhất | +| `username` | `String(50)` | UNIQUE, NOT NULL | Tên đăng nhập | +| `email` | `String(255)` | UNIQUE, NOT NULL | Email | +| `password_hash` | `String(255)` | NOT NULL | Mật khẩu đã hash (bcrypt) | +| `display_name` | `String(100)` | NULLABLE | Tên hiển thị | +| `is_active` | `Boolean` | DEFAULT True | Trạng thái kích hoạt | +| `is_admin` | `Boolean` | DEFAULT False | Cờ admin | +| `role` | `String(20)` | DEFAULT "trader" | Vai trò: admin/trader/viewer | +| `preferences` | `JSON` | NULLABLE | Tùy chọn người dùng (default_exchange, default_timeframe) | +| `created_at` | `TIMESTAMP(tz)` | server_default=now() | Thời điểm tạo | +| `updated_at` | `TIMESTAMP(tz)` | onupdate=now() | Thời điểm cập nhật | + +**Relationships:** +- `watchlists` → `Watchlist` (one-to-many, cascade delete) +- `credentials` → `ExchangeCredential` (one-to-many, cascade delete) +- `refresh_tokens` → `RefreshToken` (one-to-many, cascade delete) + +**Dependencies:** `app.database.Base` + +--- + +### 3.3 exchange.py + +**Đường dẫn:** `app/models/exchange.py` (52 dòng) +**Bảng:** `exchanges` + +#### Mục đích +Danh sách các sàn giao dịch được hỗ trợ (Binance, MEXC, Gate.io, BingX, Bybit...). + +#### Class: `Exchange(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `Integer` | PK, autoincrement | Định danh | +| `name` | `String(50)` | UNIQUE, NOT NULL | Tên định danh (binance, mexc...) | +| `display_name` | `String(100)` | NULLABLE | Tên hiển thị | +| `base_url` | `String(255)` | NULLABLE | REST API base URL | +| `ws_url` | `String(255)` | NULLABLE | WebSocket URL | +| `is_active` | `Boolean` | DEFAULT True | Trạng thái hoạt động | +| `created_at` | `TIMESTAMP(tz)` | server_default=now() | Thời điểm tạo | + +**Indexes:** `ix_exchanges_is_active` (P2-8) + +**Relationships:** +- `symbols` → `Symbol` (one-to-many, cascade delete) +- `credentials` → `ExchangeCredential` (one-to-many, cascade delete) + +**Dependencies:** `app.database.Base` + +--- + +### 3.4 symbol.py + +**Đường dẫn:** `app/models/symbol.py` (46 dòng) +**Bảng:** `symbols` + +#### Mục đích +Danh sách trading pairs (symbols) trên từng sàn, ví dụ BTC/USDT, ETH/USDT. + +#### Class: `Symbol(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `Integer` | PK, autoincrement | Định danh | +| `exchange_id` | `Integer` | FK → exchanges.id, NOT NULL | Sàn giao dịch | +| `symbol` | `String(50)` | NOT NULL | Mã cặp giao dịch (BTC/USDT) | +| `base` | `String(20)` | NOT NULL | Base currency (BTC) | +| `quote` | `String(20)` | NOT NULL | Quote currency (USDT) | +| `is_active` | `Boolean` | DEFAULT True | Đang hoạt động | +| `is_trading` | `Boolean` | DEFAULT False | Có đang giao dịch | + +**Constraints:** +- `uq_symbol_exchange_symbol`: UNIQUE(exchange_id, symbol) — mỗi symbol xuất hiện một lần trên mỗi sàn + +**Indexes:** `ix_symbols_is_active` (P2-8) + +**Relationships:** +- `exchange` → `Exchange` (many-to-one) +- `watchlists` → `Watchlist` (one-to-many, cascade delete) + +**Dependencies:** `app.database.Base` + +--- + +### 3.5 candle.py + +**Đường dẫn:** `app/models/candle.py` (71 dòng) +**Bảng:** `candles` + +#### Mục đích +Lưu trữ dữ liệu nến (OHLCV) — đây là bảng dữ liệu lớn nhất, được partition theo thời gian. + +#### Class: `Candle(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `symbol_id` | `Integer` | PK, FK → symbols.id | Symbol | +| `timeframe` | `String(10)` | PK, NOT NULL | Khung thời gian (1m, 5m, 15m, 1h, 4h, 1d) | +| `timestamp` | `TIMESTAMP(tz)` | PK, NOT NULL | Thời điểm nến | +| `open` | `Numeric(20,8)` | NOT NULL | Giá mở cửa | +| `high` | `Numeric(20,8)` | NOT NULL | Giá cao nhất | +| `low` | `Numeric(20,8)` | NOT NULL | Giá thấp nhất | +| `close` | `Numeric(20,8)` | NOT NULL | Giá đóng cửa | +| `volume` | `Numeric(30,8)` | NOT NULL | Khối lượng | + +**Primary Key:** Composite (symbol_id, timeframe, timestamp) + +**Indexes:** +- `ix_candles_symbol_timeframe_ts_desc`: DESC index cho truy vấn lấy nến mới nhất + +**Table Options:** +- `postgresql_partition_by = "RANGE (timestamp)"` — Bảng được partition theo thời gian để tối ưu query + +**Dependencies:** `app.database.Base` + +--- + +### 3.6 credential.py + +**Đường dẫn:** `app/models/credential.py` (73 dòng) +**Bảng:** `exchange_credentials` + +#### Mục đích +Lưu trữ API key đã mã hóa của người dùng cho từng sàn giao dịch. API secret được mã hóa bằng AES-256-CBC. + +#### Class: `ExchangeCredential(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `UUID` | PK, gen_random_uuid() | Định danh | +| `user_id` | `UUID` | FK → users.id, ON DELETE CASCADE | Người dùng | +| `exchange_id` | `Integer` | FK → exchanges.id | Sàn giao dịch | +| `api_key` | `String(255)` | NOT NULL | API key (plaintext) | +| `api_secret_enc` | `String(512)` | NOT NULL | API secret đã mã hóa AES-256-CBC | +| `api_secret_iv` | `String(64)` | NOT NULL | IV cho AES-CBC | +| `passphrase` | `String(255)` | NULLABLE | Passphrase cho sàn yêu cầu | +| `is_testnet` | `Boolean` | DEFAULT False | Dùng testnet | +| `is_active` | `Boolean` | DEFAULT True | Trạng thái hoạt động | +| `created_at` | `TIMESTAMP(tz)` | server_default=now() | Thời điểm tạo | +| `updated_at` | `TIMESTAMP(tz)` | onupdate=now() | Thời điểm cập nhật | + +**Relationships:** +- `user` → `User` (many-to-one) +- `exchange` → `Exchange` (many-to-one) + +**Dependencies:** `app.database.Base` + +--- + +### 3.7 refresh_token.py + +**Đường dẫn:** `app/models/refresh_token.py` (56 dòng) +**Bảng:** `refresh_tokens` + +#### Mục đích +Lưu trữ refresh token đã hash để quản lý phiên đăng nhập và thu hồi token. + +#### Class: `RefreshToken(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `UUID` | PK, gen_random_uuid() | Định danh | +| `user_id` | `UUID` | FK → users.id, ON DELETE CASCADE | Người dùng | +| `token_hash` | `String(64)` | NOT NULL | SHA-256 hash của refresh token | +| `expires_at` | `TIMESTAMP(tz)` | NOT NULL | Thời điểm hết hạn | +| `revoked` | `Boolean` | DEFAULT False | Đã thu hồi | +| `created_at` | `TIMESTAMP(tz)` | server_default=now() | Thời điểm tạo | +| `user_agent` | `String(255)` | NULLABLE | User-Agent của client | +| `ip_address` | `INET` | NULLABLE | Địa chỉ IP | + +**Indexes:** `ix_refresh_tokens_user_id` + +**Relationships:** +- `user` → `User` (many-to-one) + +**Dependencies:** `app.database.Base` + +--- + +### 3.8 watchlist.py + +**Đường dẫn:** `app/models/watchlist.py` (61 dòng) +**Bảng:** `user_watchlists` + +#### Mục đích +Danh sách theo dõi (watchlist) của người dùng — liên kết user với các symbol họ quan tâm. + +#### Class: `Watchlist(Base)` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `UUID` | PK, gen_random_uuid() | Định danh | +| `user_id` | `UUID` | FK → users.id, ON DELETE CASCADE | Người dùng | +| `symbol_id` | `Integer` | FK → symbols.id, ON DELETE CASCADE | Symbol | +| `label` | `String(50)` | NULLABLE | Nhãn tùy chỉnh | +| `sort_order` | `Integer` | DEFAULT 0 | Thứ tự sắp xếp | +| `created_at` | `TIMESTAMP(tz)` | server_default=now() | Thời điểm tạo | + +**Constraints:** `uq_watchlist_user_symbol`: UNIQUE(user_id, symbol_id) + +**Relationships:** +- `user` → `User` (many-to-one) +- `symbol` → `Symbol` (many-to-one) + +**Dependencies:** `app.database.Base` + +--- + +### 3.9 signal.py + +**Đường dẫn:** `app/models/signal.py` (98 dòng) +**Bảng:** `signals`, `hypothetical_trades` + +#### Mục đích +Lưu tín hiệu giao dịch được phát hiện từ phân tích kỹ thuật (Double Bollinger Bands + RSI) và các giao dịch giả lập (paper trading) tương ứng. + +#### Class: `Signal(Base)` — Bảng `signals` + +| Cột | Kiểu dữ liệu | Ràng buộc | Mô tả | +|-----|-------------|-----------|-------| +| `id` | `Integer` | PK, autoincrement | Định danh | +| `symbol` | `String(50)` | NOT NULL, INDEX | Mã cặp giao dịch | +| `exchange` | `String(20)` | NOT NULL, DEFAULT "mexc" | Sàn giao dịch | +| `timeframe` | `String(10)` | NOT NULL | Khung thời gian | +| `signal_type` | `String(30)` | NOT NULL | Loại tín hiệu (STRONG_BUY, BUY, SELL, STRONG_SELL, NEUTRAL) | +| `strength` | `String(20)` | NOT NULL | Độ mạnh | +| `price` | `Numeric(20,8)` | NOT NULL | Giá tại thời điểm tín hiệu | +| `timestamp` | `DateTime(tz)` | NOT NULL | Thời điểm tín hiệu | +| `indicators_snapshot` | `Text` | NULLABLE | JSON snapshot các chỉ báo | +| `status` | `String(20)` | DEFAULT "ACTIVE" | Trạng thái | +| `note` | `Text` | NULLABLE | Ghi chú | +| `created_at` | `DateTime(tz)` | DEFAULT now | Thời điểm tạo | + +**Indexes:** `ix_signals_symbol_created` + +#### Class: `HypotheticalTrade(Base)` — Bảng `hypothetical_trades` + +Giao dịch giả lập (paper trade) tự động mở khi có tín hiệu: +- STRONG_BUY/BUY → LONG +- STRONG_SELL/SELL → SHORT +- Đóng khi có tín hiệu ngược chiều hoặc stop loss + +| Cột | Kiểu dữ liệu | Mô tả | +|-----|-------------|-------| +| `id` | `Integer` PK | Định danh | +| `user_id` | `UUID` FK Nullable | Người dùng | +| `signal_id` | `Integer` FK Nullable | Tín hiệu mở lệnh | +| `symbol` | `String(50)` | Cặp giao dịch | +| `exchange` | `String(20)` | Sàn | +| `timeframe` | `String(10)` | Khung thời gian | +| `direction` | `String(10)` | LONG / SHORT | +| `entry_price` | `Numeric(20,8)` | Giá vào lệnh | +| `entry_time` | `DateTime(tz)` | Thời điểm vào lệnh | +| `entry_reason` | `String(30)` Nullable | Lý do vào lệnh | +| `exit_price` | `Numeric(20,8)` Nullable | Giá thoát lệnh | +| `exit_time` | `DateTime(tz)` Nullable | Thời điểm thoát | +| `exit_reason` | `String(30)` Nullable | Lý do thoát | +| `quantity` | `Numeric(20,8)` | Khối lượng | +| `pnl` | `Numeric(20,8)` Nullable | Lợi nhuận/lỗ | +| `pnl_percent` | `Numeric(14,4)` Nullable | % lợi nhuận | +| `status` | `String(10)` DEFAULT "OPEN" | OPEN / CLOSED | +| `created_at` | `DateTime(tz)` | Thời điểm tạo | +| `closed_at` | `DateTime(tz)` Nullable | Thời điểm đóng | + +**Indexes:** `ix_hyp_trades_symbol`, `ix_hyp_trades_status` + +**Dependencies:** `app.database.Base` + +--- + +### 3.10 alert.py + +**Đường dẫn:** `app/models/alert.py` (71 dòng) +**Bảng:** `alert_conditions` + +#### Mục đích +Lưu các điều kiện cảnh báo (alert) đa điều kiện do người dùng định nghĩa. Mỗi alert có nhiều conditions (JSON), được đánh giá sau mỗi lần sinh tín hiệu. + +#### Class: `AlertCondition(Base)` + +| Cột | Kiểu dữ liệu | Mô tả | +|-----|-------------|-------| +| `id` | `Integer` PK | Định danh | +| `user_id` | `UUID` FK → users.id | Người dùng sở hữu | +| `name` | `String(100)` | Tên alert | +| `conditions` | `JSON` DEFAULT list | Mảng các condition objects: `[{"indicator": "rsi", "operator": ">", "value": 70, "timeframe": "1h"}]` | +| `notify_platform` | `String(20)` DEFAULT "telegram" | Kênh thông báo: telegram/discord/both | +| `is_active` | `Boolean` DEFAULT True | Trạng thái kích hoạt | +| `created_at` | `DateTime(tz)` | Thời điểm tạo | +| `updated_at` | `DateTime(tz)` | Thời điểm cập nhật | + +**Indexes:** `ix_alert_conditions_user_active` + +**Dependencies:** `app.database.Base` + +--- + +### 3.11 audit_log.py + +**Đường dẫn:** `app/models/audit_log.py` (48 dòng) +**Bảng:** `audit_logs` + +#### Mục đích +Ghi log kiểm toán (audit trail) cho mọi hành động quan trọng trong hệ thống. + +#### Class: `AuditLog(Base)` + +| Cột | Kiểu dữ liệu | Mô tả | +|-----|-------------|-------| +| `id` | `Integer` PK | Định danh | +| `user_id` | `UUID` Nullable, INDEX | Người thực hiện hành động | +| `action` | `String(50)` | Hành động (login, create_signal, place_order...) | +| `resource` | `String(100)` | Đối tượng bị tác động | +| `details` | `JSON` Nullable | Chi tiết bổ sung | +| `created_at` | `DateTime(tz)` server_default=now() | Thời điểm | + +**Indexes:** `ix_audit_logs_action_created_at` + +**Dependencies:** `app.database.Base` + +--- + +### 3.12 real_trade.py + +**Đường dẫn:** `app/models/real_trade.py` (95 dòng) +**Bảng:** `real_trades` + +#### Mục đích +Lưu các giao dịch thực tế đã đặt trên sàn (không phải paper trading). + +#### Class: `RealTrade(Base)` + +| Cột | Kiểu dữ liệu | Mô tả | +|-----|-------------|-------| +| `id` | `Integer` PK | Định danh | +| `user_id` | `UUID` FK → users.id | Người dùng | +| `exchange` | `String(20)` | Sàn giao dịch | +| `symbol` | `String(50)` | Cặp giao dịch | +| `side` | `String(10)` | buy/sell | +| `order_type` | `String(10)` DEFAULT "market" | market/limit | +| `amount` | `Numeric(20,8)` | Khối lượng | +| `price` | `Numeric(20,8)` Nullable | Giá limit (null với market order) | +| `filled_amount` | `Numeric(20,8)` DEFAULT 0 | Khối lượng đã khớp | +| `status` | `String(20)` DEFAULT "open" | open/filled/cancelled/rejected | +| `pnl` | `Numeric(20,8)` Nullable | Lợi nhuận thực tế | +| `pnl_percent` | `Numeric(10,4)` Nullable | % lợi nhuận | +| `order_id` | `String(100)` Nullable | ID lệnh trên sàn | +| `created_at` | `DateTime(tz)` | Thời điểm tạo | +| `closed_at` | `DateTime(tz)` Nullable | Thời điểm đóng | + +**Indexes:** `ix_real_trades_user_id`, `ix_real_trades_symbol`, `ix_real_trades_status`, `ix_real_trades_created_at` + +**Dependencies:** `app.database.Base` + +--- + +## 4. core/ — Module Lõi + +### 4.1 security.py + +**Đường dẫn:** `app/core/security.py` (351 dòng) + +#### Mục đích +Module bảo mật trung tâm: hash mật khẩu, JWT RS256 với key rotation, mã hóa AES-256-CBC cho API keys. + +#### Class/Function chính + +##### Password Hashing +| Function | Mô tả | +|----------|-------| +| `hash_password(password: str) -> str` | Hash mật khẩu bằng bcrypt (sync) | +| `verify_password(plain: str, hashed: str) -> bool` | Xác minh mật khẩu (sync) | +| `hash_password_async(password: str) -> str` | Hash mật khẩu (async, chạy trong thread pool) | +| `verify_password_async(plain: str, hashed: str) -> bool` | Xác minh mật khẩu (async, thread pool) | + +##### JWT RS256 Token Management (hỗ trợ multi-key rotation) +| Function | Mô tả | +|----------|-------| +| `create_access_token(data: dict, expires_delta?: timedelta) -> str` | Tạo access token ngắn hạn (RS256) với `kid` header | +| `create_refresh_token(data: dict) -> str` | Tạo refresh token dài hạn (RS256) với `jti`, `type: refresh` | +| `decode_token(token: str) -> dict` | Giải mã và xác minh JWT với TẤT CẢ public keys hợp lệ (hỗ trợ key rotation) | + +**Cơ chế Key Rotation:** +- Token được ký bằng private key hiện tại, mang `kid` (SHA-256 fingerprint của public key) +- Token được verify với tất cả public keys trong `JWT_PUBLIC_KEYS_DIR` +- Khi rotate: thêm key mới, token cũ vẫn hợp lệ đến khi hết hạn +- Tối ưu: thử key khớp `kid` trước, fallback quét toàn bộ + +**Constants:** `ALGORITHM = "RS256"` + +##### AES-256-CBC Encryption +| Function | Mô tả | +|----------|-------| +| `generate_encryption_key() -> str` | Sinh khóa mã hóa 256-bit hex ngẫu nhiên | +| `encrypt_api_key(api_key: str, key_hex?: str) -> Tuple[str, str]` | Mã hóa API key → (ciphertext_hex, iv_hex) | +| `decrypt_api_key(ciphertext_hex: str, iv_hex: str, key_hex?: str) -> str` | Giải mã API key → plaintext | + +##### Token Utilities +| Function | Mô tả | +|----------|-------| +| `generate_token_hash(token: str) -> str` | SHA-256 hash của token (lưu refresh token) | +| `generate_jti() -> str` | UUID4 hex cho JWT token ID | + +##### Internal Helpers +- `_load_private_key()` — Đọc private key từ file +- `_compute_kid(public_key_pem)` — Tính Key ID từ public key +- `_cached_public_key()` — Cache public key hiện tại (LRU) +- `_load_all_public_keys()` — Load tất cả public keys từ thư mục +- `_resolve_key(key_hex?)` — Lấy AES key từ tham số hoặc settings + +**Dependencies:** +- `hashlib`, `uuid`, `os`, `datetime`, `functools.lru_cache` +- `fastapi.HTTPException` +- `jose.jwt`, `jose.exceptions.ExpiredSignatureError` +- `passlib.context.CryptContext` (bcrypt) +- `cryptography.hazmat.primitives.ciphers` (AES-CBC) +- `app.config.settings` + +--- + +### 4.2 deps.py + +**Đường dẫn:** `app/core/deps.py` (104 dòng) + +#### Mục đích +FastAPI dependency injection: cung cấp DB session, xác thực và phân quyền người dùng theo role. + +#### Function chính + +| Function | Mô tả | Return | +|----------|-------|--------| +| `get_db_session()` | Cung cấp AsyncSession qua FastAPI Depends | `AsyncGenerator[AsyncSession]` | +| `get_current_user(request, db)` | Trích xuất Bearer token từ Authorization header, decode, lấy User từ DB. Raise `InvalidCredentialsException`/`InvalidTokenException` nếu lỗi | `User` | +| `get_current_active_user(current_user)` | Kiểm tra `is_active`, kế thừa `get_current_user` | `User` | +| `get_current_trader_user(current_user)` | Yêu cầu role `admin` hoặc `trader`, raise `ForbiddenException` | `User` | +| `get_current_viewer_user(current_user)` | Mọi role đều được phép (view-only) | `User` | +| `get_current_admin_user(current_user)` | Yêu cầu `is_admin=True` VÀ `role="admin"` (P1-22: kiểm tra cả hai) | `User` | + +**Flow xác thực:** +1. `get_db_session()` → cấp DB session +2. `get_current_user()` → lấy token từ header → decode → query user +3. Các dependency phân quyền kế thừa `get_current_user` hoặc `get_current_active_user` + +**Dependencies:** +- `fastapi.Depends`, `fastapi.Header`, `fastapi.Request` +- `sqlalchemy.ext.asyncio.AsyncSession` +- `app.core.exceptions` (InvalidCredentialsException, InvalidTokenException, ForbiddenException) +- `app.core.security.decode_token` +- `app.database.get_db` +- `app.models.User` + +--- + +### 4.3 middleware.py + +**Đường dẫn:** `app/core/middleware.py` (73 dòng) + +#### Mục đích +ASGI middleware ghi log mọi HTTP request với method, path, status code, và duration (ms) sử dụng structlog. + +#### Class: `RequestLoggingMiddleware` + +- Implement ASGI middleware pattern (`__call__(scope, receive, send)`) +- Wrap `send` để capture HTTP status code +- Log levels: + - `logger.error` nếu có unhandled exception (status 500) + - `logger.warning` nếu status >= 400 + - `logger.info` cho request thành công (< 400) +- Chỉ xử lý HTTP scope (`scope["type"] == "http"`), bỏ qua WebSocket + +#### Function: `register_middleware(app: ASGIApp)` +- Helper để đăng ký middleware vào FastAPI app: `app.add_middleware(RequestLoggingMiddleware)` + +**Dependencies:** +- `structlog` +- `starlette.requests.Request`, `starlette.responses.Response` +- `starlette.types.ASGIApp`, `Receive`, `Scope`, `Send` + +--- + +### 4.4 exceptions.py + +**Đường dẫn:** `app/core/exceptions.py` (74 dòng) + +#### Mục đích +Định nghĩa hệ thống phân cấp exception cho ứng dụng, mỗi exception mang `status_code`, `detail`, và `code` để trả về JSON response chuẩn. + +#### Class Hierarchy + +``` +Exception + └── AppException (base, status=500, code="internal_error") + ├── NotFoundException (404, "not_found") + ├── AuthException (401, "auth_error") + │ ├── InvalidCredentialsException (401, "invalid_credentials") + │ ├── TokenExpiredException (401, "token_expired") + │ └── InvalidTokenException (401, "invalid_token") + ├── ForbiddenException (403, "forbidden") + ├── ValidationException (422, "validation_error") + ├── RateLimitException (429, "rate_limit") + └── ConflictException (409, "conflict") +``` + +#### Class `AppException(Exception)` +- Base class, constructor cho phép override `status_code`, `detail`, `code` +- Gọi `super().__init__(self.detail)` + +**Dependencies:** Không có (pure Python) + +--- + +## 5. schemas/ — Pydantic Schemas + +### 5.1 auth.py + +**Đường dẫn:** `app/schemas/auth.py` (69 dòng) + +#### Mục đích +Pydantic models cho xác thực: đăng ký, đăng nhập, token response, refresh, đổi mật khẩu. + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `RegisterRequest` | Body đăng ký | username, email, password (min 8 ký tự) | +| `LoginRequest` | Body đăng nhập | username, password | +| `TokenResponse` | Response chứa token | access_token, refresh_token, token_type="bearer" | +| `RefreshRequest` | Body refresh token | refresh_token | +| `UserResponse` | Thông tin user public | id(UUID), username, email, display_name, is_active, is_admin, role, preferences, created_at | +| `UserSessionResponse` | Thông tin phiên | id, created_at, user_agent, ip_address, is_current | +| `LogoutRequest` | Body logout | refresh_token | +| `ChangePasswordRequest` | Body đổi mật khẩu | old_password, new_password (min 8 ký tự) | + +**Dependencies:** `pydantic.BaseModel`, `pydantic.field_validator` + +--- + +### 5.2 user.py + +**Đường dẫn:** `app/schemas/user.py` (63 dòng) + +#### Mục đích +Pydantic models cho quản lý user (cập nhật profile, admin tạo user, reset password). + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `UserUpdateRequest` | User tự cập nhật profile | display_name, email, preferences (đều optional) | +| `AdminUserUpdateRequest` | Admin cập nhật user | is_active, is_admin, role, email, display_name (đều optional) | +| `AdminCreateUserRequest` | Admin tạo user mới | username, email (validated), password (min 8, có số, có ký tự đặc biệt), display_name, is_admin, role | +| `AdminResetPasswordRequest` | Admin reset password | new_password (validated mạnh) | + +**Validation functions riêng:** +- `_validate_email(email)` — Regex email chuẩn +- `_validate_strong_password(password)` — Tối thiểu 8 ký tự, ít nhất 1 số, 1 ký tự đặc biệt + +**Dependencies:** `pydantic.BaseModel`, `pydantic.field_validator`, `re` + +--- + +### 5.3 symbol.py + +**Đường dẫn:** `app/schemas/symbol.py` (37 dòng) + +#### Mục đích +Pydantic models cho symbols và watchlist. + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `SymbolResponse` | Thông tin symbol | id(int), exchange_id, symbol, base, quote, is_active | +| `SymbolSearchResponse` | Kết quả tìm kiếm | symbols: list[SymbolResponse] | +| `WatchlistResponse` | Item trong watchlist | id(UUID), symbol_id, symbol, exchange, label, sort_order | +| `WatchlistCreateRequest` | Thêm vào watchlist | symbol_id, label(optional), sort_order(optional) | +| `WatchlistUpdateRequest` | Cập nhật watchlist | label(optional), sort_order(optional) | + +**Dependencies:** `pydantic.BaseModel` + +--- + +### 5.4 exchange.py + +**Đường dẫn:** `app/schemas/exchange.py` (24 dòng) + +#### Mục đích +Pydantic models cho quản lý sàn giao dịch. + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `ExchangeResponse` | Thông tin sàn | id, name, display_name, is_active | +| `ExchangeCreateRequest` | Tạo sàn mới | name, display_name, base_url, ws_url | +| `ExchangeUpdateRequest` | Cập nhật sàn | display_name, base_url, ws_url, is_active (đều optional) | + +**Dependencies:** `pydantic.BaseModel` + +--- + +### 5.5 candle.py + +**Đường dẫn:** `app/schemas/candle.py` (29 dòng) + +#### Mục đích +Pydantic models cho dữ liệu nến (OHLCV) và indicators. + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `CandleResponse` | Một cây nến | symbol_id, timeframe, timestamp, open, high, low, close, volume (float) | +| `CandleListResponse` | Danh sách nến có phân trang | candles, cursor(optional), has_more | +| `IndicatorResponse` | Kết quả indicator | symbol_id, timeframe, timestamp, indicators(dict) | + +**Dependencies:** `pydantic.BaseModel` + +--- + +### 5.6 signal.py + +**Đường dẫn:** `app/schemas/signal.py` (89 dòng) + +#### Mục đích +Pydantic models cho tín hiệu giao dịch, giao dịch giả lập, và báo cáo hiệu suất. + +#### Classes + +| Class | Mục đích | Fields chính | Tính năng đặc biệt | +|-------|----------|-------------|-------------------| +| `SignalResponse` | Một tín hiệu | id, symbol, exchange, timeframe, signal_type, strength, price, timestamp, indicators_snapshot, confidence, status, note, created_at | `model_validator` tự động extract `confidence` từ `indicators_snapshot` JSON | +| `TradeResponse` | Một giao dịch giả lập | id, signal_id, symbol, exchange, timeframe, direction, entry/exit price/time/reason, quantity, pnl, pnl_percent, status | `from_attributes=True` | +| `SignalListResponse` | Danh sách tín hiệu | signals, total | | +| `TradeListResponse` | Danh sách giao dịch | trades, total, total_pnl, win_rate | | +| `ReviewResponse` | Báo cáo hiệu suất (weekly/monthly) | period, start_date, end_date, total_signals, total_trades, wins, losses, win_rate, total_pnl, best_trade, worst_trade, signals_by_type, symbol_performance | | + +**Dependencies:** `pydantic.BaseModel`, `pydantic.model_validator`, `json` + +--- + +### 5.7 strategy.py + +**Đường dẫn:** `app/schemas/strategy.py` (66 dòng) + +#### Mục đích +Pydantic models cho cấu hình chiến lược giao dịch (13 voting algorithms). + +#### Constants + +**`STRATEGY_NAMES`**: 13 thuật toán: +1. `double_bb_rsi` — Double BB + RSI +2. `macd_crossover` — MACD Crossover +3. `supertrend` — SuperTrend +4. `volume_breakout` — Volume Breakout +5. `ichimoku_cloud` — Ichimoku Cloud +6. `divergence` — Divergence +7. `smc` — Market Structure (SMC) +8. `mtf` — Multi-Timeframe +9. `obv` — OBV Crossover +10. `stoch_rsi` — Stochastic RSI +11. `mfi` — Money Flow Index +12. `fvg` — Fair Value Gap +13. `candlestick` — Candlestick Patterns + +**`STRATEGY_DISPLAY`**: dict ánh xạ name → display name + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `StrategyEntry` | Một strategy với trạng thái | name, display_name, enabled | +| `StrategyListResponse` | Response GET strategies | strategies, thresholds(dict) | +| `StrategyConfigRequest` | Body PUT cập nhật | enabled_strategies(optional), thresholds(optional) | + +**Dependencies:** `pydantic.BaseModel` + +--- + +### 5.8 real_trade.py + +**Đường dẫn:** `app/schemas/real_trade.py` (78 dòng) + +#### Mục đích +Pydantic models cho giao dịch thực tế và thống kê win-rate. + +#### Classes + +| Class | Mục đích | Fields chính | Tính năng đặc biệt | +|-------|----------|-------------|-------------------| +| `RealTradeResponse` | Một giao dịch thực | id, user_id(str), exchange, symbol, side, order_type, amount, price, filled_amount, status, pnl, pnl_percent, order_id, created_at, closed_at | `field_validator` chuyển UUID → str cho user_id | +| `RealTradeListResponse` | Danh sách giao dịch | trades, total, total_pnl, win_rate | | +| `RealTradeCreateRequest` | Body tạo giao dịch | exchange, symbol, side, order_type, amount, price, filled_amount, status, order_id | | +| `WinRatePeriod` | Win-rate một kỳ | trades, wins, win_rate | | +| `WinRateResponse` | Win-rate theo ngày/tuần/tháng | daily, weekly, monthly: WinRatePeriod | | + +**Dependencies:** `pydantic.BaseModel`, `pydantic.field_validator`, `uuid.UUID` + +--- + +### 5.9 credential.py + +**Đường dẫn:** `app/schemas/credential.py` (34 dòng) + +#### Mục đích +Pydantic models cho quản lý API credentials. + +#### Classes + +| Class | Mục đích | Fields chính | +|-------|----------|-------------| +| `CredentialResponse` | Thông tin credential | id(UUID), exchange_id, exchange_name, api_key, is_testnet, is_active | +| `CredentialCreateRequest` | Body thêm credential | exchange_id, api_key, api_secret, passphrase(optional), is_testnet | +| `CredentialUpdateRequest` | Body cập nhật credential | api_key, api_secret, passphrase, is_active (đều optional) | + +**Dependencies:** `pydantic.BaseModel`, `pydantic.field_validator` + +--- + +### 5.10 ws_message.py + +**Đường dẫn:** `app/schemas/ws_message.py` (31 dòng) + +#### Mục đích +Pydantic models cho WebSocket messages: subscription, candle update, ticker update, connection status. + +#### Classes + +| Class | Mục đích | Fields | +|-------|----------|--------| +| `WSSubscription` | Client subscribe/unsubscribe | symbol, timeframe, exchange, action(Literal["subscribe","unsubscribe"]) | +| `WSCandleUpdate` | Push cập nhật nến | type="candle", data: CandleResponse | +| `WSTickerUpdate` | Push cập nhật giá | type="ticker", symbol, price, change_24h, volume | +| `WSConnectionStatus` | Trạng thái kết nối WS | type="connection", status("connected"/"reconnecting"/"disconnected") | + +**Dependencies:** `pydantic.BaseModel`, `app.schemas.candle.CandleResponse` + +--- + +### 5.11 health.py + +**Đường dẫn:** `app/schemas/health.py` (31 dòng) + +#### Mục đích +Pydantic models cho health check endpoint. + +#### Classes + +| Class | Mục đích | Fields | +|-------|----------|--------| +| `DbHealth` | Trạng thái database | connected, latency_ms, pool_size | +| `ExchangeHealth` | Trạng thái kết nối sàn | exchange, connected, last_sync | +| `HealthResponse` | Health check cơ bản | status, version, uptime, db_connected | +| `DetailedHealthResponse` | Health check chi tiết | status, version, uptime, db(DbHealth), exchange_connections(list[ExchangeHealth]) | + +**Dependencies:** `pydantic.BaseModel` + +--- + +## Tổng Kết + +| Module | Số file | Số class/function chính | Tổng dòng code | +|--------|---------|------------------------|---------------| +| **config.py** | 1 | 1 class (Settings) + 1 singleton | ~43 | +| **database.py** | 1 | 1 class (Base) + 3 globals + 1 function | ~65 | +| **models/** | 12 (11 model + 1 init) | 11 ORM classes | ~780 | +| **core/security.py** | 1 | 15 functions | ~351 | +| **core/deps.py** | 1 | 6 dependency functions | ~104 | +| **core/middleware.py** | 1 | 1 class + 1 helper function | ~73 | +| **core/exceptions.py** | 1 | 1 base + 9 exception classes | ~74 | +| **schemas/** | 12 (11 schema + 1 init) | ~40 Pydantic classes + constants | ~470 | +| **Tổng cộng** | ~30 file | ~90+ classes/functions | ~1,960 dòng | diff --git a/docs/frontend-doc.md b/docs/frontend-doc.md new file mode 100644 index 0000000..b602213 --- /dev/null +++ b/docs/frontend-doc.md @@ -0,0 +1,638 @@ +# 📋 Tài liệu Frontend Trading Portal + +> **Dự án:** React + TypeScript + Redux Toolkit + Vite +> **Đường dẫn:** `/opt/data/trading-portal/frontend/src/` +> **Ngày tạo:** 03/07/2026 + +--- + +## I. CẤU TRÚC THƯ MỤC + +``` +src/ +├── main.tsx # Entry point +├── App.tsx # Root component + routing +├── index.css # Global styles +├── App.css +├── vite-env.d.ts +├── assets/ # Static assets (react.svg, hero.png, vite.svg) +├── app/ +│ ├── store.ts # Redux store +│ └── hooks.ts # Typed hooks (useAppDispatch, useAppSelector) +├── types/ +│ └── trading.ts # TypeScript interfaces +├── components/ +│ ├── ErrorBoundary.tsx # React error boundary +│ └── Skeleton.tsx # Loading skeleton components +├── translations/ +│ ├── index.tsx # TProvider + useT context +│ ├── en.ts # English translations +│ └── vi.ts # Vietnamese translations +├── features/ +│ ├── auth/ +│ │ ├── authSlice.ts # Redux slice for auth +│ │ ├── LoginPage.tsx # Login page +│ │ └── RegisterPage.tsx # Register page +│ ├── dashboard/ +│ │ ├── DashboardPage.tsx # Main dashboard +│ │ ├── ChartContainer.tsx # Chart with lightweight-charts +│ │ ├── ChartToolbar.tsx # Symbol/exchange/timeframe selector +│ │ ├── OrderPanel.tsx # Buy/sell order panel +│ │ ├── SignalPanel.tsx # Signals & trades panel +│ │ └── WatchlistPanel.tsx # Watchlist management +│ ├── profile/ +│ │ └── ProfilePage.tsx # User profile (6 tabs) +│ ├── backtest/ +│ │ └── BacktestPage.tsx # Backtest runner page +│ ├── alerts/ +│ │ └── AlertsPage.tsx # Smart alerts CRUD +│ ├── analytics/ +│ │ └── AnalyticsPage.tsx # Performance analytics (demo data) +│ ├── admin/ +│ │ └── AdminPage.tsx # Admin panel (users/exchanges/health) +│ └── api/ +│ ├── apiService.ts # Core HTTP client + auth endpoints +│ ├── alertApi.ts # Alert CRUD API +│ ├── signalApi.ts # Signal & trade API +│ ├── watchlistApi.ts # Watchlist API +│ ├── realTradeApi.ts # Real trade API +│ └── websocketService.ts # WebSocket client (singleton) +└── pages/ + └── AuditLogPage.tsx # Audit log (admin only) +``` + +--- + +## II. PAGES (TRANG) + +### 1. LoginPage (`/login`) +- **File:** `features/auth/LoginPage.tsx` +- **Component chính:** `LoginPage` (default export) +- **Route:** `/login` +- **Props:** Không (tự quản lý state) +- **State cục bộ:** + - `username: string` - Tên đăng nhập + - `password: string` - Mật khẩu + - `error: string` - Thông báo lỗi + - `isSubmitting: boolean` - Trạng thái đang submit +- **Redux:** dispatch `loginThunk({username, password})` +- **API calls:** + - `POST /api/v1/auth/login` – Đăng nhập (qua `apiService.login()`) + - `GET /api/v1/auth/me` – Lấy thông tin user sau login (qua `apiService.getMe()`) +- **i18n:** Sử dụng `useT()` hook, bản dịch `login.*` +- **Ghi chú:** Redirect về `/dashboard` sau login thành công. Nút Register bị tạm ẩn. + +### 2. RegisterPage (`/register` - ĐÃ ẨN) +- **File:** `features/auth/RegisterPage.tsx` +- **Component chính:** `RegisterPage` (default export) +- **Route:** `/register` (bị comment trong App.tsx) +- **Props:** Không +- **State cục bộ:** `username`, `email`, `password`, `confirmPassword`, `error` +- **Redux:** dispatch `registerThunk({username, email, password})` +- **API calls:** + - `POST /api/v1/auth/register` – Đăng ký + - `POST /api/v1/auth/login` – Auto-login sau register + - `GET /api/v1/auth/me` – Lấy user info +- **Validation:** Kiểm tra password khớp với confirmPassword + +### 3. DashboardPage (`/dashboard`) +- **File:** `features/dashboard/DashboardPage.tsx` +- **Component chính:** `DashboardPage` (default export) +- **Route:** `/dashboard` (trang mặc định sau login) +- **Props:** Không +- **State cục bộ:** + - `symbol: string` – Cặp giao dịch (mặc định BTC/USDT) + - `exchange: string` – Sàn (từ user preferences, mặc định mexc) + - `timeframe: string` – Khung thời gian (từ user preferences, mặc định 1h) + - `exchanges: Exchange[]` – Danh sách sàn + - `lastPrice: number | null` – Giá cuối cùng + - `balance: BalanceData[] | null` – Số dư từ API + - `isMobile: boolean` – Responsive + - `showSidePanel: boolean` – Hiển thị side panel mobile + - `showNavDropdown: boolean` – Dropdown nav mobile + - `initializing: boolean` – Trạng thái khởi tạo +- **Redux:** + - Selector: `auth.user`, `auth.isAuthenticated`, `auth.isLoading` + - Dispatch: `fetchCurrentUser()`, `logoutThunk()` +- **API calls:** + - `GET /api/v1/exchanges` – Lấy danh sách sàn (qua `apiFetch`) + - `GET /api/v1/orders/balance?exchange_name=...` – Lấy số dư (qua `apiFetch`) +- **Sub-components:** + - `ChartToolbar` – Thanh công cụ chọn sàn/symbol/timeframe + - `ChartContainer` – Biểu đồ nến + indicator + - `SignalPanel` – Panel tín hiệu + trades + - `WatchlistPanel` – Danh sách theo dõi + - `OrderPanel` – Đặt lệnh mua/bán +- **Responsive:** Grid layout `1fr 340px` trên desktop, 1 cột trên mobile (< 768px). Side panel overlay trên mobile. +- **Skeleton:** Hiển thị `DashboardSkeleton` khi đang load +- **Auth guard:** Redirect về `/login` nếu chưa xác thực +- **Trạng thái đặc biệt:** + - Role `viewer` – Ẩn OrderPanel, hiện "View-only mode" + - Không có symbol – Hiện placeholder "Select a symbol from watchlist" + +### 4. ProfilePage (`/profile`) +- **File:** `features/profile/ProfilePage.tsx` (716 dòng) +- **Component chính:** `ProfilePage` (default export) +- **Route:** `/profile` +- **Props:** Không +- **6 tabs nội bộ:** `info`, `keys`, `sessions`, `settings`, `history`, `backtest` +- **Redux:** Selector `auth.user` + +#### 4a. InfoTab +- **State:** `profile`, `loading`, `editing`, `msg`, `form` (display_name, email), `pwModal`, `pwForm` +- **API calls:** + - `GET /api/v1/auth/me` – Lấy profile + - `PUT /api/v1/auth/me` – Cập nhật profile + - `POST /api/v1/auth/change-password` – Đổi mật khẩu +- **Tính năng:** Xem/sửa display name, email, role; modal đổi mật khẩu + +#### 4b. KeysTab +- **State:** `keys`, `loading`, `showAdd`, `form` (exchange_name, api_key, api_secret, passphrase), `msg`, `testResults`, `expandedId`, `keyDetails` +- **API calls:** + - `GET /api/v1/credentials` – Lấy danh sách API keys + - `POST /api/v1/credentials` – Thêm key mới + - `DELETE /api/v1/credentials/:id` – Xoá key + - `POST /api/v1/credentials/:id/test` – Test key + - `GET /api/v1/credentials/:id/balance` – Xem balance + - `GET /api/v1/credentials/:id/orders` – Xem open orders +- **Tính năng:** CRUD API keys, test kết nối, xem balance + open orders khi expand + +#### 4c. SessionsTab +- **API calls:** + - `GET /api/v1/auth/sessions` – Lấy active sessions + - `DELETE /api/v1/auth/sessions/:hash` – Thu hồi session +- **Tính năng:** Xem các phiên đăng nhập, thiết bị, trình duyệt, IP; thu hồi session + +#### 4d. SettingsTab +- **State:** 16 state variables (prefExchange, prefTimeframe, prefTheme, prefUpColor, prefDownColor, prefNotifSignal, prefNotifTrade, prefLanguage, prefTradeSize, prefAutoTrade, prefAutoTradeTokens, tokenSearch, allSymbols, strategyList, enabledStrategies) +- **API calls:** + - `GET /api/v1/auth/me` – Load preferences + - `PUT /api/v1/auth/me` – Save preferences + - `GET /watchlist/all-symbols` – Load available tokens + - `GET /api/v1/strategies` – Load strategy list + - `PUT /api/v1/strategies` – Save enabled strategies +- **Tính năng:** Cấu hình exchange/timeframe mặc định, theme (dark/light), màu chart, ngôn ngữ, thông báo, trade size, auto-trade, toggle strategies, chọn tokens auto-trade + +#### 4e. HistoryTab +- **Sub-tabs:** `signals` và `real` +- **API calls:** + - `GET /api/v1/signals/trades?limit=100` – Signal trades + - `GET /api/v1/real-trades?limit=100` – Real trades + - `GET /api/v1/real-trades/win-rate` – Win rate thống kê +- **Tính năng:** Bảng signal trades + real trades, win rate cards (daily/weekly/monthly) + +#### 4f. BacktestTab +- **API calls:** + - `GET /api/v1/backtest/symbols?exchange=...` – Load symbols + - `GET /api/v1/backtest/run?...` – Run backtest + - `POST /api/v1/backtest/save?...` – Lưu kết quả +- **Tính năng:** Chạy backtest với các tham số, hiển thị kết quả (trades, wins, losses, win rate, PnL, profit factor), lưu vào history + +### 5. BacktestPage (`/backtest`) +- **File:** `features/backtest/BacktestPage.tsx` (323 dòng) +- **Component chính:** `BacktestPage` (default export) +- **Route:** `/backtest` +- **Props:** Không +- **State:** `exchange`, `symbol`, `symbols`, `timeframe`, `days`, `tradeSize`, `result`, `loading`, `error` +- **API calls:** + - `GET /api/v1/backtest/symbols?exchange=...` – Lấy danh sách symbols + - `GET /api/v1/backtest/run?symbol=...&exchange=...&timeframe=...&days=...&trade_size=...` – Chạy backtest +- **UI:** Summary cards (Trades, Win Rate, Total PnL, Profit Factor, Wins/Losses, Candles), Signal Breakdown, Best/Worst Trades, Recent Signals table, Recent Trades table +- **i18n:** `useT()` – key `Backtest` +- **Sub-component nội bộ:** `SummaryCard` (label, value, color) + +### 6. AlertsPage (`/alerts`) +- **File:** `features/alerts/AlertsPage.tsx` (507 dòng) +- **Component chính:** `AlertsPage` (default export) +- **Route:** `/alerts` +- **Props:** Không +- **State:** `alerts`, `loading`, `error`, `initializing`, `showModal`, `editTarget`, `formName`, `formPlatform`, `formConditions`, `formError`, `saving` +- **Redux:** Selector `auth.user`, `auth.isAuthenticated`; dispatch `fetchCurrentUser()`, `logoutThunk()` +- **API calls (qua alertApi):** + - `GET /api/v1/alerts` – Lấy danh sách alerts + - `POST /api/v1/alerts` – Tạo alert mới + - `PUT /api/v1/alerts/:id` – Cập nhật alert + - `DELETE /api/v1/alerts/:id` – Xoá alert +- **Tính năng:** CRUD multi-condition alerts. Mỗi alert có name, platform (telegram/discord/both), conditions (indicator + operator + value + timeframe). Indicator options: RSI, MACD, BB Width, Volume, Price, SMA, EMA, Momentum. Operator options: >, <, >=, <=, ==, cross_above, cross_below. Volume có thêm type (absolute/avg_multiplier). SMA/EMA có thêm period. +- **Auth guard:** Chỉ hiển thị khi đã xác thực + +### 7. AnalyticsPage (`/analytics`) +- **File:** `features/analytics/AnalyticsPage.tsx` (461 dòng) +- **Component chính:** `AnalyticsPage` (default export) +- **Route:** `/analytics` +- **Props:** Không +- **State:** `dataMode` (signal/real), `isMobile`, `initializing` +- **Redux:** Selector `auth.user`, `auth.isAuthenticated`, `auth.isLoading`; dispatch `fetchCurrentUser()`, `logoutThunk()` +- **API calls:** Không (dùng dữ liệu demo được generate) +- **Dữ liệu demo:** `generateDemoData(isSignal)` tạo 30 ngày dữ liệu PnL ngẫu nhiên, equity curve, stats +- **Sub-components:** `BarChart` (SVG bar chart), `LineChart` (SVG equity curve), `StatCard` +- **UI:** Stats grid (Total P&L, Win Rate, Best/Worst Trade, Profit Factor, Max Drawdown), Daily PnL bar chart, Equity curve line chart, Trade Summary table +- **Toggle:** Nút Signal / Real để chuyển giữa 2 chế độ dữ liệu demo + +### 8. AdminPage (`/admin`) +- **File:** `features/admin/AdminPage.tsx` (822 dòng) +- **Component chính:** `AdminPage` (default export) +- **Route:** `/admin` +- **Props:** Không +- **3 tabs:** `users`, `exchanges`, `health` +- **Redux:** Selector `auth.user`, `auth.isAuthenticated`, `auth.isLoading`; dispatch `fetchCurrentUser()`, `logoutThunk()` +- **Auth guard:** Chỉ admin (`user.is_admin`) mới truy cập được + +#### 8a. Users Tab +- **State:** `users`, `usersLoading`, `usersError`, `userSearch`, `modal`, `formData`, `formError` +- **API calls:** + - `GET /api/v1/admin/users` – Danh sách users + - `PUT /api/v1/admin/users/:id` – Cập nhật user (active/admin/email/display_name/role) + - `POST /api/v1/admin/users` – Tạo user mới + - `POST /api/v1/admin/users/:id/reset-password` – Reset password + - `DELETE /api/v1/admin/users/:id` – Xoá user +- **Tính năng:** Bảng users với tìm kiếm, toggle active/admin, modal thêm/sửa/xoá/reset-password. Role permission legend (Admin, Trader, Viewer). + +#### 8b. Exchanges Tab +- **API calls:** + - `GET /api/v1/admin/exchanges` – Danh sách exchanges + - `POST /api/v1/admin/exchanges` – Tạo exchange + - `PUT /api/v1/admin/exchanges/:id` – Cập nhật exchange +- **Tính năng:** Bảng exchanges, toggle active/deactivate, form thêm exchange (name, display_name, base_url, ws_url) + +#### 8c. Health Tab +- **API calls:** + - `GET /api/v1/admin/health/detailed` – System health +- **Tính năng:** Status (Healthy/Degraded), Uptime, Version; Database info (connected, latency, pool size); Exchange connections table + +### 9. AuditLogPage (`/audit`) +- **File:** `pages/AuditLogPage.tsx` (313 dòng) +- **Component chính:** `AuditLogPage` (default export) +- **Route:** `/audit` +- **Props:** Không +- **State:** `logs`, `total`, `offset`, `loading`, `error`, `actionFilter`, `initializing` +- **Redux:** Selector `auth.user`, `auth.isAuthenticated`, `auth.isLoading`; dispatch `fetchCurrentUser()`, `logoutThunk()` +- **API calls:** + - `GET /api/v1/audit?limit=...&offset=...&action=...` – Lấy audit logs (qua `apiFetch`) +- **Tính năng:** Bảng audit log phân trang (50/page), filter theo action type (trade_open/close, signal_generated, user_login, config_change, strategy_toggle, permission_change). Columns: Time, User, Action, Resource, Details. Màu sắc khác nhau cho từng action type. +- **Auth guard:** Chỉ admin truy cập được + +--- + +## III. COMPONENTS (THÀNH PHẦN) + +### 1. ErrorBoundary +- **File:** `components/ErrorBoundary.tsx` +- **Loại:** React Class Component +- **Props:** `children: ReactNode`, `fallback?: ReactNode` +- **State:** `hasError: boolean`, `error: Error | null` +- **Tính năng:** Bắt lỗi React render. Hiển thị fallback UI hoặc mặc định (thông báo lỗi + nút "Reload App"). Log error ra console. +- **Sử dụng:** Bọc `` trong `main.tsx` + +### 2. Skeleton / DashboardSkeleton +- **File:** `components/Skeleton.tsx` +- **Components:** + - `Skeleton` – Component loading skeleton đơn lẻ + - **Props:** `width`, `height`, `borderRadius`, `count`, `style` + - **Tính năng:** Hiệu ứng shimmer animation. Tự động inject CSS keyframes. + - `DashboardSkeleton` – Skeleton cho toàn dashboard + - **Props:** `lines` (default 5) + - **Tính năng:** Title skeleton + chart area skeleton + N dòng text skeleton +- **Sử dụng:** DashboardPage hiển thị `DashboardSkeleton` khi loading/initializing + +### 3. ChartContainer +- **File:** `features/dashboard/ChartContainer.tsx` (588 dòng) +- **Component chính:** `ChartContainer` (default export) +- **Props:** + ```ts + symbol: string // Cặp giao dịch (vd: BTC/USDT) + exchange: string // Tên sàn (vd: mexc) + timeframe: string // Khung thời gian (vd: 1h) + onPriceUpdate?: (price: number | null) => void // Callback cập nhật giá + ``` +- **State:** `loading`, `error` +- **Refs:** `chartContainerRef`, `chartRef`, `seriesRef` (chứa tất cả series: candle, volume, sma, ema, bbUpper2, bbLower2, bbUpper1, bbLower1, bbMiddle, stochK, stochD, obLine, osLine, markersPlugin) +- **Thư viện:** `lightweight-charts` (TradingView) +- **Chi tiết kỹ thuật:** + - Khởi tạo chart với theme dark (#0d1117) + - **Các series:** + - Candlestick (nến, màu xanh/đỏ) + - Histogram (volume, separate pane) + - Line: SMA 20 (vàng), EMA 12 (cam) + - Bollinger Bands 2σ (đỏ), 1σ (vàng), Middle (xanh dashed) + - Stoch RSI %K (tím), %D (cam) + Overbought 80 / Oversold 20 lines + - SMC Market Structure markers (swing highs/lows) + - **Scale margins:** Candlestick 55%, Stoch RSI 25%, Volume 20% + - **ResizeObserver** để responsive chart + - **API calls (fetch trực tiếp):** + - `GET /api/v1/symbols/candles?symbol=...&exchange=...&timeframe=...&limit=200` – Lấy 200 nến + - `GET /api/v1/symbols/indicators?symbol=...&exchange=...&timeframe=...` – Lấy indicators + - **WebSocket:** Kết nối `wsService`, subscribe candle real-time, update chart khi có data mới. Cleanup: unsubscribe khi unmount hoặc đổi symbol. +- **Kiến trúc:** 3 useEffect: + 1. Khởi tạo chart một lần + 2. Fetch dữ liệu khi symbol/exchange/timeframe thay đổi + 3. WebSocket subscription + +### 4. ChartToolbar +- **File:** `features/dashboard/ChartToolbar.tsx` (227 dòng) +- **Component chính:** `ChartToolbar` (default export) +- **Props:** + ```ts + exchanges: Exchange[] // Danh sách sàn + symbol: string // Symbol hiện tại + exchange: string // Exchange hiện tại + timeframe: string // Timeframe hiện tại + onSymbolChange: (s: string) => void + onExchangeChange: (e: string) => void + onTimeframeChange: (t: string) => void + ``` +- **State:** `symbols` (danh sách symbols từ API), `loadingSymbols`, `isMobile` +- **API calls (fetch trực tiếp):** + - `GET /api/v1/symbols?exchange=...&active_only=true` – Lấy symbols khi đổi exchange +- **UI:** Select exchange + select symbol + timeframe buttons (15m, 30m, 1h, 4h, 1d, 1w, 1M) +- **Responsive:** Layout column trên mobile, row trên desktop. Timeframe có scroll ngang. + +### 5. OrderPanel +- **File:** `features/dashboard/OrderPanel.tsx` (467 dòng) +- **Component chính:** `OrderPanel` (default export) +- **Props:** + ```ts + symbol: string + lastPrice: number | null + balance: {asset, free, used, total}[] | null + isConnected: boolean + ``` +- **State:** `currentTime`, `amount`, `orderLoading`, `orderError`, `orderSuccess`, `pendingOrder`, `priceDirection`, `isMobile` +- **Redux:** Selector `auth.user` (lấy `preferences.trade_size`) +- **API calls (qua apiFetch):** + - `POST /api/v1/orders/place` – Đặt lệnh market (body: {symbol, side, order_type: 'market', amount}) +- **Tính năng:** + - Hiển thị symbol, last price (với chỉ báo ▲/▼), đồng hồ real-time + - Hiển thị balance USDT + - Input amount, nút Buy/Sell + - **P1-21:** Confirmation dialog trước khi đặt lệnh thật + - Success/error banners + - Hỗ trợ responsive (mobile friendly) + - Disable khi không có API key (`isConnected = false`) +- **Helper:** `formatPrice()` – format giá theo độ lớn (8 decimals cho giá rất nhỏ, 2 decimals cho giá bình thường) + +### 6. SignalPanel +- **File:** `features/dashboard/SignalPanel.tsx` (322 dòng) +- **Component chính:** `SignalPanel` (default export) +- **Props:** `symbol: string` +- **State:** `activeTab` (signals/trades), `signals`, `trades`, `loading`, `totalPnl`, `winRate`, `isMobile` +- **API calls (qua signalApi):** + - `GET /api/v1/signals?symbol=...&limit=20` – Lấy signals + - `GET /api/v1/signals/trades?symbol=...&limit=50` – Lấy trades +- **Tính năng:** + - Header + refresh button + - PnL summary (Total P&L, Win Rate) + - 2 tabs: Signals (danh sách tín hiệu với icon, loại, giá) và Trades (danh sách giao dịch với direction, entry/exit, PnL, reason) + - Auto-refresh mỗi 60 giây +- **Signal config (SIGNAL_CONFIG):** STRONG_BUY (🟢), BUY (✅), STRONG_SELL (🔴), SELL (❌), CAUTION_LONG (⚠️), CAUTION_SHORT (⚠️), SQUEEZE_ALERT (🔥) + +### 7. WatchlistPanel +- **File:** `features/dashboard/WatchlistPanel.tsx` (363 dòng) +- **Component chính:** `WatchlistPanel` (default export) +- **Props:** + ```ts + currentSymbol: string + onSymbolChange: (symbol: string) => void + exchange: string + ``` +- **State:** `items`, `loading`, `signals`, `isMobile`, `showAdd`, `search`, `symbols`, `searching`, `error` +- **API calls (qua watchlistApi + signalApi):** + - `GET /api/v1/watchlist` – Lấy watchlist + - `POST /api/v1/watchlist` – Thêm symbol vào watchlist + - `DELETE /api/v1/watchlist/:id` – Xoá khỏi watchlist + - `GET /api/v1/watchlist/all-symbols?exchange=...&q=...` – Tìm kiếm symbols + - `GET /api/v1/signals?limit=50` – Lấy tín hiệu để hiển thị indicator trên từng item +- **Tính năng:** + - Danh sách symbols đang theo dõi, item active được highlight + - Mỗi item hiển thị symbol + signal indicator icon (nếu có) + - Nút xoá (✕) xuất hiện khi hover + - Panel "Add Symbol" với search input, hiển thị danh sách symbols có thể thêm (đã lọc những symbol đã có trong watchlist) + - Auto-refresh signals mỗi 60 giây + +--- + +## IV. REDUX SLICES + +### authSlice +- **File:** `features/auth/authSlice.ts` +- **Tên slice:** `auth` +- **Initial state (AuthState):** + ```ts + { user: null, accessToken: string|null, refreshToken: string|null, + isAuthenticated: boolean, isLoading: false, error: null } + ``` + - Khởi tạo từ localStorage (`access_token`, `refresh_token`) +- **Reducers (đồng bộ):** + - `setCredentials(state, action: PayloadAction<{user, accessToken, refreshToken}>)` – Set thông tin đăng nhập + - `clearCredentials(state)` – Xoá credentials, xoá localStorage + - `clearError(state)` – Xoá error message +- **Async Thunks:** + - `loginThunk({username, password})` – Đăng nhập → lưu token → fetch user → return {user, tokens} + - `registerThunk({username, email, password})` – Đăng ký → auto-login → return tokens + - `fetchCurrentUser()` – Fetch `/auth/me` → cập nhật user + - `logoutThunk()` – Gọi `/auth/logout` +- **ExtraReducers:** Xử lý pending/fulfilled/rejected cho login, register, fetchCurrentUser, logout + +### Store +- **File:** `app/store.ts` +- **Cấu hình:** `configureStore` với 1 reducer: `auth: authReducer` +- **Types export:** `RootState`, `AppDispatch` + +### Hooks +- **File:** `app/hooks.ts` +- `useAppDispatch` – `useDispatch` đã typed với `AppDispatch` +- `useAppSelector` – `useSelector` đã typed với `RootState` + +--- + +## V. API SERVICES + +### 1. apiService.ts (Core HTTP Client) +- **File:** `features/api/apiService.ts` (266 dòng) +- **BASE_URL:** `/api/v1` +- **Class:** `ApiServiceError` (message, status, data) +- **Core function:** `apiFetch(url, options)` – HTTP client với: + - Tự động gắn `Authorization: Bearer ***` header + - Tự động refresh token khi nhận 401 + - Token refresh: `POST /api/v1/auth/refresh` + - Xử lý lỗi, trả về JSON hoặc `undefined` (204) +- **Auth endpoints:** + - `login(username, password)` → `POST /auth/login` + - `register(username, email, password)` → `POST /auth/register` + - `getMe()` → `GET /auth/me` + - `logout()` → `POST /auth/logout` +- **Exchange/Symbol endpoints:** + - `getExchanges()` → `GET /exchanges` + - `getSymbols(exchange, activeOnly)` → `GET /symbols` + - `getCandles(symbol, exchange, timeframe, limit)` → `GET /symbols/:symbol/candles` + - `getIndicators(symbol, exchange, timeframe)` → `GET /symbols/:symbol/indicators` +- **Admin endpoints:** + - `adminGetUsers()` → `GET /admin/users` + - `adminUpdateUser(userId, data)` → `PUT /admin/users/:id` + - `adminCreateUser(data)` → `POST /admin/users` + - `adminDeleteUser(userId)` → `DELETE /admin/users/:id` + - `adminResetPassword(userId, newPassword)` → `POST /admin/users/:id/reset-password` + - `adminGetExchanges()` → `GET /admin/exchanges` + - `adminCreateExchange(data)` → `POST /admin/exchanges` + - `adminUpdateExchange(id, data)` → `PUT /admin/exchanges/:id` + - `adminGetHealth()` → `GET /admin/health/detailed` +- **Exports thêm:** `ApiServiceError`, `clearTokens`, `setTokens` + +### 2. alertApi.ts +- **File:** `features/api/alertApi.ts` (89 dòng) +- **Types:** `ConditionObject`, `AlertItem`, `AlertListResponse` +- **Functions:** + - `fetchAlerts()` → `GET /api/v1/alerts` + - `createAlert({name, conditions, notify_platform})` → `POST /api/v1/alerts` + - `updateAlert(id, {name?, conditions?, notify_platform?, is_active?})` → `PUT /api/v1/alerts/:id` + - `deleteAlert(id)` → `DELETE /api/v1/alerts/:id` +- **Headers:** Token từ localStorage + +### 3. signalApi.ts +- **File:** `features/api/signalApi.ts` (107 dòng) +- **Types:** `Signal`, `Trade`, `SignalListResponse`, `TradeListResponse`, `SIGNAL_CONFIG` +- **Functions:** + - `fetchSignals(symbol?, limit=50)` → `GET /api/v1/signals` + - `fetchTrades(symbol?, status?, limit=100)` → `GET /api/v1/signals/trades` +- **Utilities:** + - `SIGNAL_CONFIG` – Cấu hình hiển thị cho từng loại signal (icon, label, color, bg) + - `formatPrice(price)` – Format giá (>=1000: 2 decimals, >=1: 4 decimals, else: 6 decimals) + - `formatPnL(pnl)` – Format PnL với dấu +/- + - `formatPnLPercent(pct)` – Format PnL phần trăm + +### 4. watchlistApi.ts +- **File:** `features/api/watchlistApi.ts` (62 dòng) +- **Types:** `WatchlistItem`, `SymbolOption` +- **Functions:** + - `fetchWatchlist()` → `GET /api/v1/watchlist` + - `addToWatchlist(symbolId, label?)` → `POST /api/v1/watchlist` + - `removeFromWatchlist(id)` → `DELETE /api/v1/watchlist/:id` + - `fetchAllSymbols(exchange, q?)` → `GET /api/v1/watchlist/all-symbols` + +### 5. realTradeApi.ts +- **File:** `features/api/realTradeApi.ts` (71 dòng) +- **Types:** `RealTrade`, `RealTradeListResponse`, `WinRatePeriod`, `WinRateResponse` +- **Functions:** + - `fetchRealTrades(symbol?, status?, limit=100)` → `GET /api/v1/real-trades` + - `fetchWinRate()` → `GET /api/v1/real-trades/win-rate` + +--- + +## VI. WEBSOCKET + +- **File:** `features/api/websocketService.ts` (172 dòng) +- **Class:** `WebSocketService` (singleton export `wsService`) +- **Kết nối:** `connect(token)` → WebSocket tới `/ws/v1/candles?token=...` + - Dùng `wss:` nếu HTTPS, `ws:` nếu HTTP +- **Reconnect:** Exponential backoff (1s → 2s → 4s → ... → 30s max), tối đa 10 lần +- **Subscription:** + - `subscribe(symbol, timeframe, exchange)` – Gửi `{action: 'subscribe', ...}` qua WS + - `unsubscribe(symbol, timeframe, exchange)` – Gửi `{action: 'unsubscribe', ...}` + - Tự động re-subscribe khi reconnect +- **Message types:** + - `type: 'candle'` → Gọi `candleCallback(data)` với dữ liệu: symbol, exchange, timeframe, timestamp, open, high, low, close, volume + - `type: 'signal'` → Gọi `signalCallback(data)` với dữ liệu: id, symbol, signal_type, strength, price, timestamp +- **Callbacks:** + - `onCandle(callback)` – Đăng ký callback cho candle updates + - `onSignal(callback)` – Đăng ký callback cho signal updates +- **Properties:** `connected: boolean` +- **Disconnect:** `disconnect()` – Ngắt kết nối, xoá subscriptions, callbacks, timer + +--- + +## VII. TYPES + +- **File:** `types/trading.ts` (51 dòng) +- **Interfaces:** + - `Candle` – time (unix timestamp), open, high, low, close, volume + - `Symbol` – id, symbol, base, quote, exchange_id, active + - `Exchange` – id, name, active, connected?, display_name?, is_active? + - `Indicators` – `[key: string]: number[] | number[][]` + - `User` – id, username, email, is_active, is_admin?, role?, preferences?, created_at?, display_name? + - `AuthState` – user, accessToken, refreshToken, isAuthenticated, isLoading, error + +--- + +## VIII. TRANSLATIONS (i18n) + +- **File chính:** `translations/index.tsx` +- **Cấu trúc:** React Context (`TProvider` + `useT` hook) +- **Hỗ trợ:** `en`, `vi` (2 ngôn ngữ) +- **Cách dùng:** `const { t } = useT();` → `t('key.name', { var: value })` +- **Fallback:** Nếu key không tồn tại trong ngôn ngữ hiện tại → fallback về `en` → nếu vẫn không có → trả về chính key + +### Bản dịch tiếng Anh (`translations/en.ts` - 170 dòng) +Các nhóm key: +- `nav.*` – Navigation (tradingPortal, profile, panelToggle, logout) +- `login.*` – Login page (title, subtitle, username, password, submit, signing, error) +- `dash.*` – Dashboard (chart, watchlist, order, signals, quickBuy, quickSell, tradeSize) +- `chart.*` – ChartToolbar (exchange, symbol, search, loading, noData, error) +- `wl.*` – Watchlist (title, search, add, remove, loading, empty, error) +- `order.*` – OrderPanel (buy, sell, limit, market, stop, amount, price, total, place, placing, success, loginRequired, noKey, balance, available) +- `sig.*` – Signals (title, strongBuy, buy, strongSell, sell, cautionLong, cautionShort, squeeze, noSignal, loading, price, rsi, bb, signal, time) +- `profile.tabs.*` – Profile tabs +- `profile.info.*` – Profile info +- `profile.password.*` – Change password +- `profile.keys.*` – API keys +- `profile.prefs.*` – Settings/preferences +- `profile.sessions.*` – Sessions +- `profile.history.*` – History +- `sigHistory.*` – Signal history summary + +### Bản dịch tiếng Việt (`translations/vi.ts` - 170 dòng) +Cấu trúc tương tự `en.ts`, nội dung đã được dịch sang tiếng Việt. + +--- + +## IX. APP ROUTING + +- **File:** `App.tsx` +- **Router:** `BrowserRouter` từ `react-router-dom` +- **Language:** State `lang` (en/vi) lưu trong localStorage, `TProvider` bọc toàn bộ app +- **Routes:** + +| Path | Component | Ghi chú | +|------|-----------|---------| +| `/` | Redirect → `/dashboard` | | +| `/login` | `LoginPage` | | +| `/register` | `RegisterPage` | **Đã comment, tạm ẩn** | +| `/dashboard` | `DashboardPage` | Mặc định sau login | +| `/admin` | `AdminPage` | Chỉ admin | +| `/profile` | `ProfilePage` | 6 tabs (info, keys, sessions, settings, history, backtest) | +| `/backtest` | `BacktestPage` | Trang backtest độc lập | +| `/analytics` | `AnalyticsPage` | Demo data | +| `/alerts` | `AlertsPage` | Smart alerts | +| `/audit` | `AuditLogPage` | Chỉ admin | +| `*` | Redirect → `/login` | Catch-all | + +--- + +## X. TỔNG QUAN KIẾN TRÚC + +``` +main.tsx + └─ ErrorBoundary + └─ App + └─ Provider (Redux Store) + └─ TProvider (i18n Context) + └─ BrowserRouter + └─ Routes (9 routes) +``` + +### Data flow: +1. **Auth:** `authSlice` quản lý toàn bộ trạng thái xác thực. Token lưu trong localStorage. `apiFetch` tự động refresh token khi 401. +2. **API:** `apiService.ts` là HTTP client chính với auto-refresh. Các module API riêng (alertApi, signalApi, watchlistApi, realTradeApi) sử dụng fetch trực tiếp với token từ localStorage. +3. **WebSocket:** Singleton `wsService` kết nối tới `/ws/v1/candles`. ChartContainer subscribe/unsubscribe theo symbol. Hỗ trợ auto-reconnect với exponential backoff. +4. **State management:** Redux chỉ dùng cho auth. Các page/component tự quản lý state cục bộ với `useState` + `useEffect`. +5. **Styling:** Inline styles (CSS-in-JS) với theme dark (#0d1117 background, #c9d1d9 text, #30363d borders). Không dùng CSS modules hay styled-components. +6. **Responsive:** Tất cả các page đều có `isMobile` state (breakpoint 768px). Mobile có nav dropdown, panel overlay, layout xếp dọc. + +### Tổng số file code: 28 file TypeScript/TSX (~6,500 dòng code) +- 9 pages +- 7 components (2 global + 5 dashboard) +- 1 Redux slice +- 5 API services +- 1 WebSocket service +- 3 translation files +- 2 app config files (store, hooks) +- 1 types file