65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
|
|
"""桥侧计划 API(07-30 用户需求):对外提供每日选股计划。
|
|||
|
|
|
|||
|
|
容器常驻命令改为 uvicorn 后随容器启动(docker-compose 已配端口,默认 8300);
|
|||
|
|
cron 的 docker exec 构建/出计划照旧,互不影响。局域网内部服务,v1 无鉴权。
|
|||
|
|
|
|||
|
|
GET /health 存活 + 最新计划日
|
|||
|
|
GET /plan 最新一天的计划(JSON)
|
|||
|
|
GET /plan?date=2026-07-30 指定日期
|
|||
|
|
GET /plan?format=md Markdown 原文(浏览器直接可读)
|
|||
|
|
GET /plan/dates 可用日期列表
|
|||
|
|
POST /plan/refresh?date=... 重新生成该日计划文件(data/plan/*.md)
|
|||
|
|
|
|||
|
|
将来接 XXL-JOB / 事件回调,触发器打这层即可,不必进容器。
|
|||
|
|
"""
|
|||
|
|
import pandas as pd
|
|||
|
|
from fastapi import FastAPI, HTTPException
|
|||
|
|
from fastapi.responses import PlainTextResponse
|
|||
|
|
|
|||
|
|
import db
|
|||
|
|
import plan
|
|||
|
|
|
|||
|
|
app = FastAPI(title="akg-factor-bridge · 每日选股计划", version="0.1")
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.get("/health")
|
|||
|
|
def health():
|
|||
|
|
try:
|
|||
|
|
d = plan._latest_date("t_factor_akg_score") # noqa: SLF001 —— 桥内自用
|
|||
|
|
except Exception as e: # noqa: BLE001 —— 库连不上也要能回答"我还活着"
|
|||
|
|
return {"ok": False, "error": repr(e)}
|
|||
|
|
return {"ok": True, "latest_plan_date": d}
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.get("/plan/dates")
|
|||
|
|
def plan_dates(limit: int = 30):
|
|||
|
|
df = db.read_mysql(
|
|||
|
|
"factor", "SELECT DISTINCT trade_date FROM t_factor_akg_score "
|
|||
|
|
"ORDER BY trade_date DESC LIMIT %s", (int(limit),))
|
|||
|
|
if df.empty:
|
|||
|
|
return {"dates": []}
|
|||
|
|
return {"dates": [pd.Timestamp(x).date().isoformat()
|
|||
|
|
for x in df["trade_date"]]}
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.get("/plan")
|
|||
|
|
def get_plan(date: str | None = None, format: str = "json",
|
|||
|
|
top: int = 20, obs_top: int = 10, theme_cap: int = 5):
|
|||
|
|
try:
|
|||
|
|
data = plan.collect(date, top, obs_top, theme_cap)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
raise HTTPException(status_code=404, detail=str(e))
|
|||
|
|
if format == "md":
|
|||
|
|
return PlainTextResponse(plan.render_md(data),
|
|||
|
|
media_type="text/markdown; charset=utf-8")
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.post("/plan/refresh")
|
|||
|
|
def refresh(date: str | None = None):
|
|||
|
|
try:
|
|||
|
|
out = plan.generate(date)
|
|||
|
|
except SystemExit as e:
|
|||
|
|
raise HTTPException(status_code=404, detail=str(e))
|
|||
|
|
return {"ok": True, "file": out}
|