Files
MoFin/scripts/market_watch.py
T
zhiwei 9b9c37002a Initial: MoFin 持仓分析与策略管理系统
核心模块:
- 策略生命周期管理 (strategy_lifecycle.py)
- 技术分析引擎 (technical_analysis.py)
- 双维度策略评估 (strategy_evaluator.py)
- 实时行情获取 (get_realtime_prices.py)
- Web Dashboard (server.py, :8899)

提示词版本管理:
- prompt_manager 模块 — 统一管理所有知微提示词
- 8个提示词共24个版本已录入
- 策略→提示词版本关联追踪
- Dashboard「提示词」Tab

数据源增强:
- 服务端 POST /api/update/realtime 端点已就绪
- clients/tdx-relay/ — 小小莫在Windows上开发的通达信中继
- 解决港股15分钟延迟问题
2026-06-12 22:54:51 +08:00

106 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""market_watch.py — 行業熱點 + 市場洞察數據採集,寫入 dashboard data/market.json"""
import json
import urllib.request
from datetime import datetime
from pathlib import Path
DATA_DIR = Path(__file__).parent / "data"
# ── 後端A:東方財富 push2 API(首選) ──
def _fetch_em(url):
"""通用 EM API 請求"""
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"})
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode("utf-8"))
def fetch_sector_em():
"""東方財富行業板塊"""
try:
data = _fetch_em("https://push2.eastmoney.com/api/qt/clist/get?pn=1&pz=15&po=1&np=1&fields=f2,f3,f4,f12,f14&fs=m:90+t:2")
return [{"name": i["f14"], "code": i["f12"], "price": i.get("f2", 0), "change": i.get("f3", 0)}
for i in data.get("data", {}).get("diff", [])]
except Exception as e:
return None
def fetch_concept_em():
"""東方財富概念板塊"""
try:
data = _fetch_em("https://push2.eastmoney.com/api/qt/clist/get?pn=1&pz=10&po=1&np=1&fields=f2,f3,f4,f12,f14&fs=m:90+t:3")
return [{"name": i["f14"], "code": i["f12"], "change": i.get("f3", 0)}
for i in data.get("data", {}).get("diff", [])]
except Exception as e:
return None
# ── 後端B:同花順 THS(降級) ──
def fetch_sector_ths():
"""THS 行業板塊(含漲跌家數、資金流向)"""
try:
import akshare as ak
df = ak.stock_board_industry_summary_ths()
return [{
"name": r["板块"], "code": "", "price": 0,
"change": float(r.get("涨跌幅", 0)),
"up_count": int(r.get("上涨家数", 0)),
"down_count": int(r.get("下跌家数", 0)),
"net_inflow": float(r.get("净流入", 0)),
} for _, r in df.iterrows()]
except Exception as e:
print(f"⚠️ THS行業失敗: {e}")
return []
def fetch_concept_ths():
"""THS 概念板塊(僅名稱,無實時漲跌)"""
try:
import akshare as ak
df = ak.stock_board_concept_name_ths()
return [{"name": r["name"], "code": str(r["code"]), "change": 0}
for _, r in df.head(15).iterrows()]
except Exception as e:
print(f"⚠️ THS概念失敗: {e}")
return []
# ── 主流程 ──
def get_market_mood(sectors):
if not sectors:
return "unknown"
ratio = sum(1 for s in sectors if s.get("change", 0) > 0) / len(sectors)
return "bullish" if ratio > 0.7 else "neutral" if ratio > 0.4 else "bearish"
def main():
sectors = fetch_sector_em()
if sectors is None:
sectors = fetch_sector_ths()
concepts = fetch_concept_em()
if concepts is None:
concepts = fetch_concept_ths()
sorted_sectors = sorted(sectors, key=lambda s: s.get("change", 0), reverse=True)
top_gainers = [s for s in sorted_sectors if s.get("change", 0) > 0][:5]
top_losers = [s for s in reversed(sorted_sectors) if s.get("change", 0) < 0][:3]
market_data = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
"total_sectors": len(sectors),
"up_ratio": round(sum(1 for s in sectors if s.get("change", 0) > 0) / max(len(sectors), 1) * 100, 1),
"mood": get_market_mood(sectors),
"top_gainers": top_gainers,
"top_losers": top_losers,
"sectors": sectors,
"concepts": concepts,
}
DATA_DIR.mkdir(parents=True, exist_ok=True)
with open(DATA_DIR / "market.json", "w", encoding="utf-8") as f:
json.dump(market_data, f, ensure_ascii=False, indent=2)
# 安静:数据只写文件,不打印到stdout,避免cron输出被推送
if __name__ == "__main__":
main()