#!/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()