""" 智能盯盘 - 主引擎 整合DeepSeek AI决策、数据获取、交易执行、通知等功能 """ import logging import time from typing import Dict, List, Optional from datetime import datetime import threading from smart_monitor_deepseek import SmartMonitorDeepSeek from smart_monitor_data import SmartMonitorDataFetcher from smart_monitor_qmt import SmartMonitorQMT, SmartMonitorQMTSimulator from smart_monitor_db import SmartMonitorDB from notification_service import notification_service # 复用主程序的通知服务 from config_manager import config_manager # 复用主程序的配置管理器 class SmartMonitorEngine: """智能盯盘引擎""" def __init__(self, deepseek_api_key: str = None, qmt_account_id: str = None, use_simulator: bool = None): """ 初始化智能盯盘引擎 Args: deepseek_api_key: DeepSeek API密钥(可选,从配置读取) qmt_account_id: miniQMT账户ID(可选,从配置读取) use_simulator: 是否使用模拟交易(可选,从配置读取) """ self.logger = logging.getLogger(__name__) # 从配置管理器读取配置 config = config_manager.read_env() # DeepSeek API if deepseek_api_key is None: deepseek_api_key = config.get('DEEPSEEK_API_KEY', '') # MiniQMT配置 if qmt_account_id is None: qmt_account_id = config.get('MINIQMT_ACCOUNT_ID', '') if use_simulator is None: # 如果MINIQMT_ENABLED=false,则使用模拟器 miniqmt_enabled = config.get('MINIQMT_ENABLED', 'false').lower() == 'true' use_simulator = not miniqmt_enabled # 初始化各个模块 self.deepseek = SmartMonitorDeepSeek(deepseek_api_key) self.data_fetcher = SmartMonitorDataFetcher() self.db = SmartMonitorDB() self.notification = notification_service # 使用主程序的通知服务 # 初始化交易接口 if use_simulator: self.qmt = SmartMonitorQMTSimulator() self.qmt.connect(qmt_account_id or "simulator") self.logger.info("使用模拟交易模式") else: self.qmt = SmartMonitorQMT() if qmt_account_id: success = self.qmt.connect(qmt_account_id) if success: self.logger.info(f"已连接miniQMT账户: {qmt_account_id}") else: self.logger.warning(f"连接miniQMT失败,切换到模拟模式") self.qmt = SmartMonitorQMTSimulator() self.qmt.connect("simulator") else: self.logger.warning("未配置miniQMT账户,使用模拟模式") self.qmt = SmartMonitorQMTSimulator() self.qmt.connect("simulator") # 监控线程控制 self.monitoring_threads = {} self.stop_flags = {} self.logger.info("智能盯盘引擎初始化完成") def analyze_stock(self, stock_code: str, auto_trade: bool = False, notify: bool = True, has_position: bool = False, position_cost: float = 0, position_quantity: int = 0) -> Dict: """ 分析单只股票并做出决策 Args: stock_code: 股票代码 auto_trade: 是否自动交易 notify: 是否发送通知 has_position: 是否已持仓(可选) position_cost: 持仓成本(可选) position_quantity: 持仓数量(可选) Returns: 分析结果 """ try: self.logger.info(f"[{stock_code}] 开始分析...") # 1. 检查交易时段 session_info = self.deepseek.get_trading_session() self.logger.info(f"[{stock_code}] 当前时段: {session_info['session']}") # 2. 获取市场数据 market_data = self.data_fetcher.get_comprehensive_data(stock_code) if not market_data: return { 'success': False, 'error': '获取市场数据失败' } # 3. 获取账户信息 account_info = self.qmt.get_account_info() # 4. 检查是否已持有该股票 # 优先使用传入的持仓信息,否则从QMT获取 if has_position and position_cost > 0 and position_quantity > 0: # 使用用户设置的持仓信息 self.logger.info(f"[{stock_code}] 使用监控任务设置的持仓: {position_quantity}股 @ {position_cost:.2f}元") else: # 从QMT获取持仓 position = self.qmt.get_position(stock_code) has_position = position is not None if has_position: position_cost = position.get('cost_price', 0) position_quantity = position.get('quantity', 0) account_info['current_position'] = position self.logger.info(f"[{stock_code}] 从QMT获取持仓: {position_quantity}股, " f"成本价: {position_cost:.2f}, " f"浮动盈亏: {position.get('profit_loss_pct', 0):+.2f}%") # 5. 调用DeepSeek AI决策 ai_result = self.deepseek.analyze_stock_and_decide( stock_code=stock_code, market_data=market_data, account_info=account_info, has_position=has_position, position_cost=position_cost, position_quantity=position_quantity ) if not ai_result['success']: return { 'success': False, 'error': 'AI决策失败', 'details': ai_result } decision = ai_result['decision'] self.logger.info(f"[{stock_code}] AI决策: {decision['action']} " f"(信心度: {decision['confidence']}%)") self.logger.info(f"[{stock_code}] 决策理由: {decision['reasoning'][:100]}...") # 6. 保存AI决策到数据库 decision_id = self.db.save_ai_decision({ 'stock_code': stock_code, 'stock_name': market_data.get('name'), 'trading_session': session_info['session'], 'action': decision['action'], 'confidence': decision['confidence'], 'reasoning': decision['reasoning'], 'position_size_pct': decision.get('position_size_pct'), 'stop_loss_pct': decision.get('stop_loss_pct'), 'take_profit_pct': decision.get('take_profit_pct'), 'risk_level': decision.get('risk_level'), 'key_price_levels': decision.get('key_price_levels', {}), 'market_data': market_data, 'account_info': account_info }) # 7. 执行交易(如果开启自动交易) execution_result = None if auto_trade and session_info['can_trade']: execution_result = self._execute_decision( stock_code=stock_code, decision=decision, market_data=market_data, has_position=has_position ) # 更新决策执行状态 self.db.update_decision_execution( decision_id=decision_id, executed=execution_result.get('success', False), result=str(execution_result) ) # 8. 发送通知 if notify: self._send_notification( stock_code=stock_code, stock_name=market_data.get('name'), decision=decision, execution_result=execution_result, market_data=market_data ) return { 'success': True, 'stock_code': stock_code, 'stock_name': market_data.get('name'), 'session_info': session_info, 'market_data': market_data, 'decision': decision, 'decision_id': decision_id, 'execution_result': execution_result } except Exception as e: self.logger.error(f"[{stock_code}] 分析失败: {e}") import traceback traceback.print_exc() return { 'success': False, 'error': str(e) } def _execute_decision(self, stock_code: str, decision: Dict, market_data: Dict, has_position: bool) -> Dict: """ 执行AI决策 Args: stock_code: 股票代码 decision: AI决策 market_data: 市场数据 has_position: 是否已持有 Returns: 执行结果 """ action = decision['action'] try: if action == 'BUY' and not has_position: # 买入逻辑 return self._execute_buy(stock_code, decision, market_data) elif action == 'SELL' and has_position: # 卖出逻辑 return self._execute_sell(stock_code, decision, market_data) elif action == 'HOLD': # 持有,不操作 return { 'success': True, 'action': 'HOLD', 'message': 'AI建议持有,未执行交易' } else: return { 'success': False, 'error': f'无效操作: {action}' } except Exception as e: self.logger.error(f"[{stock_code}] 执行交易失败: {e}") return { 'success': False, 'error': str(e) } def _execute_buy(self, stock_code: str, decision: Dict, market_data: Dict) -> Dict: """执行买入""" try: # 获取账户信息 account_info = self.qmt.get_account_info() available_cash = account_info['available_cash'] # 计算买入金额 position_size_pct = decision.get('position_size_pct', 20) buy_amount = available_cash * (position_size_pct / 100) # 计算买入数量(必须是100的整数倍) current_price = market_data['current_price'] quantity = int(buy_amount / current_price / 100) * 100 if quantity < 100: return { 'success': False, 'error': f'资金不足,最少需要买入100股(约{current_price * 100:.2f}元)' } # 执行买入 result = self.qmt.buy_stock( stock_code=stock_code, quantity=quantity, price=current_price, order_type='market' ) if result['success']: # 保存交易记录 self.db.save_trade_record({ 'stock_code': stock_code, 'stock_name': market_data.get('name'), 'trade_type': 'BUY', 'quantity': quantity, 'price': current_price, 'amount': quantity * current_price, 'order_id': result.get('order_id'), 'order_status': '已提交' }) # 保存持仓监控 self.db.save_position({ 'stock_code': stock_code, 'stock_name': market_data.get('name'), 'quantity': quantity, 'cost_price': current_price, 'current_price': current_price, 'profit_loss': 0, 'profit_loss_pct': 0, 'holding_days': 0, 'buy_date': datetime.now().strftime('%Y-%m-%d'), 'stop_loss_price': current_price * (1 - decision.get('stop_loss_pct', 5) / 100), 'take_profit_price': current_price * (1 + decision.get('take_profit_pct', 10) / 100) }) self.logger.info(f"[{stock_code}] 买入成功: {quantity}股 @ {current_price:.2f}元") return result except Exception as e: self.logger.error(f"[{stock_code}] 买入失败: {e}") return { 'success': False, 'error': str(e) } def _execute_sell(self, stock_code: str, decision: Dict, market_data: Dict) -> Dict: """执行卖出""" try: # 获取持仓 position = self.qmt.get_position(stock_code) if not position: return { 'success': False, 'error': '未持有该股票' } # 可卖数量(考虑T+1限制) can_sell = position['can_sell'] if can_sell <= 0: return { 'success': False, 'error': 'T+1限制,今天买入的股票明天才能卖出' } # 执行卖出 current_price = market_data['current_price'] result = self.qmt.sell_stock( stock_code=stock_code, quantity=can_sell, price=current_price, order_type='market' ) if result['success']: # 计算盈亏 profit_loss = (current_price - position['cost_price']) * can_sell # 保存交易记录 self.db.save_trade_record({ 'stock_code': stock_code, 'stock_name': market_data.get('name'), 'trade_type': 'SELL', 'quantity': can_sell, 'price': current_price, 'amount': can_sell * current_price, 'order_id': result.get('order_id'), 'order_status': '已提交', 'profit_loss': profit_loss }) # 更新或关闭持仓记录 if can_sell >= position['quantity']: self.db.close_position(stock_code) self.logger.info(f"[{stock_code}] 卖出成功: {can_sell}股 @ {current_price:.2f}元, " f"盈亏: {profit_loss:+.2f}元") return result except Exception as e: self.logger.error(f"[{stock_code}] 卖出失败: {e}") return { 'success': False, 'error': str(e) } def _send_notification(self, stock_code: str, stock_name: str, decision: Dict, execution_result: Optional[Dict], market_data: Dict): """发送通知(使用主程序的通知服务)""" try: # 构建通知内容 action_text = { 'BUY': '买入', 'SELL': '卖出', 'HOLD': '持有' }.get(decision['action'], decision['action']) message = f"{action_text}信号 - {stock_name}({stock_code})" # 构建详细内容 content = f""" 【股票信息】 代码: {stock_code} 名称: {stock_name} 当前价: {market_data.get('current_price', 0):.2f}元 涨跌幅: {market_data.get('change_pct', 0):+.2f}% 【AI决策】 操作: {action_text} 信心度: {decision['confidence']}% 风险等级: {decision.get('risk_level', 'N/A')} 【决策理由】 {decision['reasoning'][:200]}... 【技术指标】 MA5: {market_data.get('ma5', 0):.2f} | MA20: {market_data.get('ma20', 0):.2f} MACD: {market_data.get('macd', 0):.4f} | RSI(6): {market_data.get('rsi6', 0):.2f} """ if execution_result: if execution_result.get('success'): content += f"\n✅ 操作已自动执行成功" else: content += f"\n❌ 执行失败: {execution_result.get('error')}" content += f"\n\n⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" # 使用主程序的通知服务格式 notification_data = { 'symbol': stock_code, 'name': stock_name, 'type': '智能盯盘', 'message': message, 'details': content, 'triggered_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } # 直接调用主程序的通知服务发送 success = self.notification.send_notification(notification_data) if success: self.logger.info(f"[{stock_code}] 通知已发送") else: self.logger.warning(f"[{stock_code}] 通知发送失败") # 同时保存到智能盯盘的数据库 self.db.save_notification({ 'stock_code': stock_code, 'notify_type': 'decision', 'subject': f"智能盯盘 - {message}", 'content': content, 'status': 'sent' if success else 'failed' }) except Exception as e: self.logger.error(f"[{stock_code}] 发送通知失败: {e}") import traceback traceback.print_exc() def start_monitor(self, stock_code: str, check_interval: int = 300, auto_trade: bool = False, notify: bool = True, has_position: bool = False, position_cost: float = 0, position_quantity: int = 0): """ 启动股票监控(在独立线程中运行) Args: stock_code: 股票代码 check_interval: 检查间隔(秒) auto_trade: 是否自动交易 notify: 是否发送通知 has_position: 是否已持仓 position_cost: 持仓成本 position_quantity: 持仓数量 """ if stock_code in self.monitoring_threads: self.logger.warning(f"[{stock_code}] 监控已在运行中") return # 创建停止标志 stop_flag = threading.Event() self.stop_flags[stock_code] = stop_flag # 创建监控线程 thread = threading.Thread( target=self._monitor_loop, args=(stock_code, check_interval, auto_trade, notify, stop_flag, has_position, position_cost, position_quantity), daemon=True ) self.monitoring_threads[stock_code] = thread thread.start() position_info = f"(持仓: {position_quantity}股 @ {position_cost:.2f}元)" if has_position else "" self.logger.info(f"[{stock_code}] 监控已启动,间隔: {check_interval}秒 {position_info}") def stop_monitor(self, stock_code: str): """停止股票监控""" if stock_code not in self.monitoring_threads: self.logger.warning(f"[{stock_code}] 监控未运行") return # 设置停止标志 self.stop_flags[stock_code].set() # 等待线程结束 self.monitoring_threads[stock_code].join(timeout=5) # 清理 del self.monitoring_threads[stock_code] del self.stop_flags[stock_code] self.logger.info(f"[{stock_code}] 监控已停止") def _monitor_loop(self, stock_code: str, check_interval: int, auto_trade: bool, notify: bool, stop_flag: threading.Event, has_position: bool = False, position_cost: float = 0, position_quantity: int = 0): """监控循环(在独立线程中运行)""" self.logger.info(f"[{stock_code}] 监控线程已启动") while not stop_flag.is_set(): try: # 执行分析 result = self.analyze_stock( stock_code=stock_code, auto_trade=auto_trade, notify=notify, has_position=has_position, position_cost=position_cost, position_quantity=position_quantity ) if result['success']: self.logger.info(f"[{stock_code}] 分析完成: {result['decision']['action']}") else: self.logger.error(f"[{stock_code}] 分析失败: {result.get('error')}") except Exception as e: self.logger.error(f"[{stock_code}] 监控循环异常: {e}") # 等待下一次检查 stop_flag.wait(check_interval) self.logger.info(f"[{stock_code}] 监控线程已退出") if __name__ == '__main__': # 测试代码 import os from dotenv import load_dotenv load_dotenv() logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) # 使用模拟模式测试 engine = SmartMonitorEngine( deepseek_api_key=os.getenv('DEEPSEEK_API_KEY'), use_simulator=True ) # 测试分析贵州茅台 print("\n测试分析贵州茅台(600519)...") result = engine.analyze_stock('600519', auto_trade=False, notify=False) if result['success']: print(f"\n分析成功!") print(f" 决策: {result['decision']['action']}") print(f" 信心度: {result['decision']['confidence']}%") print(f" 理由: {result['decision']['reasoning'][:100]}...") else: print(f"\n分析失败: {result.get('error')}")