99 lines
3.0 KiB
Python
99 lines
3.0 KiB
Python
"""模拟盘 API 路由(spec §10)。
|
|
|
|
create 建 paper_account(持久化配置);GET 查询净值/成交/状态。
|
|
回放执行(engine.run)由 orchestrator 异步触发或容器内同步跑,端到端冒烟在容器
|
|
(本机无 NAS parquet + vnpy 完整依赖),本模块只做 account 管理 + 查询。
|
|
"""
|
|
import sqlite3
|
|
|
|
from fastapi import APIRouter, Depends, Header, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from .auth import verify_token as verify_token_impl
|
|
|
|
router = APIRouter()
|
|
_db_path = {"path": None}
|
|
|
|
|
|
def set_db_path(p):
|
|
_db_path["path"] = p
|
|
if p:
|
|
from sanguo_trader.persistence import init_db
|
|
init_db(p) # app 启动建表(幂等),保证 GET 查询不报 no such table
|
|
|
|
|
|
async def verify_token(authorization: str | None = Header(None)):
|
|
if authorization is None or not authorization.startswith("Bearer "):
|
|
raise HTTPException(401, "Missing/invalid authorization")
|
|
return verify_token_impl(authorization.split(" ", 1)[1])
|
|
|
|
|
|
class StrategyCfg(BaseModel):
|
|
name: str
|
|
params: dict = {}
|
|
match_session: str = "next_open"
|
|
symbol: str
|
|
listing_days: int = 0
|
|
|
|
|
|
class PaperCreateRequest(BaseModel):
|
|
name: str = "paper"
|
|
mode: str = "replay"
|
|
interval: str = "d"
|
|
symbols: list[str]
|
|
strategies: list[StrategyCfg]
|
|
initial_capital: float = 1_000_000
|
|
rate: float = 0.0003
|
|
slippage: float = 0.0
|
|
pricetick: float = 0.01
|
|
stamp_duty_rate: float = 0.0005
|
|
transfer_fee_rate: float = 0.00001
|
|
min_commission: float = 5.0
|
|
start: str
|
|
end: str
|
|
|
|
|
|
@router.post("/paper/create", dependencies=[Depends(verify_token)])
|
|
def create_paper(req: PaperCreateRequest):
|
|
from sanguo_trader.persistence import init_db, save_account
|
|
|
|
db = _db_path["path"] or ":memory:"
|
|
init_db(db)
|
|
aid = save_account(db, req.model_dump())
|
|
return {"account_id": aid, "status": "created"}
|
|
|
|
|
|
@router.get("/paper/{aid}", dependencies=[Depends(verify_token)])
|
|
def get_paper(aid: int):
|
|
db = _db_path["path"]
|
|
with sqlite3.connect(db) as conn:
|
|
conn.row_factory = sqlite3.Row
|
|
row = conn.execute(
|
|
"SELECT * FROM paper_accounts WHERE id=?", (aid,)
|
|
).fetchone()
|
|
if not row:
|
|
raise HTTPException(404, "account not found")
|
|
return dict(row)
|
|
|
|
|
|
@router.get("/paper/{aid}/equity", dependencies=[Depends(verify_token)])
|
|
def get_equity(aid: int):
|
|
from sanguo_trader.persistence import list_daily_balance
|
|
|
|
return list_daily_balance(_db_path["path"], aid)
|
|
|
|
|
|
@router.get("/paper/{aid}/trades", dependencies=[Depends(verify_token)])
|
|
def get_trades(aid: int):
|
|
from sanguo_trader.persistence import list_trades
|
|
|
|
return list_trades(_db_path["path"], aid)
|
|
|
|
|
|
@router.get("/paper/{aid}/strategies", dependencies=[Depends(verify_token)])
|
|
def get_strategies(aid: int):
|
|
"""分策略归因:成交/拒单/费用聚合(spec §7)。"""
|
|
from sanguo_trader.persistence import list_strategy_summary
|
|
|
|
return list_strategy_summary(_db_path["path"], aid)
|