176 lines
7.5 KiB
Python
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)
|