astock-agent/backend/app/api/monitor.py
2026-04-15 08:58:21 +08:00

107 lines
3.6 KiB
Python

"""涨跌停/异动监控 API"""
from fastapi import APIRouter
from app.data.tushare_client import tushare_client
from app.config import is_market_session
router = APIRouter(prefix="/api/monitor", tags=["monitor"])
@router.get("/limits")
async def get_limits():
"""获取涨跌停数据"""
trade_date = tushare_client.get_latest_trade_date()
limit_df = tushare_client.get_limit_list(trade_date)
if limit_df.empty:
return {"trade_date": trade_date, "limit_up": [], "limit_down": []}
# 拆分涨停和跌停
up_df = limit_df[limit_df["limit"] == "U"].sort_values("pct_chg", ascending=False)
down_df = limit_df[limit_df["limit"] == "D"].sort_values("pct_chg", ascending=True)
def _parse(df):
results = []
for _, r in df.head(50).iterrows():
results.append({
"ts_code": r.get("ts_code", ""),
"name": r.get("name", ""),
"close": float(r.get("close", 0)),
"pct_chg": float(r.get("pct_chg", 0)),
"limit_times": int(r.get("limit_times", 0)),
"first_time": str(r.get("first_time", "")),
"last_time": str(r.get("last_time", "")),
"open_times": int(r.get("open_times", 0)),
"fd_amount": float(r.get("fd_amount", 0)) if r.get("fd_amount") else 0,
"up_stat": str(r.get("up_stat", "")),
})
return results
return {
"trade_date": trade_date,
"is_realtime": is_market_session(),
"limit_up": _parse(up_df),
"limit_down": _parse(down_df),
}
@router.get("/unusual")
async def get_unusual():
"""获取异动股(量比>3、振幅>8%、快速拉升)"""
trade_date = tushare_client.get_latest_trade_date()
# 获取全市场日线
daily = tushare_client.get_daily_all(trade_date)
if daily.empty:
return {"trade_date": trade_date, "stocks": []}
basic = tushare_client.get_daily_basic(trade_date)
if not basic.empty:
daily = daily.merge(basic[["ts_code", "turnover_rate", "volume_ratio"]], on="ts_code", how="left")
stock_basic = tushare_client.get_stock_basic()
name_map = {}
if not stock_basic.empty:
for _, r in stock_basic.iterrows():
name_map[r["ts_code"]] = r["name"]
# 筛选异动条件
unusual = []
for _, r in daily.iterrows():
ts = r.get("ts_code", "")
if not ts.endswith((".SH", ".SZ")):
continue
pct = r.get("pct_chg", 0)
vol_ratio = r.get("volume_ratio", 0)
high = r.get("high", 0)
low = r.get("low", 0)
pre_close = r.get("pre_close", 0)
amplitude = (high - low) / pre_close * 100 if pre_close > 0 else 0
tags = []
if vol_ratio and vol_ratio > 3:
tags.append("巨量")
if amplitude > 8:
tags.append("高振幅")
if pct > 7:
tags.append("急涨")
elif pct < -7:
tags.append("急跌")
if tags:
unusual.append({
"ts_code": ts,
"name": name_map.get(ts, ts),
"close": float(r.get("close", 0)),
"pct_chg": float(pct),
"amplitude": round(float(amplitude), 2),
"volume_ratio": round(float(vol_ratio), 2) if vol_ratio and vol_ratio == vol_ratio else 0,
"turnover_rate": round(float(r.get("turnover_rate", 0)), 2),
"tags": tags,
})
unusual.sort(key=lambda x: abs(x["pct_chg"]), reverse=True)
return {"trade_date": trade_date, "stocks": unusual[:50]}