alphax/app/web/routes_live_trading.py
2026-05-24 10:34:00 +08:00

176 lines
7.5 KiB
Python

from fastapi import APIRouter, Body, Cookie, HTTPException
from app.db.live_trading import (
create_live_order_intent,
delete_live_account,
get_live_trading_summary,
create_live_order_intents_for_accounts,
list_live_accounts,
list_live_order_events,
list_live_order_intents,
prepare_intent_from_paper_trade,
update_live_account,
upsert_live_account,
)
from app.services.live_trading_smoke import run_binance_testnet_smoke
from app.services.live_trading_account import get_live_account_overview
from app.services.live_trading_sync import sync_open_paper_trades_to_live, sync_paper_trade_to_live
from app.integrations.binance_live import LiveTradingConfigError
from app.web.shared import require_admin
router = APIRouter()
@router.get("/api/live-trading/summary")
async def api_live_trading_summary(altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
return get_live_trading_summary()
@router.get("/api/live-trading/accounts")
async def api_live_trading_accounts(altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
return list_live_accounts()
@router.get("/api/live-trading/accounts/{account_id}/overview")
async def api_live_trading_account_overview(account_id: int, refresh: int = 0, altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
return get_live_account_overview(account_id, refresh=bool(refresh))
@router.post("/api/live-trading/accounts")
async def api_live_trading_account(payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
return upsert_live_account(
account_code=payload.get("account_code", ""),
exchange=payload.get("exchange", ""),
market_type=payload.get("market_type", ""),
testnet=payload.get("testnet") if "testnet" in payload else None,
status=payload.get("status", ""),
api_key_env=payload.get("api_key_env", ""),
api_secret_env=payload.get("api_secret_env", ""),
permissions=payload.get("permissions") if isinstance(payload.get("permissions"), dict) else None,
risk_config=payload.get("risk_config") if isinstance(payload.get("risk_config"), dict) else None,
)
@router.put("/api/live-trading/accounts/{account_id}")
async def api_live_trading_update_account(account_id: int, payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
try:
result = update_live_account(
account_id,
account_code=payload.get("account_code", ""),
exchange=payload.get("exchange", ""),
market_type=payload.get("market_type", ""),
testnet=payload.get("testnet") if "testnet" in payload else None,
status=payload.get("status", ""),
api_key_env=payload.get("api_key_env", ""),
api_secret_env=payload.get("api_secret_env", ""),
permissions=payload.get("permissions") if isinstance(payload.get("permissions"), dict) else None,
risk_config=payload.get("risk_config") if isinstance(payload.get("risk_config"), dict) else None,
)
except Exception as exc:
raise HTTPException(status_code=409, detail=f"account_update_failed: {exc}") from exc
if not result.get("ok"):
raise HTTPException(status_code=404, detail=result.get("reason", "account_not_found"))
return result
@router.delete("/api/live-trading/accounts/{account_id}")
async def api_live_trading_delete_account(account_id: int, altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
result = delete_live_account(account_id)
if not result.get("ok"):
raise HTTPException(status_code=404, detail=result.get("reason", "account_not_found"))
return result
@router.post("/api/live-trading/accounts/default")
async def api_live_trading_default_account(altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
return upsert_live_account()
@router.get("/api/live-trading/intents")
async def api_live_trading_intents(
limit: int = 50,
offset: int = 0,
status: str = "",
altcoin_session: str = Cookie(default=""),
):
require_admin(altcoin_session)
return list_live_order_intents(limit=limit, offset=offset, status=status)
@router.post("/api/live-trading/intents")
async def api_live_trading_create_intent(payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
account_ids = payload.pop("account_ids", None)
if isinstance(account_ids, list) and account_ids:
return create_live_order_intents_for_accounts(payload, account_ids=account_ids, source_type="manual")
return create_live_order_intent(payload, source_type="manual")
@router.post("/api/live-trading/intents/from-paper/{paper_trade_id}")
async def api_live_trading_from_paper(paper_trade_id: int, payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
account_ids = payload.get("account_ids") if isinstance(payload, dict) else None
return prepare_intent_from_paper_trade(paper_trade_id, account_ids=account_ids if isinstance(account_ids, list) else None)
@router.post("/api/live-trading/sync/from-paper/{paper_trade_id}")
async def api_live_trading_sync_from_paper(paper_trade_id: int, payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
account_ids = payload.get("account_ids") if isinstance(payload, dict) else None
execute = bool(payload.get("execute", True)) if isinstance(payload, dict) else True
return sync_paper_trade_to_live(
paper_trade_id,
account_ids=account_ids if isinstance(account_ids, list) else None,
execute=execute,
)
@router.post("/api/live-trading/sync/open-paper-trades")
async def api_live_trading_sync_open_paper_trades(payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
return sync_open_paper_trades_to_live(
limit=int(payload.get("limit") or 20),
execute=bool(payload.get("execute", True)),
)
@router.get("/api/live-trading/events")
async def api_live_trading_events(
limit: int = 80,
offset: int = 0,
intent_id: int = 0,
altcoin_session: str = Cookie(default=""),
):
require_admin(altcoin_session)
return list_live_order_events(limit=limit, offset=offset, intent_id=intent_id)
@router.post("/api/live-trading/smoke/binance")
async def api_live_trading_binance_smoke(payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
require_admin(altcoin_session)
try:
return run_binance_testnet_smoke(
account_id=int(payload.get("account_id") or 0),
symbol=str(payload.get("symbol") or "BTC/USDT"),
notional_usdt=float(payload.get("notional_usdt") or 10),
leverage=float(payload.get("leverage") or 1),
intent_id=int(payload.get("intent_id") or 0),
)
except LiveTradingConfigError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
except Exception as exc:
raise HTTPException(status_code=502, detail=f"exchange smoke failed: {exc}") from exc
@router.post("/api/live-trading/smoke/binance-testnet")
async def api_live_trading_binance_testnet_smoke(payload: dict = Body(default={}), altcoin_session: str = Cookie(default="")):
return await api_live_trading_binance_smoke(payload=payload, altcoin_session=altcoin_session)