diff --git a/app/api/account.py b/app/api/account.py index 318e268..446076b 100644 --- a/app/api/account.py +++ b/app/api/account.py @@ -20,7 +20,7 @@ def get_user_api(user_id: str): raise HTTPException(status_code=404, detail="Không tìm thấy userId") @router.get("/account", response_model=AccountResponseSchema, tags=["Account"]) -def get_account_info(userId: str = Query(..., description="ID của user để lấy API key/secret")): +def get_account_info(userId: str = Query(..., description="User ID to get API key/secret")): try: api_key, api_secret = get_user_api(userId) client = get_bybit_client(api_key, api_secret) @@ -31,4 +31,4 @@ def get_account_info(userId: str = Query(..., description="ID của user để l except HTTPException as e: raise e except Exception as e: - raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}") + raise HTTPException(status_code=500, detail=f"System error: {e}") diff --git a/app/api/candles.py b/app/api/candles.py index bea9e94..0fc1028 100644 --- a/app/api/candles.py +++ b/app/api/candles.py @@ -45,17 +45,17 @@ def rsi(series, period=14): @router.get("/candles", response_model=CandleResponseSchema, response_model_exclude_none=True, tags=["Candle"]) def get_candles( - userId: str = Query(..., description="ID của user để lấy API key/secret"), - symbol: str = Query(..., description="Mã giao dịch, ví dụ: BTCUSDT"), - interval: str = Query("1", description="Khung thời gian nến, ví dụ: 1, 3, 5, 15, 30, 60, 240, D, W, M"), - limit: Optional[int] = Query(200, description="Số lượng nến trả về (tối đa 1000)"), - start: Optional[int] = Query(None, description="Timestamp bắt đầu (miliseconds)"), - end: Optional[int] = Query(None, description="Timestamp kết thúc (miliseconds)") + userId: str = Query(..., description="User ID to get API key/secret"), + symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"), + interval: str = Query("1", description="Candle interval, e.g. 1, 3, 5, 15, 30, 60, 240, D, W, M"), + limit: Optional[int] = Query(200, description="Number of candles to return (max 1000)"), + start: Optional[int] = Query(None, description="Start timestamp (milliseconds)"), + end: Optional[int] = Query(None, description="End timestamp (milliseconds)") ): api_key, api_secret = get_user_api(userId) client = get_bybit_client(api_key, api_secret) params = { - "category": "linear", # hoặc "spot" nếu lấy spot + "category": "linear", # or "spot" for spot "symbol": symbol, "interval": interval, "limit": limit @@ -70,7 +70,7 @@ def get_candles( candles = res.get('result', {}).get('list', []) if not candles: return {"data": [], "indicators": {}} - # Chuyển đổi sang DataFrame + # Convert to DataFrame df = pd.DataFrame(candles, columns=[ "timestamp", "open", "high", "low", "close", "volume", "turnover" ]) @@ -84,7 +84,7 @@ def get_candles( "turnover": np.float64 }) df = df.sort_values("timestamp", ascending=False) - # Tính chỉ báo + # Calculate indicators close = df["close"] macd_line, macd_signal, macd_hist = macd(close, fast=45, slow=90, signal=9) df["macd"] = macd_line @@ -95,7 +95,7 @@ def get_candles( df["ema100"] = ema(close, 100) df["ema200"] = ema(close, 200) df["rsi"] = rsi(close) - # Trả về kết quả + # Return result data = df.replace({np.nan: None}).to_dict(orient="records") return { "data": data, diff --git a/app/api/orders.py b/app/api/orders.py index 2e945c0..55a8d7e 100644 --- a/app/api/orders.py +++ b/app/api/orders.py @@ -21,14 +21,14 @@ def get_user_api(user_id: str): @router.get("/orders", tags=["Order"]) def get_orders( - userId: str = Query(..., description="ID của user để lấy API key/secret"), - symbol: str = Query(..., description="Mã giao dịch, ví dụ: BTCUSDT"), - category: str = Query("linear", description="Loại lệnh: linear (future) hoặc spot") + userId: str = Query(..., description="User ID to get API key/secret"), + symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"), + category: str = Query("linear", description="Order type: linear (future) or spot") ): api_key, api_secret = get_user_api(userId) client = get_bybit_client(api_key, api_secret) res = client.get_open_orders( - category=category, # "linear" cho future, "spot" cho spot + category=category, # "linear" for future, "spot" for spot symbol=symbol ) return res @@ -36,13 +36,13 @@ def get_orders( # Submit order @router.post("/orders", tags=["Order"]) def submit_order( - userId: str = Body(..., embed=True, description="ID của user để lấy API key/secret"), - symbol: str = Body(..., embed=True, description="Mã giao dịch, ví dụ: BTCUSDT"), - side: str = Body(..., embed=True, description="side: Buy hoặc Sell"), - orderType: str = Body(..., embed=True, description="Loại lệnh: Market hoặc Limit"), - qty: float = Body(..., embed=True, description="Số lượng"), - category: str = Body("linear", embed=True, description="Loại lệnh: linear (future) hoặc spot"), - price: Optional[float] = Body(None, embed=True, description="Giá (bắt buộc với Limit)") + userId: str = Body(..., embed=True, description="User ID to get API key/secret"), + symbol: str = Body(..., embed=True, description="Trading symbol, e.g. BTCUSDT"), + side: str = Body(..., embed=True, description="Order side: Buy or Sell"), + orderType: str = Body(..., embed=True, description="Order type: Market or Limit"), + qty: float = Body(..., embed=True, description="Order quantity"), + category: str = Body("linear", embed=True, description="Order type: linear (future) or spot"), + price: Optional[float] = Body(None, embed=True, description="Order price (required for Limit)") ): api_key, api_secret = get_user_api(userId) client = get_bybit_client(api_key, api_secret) @@ -55,7 +55,7 @@ def submit_order( } if orderType.lower() == "limit": if price is None: - raise HTTPException(status_code=400, detail="Thiếu giá cho lệnh Limit") + raise HTTPException(status_code=400, detail="Missing price for Limit order") params["price"] = price res = client.place_order(**params) return res @@ -64,9 +64,9 @@ def submit_order( @router.delete("/orders/{order_id}", tags=["Order"]) def cancel_order( order_id: str, - userId: str = Query(..., description="ID của user để lấy API key/secret"), - symbol: str = Query(..., description="Mã giao dịch, ví dụ: BTCUSDT"), - category: str = Query("linear", description="Loại lệnh: linear (future) hoặc spot") + userId: str = Query(..., description="User ID to get API key/secret"), + symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"), + category: str = Query("linear", description="Order type: linear (future) or spot") ): api_key, api_secret = get_user_api(userId) client = get_bybit_client(api_key, api_secret) diff --git a/app/api/positions.py b/app/api/positions.py index ed38522..16f9140 100644 --- a/app/api/positions.py +++ b/app/api/positions.py @@ -21,9 +21,9 @@ def get_user_api(user_id: str): @router.get("/positions", tags=["Position"]) def get_positions( - userId: str = Query(..., description="ID của user để lấy API key/secret"), - symbol: Optional[str] = Query(None, description="Mã giao dịch, ví dụ: BTCUSDT"), - category: str = Query("linear", description="Loại lệnh: linear (future) hoặc spot") + userId: str = Query(..., description="User ID to get API key/secret"), + symbol: Optional[str] = Query(None, description="Trading symbol, e.g. BTCUSDT"), + category: str = Query("linear", description="Order type: linear (future) or spot") ): api_key, api_secret = get_user_api(userId) client = get_bybit_client(api_key, api_secret) diff --git a/app/api/user/user.py b/app/api/user/user.py index 35c84ef..596bb71 100644 --- a/app/api/user/user.py +++ b/app/api/user/user.py @@ -15,21 +15,21 @@ def list_users(): accounts = json.load(f) return accounts except Exception as e: - raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}") + raise HTTPException(status_code=500, detail=f"Error reading accounts file: {e}") # API thêm user @router.post("/users", tags=["User"]) def add_user( - id: str = Body(..., description="ID user"), + id: str = Body(..., description="User ID"), bybit_api_key: str = Body(..., description="Bybit API Key"), bybit_api_secret: str = Body(..., description="Bybit API Secret"), - user_name: str = Body(..., description="Tên user") + user_name: str = Body(..., description="User name") ): try: with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f: accounts = json.load(f) if any(acc['id'] == id for acc in accounts): - raise HTTPException(status_code=400, detail="ID đã tồn tại") + raise HTTPException(status_code=400, detail="ID already exists") new_user = { "id": id, "bybit_api_key": bybit_api_key, @@ -40,11 +40,11 @@ def add_user( f.seek(0) json.dump(accounts, f, ensure_ascii=False, indent=2) f.truncate() - return {"message": "Thêm user thành công"} + return {"message": "User added successfully"} except HTTPException as e: raise e except Exception as e: - raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}") + raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}") # API sửa user @router.put("/users/{user_id}", tags=["User"]) @@ -68,12 +68,12 @@ def update_user( f.seek(0) json.dump(accounts, f, ensure_ascii=False, indent=2) f.truncate() - return {"message": "Cập nhật user thành công"} - raise HTTPException(status_code=404, detail="Không tìm thấy userId") + return {"message": "User updated successfully"} + raise HTTPException(status_code=404, detail="User ID not found") except HTTPException as e: raise e except Exception as e: - raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}") + raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}") # API xóa user @router.delete("/users/{user_id}", tags=["User"]) @@ -83,12 +83,12 @@ def delete_user(user_id: str): accounts = json.load(f) new_accounts = [acc for acc in accounts if acc['id'] != user_id] if len(new_accounts) == len(accounts): - raise HTTPException(status_code=404, detail="Không tìm thấy userId") + raise HTTPException(status_code=404, detail="User ID not found") f.seek(0) json.dump(new_accounts, f, ensure_ascii=False, indent=2) f.truncate() - return {"message": "Xóa user thành công"} + return {"message": "User deleted successfully"} except HTTPException as e: raise e except Exception as e: - raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}") \ No newline at end of file + raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}") \ No newline at end of file diff --git a/app/schemas/account.py b/app/schemas/account.py index 278e052..379bb5a 100644 --- a/app/schemas/account.py +++ b/app/schemas/account.py @@ -2,45 +2,45 @@ from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any class CoinInfo(BaseModel): - availableToBorrow: Optional[str] = Field(None, description="Số lượng có thể vay") - bonus: Optional[str] = Field(None, description="Tiền thưởng") - accruedInterest: Optional[str] = Field(None, description="Lãi tích lũy") - availableToWithdraw: Optional[str] = Field(None, description="Số lượng có thể rút") - totalOrderIM: Optional[str] = Field(None, description="Initial margin cho lệnh") - equity: Optional[str] = Field(None, description="Tổng tài sản coin") - totalPositionMM: Optional[str] = Field(None, description="Maintenance margin cho vị thế") - usdValue: Optional[str] = Field(None, description="Giá trị USD của coin") - unrealisedPnl: Optional[str] = Field(None, description="Lãi/lỗ chưa chốt") - collateralSwitch: Optional[bool] = Field(None, description="Có dùng làm tài sản thế chấp không") - spotHedgingQty: Optional[str] = Field(None, description="Số lượng spot hedging") - borrowAmount: Optional[str] = Field(None, description="Số lượng đã vay") - totalPositionIM: Optional[str] = Field(None, description="Initial margin cho vị thế") - walletBalance: Optional[str] = Field(None, description="Số dư ví coin") - cumRealisedPnl: Optional[str] = Field(None, description="Lãi/lỗ đã chốt tích lũy") - locked: Optional[str] = Field(None, description="Số lượng bị khóa") - marginCollateral: Optional[bool] = Field(None, description="Có dùng làm tài sản margin không") - coin: str = Field(..., description="Mã coin") + availableToBorrow: Optional[str] = Field(None, description="Amount available to borrow") + bonus: Optional[str] = Field(None, description="Bonus amount") + accruedInterest: Optional[str] = Field(None, description="Accrued interest") + availableToWithdraw: Optional[str] = Field(None, description="Amount available to withdraw") + totalOrderIM: Optional[str] = Field(None, description="Initial margin for orders") + equity: Optional[str] = Field(None, description="Total coin equity") + totalPositionMM: Optional[str] = Field(None, description="Maintenance margin for positions") + usdValue: Optional[str] = Field(None, description="USD value of the coin") + unrealisedPnl: Optional[str] = Field(None, description="Unrealized PnL") + collateralSwitch: Optional[bool] = Field(None, description="Is used as collateral") + spotHedgingQty: Optional[str] = Field(None, description="Spot hedging quantity") + borrowAmount: Optional[str] = Field(None, description="Borrowed amount") + totalPositionIM: Optional[str] = Field(None, description="Initial margin for positions") + walletBalance: Optional[str] = Field(None, description="Coin wallet balance") + cumRealisedPnl: Optional[str] = Field(None, description="Cumulative realized PnL") + locked: Optional[str] = Field(None, description="Locked amount") + marginCollateral: Optional[bool] = Field(None, description="Is used as margin collateral") + coin: str = Field(..., description="Coin symbol") class AccountInfo(BaseModel): - totalEquity: str = Field(..., description="Tổng tài sản quy USD") + totalEquity: str = Field(..., description="Total equity in USD") accountIMRate: str = Field(..., description="Initial margin rate") - totalMarginBalance: str = Field(..., description="Tổng margin balance") - totalInitialMargin: str = Field(..., description="Tổng initial margin") - accountType: str = Field(..., description="Loại tài khoản (ví dụ: UNIFIED)") - totalAvailableBalance: str = Field(..., description="Tổng số dư khả dụng") + totalMarginBalance: str = Field(..., description="Total margin balance") + totalInitialMargin: str = Field(..., description="Total initial margin") + accountType: str = Field(..., description="Account type (e.g. UNIFIED)") + totalAvailableBalance: str = Field(..., description="Total available balance") accountMMRate: str = Field(..., description="Maintenance margin rate") - totalPerpUPL: str = Field(..., description="Lãi/lỗ chưa chốt của perpetual") - totalWalletBalance: str = Field(..., description="Tổng số dư ví") + totalPerpUPL: str = Field(..., description="Unrealized PnL of perpetual positions") + totalWalletBalance: str = Field(..., description="Total wallet balance") accountLTV: str = Field(..., description="Loan to value") - totalMaintenanceMargin: str = Field(..., description="Tổng maintenance margin") - coin: List[CoinInfo] = Field(..., description="Danh sách coin") + totalMaintenanceMargin: str = Field(..., description="Total maintenance margin") + coin: List[CoinInfo] = Field(..., description="List of coins") class ResultInfo(BaseModel): - list: List[AccountInfo] = Field(..., description="Danh sách tài khoản") + list: List[AccountInfo] = Field(..., description="List of accounts") class AccountResponseSchema(BaseModel): - retCode: int = Field(..., description="Mã kết quả trả về (0 là thành công)") - retMsg: str = Field(..., description="Thông báo kết quả") - result: ResultInfo = Field(..., description="Kết quả chi tiết") - retExtInfo: Dict[str, Any] = Field(..., description="Thông tin mở rộng") - time: int = Field(..., description="Timestamp trả về") \ No newline at end of file + retCode: int = Field(..., description="Return code (0 means success)") + retMsg: str = Field(..., description="Return message") + result: ResultInfo = Field(..., description="Detailed result") + retExtInfo: Dict[str, Any] = Field(..., description="Extra information") + time: int = Field(..., description="Response timestamp") \ No newline at end of file diff --git a/app/schemas/candle.py b/app/schemas/candle.py index ac46bd3..14c9a02 100644 --- a/app/schemas/candle.py +++ b/app/schemas/candle.py @@ -2,13 +2,13 @@ from pydantic import BaseModel, Field from typing import List, Optional, Any class CandleSchema(BaseModel): - timestamp: int = Field(..., description="Thời gian (miliseconds)") - open: float = Field(..., description="Giá mở cửa") - high: float = Field(..., description="Giá cao nhất") - low: float = Field(..., description="Giá thấp nhất") - close: float = Field(..., description="Giá đóng cửa") - volume: float = Field(..., description="Khối lượng giao dịch") - turnover: float = Field(..., description="Giá trị giao dịch") + timestamp: int = Field(..., description="Timestamp (milliseconds)") + open: float = Field(..., description="Open price") + high: float = Field(..., description="High price") + low: float = Field(..., description="Low price") + close: float = Field(..., description="Close price") + volume: float = Field(..., description="Volume") + turnover: float = Field(..., description="Turnover value") macd: Optional[float] = Field(None, description="MACD value") macdsignal: Optional[float] = Field(None, description="MACD signal") macdhist: Optional[float] = Field(None, description="MACD histogram")