runner_manager.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
运行模式管理模块
负责程序运行模式的管理和切换
支持三种运行模式:控制台运行模式、网页端运行模式、桌面GUI运行模式
"""

import os
import sys
import logging
import argparse
from pathlib import Path
from typing import Dict, List, Tuple, Any, Optional

# 配置日志
logger = logging.getLogger(__name__)

class RunnerManager:
    """运行模式管理类"""
    
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        """单例模式创建实例"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        """初始化运行模式管理器"""
        if self._initialized:
            return
        
        self._initialized = True
        self._runners = {}
        self._current_runner = None
        self._load_runners()
    
    def _load_runners(self):
        """加载所有运行模式"""
        try:
            # 运行模式列表
            self._runners = {
                'console': ConsoleRunner(),
                'web': WebRunner(),
                'gui': GUIRunner()
            }
            
            logger.info(f"共加载 {len(self._runners)} 种运行模式")
            
        except Exception as e:
            logger.error(f"运行模式加载失败:{e}")
    
    def get_runner(self, runner_name: str) -> Optional[Any]:
        """获取指定名称的运行模式实例"""
        try:
            if runner_name not in self._runners:
                logger.error(f"未找到指定运行模式:{runner_name}")
                return None
            
            logger.info(f"运行模式获取成功:{runner_name}")
            return self._runners[runner_name]
            
        except Exception as e:
            logger.error(f"运行模式获取失败:{e}")
            return None
    
    def list_runners(self) -> List[Dict[str, Any]]:
        """列出所有可用的运行模式"""
        runners = []
        
        for runner_name, runner_instance in self._runners.items():
            runners.append({
                'name': runner_name,
                'class': runner_instance.__class__.__name__,
                'description': self._get_runner_description(runner_instance)
            })
        
        return runners
    
    def _get_runner_description(self, runner_instance: Any) -> str:
        """获取运行模式实例的描述信息"""
        if runner_instance.__class__.__doc__:
            # 去除文档字符串中的换行和缩进
            return runner_instance.__class__.__doc__.strip().split('\n')[0]
        else:
            return '未指定描述'
    
    def run(self, runner_name: str, args: argparse.Namespace = None) -> None:
        """运行指定的运行模式"""
        try:
            runner = self.get_runner(runner_name)
            if not runner:
                return
            
            # 保存当前运行模式
            self._current_runner = runner
            
            # 运行模式
            runner.run(args)
            
        except Exception as e:
            logger.error(f"运行模式启动失败:{runner_name} - {e}")
    
    def get_current_runner(self) -> Optional[Any]:
        """获取当前运行模式实例"""
        return self._current_runner
    
    def stop(self) -> None:
        """停止当前运行模式"""
        try:
            if self._current_runner:
                self._current_runner.stop()
                logger.info("运行模式停止成功")
            else:
                logger.warning("当前没有正在运行的模式")
                
        except Exception as e:
            logger.error(f"运行模式停止失败:{e}")

class BaseRunner:
    """运行模式基类"""
    
    def run(self, args: argparse.Namespace = None) -> None:
        """运行方法(子类必须实现)"""
        raise NotImplementedError("子类必须实现run方法")
    
    def stop(self) -> None:
        """停止方法(可选实现)"""
        pass

class ConsoleRunner(BaseRunner):
    """控制台运行模式"""
    
    def run(self, args: argparse.Namespace = None) -> None:
        """运行控制台模式"""
        try:
            logger.info("启动控制台运行模式")
            
            # 初始化策略管理器
            from src.strategy_manager import strategy_manager
            
            # 根据命令参数执行相应操作
            if args.init:
                self._init_project()
            elif args.select:
                self._run_selection(args)
            elif args.backtest:
                self._run_backtest(args)
            elif args.optimize:
                self._run_optimization(args)
            elif args.schedule:
                self._start_scheduler()
            elif args.test:
                self._run_test()
            elif args.update:
                self._update_data()
            else:
                logger.warning("未指定操作,程序将退出")
                
        except Exception as e:
            logger.error(f"控制台模式运行失败:{e}")
    
    def _init_project(self) -> None:
        """初始化项目"""
        try:
            logger.info("开始初始化项目...")
            
            from main import init_project
            init_project()
            
        except Exception as e:
            logger.error(f"项目初始化失败:{e}")
    
    def _run_selection(self, args: argparse.Namespace) -> None:
        """运行选股"""
        try:
            # 解析策略列表
            strategy_names = args.strategies.split(",")
            logger.info(f"使用策略:{', '.join(strategy_names)}")
            
            from src.stock_selector import StockSelector
            selector = StockSelector()
            
            result = selector.run(strategy_names)
            
            if result is not None and not result.empty:
                strategy_name = "-".join(strategy_names)
                selector.save_results(result, strategy_name=strategy_name)
                selector.print_results(result, strategy_name=strategy_name)
            else:
                logger.warning("未选出符合条件的股票")
                
        except Exception as e:
            logger.error(f"选股失败:{e}")
    
    def _run_backtest(self, args: argparse.Namespace) -> None:
        """运行回测"""
        try:
            logger.info("开始回测...")
            
            from src.backtester import Backtester
            backtester = Backtester()
            
            report = backtester.run()
            backtester.save_report(report)
            backtester.print_report(report)
            
        except Exception as e:
            logger.error(f"回测失败:{e}")
    
    def _run_optimization(self, args: argparse.Namespace) -> None:
        """运行参数优化"""
        try:
            logger.info("开始参数优化...")
            
            from src.parameter_optimizer import ParameterOptimizer
            optimizer = ParameterOptimizer()
            
            best_params = optimizer.run()
            optimizer.save_best_parameters(best_params)
            
        except Exception as e:
            logger.error(f"参数优化失败:{e}")
    
    def _start_scheduler(self) -> None:
        """启动调度器"""
        try:
            logger.info("开始调度服务...")
            
            from src.scheduler import Scheduler
            scheduler = Scheduler()
            scheduler.start()
            
        except Exception as e:
            logger.error(f"调度服务启动失败:{e}")
    
    def _run_test(self) -> None:
        """运行测试"""
        try:
            logger.info("开始一键测试...")
            
            from main import run_test
            run_test()
            
        except Exception as e:
            logger.error(f"测试失败:{e}")
    
    def _update_data(self) -> None:
        """更新数据"""
        try:
            logger.info("开始更新历史数据...")
            
            from main import update_data
            update_data()
            
        except Exception as e:
            logger.error(f"数据更新失败:{e}")
    
    def stop(self) -> None:
        """停止控制台模式"""
        logger.info("控制台模式停止")

class WebRunner(BaseRunner):
    """网页端运行模式"""
    
    def run(self, args: argparse.Namespace = None) -> None:
        """运行网页端模式"""
        try:
            logger.info("启动网页端运行模式")
            
            from web.app import WebServer
            
            # 获取网页服务配置
            host = args.host if args and hasattr(args, 'host') else '0.0.0.0'
            port = args.port if args and hasattr(args, 'port') else 5000
            
            logger.info(f"网页服务地址:http://{host}:{port}")
            
            server = WebServer(host=host, port=port)
            server.run()
            
        except Exception as e:
            logger.error(f"网页端模式运行失败:{e}")
    
    def stop(self) -> None:
        """停止网页端模式"""
        logger.info("网页端模式停止")

class GUIRunner(BaseRunner):
    """桌面GUI运行模式"""
    
    def run(self, args: argparse.Namespace = None) -> None:
        """运行桌面GUI模式"""
        try:
            logger.info("启动桌面GUI运行模式")
            
            # 检查是否安装了Tkinter
            try:
                import tkinter
                from tkinter import ttk
            except ImportError as e:
                logger.error(f"桌面GUI模式需要Tkinter库,请安装:{e}")
                return
            
            logger.warning("桌面GUI模式尚未完全实现,仅显示基础界面")
            
            self._create_gui()
            
        except Exception as e:
            logger.error(f"桌面GUI模式运行失败:{e}")
    
    def _create_gui(self):
        """创建GUI界面"""
        try:
            import tkinter as tk
            from tkinter import ttk
            from tkinter import messagebox
            
            # 创建主窗口
            root = tk.Tk()
            root.title("超短线量化选股程序")
            root.geometry("800x600")
            
            # 创建菜单栏
            menubar = tk.Menu(root)
            
            # 文件菜单
            file_menu = tk.Menu(menubar, tearoff=0)
            file_menu.add_command(label="退出", command=root.quit)
            menubar.add_cascade(label="文件", menu=file_menu)
            
            # 操作菜单
            action_menu = tk.Menu(menubar, tearoff=0)
            action_menu.add_command(label="选股", command=self._run_selection_gui)
            action_menu.add_command(label="回测", command=self._run_backtest_gui)
            action_menu.add_command(label="参数优化", command=self._run_optimization_gui)
            action_menu.add_command(label="更新数据", command=self._update_data_gui)
            menubar.add_cascade(label="操作", menu=action_menu)
            
            # 帮助菜单
            help_menu = tk.Menu(menubar, tearoff=0)
            help_menu.add_command(label="关于", command=self._show_about)
            menubar.add_cascade(label="帮助", menu=help_menu)
            
            root.config(menu=menubar)
            
            # 创建标签
            label = ttk.Label(root, text="超短线量化选股程序", font=("Arial", 16))
            label.pack(pady=20)
            
            # 创建状态标签
            status_label = ttk.Label(root, text="就绪")
            status_label.pack(pady=10)
            
            # 运行主事件循环
            root.mainloop()
            
        except Exception as e:
            logger.error(f"GUI界面创建失败:{e}")
    
    def _run_selection_gui(self):
        """运行选股"""
        logger.info("选股操作被调用")
    
    def _run_backtest_gui(self):
        """运行回测"""
        logger.info("回测操作被调用")
    
    def _run_optimization_gui(self):
        """运行参数优化"""
        logger.info("参数优化操作被调用")
    
    def _update_data_gui(self):
        """更新数据"""
        logger.info("数据更新操作被调用")
    
    def _show_about(self):
        """显示关于信息"""
        import tkinter as tk
        from tkinter import messagebox
        
        messagebox.showinfo("关于", "超短线量化选股程序\n版本:1.0.0")
    
    def stop(self) -> None:
        """停止桌面GUI模式"""
        logger.info("桌面GUI模式停止")

# 创建运行模式管理器实例
runner_manager = RunnerManager()