update
This commit is contained in:
parent
ac32800ba7
commit
7cc42ee6a1
7 changed files with 82 additions and 82 deletions
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
raise HTTPException(status_code=500, detail=f"Error writing accounts file: {e}")
|
||||
|
|
@ -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ề")
|
||||
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")
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue