199 lines
5.7 KiB
Python
Executable File
199 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
铜陵有色专项监控 - 实时股价监控
|
||
使用腾讯财经接口,确保数据准确性
|
||
"""
|
||
|
||
import requests
|
||
import json
|
||
import time
|
||
import os
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
# ============ 配置 ============
|
||
|
||
WATCHLIST = [
|
||
{
|
||
"code": "000630",
|
||
"name": "铜陵有色",
|
||
"market": "sz",
|
||
"alerts": {
|
||
"change_pct_above": 5.0, # 涨超 5% 预警
|
||
"change_pct_below": -5.0, # 跌超 5% 预警
|
||
}
|
||
}
|
||
]
|
||
|
||
# 企业微信配置
|
||
WECOM_BOT_ID = "aibwl3AhnzfRPRTEZvBVwlB-vRD33yJdUVX"
|
||
WECOM_SECRET = "1eUB2yd2R7bll6VjBQ5OGptJj2YiwutMUmACe9UGC7k"
|
||
ALLOW_USERS = ["HouHuan", "WanMeiShengHuo", "XinNingXianGuoNaiChaKaFeiZhaJiHa"]
|
||
|
||
# ============ 核心功能 ============
|
||
|
||
def fetch_tencent_price(stock):
|
||
"""腾讯财经接口获取实时股价"""
|
||
code = f"{stock['market']}{stock['code']}"
|
||
url = f"http://qt.gtimg.cn/q={code}"
|
||
|
||
try:
|
||
resp = requests.get(url, timeout=5)
|
||
resp.encoding = 'gbk'
|
||
data = resp.text.strip()
|
||
|
||
# 解析:v_sz000630="51~名称~代码~现价~昨收~今开~...
|
||
if '="' not in data:
|
||
return None
|
||
|
||
parts = data.strip('"').split('~')
|
||
|
||
# 找到前缀位置
|
||
start_idx = 0
|
||
for i, p in enumerate(parts):
|
||
if p.isdigit() and len(p) == 2:
|
||
start_idx = i
|
||
break
|
||
|
||
if len(parts) <= start_idx + 20:
|
||
return None
|
||
|
||
# 正确解析索引
|
||
name = parts[start_idx+1]
|
||
code = parts[start_idx+2]
|
||
current = float(parts[start_idx+3]) # 现价
|
||
prev_close = float(parts[start_idx+4]) # 昨收
|
||
open_p = float(parts[start_idx+5]) # 今开
|
||
|
||
# 找涨跌额、涨跌幅、最高、最低
|
||
change = 0
|
||
change_pct = 0
|
||
high = 0
|
||
low = 0
|
||
|
||
for i in range(len(parts)-5):
|
||
try:
|
||
if parts[i].startswith('-') and '.' in parts[i]:
|
||
v1 = float(parts[i])
|
||
v2 = float(parts[i+1])
|
||
if -10 < v2 < 10: # 涨跌幅一般在 -10~10 之间
|
||
change = v1
|
||
change_pct = v2
|
||
high = float(parts[i+2])
|
||
low = float(parts[i+3])
|
||
break
|
||
except:
|
||
continue
|
||
|
||
return {
|
||
'name': name,
|
||
'code': code,
|
||
'price': current,
|
||
'prev_close': prev_close,
|
||
'open': open_p,
|
||
'high': high,
|
||
'low': low,
|
||
'change': change,
|
||
'change_pct': change_pct,
|
||
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
except Exception as e:
|
||
print(f"❌ 数据获取失败:{e}")
|
||
return None
|
||
|
||
def check_alerts(stock, data):
|
||
"""检查是否触发预警"""
|
||
alerts = []
|
||
cfg = stock.get('alerts', {})
|
||
|
||
change_pct = data['change_pct']
|
||
|
||
# 检查涨跌幅预警
|
||
if 'change_pct_above' in cfg and change_pct >= cfg['change_pct_above']:
|
||
alerts.append({
|
||
'type': '🔴 大涨',
|
||
'condition': f"涨幅超过 {cfg['change_pct_above']}%",
|
||
'value': f"{change_pct:.2f}%"
|
||
})
|
||
|
||
if 'change_pct_below' in cfg and change_pct <= cfg['change_pct_below']:
|
||
alerts.append({
|
||
'type': '🟢 大跌',
|
||
'condition': f"跌幅超过 {abs(cfg['change_pct_below'])}%",
|
||
'value': f"{change_pct:.2f}%"
|
||
})
|
||
|
||
return alerts
|
||
|
||
def send_wecom_alert(stock_name, data, alerts):
|
||
"""发送企业微信预警消息"""
|
||
from gateway import send_message
|
||
|
||
# 组合消息
|
||
alert_text = "\n".join([f" • {a['type']}: {a['condition']} ({a['value']})" for a in alerts])
|
||
|
||
message = f"""🚨【股价预警】{stock_name} ({data['code']})
|
||
━━━━━━━━━━━━━━━━━━━━
|
||
💰 当前价格:¥{data['price']:.2f} ({data['change_pct']:+.2f}%)
|
||
|
||
🎯 触发预警:
|
||
{alert_text}
|
||
|
||
📊 详细数据:
|
||
• 昨收:¥{data['prev_close']:.2f}
|
||
• 今开:¥{data['open']:.2f}
|
||
• 最高:¥{data['high']:.2f}
|
||
• 最低:¥{data['low']:.2f}
|
||
|
||
⏰ 数据时间:{data['time']}
|
||
|
||
💡 建议关注后续走势,注意风险控制。
|
||
"""
|
||
|
||
# 发送给所有允许的用户
|
||
for user in ALLOW_USERS:
|
||
try:
|
||
send_message(
|
||
channel="wecom",
|
||
target=user,
|
||
message=message
|
||
)
|
||
print(f"✅ 预警已发送给用户:{user}")
|
||
except Exception as e:
|
||
print(f"❌ 发送失败 {user}: {e}")
|
||
|
||
def monitor_once():
|
||
"""执行一次监控"""
|
||
print(f"\n{'='*60}")
|
||
print(f"📊 铜陵有色监控 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
print(f"{'='*60}")
|
||
|
||
for stock in WATCHLIST:
|
||
data = fetch_tencent_price(stock)
|
||
|
||
if not data:
|
||
print(f"❌ {stock['name']} 数据获取失败")
|
||
continue
|
||
|
||
print(f"✅ {stock['name']} ({stock['code']})")
|
||
print(f" 现价:¥{data['price']:.2f} ({data['change_pct']:+.2f}%)")
|
||
print(f" 昨收:¥{data['prev_close']:.2f}")
|
||
print(f" 涨跌:¥{data['change']:.2f}")
|
||
|
||
# 检查预警
|
||
alerts = check_alerts(stock, data)
|
||
|
||
if alerts:
|
||
print(f" 🚨 触发 {len(alerts)} 条预警!")
|
||
send_wecom_alert(stock['name'], data, alerts)
|
||
else:
|
||
print(f" ✅ 正常波动范围")
|
||
|
||
print(f"{'='*60}\n")
|
||
|
||
# ============ 主程序 ============
|
||
|
||
if __name__ == "__main__":
|
||
# 单次执行
|
||
monitor_once()
|