#!/usr/bin/env python3 """ A股量化交易主程序 """ import sys from pathlib import Path # 将src目录添加到Python路径 current_dir = Path(__file__).parent src_dir = current_dir / "src" sys.path.insert(0, str(src_dir)) from loguru import logger from src.utils.config_loader import config_loader from src.data.data_fetcher import ADataFetcher from src.data.sentiment_fetcher import SentimentFetcher from src.utils.notification import NotificationManager from src.strategy.kline_pattern_strategy import KLinePatternStrategy from src.database.mysql_database_manager import MySQLDatabaseManager def setup_logging(): """设置日志配置""" log_config = config_loader.get_logging_config() # 移除默认的控制台日志 logger.remove() # 添加控制台输出 logger.add( sys.stdout, level=log_config.get('level', 'INFO'), format=log_config.get('format', '{time} | {level} | {message}') ) # 添加文件输出 log_file = Path(log_config.get('file_path', 'logs/trading.log')) log_file.parent.mkdir(parents=True, exist_ok=True) logger.add( log_file, level=log_config.get('level', 'INFO'), format=log_config.get('format', '{time} | {level} | {message}'), rotation=log_config.get('rotation', '1 day'), retention=log_config.get('retention', '30 days') ) logger.info("日志系统初始化完成") def main(): """主函数""" print("="*60) print(" A股量化交易系统") print("="*60) try: # 初始化日志 setup_logging() # 加载配置 config = config_loader.load_config() logger.info("配置文件加载成功") # 初始化数据获取器 data_fetcher = ADataFetcher() sentiment_fetcher = SentimentFetcher() # 初始化MySQL数据库管理器 db_manager = MySQLDatabaseManager() logger.info("MySQL数据库管理器初始化完成") # 初始化通知管理器 notification_config = config.get('notification', {}) notification_manager = NotificationManager(notification_config) # 初始化K线形态策略 strategy_config = config.get('strategy', {}).get('kline_pattern', {}) if strategy_config.get('enabled', False): kline_strategy = KLinePatternStrategy(data_fetcher, notification_manager, strategy_config, db_manager) logger.info("K线形态策略已启用") else: kline_strategy = None logger.info("K线形态策略未启用") # 显示系统信息 logger.info("系统启动成功") print("\n系统功能:") print("1. 数据获取 - 实时行情、历史数据、财务数据") print("2. 舆情分析 - 北向资金、融资融券、热点股票、龙虎榜") print("3. K线形态策略 - 两阳线+阴线+阳线突破形态识别") print("4. 股票筛选 - 基于技术指标和基本面的选股") print("5. 实时监控 - 价格变动、成交量异常监控") print("6. 策略回测 - 历史数据验证交易策略") # 获取市场概况 print("\n正在获取市场概况...") market_overview = data_fetcher.get_market_overview() if market_overview: print(f"\n市场概况 (更新时间: {market_overview.get('update_time', 'N/A')}):") for market, data in market_overview.items(): if market != 'update_time' and isinstance(data, dict): price = data.get('close', data.get('current', 'N/A')) change = data.get('change', 'N/A') change_pct = data.get('change_pct', 'N/A') print(f" {market.upper()}: 价格={price}, 涨跌={change}, 涨跌幅={change_pct}%") print("\n系统就绪,等待指令...") print("输入 'help' 查看帮助,输入 'quit' 退出程序") # 简单的交互式命令行 while True: try: command = input("\n> ").strip().lower() if command == 'quit' or command == 'exit': print("感谢使用A股量化交易系统!") break elif command == 'help': print_help() elif command == 'status': print_system_status() elif command.startswith('search '): keyword = command[7:] # 移除'search ' search_stocks(data_fetcher, keyword) elif command == 'market': show_market_overview(data_fetcher) elif command == 'sentiment': show_market_sentiment(sentiment_fetcher) elif command == 'hotstock': show_hot_stocks(sentiment_fetcher) elif command == 'northflow': show_north_flow(sentiment_fetcher) elif command == 'dragon': show_dragon_tiger_list(sentiment_fetcher) elif command.startswith('analyze '): stock_code = command[8:] # 移除'analyze ' analyze_stock_sentiment(sentiment_fetcher, stock_code) elif command == 'strategy': show_strategy_info(kline_strategy) elif command.startswith('scan '): stock_code = command[5:] # 移除'scan ' scan_single_stock(kline_strategy, stock_code) elif command == 'scanmarket': scan_market_patterns(kline_strategy) elif command == 'testnotify': test_notification(notification_manager) else: print("未知命令,输入 'help' 查看帮助") except KeyboardInterrupt: print("\n\n程序被用户中断") break except Exception as e: logger.error(f"命令执行错误: {e}") print(f"执行错误: {e}") except Exception as e: logger.error(f"程序启动失败: {e}") print(f"启动失败: {e}") sys.exit(1) def print_help(): """打印帮助信息""" print("\n可用命令:") print(" help - 显示此帮助信息") print(" status - 显示系统状态") print(" market - 显示市场概况") print(" search <关键词> - 搜索股票") print(" sentiment - 显示市场舆情综合概览") print(" hotstock - 显示热门股票排行") print(" northflow - 显示北向资金流向") print(" dragon - 显示龙虎榜数据") print(" analyze <股票代码> - 分析单只股票舆情") print(" strategy - 显示K线形态策略信息") print(" scan <股票代码> - 扫描单只股票K线形态") print(" scanmarket - 扫描市场K线形态") print(" testnotify - 测试通知功能") print(" quit/exit - 退出程序") def print_system_status(): """显示系统状态""" config = config_loader.config print("\n系统状态:") print(f" 配置文件: 已加载") print(f" 数据源: {config.get('data', {}).get('sources', {}).get('primary', 'N/A')}") print(f" 日志级别: {config.get('logging', {}).get('level', 'N/A')}") print(f" 实时监控: {'启用' if config.get('monitor', {}).get('realtime', {}).get('enabled', False) else '禁用'}") def search_stocks(data_fetcher: ADataFetcher, keyword: str): """搜索股票""" if not keyword: print("请提供搜索关键词") return print(f"\n搜索股票: {keyword}") results = data_fetcher.search_stocks(keyword) if not results.empty: print(f"找到 {len(results)} 个结果:") for idx, row in results.head(10).iterrows(): # 只显示前10个结果 code = row.get('stock_code', 'N/A') name = row.get('short_name', 'N/A') print(f" {code} - {name}") if len(results) > 10: print(f" ... 还有 {len(results) - 10} 个结果") else: print("未找到匹配的股票") def show_market_overview(data_fetcher: ADataFetcher): """显示市场概况""" print("\n正在获取最新市场数据...") overview = data_fetcher.get_market_overview() if overview: print(f"\n市场概况 (更新时间: {overview.get('update_time', 'N/A')}):") for market, data in overview.items(): if market != 'update_time' and isinstance(data, dict): price = data.get('close', data.get('current', 'N/A')) change = data.get('change', 'N/A') change_pct = data.get('change_pct', 'N/A') volume = data.get('volume', 'N/A') print(f" {market.upper()}: 价格={price}, 涨跌={change}, 涨跌幅={change_pct}%, 成交量={volume}") else: print("无法获取市场数据") def show_market_sentiment(sentiment_fetcher: SentimentFetcher): """显示市场舆情综合概览""" print("\n正在获取市场舆情数据...") overview = sentiment_fetcher.get_market_sentiment_overview() if overview: print(f"\n市场舆情综合概览 (更新时间: {overview.get('update_time', 'N/A')}):") # 北向资金 if 'north_flow' in overview: north_data = overview['north_flow'] print(f"\n📊 北向资金:") print(f" 总净流入: {north_data.get('net_total', 'N/A')} 万元") print(f" 沪股通: {north_data.get('net_hgt', 'N/A')} 万元") print(f" 深股通: {north_data.get('net_sgt', 'N/A')} 万元") print(f" 更新时间: {north_data.get('update_time', 'N/A')}") # 融资融券 if 'latest_margin' in overview: margin_data = overview['latest_margin'] print(f"\n📈 融资融券:") print(f" 融资余额: {margin_data.get('rzye', 'N/A')} 亿元") print(f" 融券余额: {margin_data.get('rqye', 'N/A')} 亿元") print(f" 两融余额: {margin_data.get('rzrqye', 'N/A')} 亿元") # 热门股票(前5名) if 'hot_stocks_east' in overview and not overview['hot_stocks_east'].empty: print(f"\n🔥 东财热门股票TOP5:") for idx, row in overview['hot_stocks_east'].head(5).iterrows(): code = row.get('stock_code', 'N/A') name = row.get('short_name', 'N/A') rank = row.get('rank', idx + 1) print(f" {rank}. {code} - {name}") # 热门概念(前5名) if 'hot_concepts' in overview and not overview['hot_concepts'].empty: print(f"\n💡 热门概念TOP5:") for idx, row in overview['hot_concepts'].head(5).iterrows(): name = row.get('concept_name', 'N/A') change_pct = row.get('change_pct', 'N/A') rank = row.get('rank', idx + 1) print(f" {rank}. {name} (涨跌幅: {change_pct}%)") # 龙虎榜(前3名) if 'dragon_tiger' in overview and not overview['dragon_tiger'].empty: print(f"\n🐉 今日龙虎榜TOP3:") for idx, row in overview['dragon_tiger'].head(3).iterrows(): code = row.get('stock_code', 'N/A') name = row.get('short_name', 'N/A') reason = row.get('reason', 'N/A') print(f" {idx + 1}. {code} - {name} ({reason})") else: print("无法获取市场舆情数据") def show_hot_stocks(sentiment_fetcher: SentimentFetcher): """显示热门股票排行""" print("\n正在获取热门股票数据...") # 东财人气股票 east_stocks = sentiment_fetcher.get_popular_stocks_east_100() if not east_stocks.empty: print(f"\n🔥 东财人气股票TOP10:") for idx, row in east_stocks.head(10).iterrows(): code = row.get('stock_code', 'N/A') name = row.get('short_name', 'N/A') rank = row.get('rank', idx + 1) change_pct = row.get('change_pct', 'N/A') print(f" {rank}. {code} - {name} (涨跌幅: {change_pct}%)") # 同花顺热门股票 ths_stocks = sentiment_fetcher.get_hot_stocks_ths_100() if not ths_stocks.empty: print(f"\n🌟 同花顺热门股票TOP10:") for idx, row in ths_stocks.head(10).iterrows(): code = row.get('stock_code', 'N/A') name = row.get('short_name', 'N/A') rank = row.get('rank', idx + 1) change_pct = row.get('change_pct', 'N/A') print(f" {rank}. {code} - {name} (涨跌幅: {change_pct}%)") def show_north_flow(sentiment_fetcher: SentimentFetcher): """显示北向资金流向""" print("\n正在获取北向资金数据...") # 当前流向 current_flow = sentiment_fetcher.get_north_flow_current() if not current_flow.empty: print(f"\n💰 当前北向资金流向:") for idx, row in current_flow.iterrows(): net_total = row.get('net_tgt', 'N/A') net_hgt = row.get('net_hgt', 'N/A') net_sgt = row.get('net_sgt', 'N/A') trade_time = row.get('trade_time', 'N/A') print(f" 总净流入: {net_total} 万元") print(f" 沪股通: {net_hgt} 万元") print(f" 深股通: {net_sgt} 万元") print(f" 更新时间: {trade_time}") break # 只显示第一行数据 # 历史流向(最近5天) hist_flow = sentiment_fetcher.get_north_flow_history() if not hist_flow.empty: print(f"\n📊 最近5天北向资金流向:") for idx, row in hist_flow.tail(5).iterrows(): date = row.get('trade_date', 'N/A') net_total = row.get('net_tgt', 'N/A') print(f" {date}: {net_total} 万元") def show_dragon_tiger_list(sentiment_fetcher: SentimentFetcher): """显示龙虎榜数据""" print("\n正在获取龙虎榜数据...") dragon_tiger = sentiment_fetcher.get_dragon_tiger_list_daily() if not dragon_tiger.empty: print(f"\n🐉 今日龙虎榜 (共{len(dragon_tiger)}只股票):") for idx, row in dragon_tiger.head(15).iterrows(): # 显示前15个 code = row.get('stock_code', 'N/A') name = row.get('short_name', 'N/A') reason = row.get('reason', 'N/A') change_pct = row.get('change_pct', 'N/A') amount = row.get('amount', 'N/A') print(f" {idx + 1}. {code} - {name}") print(f" 上榜原因: {reason}") print(f" 涨跌幅: {change_pct}%, 成交金额: {amount} 万元") if len(dragon_tiger) > 15: print(f" ... 还有 {len(dragon_tiger) - 15} 只股票") else: print("今日暂无龙虎榜数据") def analyze_stock_sentiment(sentiment_fetcher: SentimentFetcher, stock_code: str): """分析单只股票舆情""" if not stock_code: print("请提供股票代码") return print(f"\n正在分析股票 {stock_code} 的舆情情况...") analysis = sentiment_fetcher.analyze_stock_sentiment(stock_code) if 'error' in analysis: print(f"分析失败: {analysis['error']}") return print(f"\n📊 {stock_code} 舆情分析报告:") print(f"更新时间: {analysis.get('update_time', 'N/A')}") # 热度情况 print(f"\n🔥 热度情况:") print(f" 东财人气榜: {'在榜' if analysis.get('in_popular_east', False) else '不在榜'}") print(f" 同花顺热门榜: {'在榜' if analysis.get('in_hot_ths', False) else '不在榜'}") # 龙虎榜情况 if 'dragon_tiger' in analysis and not analysis['dragon_tiger'].empty: print(f"\n🐉 龙虎榜情况:") dragon_data = analysis['dragon_tiger'].iloc[0] reason = dragon_data.get('reason', 'N/A') amount = dragon_data.get('amount', 'N/A') print(f" 上榜原因: {reason}") print(f" 成交金额: {amount} 万元") else: print(f"\n🐉 龙虎榜情况: 今日未上榜") # 风险扫描 if 'risk_scan' in analysis and not analysis['risk_scan'].empty: print(f"\n⚠️ 风险扫描:") risk_data = analysis['risk_scan'].iloc[0] risk_level = risk_data.get('risk_level', 'N/A') risk_desc = risk_data.get('risk_desc', 'N/A') print(f" 风险等级: {risk_level}") print(f" 风险描述: {risk_desc}") else: print(f"\n⚠️ 风险扫描: 暂无数据") def show_strategy_info(kline_strategy: KLinePatternStrategy): """显示K线形态策略信息""" if kline_strategy is None: print("K线形态策略未启用") return print("\n" + "="*60) print(" K线形态策略信息") print("="*60) print(kline_strategy.get_strategy_summary()) def scan_single_stock(kline_strategy: KLinePatternStrategy, stock_code: str): """扫描单只股票K线形态""" if kline_strategy is None: print("K线形态策略未启用") return if not stock_code: print("请提供股票代码") return print(f"\n正在扫描股票 {stock_code} 的K线形态...") try: results = kline_strategy.analyze_stock(stock_code) print(f"\n📊 {stock_code} K线形态分析结果:") total_signals = 0 for timeframe, signals in results.items(): print(f"\n{timeframe.upper()} 时间周期:") if signals: for i, signal in enumerate(signals, 1): print(f" 信号 {i}:") print(f" 日期: {signal['date']}") print(f" 形态: {signal['pattern_type']}") print(f" 突破价格: {signal['breakout_price']:.2f} 元") print(f" 突破幅度: {signal['breakout_pct']:.2f}%") print(f" 阳线1实体比例: {signal['yang1_entity_ratio']:.1%}") print(f" 阳线2实体比例: {signal['yang2_entity_ratio']:.1%}") print(f" EMA20价格: {signal['ema20_price']:.2f} 元") print(f" EMA20状态: {'✅ 上方' if signal['above_ema20'] else '❌ 下方'}") print(f" 换手率: {signal.get('turnover_ratio', 0):.2f}%") total_signals += len(signals) print(f" 共发现 {len(signals)} 个信号") else: print(" 未发现形态信号") print(f"\n总计发现 {total_signals} 个信号") except Exception as e: logger.error(f"扫描股票失败: {e}") print(f"扫描失败: {e}") def scan_market_patterns(kline_strategy: KLinePatternStrategy): """扫描市场K线形态""" if kline_strategy is None: print("K线形态策略未启用") return print("\n开始扫描市场K线形态...") print("⚠️ 注意: 这可能需要较长时间,请耐心等待") try: # 获取扫描股票数量配置 scan_count = kline_strategy.config.get('scan_stocks_count', 20) print(f"扫描股票数量: {scan_count}") results = kline_strategy.scan_market(max_stocks=scan_count) if results: print(f"\n📈 市场扫描结果 (发现 {len(results)} 只股票有信号):") for stock_code, stock_results in results.items(): total_signals = sum(len(signals) for signals in stock_results.values()) print(f"\n股票: {stock_code} (共{total_signals}个信号)") for timeframe, signals in stock_results.items(): if signals: print(f" {timeframe}: {len(signals)}个信号") # 只显示最新的信号 latest_signal = signals[-1] print(f" 最新: {latest_signal['date']} 突破价格 {latest_signal['breakout_price']:.2f}元") else: print("未发现任何K线形态信号") except Exception as e: logger.error(f"市场扫描失败: {e}") print(f"扫描失败: {e}") def test_notification(notification_manager: NotificationManager): """测试通知功能""" print("\n正在测试通知功能...") try: # 发送测试消息 success = notification_manager.send_test_message() if success: print("✅ 通知测试成功") else: print("❌ 通知测试失败,请检查配置") # 发送策略信号测试 test_success = notification_manager.send_strategy_signal( stock_code="000001.SZ", stock_name="平安银行", timeframe="daily", signal_type="测试信号", price=10.50, signal_date="2024-01-15", additional_info={ "测试项目": "通知功能", "发送时间": "现在" } ) if test_success: print("✅ 策略信号通知测试成功") else: print("❌ 策略信号通知测试失败") except Exception as e: logger.error(f"通知测试失败: {e}") print(f"测试失败: {e}") if __name__ == "__main__": main()