from __future__ import annotations import asyncio from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel, Field from ..strategy import get_engine from .auth import require_user router = APIRouter(prefix="/api/plan", tags=["plan"]) @router.get("/state") async def plan_state(_user: Annotated[str, Depends(require_user)]) -> dict: return get_engine().state() @router.post("/start") async def plan_start(_user: Annotated[str, Depends(require_user)]) -> dict: return await get_engine().start() @router.post("/pause") async def plan_pause(_user: Annotated[str, Depends(require_user)]) -> dict: return await get_engine().pause() @router.post("/emergency-close") async def plan_emergency(_user: Annotated[str, Depends(require_user)]) -> dict: return await get_engine().emergency_close() class ResidualCloseBody(BaseModel): group_id: str = Field(min_length=1, max_length=128) @router.post("/residual/close") async def plan_residual_close( body: ResidualCloseBody, _user: Annotated[str, Depends(require_user)], ) -> dict: """手动平单条残留:只验流动性,不验权利金回收比例。""" matcher = get_engine().matcher result = await asyncio.to_thread(matcher.close_residual_manual, body.group_id) if not result.ok: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=result.detail or "平残留失败", ) return { "ok": True, "detail": result.detail, "data": result.data, "liquidity_wait": result.liquidity_wait, }