114 lines
3.8 KiB
Python
114 lines
3.8 KiB
Python
"""日/周/月统计 API。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
|
|
from apps.api.auth import require_auth
|
|
from apps.worker.settle import ensure_settlements_for_ymds
|
|
from packages.config import get_settings
|
|
from packages.db import Repository
|
|
from packages.domain.aggregate import leverage_stats_payload, move_points_stats_payload
|
|
from packages.domain.range import resolve_range
|
|
|
|
router = APIRouter(prefix="/stats", tags=["stats"], dependencies=[Depends(require_auth)])
|
|
|
|
|
|
def _range_info(range_name: str, date: str | None) -> dict:
|
|
s = get_settings()
|
|
try:
|
|
return resolve_range(
|
|
range_name,
|
|
date,
|
|
month_mode=s.month_range_mode,
|
|
)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
|
|
|
@router.get("/leverage")
|
|
def leverage_stats(
|
|
range: str = Query(default="day", pattern="^(day|week|month)$"),
|
|
date: str | None = Query(default=None, description="锚点日 YYYY-MM-DD(上海)"),
|
|
side: str = Query(default="both", pattern="^(C|P|both)$"),
|
|
bucket_minutes: int = Query(default=60, ge=15, le=120),
|
|
) -> dict:
|
|
s = get_settings()
|
|
info = _range_info(range, date)
|
|
repo = Repository(s.db_path)
|
|
try:
|
|
rows = repo.fetch_option_quotes(
|
|
start_ms=info["start_ms"],
|
|
end_ms=info["end_ms"],
|
|
side=side,
|
|
underlying=s.underlying,
|
|
)
|
|
return leverage_stats_payload(
|
|
rows,
|
|
range_info=info,
|
|
bucket_minutes=bucket_minutes,
|
|
min_leverage=float(s.min_option_leverage),
|
|
side=side,
|
|
)
|
|
finally:
|
|
repo.close()
|
|
|
|
|
|
@router.get("/move_points")
|
|
def move_points_stats(
|
|
range: str = Query(default="day", pattern="^(day|week|month)$"),
|
|
date: str | None = Query(default=None, description="锚点日 YYYY-MM-DD"),
|
|
side: str = Query(default="both", pattern="^(C|P|both)$"),
|
|
bucket_minutes: int = Query(default=60, ge=15, le=120),
|
|
) -> dict:
|
|
s = get_settings()
|
|
info = _range_info(range, date)
|
|
repo = Repository(s.db_path)
|
|
try:
|
|
rows = repo.fetch_option_quotes(
|
|
start_ms=info["start_ms"],
|
|
end_ms=info["end_ms"],
|
|
side=side,
|
|
underlying=s.underlying,
|
|
)
|
|
ymds = sorted({str(r.get("expiry_ymd")) for r in rows if r.get("expiry_ymd")})
|
|
# 懒回填:已到期但缺锚点时尽量补齐(本地指数优先,失败则跳过)
|
|
try:
|
|
ensure_settlements_for_ymds(
|
|
repo,
|
|
ymds,
|
|
underlying=s.underlying,
|
|
index_inst_id=s.index_inst_id,
|
|
)
|
|
except Exception: # noqa: BLE001 — 回填失败不阻断统计
|
|
pass
|
|
settlements = repo.list_settlements(ymds)
|
|
return move_points_stats_payload(
|
|
rows,
|
|
settlements,
|
|
range_info=info,
|
|
bucket_minutes=bucket_minutes,
|
|
side=side,
|
|
)
|
|
finally:
|
|
repo.close()
|
|
|
|
|
|
@router.get("/ops-map")
|
|
def ops_map(
|
|
range: str = Query(default="day", pattern="^(day|week|month)$"),
|
|
date: str | None = Query(default=None),
|
|
side: str = Query(default="both", pattern="^(C|P|both)$"),
|
|
bucket_minutes: int = Query(default=60, ge=15, le=120),
|
|
) -> dict:
|
|
lev = leverage_stats(range=range, date=date, side=side, bucket_minutes=bucket_minutes)
|
|
mov = move_points_stats(range=range, date=date, side=side, bucket_minutes=bucket_minutes)
|
|
return {
|
|
"range": range,
|
|
"date": lev.get("date"),
|
|
"side": side,
|
|
"bucket_minutes": bucket_minutes,
|
|
"leverage": lev,
|
|
"move_points": mov,
|
|
}
|