144 lines
5.3 KiB
Python
144 lines
5.3 KiB
Python
"""市场补充数据源(供 AI 分析与看板扩展)
|
|
|
|
- fetch_news: 新浪财经 7x24 快讯
|
|
- fetch_sector_fund_flow: 东方财富板块主力资金流排行(行业/概念)
|
|
- fetch_margin_summary: 东方财富两融余额汇总(T+1 数据)
|
|
|
|
均为公开接口,失败时返回 []/None,不阻塞主流程。
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
import httpx
|
|
|
|
_TIMEOUT = 10.0
|
|
_UA = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
|
|
"(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
|
|
|
|
|
|
async def fetch_news(limit: int = 30) -> list[dict]:
|
|
"""新浪财经 7x24 快讯,返回 [{time: 'MM-DD HH:MM', content}];失败返回 []"""
|
|
try:
|
|
limit = max(1, min(int(limit or 30), 50))
|
|
except (TypeError, ValueError):
|
|
limit = 30
|
|
try:
|
|
async with httpx.AsyncClient(timeout=_TIMEOUT, headers={"User-Agent": _UA}) as client:
|
|
resp = await client.get(
|
|
"https://zhibo.sina.com.cn/api/zhibo/feed",
|
|
params={"page": 1, "page_size": limit, "zhibo_id": 152, "tag_id": 0},
|
|
)
|
|
resp.raise_for_status()
|
|
items = resp.json()["result"]["data"]["feed"]["list"]
|
|
news = []
|
|
for it in items or []:
|
|
text = (it.get("rich_text") or "").strip()
|
|
if not text:
|
|
continue
|
|
news.append({
|
|
"time": (it.get("create_time") or "")[5:16], # 'MM-DD HH:MM'
|
|
"content": text[:300],
|
|
})
|
|
return news
|
|
except Exception:
|
|
return []
|
|
|
|
|
|
_FLOW_HOSTS = (
|
|
# push2 对部分客户端有 TLS 指纹拦截(peer closed),delay 镜像同接口且稳定
|
|
"https://push2delay.eastmoney.com",
|
|
"https://push2.eastmoney.com",
|
|
)
|
|
|
|
|
|
async def _fetch_flow_boards(client: httpx.AsyncClient, fs: str, po: int, pz: int) -> list[dict]:
|
|
"""拉取一类板块的主力净流入排行。po=1 降序(净流入最多),po=0 升序(净流出最多)
|
|
|
|
查询串保持字面量 + 号(与东财网页请求一致),逐 host 尝试。
|
|
"""
|
|
qs = (f"/api/qt/clist/get?fid=f62&po={po}&pz={pz}&pn=1&np=1"
|
|
f"&fltt=2&invt=2&fs={fs}&fields=f12,f14,f62,f184")
|
|
last_err: Exception | None = None
|
|
for host in _FLOW_HOSTS:
|
|
try:
|
|
resp = await client.get(host + qs)
|
|
resp.raise_for_status()
|
|
diff = (resp.json().get("data") or {}).get("diff") or []
|
|
break
|
|
except Exception as e:
|
|
last_err = e
|
|
diff = []
|
|
else:
|
|
raise ConnectionError(f"板块资金流全部数据源失败: {last_err}")
|
|
if isinstance(diff, dict): # 兼容旧版 {index: item} 结构
|
|
diff = list(diff.values())
|
|
boards = []
|
|
for d in diff:
|
|
amt = d.get("f62")
|
|
if not isinstance(amt, (int, float)):
|
|
continue
|
|
boards.append({
|
|
"code": d.get("f12", ""),
|
|
"name": d.get("f14", ""),
|
|
"mainNet": round(amt / 1e8, 1), # 亿元
|
|
"mainNetPct": d.get("f184"), # 主力净占比 %
|
|
})
|
|
return boards
|
|
|
|
|
|
async def fetch_sector_fund_flow() -> dict | None:
|
|
"""板块主力资金流排行:行业净流入/净流出 TOP6 + 概念净流入 TOP6;失败返回 None"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=_TIMEOUT, headers={"User-Agent": _UA}) as client:
|
|
industry_in, industry_out, concept_in = await asyncio.gather(
|
|
_fetch_flow_boards(client, "m:90+t:2", 1, 6),
|
|
_fetch_flow_boards(client, "m:90+t:2", 0, 6),
|
|
_fetch_flow_boards(client, "m:90+t:3", 1, 6),
|
|
)
|
|
return {
|
|
"industryInflow": industry_in, # 主力净流入降序
|
|
"industryOutflow": industry_out, # 升序(净流出最多在前)
|
|
"conceptInflow": concept_in,
|
|
"unit": "亿元",
|
|
}
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
async def fetch_margin_summary() -> dict | None:
|
|
"""沪深北两融余额汇总(交易所 T+1 披露);失败返回 None"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=_TIMEOUT, headers={"User-Agent": _UA}) as client:
|
|
resp = await client.get(
|
|
"https://datacenter-web.eastmoney.com/api/data/v1/get",
|
|
params={
|
|
"reportName": "RPTA_RZRQ_LSHJ",
|
|
"columns": "ALL",
|
|
"source": "WEB",
|
|
"sortColumns": "dim_date",
|
|
"sortTypes": "-1",
|
|
"pageSize": 2,
|
|
"pageNumber": 1,
|
|
},
|
|
)
|
|
resp.raise_for_status()
|
|
rows = ((resp.json().get("result") or {}).get("data")) or []
|
|
if not rows:
|
|
return None
|
|
|
|
def _balance(row: dict) -> float:
|
|
return float(row.get("RZYE") or 0) + float(row.get("RQYE") or 0)
|
|
|
|
latest = rows[0]
|
|
prev = rows[1] if len(rows) > 1 else None
|
|
balance = _balance(latest)
|
|
change = (balance - _balance(prev)) if prev else None
|
|
return {
|
|
"date": (latest.get("DIM_DATE") or "")[:10],
|
|
"balanceYi": round(balance / 1e8), # 亿元
|
|
"changeYi": round(change / 1e8) if change is not None else None,
|
|
"rzjmeYi": round(float(latest.get("RZJME") or 0) / 1e8, 1), # 融资净买入
|
|
}
|
|
except Exception:
|
|
return None
|