This commit is contained in:
KienVT9 2025-05-22 18:43:47 +07:00
parent 859fb7a19d
commit 990f944ec6
8 changed files with 174 additions and 94 deletions

View file

@ -1,8 +1,8 @@
from fastapi import APIRouter, Query, HTTPException, Body
from fastapi import APIRouter, Query, HTTPException
from app.schemas.account import AccountResponseSchema
from app.services.bybit_service import get_bybit_client
import json
import os
from typing import List, Optional
router = APIRouter()
@ -19,93 +19,7 @@ def get_user_api(user_id: str):
raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}")
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
# API lấy danh sách user
@router.get("/users")
def list_users():
try:
with open(ACCOUNTS_FILE, 'r', encoding='utf-8') as f:
accounts = json.load(f)
return accounts
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}")
# API thêm user
@router.post("/users")
def add_user(
id: str = Body(..., description="ID user"),
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")
):
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")
new_user = {
"id": id,
"bybit_api_key": bybit_api_key,
"bybit_api_secret": bybit_api_secret,
"user_name": user_name
}
accounts.append(new_user)
f.seek(0)
json.dump(accounts, f, ensure_ascii=False, indent=2)
f.truncate()
return {"message": "Thêm user thành công"}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
# API sửa user
@router.put("/users/{user_id}")
def update_user(
user_id: str,
bybit_api_key: Optional[str] = Body(None),
bybit_api_secret: Optional[str] = Body(None),
user_name: Optional[str] = Body(None)
):
try:
with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f:
accounts = json.load(f)
for acc in accounts:
if acc['id'] == user_id:
if bybit_api_key is not None:
acc['bybit_api_key'] = bybit_api_key
if bybit_api_secret is not None:
acc['bybit_api_secret'] = bybit_api_secret
if user_name is not None:
acc['user_name'] = user_name
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")
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
# API xóa user
@router.delete("/users/{user_id}")
def delete_user(user_id: str):
try:
with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f:
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")
f.seek(0)
json.dump(new_accounts, f, ensure_ascii=False, indent=2)
f.truncate()
return {"message": "Xóa user thành công"}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
@router.get("/account")
@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")):
try:
api_key, api_secret = get_user_api(userId)

View file

@ -1,6 +1,7 @@
from fastapi import APIRouter, Query, HTTPException
from typing import Optional
from app.services.bybit_service import get_bybit_client
from app.schemas.candle import CandleResponseSchema
import json
import os
import pandas as pd
@ -42,7 +43,7 @@ def rsi(series, period=14):
rsi = rsi.replace([np.inf, -np.inf], np.nan)
return rsi
@router.get("/candles")
@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"),

View file

@ -19,7 +19,7 @@ def get_user_api(user_id: str):
raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}")
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
@router.get("/orders")
@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"),
@ -34,7 +34,7 @@ def get_orders(
return res
# Submit order
@router.post("/orders")
@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"),
@ -61,7 +61,7 @@ def submit_order(
return res
# Cancel order
@router.delete("/orders/{order_id}")
@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"),

View file

@ -19,7 +19,7 @@ def get_user_api(user_id: str):
raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}")
raise HTTPException(status_code=404, detail="Không tìm thấy userId")
@router.get("/positions")
@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"),

94
app/api/user/user.py Normal file
View file

@ -0,0 +1,94 @@
from fastapi import APIRouter, Query, HTTPException, Body
import json
import os
from typing import Optional
router = APIRouter()
ACCOUNTS_FILE = os.path.join(os.path.dirname(__file__), '../../../accounts.json')
# API lấy danh sách user
@router.get("/users", tags=["User"])
def list_users():
try:
with open(ACCOUNTS_FILE, 'r', encoding='utf-8') as f:
accounts = json.load(f)
return accounts
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi đọc file accounts: {e}")
# API thêm user
@router.post("/users", tags=["User"])
def add_user(
id: str = Body(..., description="ID user"),
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")
):
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")
new_user = {
"id": id,
"bybit_api_key": bybit_api_key,
"bybit_api_secret": bybit_api_secret,
"user_name": user_name
}
accounts.append(new_user)
f.seek(0)
json.dump(accounts, f, ensure_ascii=False, indent=2)
f.truncate()
return {"message": "Thêm user thành công"}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
# API sửa user
@router.put("/users/{user_id}", tags=["User"])
def update_user(
user_id: str,
bybit_api_key: Optional[str] = Body(None),
bybit_api_secret: Optional[str] = Body(None),
user_name: Optional[str] = Body(None)
):
try:
with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f:
accounts = json.load(f)
for acc in accounts:
if acc['id'] == user_id:
if bybit_api_key is not None:
acc['bybit_api_key'] = bybit_api_key
if bybit_api_secret is not None:
acc['bybit_api_secret'] = bybit_api_secret
if user_name is not None:
acc['user_name'] = user_name
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")
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")
# API xóa user
@router.delete("/users/{user_id}", tags=["User"])
def delete_user(user_id: str):
try:
with open(ACCOUNTS_FILE, 'r+', encoding='utf-8') as f:
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")
f.seek(0)
json.dump(new_accounts, f, ensure_ascii=False, indent=2)
f.truncate()
return {"message": "Xóa user thành công"}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"Lỗi ghi file accounts: {e}")

View file

@ -1,5 +1,6 @@
from fastapi import FastAPI
from app.api import candles, orders, account, positions
from app.api.user import user
app = FastAPI()
@ -7,3 +8,4 @@ app.include_router(candles.router, prefix="/api")
app.include_router(orders.router, prefix="/api")
app.include_router(account.router, prefix="/api")
app.include_router(positions.router, prefix="/api")
app.include_router(user.router, prefix="/api")

46
app/schemas/account.py Normal file
View file

@ -0,0 +1,46 @@
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")
class AccountInfo(BaseModel):
totalEquity: str = Field(..., description="Tổng tài sản quy 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")
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í")
accountLTV: str = Field(..., description="Loan to value")
totalMaintenanceMargin: str = Field(..., description="Tổng maintenance margin")
coin: List[CoinInfo] = Field(..., description="Danh sách coin")
class ResultInfo(BaseModel):
list: List[AccountInfo] = Field(..., description="Danh sách tài khoản")
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ề")

23
app/schemas/candle.py Normal file
View file

@ -0,0 +1,23 @@
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")
macd: Optional[float] = Field(None, description="MACD value")
macdsignal: Optional[float] = Field(None, description="MACD signal")
macdhist: Optional[float] = Field(None, description="MACD histogram")
ema34: Optional[float] = Field(None, description="EMA 34")
ema50: Optional[float] = Field(None, description="EMA 50")
ema100: Optional[float] = Field(None, description="EMA 100")
ema200: Optional[float] = Field(None, description="EMA 200")
rsi: Optional[float] = Field(None, description="RSI")
class CandleResponseSchema(BaseModel):
data: List[CandleSchema]
indicators: Any