143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
小唯每日新闻监控 — 宏观+行业+事件(精简版)
|
||
==========================================
|
||
只使用可靠的快速数据源
|
||
|
||
用法:
|
||
python3 stock_news.py # 获取今日摘要
|
||
python3 stock_news.py --push # 获取+推送飞书
|
||
"""
|
||
|
||
import json, sys, urllib.request
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
OUTPUT = Path.home() / ".hermes" / "stock_backtest"
|
||
OUTPUT.mkdir(exist_ok=True)
|
||
FEISHU_WEBHOOK = "https://open.feishu.cn/open-apis/bot/v2/hook/446db983-e392-4d2c-bfb8-f9060e5df3ad"
|
||
|
||
|
||
def send_feishu(msg):
|
||
payload = json.dumps({"msg_type": "text", "content": {"text": msg}}).encode()
|
||
req = urllib.request.Request(FEISHU_WEBHOOK, data=payload,
|
||
headers={"Content-Type": "application/json"})
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=8):
|
||
pass
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def get_url(url, timeout=4, enc="gbk"):
|
||
"""快速HTTP GET,统一用curl避免urllib在hermes/scripts目录异常"""
|
||
import subprocess, shlex, os
|
||
env = dict(os.environ)
|
||
for k in ["http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"]:
|
||
env.pop(k, None)
|
||
cmd = ["curl", "-s", "--max-time", str(timeout), url]
|
||
try:
|
||
r = subprocess.run(cmd, capture_output=True, timeout=timeout + 2, env=env)
|
||
return r.stdout.decode(enc, errors="ignore")
|
||
except Exception:
|
||
return ""
|
||
|
||
|
||
def get_market():
|
||
"""大盘上证+沪深300 + 五粮液价格"""
|
||
result = {}
|
||
for code, name in [("sh000001","上证"), ("sh510300","沪深300"), ("sz000858","五粮液")]:
|
||
text = get_url(f"https://qt.gtimg.cn/q={code}")
|
||
parts = text.split("~")
|
||
if len(parts) > 10:
|
||
try:
|
||
price = float(parts[3])
|
||
yclose = float(parts[4])
|
||
chg = (price - yclose) / yclose * 100
|
||
result[name] = {"price": price, "change": chg}
|
||
except (ValueError, IndexError):
|
||
pass
|
||
return result
|
||
|
||
|
||
def get_oil():
|
||
"""布伦特原油 — 腾讯行情 hf_OIL ✅ 已验证可用"""
|
||
try:
|
||
url = "https://qt.gtimg.cn/q=hf_OIL"
|
||
text = get_url(url)
|
||
# hf_OIL 格式: v_hf_OIL="75.23,-1.40,75.22,75.27,77.52,75.22,05:59:59,76.30,76.15,0,77,2,2026-07-11,文字"
|
||
# parts[0]="v_hf_OIL=", parts[1]="75.23,-1.40,..."
|
||
if "=" in text:
|
||
val_part = text.split("=", 1)[1].strip('"; \n')
|
||
vals = val_part.split(",")
|
||
if len(vals) >= 8:
|
||
price = float(vals[0])
|
||
yclose = float(vals[7])
|
||
return {"value": price, "change": price - yclose}
|
||
except Exception:
|
||
pass
|
||
return None
|
||
|
||
|
||
def build_report(push=False):
|
||
today = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||
|
||
# 1. 大盘+持仓
|
||
market = get_market()
|
||
|
||
# 2. 原油
|
||
oil = get_oil()
|
||
|
||
# 五粮液信号(读取信号文件)
|
||
signal = "空仓"
|
||
try:
|
||
sig_file = OUTPUT / "四维_000858.json"
|
||
if sig_file.exists():
|
||
with open(sig_file) as f:
|
||
d = json.load(f)
|
||
signal = d.get("verdict", "空仓").split("—")[0].strip()
|
||
except Exception:
|
||
pass
|
||
|
||
msg = f"""📰 每日宏观+持仓摘要
|
||
|
||
【大盘】{today}
|
||
"""
|
||
for name, data in market.items():
|
||
e = "📈" if data["change"] > 0 else "📉"
|
||
msg += f"{e} {name}: {data['price']:.2f} ({data['change']:+.2f}%)\n"
|
||
|
||
msg += f"\n【五粮液持仓信号】{signal}\n"
|
||
|
||
if oil:
|
||
e = "📈" if oil["change"] > 0 else "📉"
|
||
msg += f"\n{e} 布伦特原油: {oil['value']} ({oil['change']:+.2f})"
|
||
else:
|
||
msg += f"\n• 原油: (获取失败)"
|
||
|
||
msg += f"""
|
||
【宏观风险提示】
|
||
• 大盘若跌破关键均线 → 空仓信号加强
|
||
• 白酒消费数据持续低迷 → 基本面承压
|
||
• 人民币贬值预期 → 外资流出白酒板块压力
|
||
|
||
【五粮液状态】
|
||
价格: {market.get('五粮液',{}).get('price','--')} ({market.get('五粮液',{}).get('change',0):+.2f}%)
|
||
MA20下方 → 空仓信号,等待金叉
|
||
|
||
小唯股票投研 · 每日新闻"""
|
||
|
||
if push:
|
||
send_feishu(msg)
|
||
|
||
# 存档
|
||
report_file = OUTPUT / f"news_{datetime.now().strftime('%Y%m%d')}.txt"
|
||
with open(report_file, "w") as f:
|
||
f.write(msg)
|
||
|
||
print(msg)
|
||
return msg
|
||
|
||
|
||
if __name__ == "__main__":
|
||
build_report(push="--push" in sys.argv) |