瀏覽代碼

新增期货市场技术形态规律分析工具,包含综合分析类和配置参数,支持大幅下跌后K线形态识别及后续走势分析。同时新增主力合约跟踪工具,提供主力合约历史变化获取功能,并更新README文档以说明新功能和使用方法。

maxfeng 1 月之前
父節點
當前提交
2acf267fae
共有 3 個文件被更改,包括 2258 次插入0 次删除
  1. 249 0
      Lib/research/README.md
  2. 1783 0
      Lib/research/future_pattern_comprehensive.py
  3. 226 0
      Lib/research/get_dominant_future.py

+ 249 - 0
Lib/research/README.md

@@ -0,0 +1,249 @@
+# 期货市场研究算法库
+
+## 概述
+本目录包含期货市场的各种研究算法和分析工具,用于量化分析期货市场的技术指标、价格走势和交易策略。所有代码基于聚宽API开发,适用于聚宽在线研究平台。
+
+## 算法文件结构
+
+### 1. 期货多均线穿越分析 (`future_ma_cross_analysis.py`)
+
+#### 功能描述
+分析期货合约的多均线穿越事件,识别同时穿越至少3条均线的K线,并分析后续收益表现。
+
+#### 主要特点
+- **多均线系统**: 使用MA5、MA10、MA20、MA30四条移动平均线
+- **穿越识别**: 精确判断K线实体上穿或下穿至少3条均线
+- **后续跟踪**: 分析穿越后5日、10日、20日、30日的收益率表现
+- **批量处理**: 自动分析所有期货主力合约
+
+#### 核心算法
+```python
+def check_multi_ma_cross(row):
+    """检查单日K线是否向上或向下穿越了至少3条均线"""
+    # 条件:开盘价在均线一侧,收盘价在均线另一侧
+    # 上穿:开盘价 < 均线 且 收盘价 > 均线
+    # 下穿:开盘价 > 均线 且 收盘价 < 均线
+```
+
+#### 输出结果
+- CSV文件:包含所有穿越事件的详细信息和后续收益率
+- 统计报告:穿越方向分布、收益率统计、品种统计等
+
+### 2. 期货市场整体变化趋势分析 (`future_all_change_trend.py`)
+
+#### 功能描述
+分析期货市场整体的价格变化趋势,计算每日市场平均变化率和正负变化分布。
+
+#### 主要特点
+- **市场全貌**: 分析所有期货品种的价格变化
+- **趋势识别**: 计算滚动平均值识别市场趋势
+- **异常检测**: 使用标准差检测异常市场波动
+- **可视化**: 生成趋势图表展示市场变化规律
+
+#### 核心算法
+```python
+def process_data(df, std_dev_multiplier=3):
+    """处理数据并过滤异常值"""
+    # 计算每日平均变化率
+    # 统计正负变化数量
+    # 计算滚动平均值
+    # 过滤异常值
+```
+
+#### 输出结果
+- 每日市场平均变化率
+- 正负变化品种数量统计
+- 滚动平均值和趋势图表
+- 异常市场行为检测报告
+
+### 3. 期货技术形态规律分析 (`future_pattern_comprehensive.py`)
+
+#### 功能描述
+**核心研究项目** - 研究期货品种在大幅下跌后出现特定K线形态的规律,并分析后续走势表现。这是一个综合性的技术形态分析工具。
+
+#### 主要特点
+- **全自动化**: 从数据获取到结果输出全程自动化
+- **多维度筛选**: 结合回撤、K线形态、时间位置等条件
+- **量化验证**: 通过10周跟踪验证形态有效性
+- **可视化展示**: 自动生成K线图标记技术形态
+- **综合集成**: 单文件包含配置、分析、测试全部功能
+
+#### 文件结构
+```
+第一部分:分析配置参数 (AnalysisConfig类)
+第二部分:技术形态分析器主类 (FuturePatternAnalyzer类)  
+第三部分:测试和演示代码 (PatternAnalysisTests类)
+第四部分:主程序入口 (main函数)
+```
+
+#### 分析流程
+1. **构建期货品种数据**: 创建80+个期货品种的8888主连合约代码
+2. **筛选大幅下跌品种**: 识别最大回撤>30%的期货品种
+3. **获取主力合约映射**: 获取下跌期间的主力合约变化
+4. **获取周线K线数据**: 扩展时间范围的周线OHLCV数据
+5. **识别目标K线形态**: 十字星+长上影线组合形态识别
+6. **分析后续10周走势**: 量化跟踪后续收益率表现
+7. **生成可视化图表**: K线图展示和统计报告
+8. **保存分析结果**: 完整的CSV数据文件输出
+
+#### 技术形态条件
+- **条件A**: 开盘价与收盘价差异 ≤ 1% (十字星特征)
+- **条件B**: 最高价相对开盘价涨幅 ≥ 3% (长上影线特征)
+- **位置条件**: 形态出现在大幅下跌期间或附近
+
+#### 配置参数
+```python
+# 时间设置
+START_DATE = '2022-01-01'  # 建议至少2年数据
+END_DATE = '2024-12-31'
+
+# 筛选条件
+MIN_DRAWDOWN_THRESHOLD = 0.30  # 30%最小回撤
+KLINE_CONDITIONS = {
+    'max_open_close_diff': 0.01,  # 1%开收差异
+    'min_high_open_gain': 0.03,   # 3%最高涨幅
+}
+
+# 分析参数
+FOLLOW_UP_WEEKS = 10           # 后续跟踪10周
+SUCCESS_RETURN_THRESHOLD = 0.05 # 5%成功率阈值
+```
+
+#### 支持的期货品种
+- **大商所(XDCE)**: A、B、C、I、J、JM、L、M、P、PP、V、Y等
+- **郑商所(XZCE)**: AP、CF、FG、MA、RM、SR、TA、ZC等
+- **上期所(XSGE)**: AG、AL、AU、CU、RB、RU、ZN等
+- **中金所(CCFX)**: IC、IF、IH、T、TF、TS
+- **广期所(GFEX)**: SI、LC
+- **上海国际能源(XINE)**: SC、BC、LU、NR
+
+#### 使用方法
+```python
+# 基本使用 - 完整分析
+results = run_pattern_analysis(mode='full')
+
+# 快速测试
+results = run_pattern_analysis(mode='quick')
+
+# 自定义参数测试
+results = run_pattern_analysis(mode='custom')
+
+# 逐步功能测试
+run_pattern_analysis(mode='step')
+
+# 直接使用分析器类
+analyzer = FuturePatternAnalyzer('2022-01-01', '2024-12-31')
+results = analyzer.run_full_analysis()
+```
+
+#### 输出文件
+- `future_major_decline_YYYYMMDD_HHMMSS.csv` - 大幅下跌期货品种
+- `future_kline_patterns_YYYYMMDD_HHMMSS.csv` - 识别的K线形态
+- `future_pattern_performance_YYYYMMDD_HHMMSS.csv` - 后续走势分析
+- `future_pattern_summary_YYYYMMDD_HHMMSS.csv` - 统计汇总
+- `kline_pattern_{品种}_{日期}.png` - K线图表
+
+#### 性能统计示例
+```
+=== 期货技术形态规律分析报告 ===
+总分析期货品种数: 85
+大幅下跌品种数: 12
+识别出的目标形态数: 8
+完成后续走势分析数: 8
+
+=== 后续走势统计分析 ===
+10周内最高收益率统计:
+  平均值: 15.32%
+  正收益比例: 7/8 (87.5%)
+
+=== 交易成功率分析 ===
+成功率: 75.0%
+```
+
+## 技术架构
+
+### 数据源
+- **聚宽API**: 使用jqdata获取期货价格数据
+- **数据频率**: 支持日线和周线数据
+- **数据字段**: OHLCV (开高低收量)
+
+### 核心库依赖
+```python
+import pandas as pd      # 数据处理
+import numpy as np       # 数值计算  
+from jqdata import *     # 聚宽API
+import matplotlib.pyplot as plt  # 图表绘制
+```
+
+### 代码规范
+- **函数命名**: 使用下划线分隔的描述性命名
+- **类设计**: 面向对象设计,功能模块化
+- **错误处理**: 完善的异常捕获和日志记录
+- **配置管理**: 集中的参数配置和管理
+- **文档注释**: 详细的函数和类文档字符串
+
+## 使用建议
+
+### 1. 运行环境
+- **推荐平台**: 聚宽在线研究环境
+- **Python版本**: Python 3.6+
+- **内存要求**: 建议至少2GB内存
+- **数据权限**: 需要聚宽期货数据访问权限
+
+### 2. 最佳实践
+- **时间范围**: 建议使用至少2年的历史数据进行分析
+- **品种选择**: 可通过配置文件筛选活跃品种
+- **参数调优**: 根据研究目标调整筛选阈值
+- **结果验证**: 结合基本面分析验证技术分析结果
+
+### 3. 性能优化
+- **批量处理**: 使用并行处理提升大规模数据分析速度
+- **内存管理**: 及时释放不需要的数据对象
+- **缓存策略**: 对重复查询的数据进行缓存
+- **测试模式**: 使用测试模式快速验证算法逻辑
+
+## 更新日志
+
+### v1.3 (2025-09)
+- **重构**: 将期货技术形态分析整合为单一综合文件
+- **优化**: 改进代码结构,增强在线平台适配性
+- **新增**: 添加多种测试模式和配置选项
+- **完善**: 统一的README文档和算法说明
+
+### v1.2 (2025-06)
+- **新增**: 期货技术形态规律分析算法
+- **扩展**: 支持更多期货品种和交易所
+- **优化**: 改进可视化效果和报告格式
+
+### v1.1 (2024-12)
+- **新增**: 多均线穿越分析功能
+- **优化**: 改进数据处理效率
+- **修复**: 修复部分品种数据获取问题
+
+### v1.0 (2024-06)
+- **初始版本**: 基础的期货市场趋势分析
+- **核心功能**: 市场整体变化趋势分析
+
+## 研究成果
+
+### 学术价值
+- 提供了期货市场技术分析的量化研究框架
+- 验证了特定K线形态在期货市场的有效性
+- 建立了系统性的期货品种分析方法论
+
+### 实用价值
+- 为量化交易策略提供技术分析工具
+- 辅助投资决策和风险管理
+- 支持期货市场的深度研究和分析
+
+### 未来发展
+- 集成机器学习模型提升预测准确性
+- 扩展更多技术指标和形态识别
+- 建立实时分析和预警系统
+- 开发策略回测和评估框架
+
+---
+**维护团队**: jukuan研究团队  
+**最后更新**: 2025年9月  
+**技术支持**: 基于聚宽平台开发,需要相应API权限  
+**使用许可**: 仅供研究和学习使用

+ 1783 - 0
Lib/research/future_pattern_comprehensive.py

@@ -0,0 +1,1783 @@
+"""
+期货市场技术形态规律分析 - 综合版本
+研究期货品种在大幅下跌后出现特定K线形态的规律,并分析后续走势
+
+本文件包含:
+- 分析配置参数
+- 主要分析类FuturePatternAnalyzer
+- 基于合约切换日志的周线数据获取功能
+- 辅助工具函数和数据导出功能
+- 完整的分析流程
+
+主要功能:
+1. get_contract_weekly_data() - 基于合约切换日志获取周线数据的正式方法
+2. get_weekly_data_by_contract_log() - 核心数据获取逻辑,直接使用get_bars获取原生周线数据
+3. 简化的get_weekly_kline_data() - 直接使用8888加权合约,避免主力合约切换复杂性
+4. validate_manual_patterns() - 手动模式验证系统,支持指定日期的模式验证
+5. run_manual_pattern_validation() - 便捷的手动验证入口函数
+6. _display_full_candlestick_charts() - 模式识别失败时的完整K线图显示
+
+数据结构简化:
+- 移除了主力合约获取和切换逻辑
+- 使用单级字典结构:键为8888合约代码,值为日期范围
+- 所有数据检索均使用8888加权合约,简化了代码复杂性
+
+验证模式:
+- 自动搜索模式:使用identify_target_pattern()自动搜索符合条件的技术形态
+- 手动验证模式:使用validate_manual_patterns()验证指定日期是否符合模式条件
+- 两种模式使用相同的验证逻辑和标准,但工作流程独立
+
+作者: jukuan研究团队
+日期: 2025-09
+适用平台: 聚宽在线研究平台
+"""
+
+import pandas as pd
+import numpy as np
+from jqdata import *
+import datetime
+import matplotlib.pyplot as plt
+import matplotlib.patches as patches
+from matplotlib.dates import DateFormatter
+import warnings
+warnings.filterwarnings('ignore')
+
+# 中文字体设置
+plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
+plt.rcParams['axes.unicode_minus'] = False
+
+# =====================================================================================
+# 第一部分:分析配置参数
+# 用户可根据需要调整以下参数来控制分析行为
+# =====================================================================================
+
+class AnalysisConfig:
+    """分析配置类 - 集中管理所有分析参数"""
+    
+    # ==================== 分析时间设置 ====================
+    START_DATE = '2022-01-01'  # 分析起始日期 (建议至少2年数据)
+    END_DATE = '2025-08-31'    # 分析结束日期
+    
+    # ==================== 筛选条件设置 ====================
+    MIN_DRAWDOWN_THRESHOLD = 0.30  # 最小回撤阈值 (0.3 = 30%)
+    
+    # K线形态识别条件
+    KLINE_CONDITIONS = {
+        'max_open_close_diff': 0.01,  # 开盘价与收盘价最大差异 (0.01 = 1%)
+        'min_high_open_gain': 0.03,   # 最高价相对开盘价的最小涨幅 (0.03 = 3%)
+    }
+    
+    # ==================== 分析参数设置 ====================
+    ANALYSIS_EXTEND_DAYS = 30    # 扩展分析天数 (在下跌期间前后各扩展的天数)
+    FOLLOW_UP_WEEKS = 10         # 后续跟踪周数
+    SUCCESS_RETURN_THRESHOLD = 0.05  # 成功率计算的收益率阈值 (0.05 = 5%)
+    
+    # ==================== 输出设置 ====================
+    GENERATE_CHARTS = True       # 是否生成K线图
+    MAX_CHART_COUNT = 10         # 最大图表生成数量 (避免生成过多图片)
+    CHART_OUTPUT_DIR = './'      # 图表保存目录
+    CSV_ENCODING = 'utf-8-sig'   # CSV文件编码
+    
+    # ==================== 期货品种过滤设置 ====================
+    # 需要排除的期货品种 (可根据需要调整)
+    EXCLUDED_FUTURES = [
+        'IF9999.CCFX',  # 
+        'LU8888.XINE',  # 
+        'NR8888.XINE',  # 
+        'LG8888.XDCE',  # 原木 - 新上市品种数据较少
+    ]
+    
+    # 重点关注的期货品种 (如果设置,将只分析这些品种)
+    FOCUS_FUTURES = []  # 空列表表示分析所有品种
+    
+    # ==================== 分析规模控制 ====================
+    MAX_ANALYSIS_FUTURES = 5    # 最大分析期货品种数 (-1表示分析所有品种)
+    VERBOSE_LOGGING = True       # 是否打印详细日志
+    SHOW_PROGRESS = True         # 是否在分析过程中显示进度
+    
+    # ==================== 数据质量控制 ====================
+    MIN_DATA_LENGTH = 100        # 最小数据长度要求 (日线数据点数)
+    MIN_WEEKLY_DATA = 20         # 最小周线数据要求
+    MAX_MISSING_DATA_RATIO = 0.1 # 最大缺失数据比例
+    
+    @classmethod
+    def get_config_dict(cls):
+        """获取分析配置字典"""
+        return {
+            'time_range': {
+                'start_date': cls.START_DATE,
+                'end_date': cls.END_DATE,
+            },
+            'filter_conditions': {
+                'min_drawdown': cls.MIN_DRAWDOWN_THRESHOLD,
+                'kline_conditions': cls.KLINE_CONDITIONS,
+            },
+            'analysis_params': {
+                'extend_days': cls.ANALYSIS_EXTEND_DAYS,
+                'follow_up_weeks': cls.FOLLOW_UP_WEEKS,
+                'success_threshold': cls.SUCCESS_RETURN_THRESHOLD,
+            },
+            'output_settings': {
+                'generate_charts': cls.GENERATE_CHARTS,
+                'max_chart_count': cls.MAX_CHART_COUNT,
+                'chart_dir': cls.CHART_OUTPUT_DIR,
+                'csv_encoding': cls.CSV_ENCODING,
+            },
+            'future_filter': {
+                'excluded': cls.EXCLUDED_FUTURES,
+                'focus_only': cls.FOCUS_FUTURES,
+            },
+            'analysis_control': {
+                'max_futures': cls.MAX_ANALYSIS_FUTURES,
+                'verbose': cls.VERBOSE_LOGGING,
+                'show_progress': cls.SHOW_PROGRESS,
+            },
+            'data_quality': {
+                'min_data_length': cls.MIN_DATA_LENGTH,
+                'min_weekly_data': cls.MIN_WEEKLY_DATA,
+                'max_missing_ratio': cls.MAX_MISSING_DATA_RATIO,
+            }
+        }
+    
+    @classmethod
+    def print_config(cls):
+        """打印当前配置信息"""
+        print("=== 期货技术形态分析配置 ===")
+        print(f"分析时间范围: {cls.START_DATE} 至 {cls.END_DATE}")
+        print(f"最小回撤阈值: {cls.MIN_DRAWDOWN_THRESHOLD*100:.0f}%")
+        print(f"开收差异阈值: {cls.KLINE_CONDITIONS['max_open_close_diff']*100:.0f}%")
+        print(f"最高涨幅阈值: {cls.KLINE_CONDITIONS['min_high_open_gain']*100:.0f}%")
+        print(f"后续跟踪周数: {cls.FOLLOW_UP_WEEKS}")
+        print(f"成功率阈值: {cls.SUCCESS_RETURN_THRESHOLD*100:.0f}%")
+        
+        if cls.EXCLUDED_FUTURES:
+            print(f"排除品种数: {len(cls.EXCLUDED_FUTURES)}")
+        if cls.FOCUS_FUTURES:
+            print(f"重点品种数: {len(cls.FOCUS_FUTURES)}")
+        
+        print(f"生成图表: {'是' if cls.GENERATE_CHARTS else '否'}")
+        print(f"最大分析品种数: {'所有' if cls.MAX_ANALYSIS_FUTURES == -1 else cls.MAX_ANALYSIS_FUTURES}")
+
+# =====================================================================================
+# 第二部分:期货技术形态分析器主类
+# 实现完整的技术形态识别和分析功能
+# =====================================================================================
+
+class FuturePatternAnalyzer:
+    """期货技术形态分析器 - 主要分析类"""
+    
+    def __init__(self, start_date=None, end_date=None, config=None):
+        """
+        初始化分析器
+        
+        参数:
+        - start_date: 分析开始日期,默认使用配置
+        - end_date: 分析结束日期,默认使用配置  
+        - config: 自定义配置,默认使用AnalysisConfig
+        """
+        # 使用提供的配置或默认配置
+        if config is None:
+            config = AnalysisConfig.get_config_dict()
+        
+        self.start_date = pd.to_datetime(start_date or config['time_range']['start_date'])
+        self.end_date = pd.to_datetime(end_date or config['time_range']['end_date'])
+        self.config = config
+        self.future_codes = {}  # 期货代码映射
+        
+        # 期货品种与交易所的映射关系
+        self.exchange_map = {
+            # 大连商品交易所 (XDCE)
+            'A': 'XDCE', 'B': 'XDCE', 'C': 'XDCE', 'CS': 'XDCE', 'FB': 'XDCE', 
+            'I': 'XDCE', 'J': 'XDCE', 'JD': 'XDCE', 'JM': 'XDCE', 'L': 'XDCE',
+            'M': 'XDCE', 'P': 'XDCE', 'PP': 'XDCE', 'PG': 'XDCE', 'RR': 'XDCE',
+            'V': 'XDCE', 'Y': 'XDCE', 'EB': 'XDCE', 'EG': 'XDCE', 'LH': 'XDCE',
+            
+            # 郑州商品交易所 (XZCE)
+            'AP': 'XZCE', 'CF': 'XZCE', 'CY': 'XZCE', 'FG': 'XZCE', 'JR': 'XZCE',
+            'LR': 'XZCE', 'MA': 'XZCE', 'OI': 'XZCE', 'PM': 'XZCE', 'RI': 'XZCE',
+            'RM': 'XZCE', 'RS': 'XZCE', 'SF': 'XZCE', 'SM': 'XZCE', 'SR': 'XZCE',
+            'TA': 'XZCE', 'WH': 'XZCE', 'ZC': 'XZCE', 'CJ': 'XZCE', 
+            'ME': 'XZCE', 'PF': 'XZCE', 'PK': 'XZCE', 'RO': 'XZCE', 'SA': 'XZCE',
+            'TC': 'XZCE', 'UR': 'XZCE', 'WS': 'XZCE', 'WT': 'XZCE',
+            
+            # 上海期货交易所 (XSGE)
+            'AG': 'XSGE', 'AL': 'XSGE', 'AU': 'XSGE', 'BU': 'XSGE', 'CU': 'XSGE',
+            'FU': 'XSGE', 'HC': 'XSGE', 'NI': 'XSGE', 'PB': 'XSGE', 'RB': 'XSGE',
+            'RU': 'XSGE', 'SN': 'XSGE', 'SP': 'XSGE', 'SS': 'XSGE', 'WR': 'XSGE',
+            'ZN': 'XSGE',
+            
+            # 上海国际能源交易中心 (XINE)
+            'BC': 'XINE', 'LU': 'XINE', 'NR': 'XINE', 'SC': 'XINE',
+            
+            # 中金所 (CCFX)
+            'IC': 'CCFX', 'IF': 'CCFX', 'IH': 'CCFX', 'T': 'CCFX', 'TF': 'CCFX', 'TS': 'CCFX',
+            
+            # 广期所 (GFEX)
+            'SI': 'GFEX', 'LC': 'GFEX', 'PS': 'GFEX'
+        }
+        
+        self._log(f"初始化分析器 - 分析期间: {self.start_date.strftime('%Y-%m-%d')} 至 {self.end_date.strftime('%Y-%m-%d')}")
+        
+    def _log(self, message):
+        """记录日志"""
+        if self.config['analysis_control']['verbose']:
+            print(message)
+    
+    def build_future_codes(self):
+        """构建期货品种基础数据"""
+        self._log("\n=== 步骤1: 构建期货品种基础数据 ===")
+        
+        # 构建8888主连合约代码
+        for code, exchange in self.exchange_map.items():
+            future_code = f"{code}8888.{exchange}"
+            
+            # 应用过滤器
+            if future_code in self.config['future_filter']['excluded']:
+                continue
+                
+            # 如果设置了重点关注品种,只添加重点品种
+            if (self.config['future_filter']['focus_only'] and 
+                future_code not in self.config['future_filter']['focus_only']):
+                continue
+                
+            self.future_codes[code] = future_code
+            
+        self._log(f"构建完成,共{len(self.future_codes)}个期货品种")
+        if self.config['analysis_control']['verbose']:
+            self._log("示例品种:")
+            for i, (code, full_code) in enumerate(list(self.future_codes.items())[:5]):
+                self._log(f"  {code} -> {full_code}")
+        
+        return self.future_codes
+    
+    def get_future_price_data(self, future_code, start_date, end_date):
+        """获取期货价格数据"""
+        try:
+            # print(f"获取{future_code}数据: {start_date} 至 {end_date}")
+            data = get_price(
+                future_code, 
+                start_date=start_date, 
+                end_date=end_date, 
+                frequency='daily',
+                fields=['open', 'close', 'high', 'low', 'volume'],
+                skip_paused=False,
+                panel=False
+            )
+            # print(f"获取的最后一条数据: {data.iloc[-1]}")
+            return data
+        except Exception as e:
+            if self.config['analysis_control']['verbose']:
+                print(f"  获取{future_code}数据失败: {str(e)}")
+            return None
+    
+    def calculate_drawdown(self, data):
+        """计算最大回撤"""
+        if data is None or len(data) == 0:
+            return None, None, None, None
+            
+        # 计算累计最高价
+        data['cum_max'] = data['high'].expanding().max()
+        
+        # 计算回撤
+        data['drawdown'] = (data['low'] - data['cum_max']) / data['cum_max']
+        
+        # 找到最大回撤
+        max_drawdown = data['drawdown'].min()  # 最大回撤是负值,所以用min
+        max_drawdown_idx = data['drawdown'].idxmin()
+        
+        # 找到最大回撤对应的高点
+        max_high_before_dd = data.loc[:max_drawdown_idx, 'cum_max'].iloc[-1]
+        high_point_idx = data[data['high'] == max_high_before_dd].index[0]
+        
+        return abs(max_drawdown), high_point_idx, max_drawdown_idx, data
+    
+    def find_major_decline_futures(self, min_drawdown=None):
+        """筛选大幅下跌的期货品种"""
+        if min_drawdown is None:
+            min_drawdown = self.config['filter_conditions']['min_drawdown']
+            
+        self._log(f"\n=== 步骤2: 筛选跌幅>{min_drawdown*100}%的期货品种 ===")
+        
+        decline_futures = []
+        total_count = len(self.future_codes)
+        max_futures = self.config['analysis_control']['max_futures']
+        analyzed_count = 0
+        
+        for code, future_code in self.future_codes.items():
+            analyzed_count += 1
+            
+            # 最大品种数限制
+            if max_futures > 0 and analyzed_count > max_futures:
+                break
+            
+            if self.config['analysis_control']['show_progress']:
+                display_total = min(total_count, max_futures) if max_futures > 0 else total_count
+                print(f"分析 {code}({future_code}) [{analyzed_count}/{display_total}]", end=" ")
+            
+            # 获取价格数据
+            price_data = self.get_future_price_data(future_code, self.start_date, self.end_date)
+            
+            if price_data is None or len(price_data) < self.config['data_quality']['min_data_length']:
+                if self.config['analysis_control']['show_progress']:
+                    print("- 数据不足")
+                continue
+            
+            # 计算最大回撤
+            max_drawdown, high_date, low_date, data_with_dd = self.calculate_drawdown(price_data)
+            
+            if max_drawdown is None or max_drawdown < min_drawdown:
+                if self.config['analysis_control']['show_progress']:
+                    print(f"- 最大回撤{max_drawdown:.1%}不符合条件")
+                continue
+                
+            # 记录符合条件的品种
+            decline_info = {
+                '品种代码': code,
+                '合约代码': future_code,
+                '最大回撤': max_drawdown,
+                '高点日期': high_date,
+                '低点日期': low_date,
+                '高点价格': data_with_dd.loc[high_date, 'high'],
+                '低点价格': data_with_dd.loc[low_date, 'low'],
+                '下跌天数': (low_date - high_date).days
+            }
+            decline_futures.append(decline_info)
+            
+            if self.config['analysis_control']['show_progress']:
+                print(f"✓ 最大回撤{max_drawdown:.1%}, 下跌期间{high_date.strftime('%Y-%m-%d')}至{low_date.strftime('%Y-%m-%d')}")
+        
+        self._log(f"\n找到{len(decline_futures)}个符合条件的期货品种")
+        return decline_futures
+    
+    def get_simplified_contract_mapping(self, decline_futures):
+        """获取8888加权合约映射 - 简化版本,直接使用加权合约"""
+        self._log("\n=== 步骤3: 创建8888加权合约映射 ===")
+        
+        contract_mapping = {}
+        
+        for future_info in decline_futures:
+            code = future_info['品种代码']
+            high_date = future_info['高点日期'] 
+            low_date = future_info['低点日期']
+            
+            # 扩展日期范围前后30天
+            extend_days = self.config['analysis_params']['extend_days']
+            start_date = high_date - pd.Timedelta(days=extend_days)
+            end_date = low_date + pd.Timedelta(days=extend_days)
+            
+            # 构建8888加权合约代码
+            contract_8888 = self.future_codes.get(code)
+            
+            if contract_8888:
+                # 直接使用8888合约,时间范围为分析期间
+                contract_mapping[contract_8888] = [start_date, end_date]
+                
+                self._log(f"  ✓ {code}: 使用8888加权合约 {contract_8888}")
+                self._log(f"    时间范围: {start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}")
+            else:
+                self._log(f"  ⚠️ {code}: 未找到对应的8888合约代码")
+        
+        self._log(f"完成8888合约映射,共{len(contract_mapping)}个合约")
+        return contract_mapping
+    
+    
+    def get_weekly_kline_data(self, contract_mapping):
+        """获取周线K线数据 - 简化版本,直接使用8888加权合约"""
+        self._log("\n=== 步骤4: 获取周线K线数据 ===")
+        
+        weekly_data = {}
+        
+        for contract_code, time_range in contract_mapping.items():
+            start_date, end_date = time_range
+            
+            self._log(f"获取{contract_code}周线数据 ({start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')})")
+            
+            try:
+                # 计算需要获取的周数,确保充足的数据覆盖
+                time_diff = end_date - start_date
+                estimated_weeks = int(time_diff.days / 7) + 20  # 额外增加20周缓冲
+                estimated_weeks = max(estimated_weeks, 40)  # 最少40周
+                
+                self._log(f"  计算获取{estimated_weeks}周数据")
+                
+                # 直接使用get_bars API获取原生周线数据,避免日线转换的精度损失
+                weekly_bars = get_bars(
+                    security=contract_code,
+                    count=estimated_weeks,
+                    unit='1w',   # 直接获取周线数据
+                    fields=['date', 'open', 'high', 'low', 'close', 'volume'],
+                    include_now=False,
+                    end_dt=end_date,
+                    fq_ref_date=None,
+                    df=True
+                )
+                
+                if weekly_bars is not None and len(weekly_bars) > 0:
+                    # 确保日期类型一致,避免 'Cannot compare type Timestamp with type date' 错误
+                    # get_bars返回的date列可能是date类型,需统一转换为pandas Timestamp
+                    weekly_bars['date'] = pd.to_datetime(weekly_bars['date'])
+                    start_date = pd.to_datetime(start_date)
+                    end_date = pd.to_datetime(end_date)
+                    
+                    # 筛选在指定时间范围内的数据
+                    mask = (weekly_bars['date'] >= start_date) & (weekly_bars['date'] <= end_date)
+                    filtered_weekly_data = weekly_bars[mask].copy()
+                    
+                    if len(filtered_weekly_data) > 0:
+                        # 设置日期为索引
+                        filtered_weekly_data.set_index('date', inplace=True)
+                        
+                        # 从合约代码中提取品种代码(用于后续识别)
+                        symbol_code = contract_code.replace('8888.', '').split('.')[0]
+                        
+                        weekly_data[symbol_code] = {
+                            'data': filtered_weekly_data,
+                            'contract_code': contract_code,
+                            'time_range': time_range
+                        }
+                        self._log(f"  ✓ {symbol_code}({contract_code}): 获取{len(filtered_weekly_data)}根周K线")
+                    else:
+                        self._log(f"  ⚠️ {contract_code}: 筛选后周线数据不足")
+                else:
+                    self._log(f"  ⚠️ {contract_code}: 未获取到周线数据")
+                
+            except Exception as e:
+                self._log(f"  ❌ 获取{contract_code}周线数据失败: {str(e)}")
+                continue
+        
+        self._log(f"完成周线数据获取,共{len(weekly_data)}个品种")
+        return weekly_data
+    
+    def get_weekly_data_by_contract_log(self, contract_switch_log, extend_weeks=10):
+        """
+        基于合约切换日志获取每个合约的周线数据
+        
+        参数:
+        - contract_switch_log: 合约切换日志,格式为列表,每个元素包含:
+          {'contract': '合约代码', 'start_date': '开始日期', 'end_date': '结束日期'}
+        - extend_weeks: 延长周数,默认10周
+        
+        返回:
+        - dict: 每个合约的周线数据,键为合约代码,值为数据和时间范围信息
+        """
+        self._log(f"\n=== 基于合约切换日志获取周线数据 ===")
+        self._log(f"延长周数: {extend_weeks}周")
+        
+        weekly_data_by_contract = {}
+        
+        for contract_info in contract_switch_log:
+            contract_code = contract_info.get('contract')
+            start_date = contract_info.get('start_date')
+            end_date = contract_info.get('end_date')
+            
+            if not all([contract_code, start_date, end_date]):
+                self._log(f"⚠️ 合约信息不完整,跳过: {contract_info}")
+                continue
+            
+            # 转换日期格式
+            if isinstance(start_date, str):
+                start_date = pd.to_datetime(start_date)
+            if isinstance(end_date, str):
+                end_date = pd.to_datetime(end_date)
+            
+            # 计算延长后的时间范围
+            extended_start_date = start_date - pd.Timedelta(weeks=extend_weeks)
+            extended_end_date = end_date + pd.Timedelta(weeks=extend_weeks)
+            
+            self._log(f"处理合约: {contract_code}")
+            self._log(f"  原始时间范围: {start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}")
+            self._log(f"  延长时间范围: {extended_start_date.strftime('%Y-%m-%d')} 至 {extended_end_date.strftime('%Y-%m-%d')}")
+            
+            try:
+                # 计算需要获取的周数,采用更稳健的方式
+                time_diff = extended_end_date - extended_start_date
+                estimated_weeks = int(time_diff.days / 7) + 20  # 额外增加20周作为充足缓冲
+                
+                # 确保最少获取60周数据(约1.2年)以提供充分的历史背景
+                estimated_weeks = max(estimated_weeks, 60)
+                
+                self._log(f"    计算获取{estimated_weeks}周数据 (时间跨度{time_diff.days}天)")
+                
+                # 直接使用get_bars API获取原生周线数据,避免日线转换的精度损失
+                weekly_bars = get_bars(
+                    security=contract_code,
+                    count=estimated_weeks,  # 扩展的周数以确保完整覆盖
+                    unit='1w',   # 直接获取周线数据,避免转换精度损失
+                    fields=['date', 'open', 'high', 'low', 'close', 'volume'],
+                    include_now=False,  # 不包含当前未完成的周期
+                    end_dt=extended_end_date,
+                    fq_ref_date=None,  # 不复权,保持原始价格
+                    df=True  # 返回DataFrame格式便于处理
+                )
+                
+                if weekly_bars is not None and len(weekly_bars) > 0:
+                    # 确保日期类型一致,避免 'Cannot compare type Timestamp with type date' 错误
+                    # get_bars返回的date列可能是date类型,需统一转换为pandas Timestamp
+                    weekly_bars['date'] = pd.to_datetime(weekly_bars['date'])
+                    extended_start_date = pd.to_datetime(extended_start_date)
+                    extended_end_date = pd.to_datetime(extended_end_date)
+                    
+                    # 筛选在延长时间范围内的数据
+                    mask = (weekly_bars['date'] >= extended_start_date) & (weekly_bars['date'] <= extended_end_date)
+                    filtered_data = weekly_bars[mask].copy()
+                    
+                    if len(filtered_data) > 0:
+                        # 设置日期为索引
+                        filtered_data.set_index('date', inplace=True)
+                        
+                        # 存储数据
+                        weekly_data_by_contract[contract_code] = {
+                            'data': filtered_data,
+                            'original_start': start_date,
+                            'original_end': end_date,
+                            'extended_start': extended_start_date,
+                            'extended_end': extended_end_date,
+                            'total_weeks': len(filtered_data)
+                        }
+                        
+                        self._log(f"  ✓ 获取到{len(filtered_data)}根周K线数据")
+                        self._log(f"    数据时间范围: {filtered_data.index[0].strftime('%Y-%m-%d')} 至 {filtered_data.index[-1].strftime('%Y-%m-%d')}")
+                        
+                        # 添加数据质量检查
+                        original_start_ts = pd.to_datetime(start_date)
+                        original_end_ts = pd.to_datetime(end_date)
+                        original_weeks = int((original_end_ts - original_start_ts).days / 7) + 1
+                        expected_total_weeks = original_weeks + 2 * extend_weeks
+                        if len(filtered_data) < expected_total_weeks * 0.7:  # 如果数据不足预期的70%
+                            self._log(f"    ⚠️ 数据可能不完整,预期约{expected_total_weeks}周,实际{len(filtered_data)}周")
+                    else:
+                        self._log(f"  ⚠️ 筛选后无有效数据")
+                        
+                else:
+                    self._log(f"  ⚠️ 未获取到周线数据")
+                    
+            except Exception as e:
+                self._log(f"  ❌ 获取{contract_code}周线数据失败: {str(e)}")
+                continue
+            
+        self._log(f"\n成功获取{len(weekly_data_by_contract)}个合约的周线数据")
+        return weekly_data_by_contract
+    
+    def identify_target_pattern(self, weekly_data, decline_futures):
+        """
+        识别目标K线形态
+        
+        当未识别到任何目标形态时,会自动显示完整的烛台图(K线图)作为数据可视化备选方案。
+        图表包含完整的OHLC数据,直接显示不保存到本地存储。
+        """
+        self._log("\n=== 步骤5: 识别目标K线形态 ===")
+        
+        pattern_results = []
+        kline_conditions = self.config['filter_conditions']['kline_conditions']
+        
+        # 创建品种代码到下跌信息的映射
+        decline_info_map = {future['品种代码']: future for future in decline_futures}
+        
+        for code, data_info in weekly_data.items():
+            data = data_info['data']
+            contract_code = data_info['contract_code']  # 8888加权合约代码
+            
+            # 从decline_futures中获取下跌时间信息
+            if code not in decline_info_map:
+                continue
+                
+            decline_info = decline_info_map[code]
+            decline_start = decline_info['高点日期']
+            decline_end = decline_info['低点日期']
+            extend_days = self.config['analysis_params']['extend_days']
+            
+            self._log(f"分析{code}的K线形态 (使用{contract_code})")
+            
+            for idx, (date, row) in enumerate(data.iterrows()):
+                open_price = row['open']
+                close_price = row['close']
+                high_price = row['high']
+                low_price = row['low']
+                volume = row['volume']
+                
+                # 检查是否在下跌期间或附近
+                date_in_range = (date >= decline_start - pd.Timedelta(days=extend_days) and 
+                               date <= decline_end + pd.Timedelta(days=extend_days))
+                
+                if not date_in_range:
+                    continue
+                
+                # 使用统一的核心验证逻辑
+                validation_result = self._core_pattern_validation(date, row, kline_conditions)
+                
+                if validation_result['validation_passed']:
+                    pattern_info = {
+                        '品种代码': code,
+                        '日期': date,
+                        '开盘价': validation_result['ohlc_data']['open'],
+                        '收盘价': validation_result['ohlc_data']['close'],
+                        '最高价': validation_result['ohlc_data']['high'],
+                        '最低价': validation_result['ohlc_data']['low'],
+                        '成交量': validation_result['ohlc_data']['volume'],
+                        '开收差异率': validation_result['indicators']['price_diff_pct']['actual_value'],
+                        '最高涨幅率': validation_result['indicators']['high_gain_pct']['actual_value'],
+                        '下跌开始日期': decline_start,
+                        '下跌结束日期': decline_end,
+                        '数据索引': idx,
+                        '使用合约': contract_code  # 8888加权合约
+                    }
+                    pattern_results.append(pattern_info)
+                    
+                    price_diff_pct = validation_result['indicators']['price_diff_pct']['actual_value'] / 100
+                    high_gain_pct = validation_result['indicators']['high_gain_pct']['actual_value'] / 100
+                    self._log(f"  ✓ 找到形态: {date.strftime('%Y-%m-%d')} 开收差{price_diff_pct:.1%} 最高涨{high_gain_pct:.1%} 合约{contract_code}")
+        
+        self._log(f"\n共识别出{len(pattern_results)}个目标K线形态")
+        
+        # 如果未识别到任何模式,显示完整的烛台图
+        if len(pattern_results) == 0:
+            self._log("\n⚠️ 未识别到任何目标形态,显示完整数据集的K线图")
+            self._display_full_candlestick_charts(weekly_data)
+        
+        return pattern_results
+    
+    def _display_full_candlestick_charts(self, weekly_data):
+        """显示完整的烛台图(K线图)"""
+        self._log("=== 显示完整数据集的K线图 ===")
+        
+        for code, data_info in weekly_data.items():
+            data = data_info['data']
+            contract_code = data_info['contract_code']
+            
+            if data is None or len(data) == 0:
+                self._log(f"⚠️ {code}: 无可用数据")
+                continue
+            
+            self._log(f"绘制{code}({contract_code})的完整K线图,共{len(data)}根K线")
+            
+            try:
+                # 创建图表
+                fig, ax = plt.subplots(figsize=(16, 10))
+                
+                # 准备数据用于绘制烛台图
+                dates = data.index
+                opens = data['open']
+                highs = data['high']
+                lows = data['low']
+                closes = data['close']
+                volumes = data['volume']
+                
+                # 绘制K线图
+                for i in range(len(data)):
+                    date = dates[i]
+                    open_price = opens.iloc[i]
+                    high_price = highs.iloc[i]
+                    low_price = lows.iloc[i]
+                    close_price = closes.iloc[i]
+                    
+                    # K线颜色:红涨绿跌
+                    color = 'red' if close_price > open_price else 'green'
+                    edge_color = 'darkred' if close_price > open_price else 'darkgreen'
+                    
+                    # 绘制影线(最高价到最低价的竖线)
+                    ax.plot([i, i], [low_price, high_price], color='black', linewidth=1)
+                    
+                    # 绘制实体(开盘价到收盘价的矩形)
+                    body_height = abs(close_price - open_price)
+                    bottom = min(open_price, close_price)
+                    
+                    # 使用矩形绘制K线实体
+                    rect = patches.Rectangle((i-0.4, bottom), 0.8, body_height,
+                                           linewidth=1, edgecolor=edge_color, 
+                                           facecolor=color, alpha=0.8)
+                    ax.add_patch(rect)
+                
+                # 设置图表标题和标签
+                ax.set_title(f'{code} ({contract_code}) 完整周线K线图\n'
+                           f'数据期间: {dates[0].strftime("%Y-%m-%d")} 至 {dates[-1].strftime("%Y-%m-%d")} '
+                           f'(共{len(data)}根K线)', 
+                           fontsize=14, fontweight='bold', pad=20)
+                
+                ax.set_xlabel('时间', fontsize=12)
+                ax.set_ylabel('价格', fontsize=12)
+                ax.grid(True, alpha=0.3)
+                
+                # 设置x轴标签
+                # 选择合适的标签间隔,避免过于密集
+                step = max(1, len(data) // 10)  # 显示约10个时间标签
+                tick_positions = range(0, len(data), step)
+                tick_labels = [dates[i].strftime('%Y-%m-%d') for i in tick_positions]
+                
+                ax.set_xticks(tick_positions)
+                ax.set_xticklabels(tick_labels, rotation=45, ha='right')
+                
+                # 添加统计信息
+                max_price = highs.max()
+                min_price = lows.min()
+                latest_close = closes.iloc[-1]
+                total_change = (latest_close - closes.iloc[0]) / closes.iloc[0] * 100
+                
+                # 在图表上添加统计文本
+                stats_text = (f'最高价: {max_price:.2f}\n'
+                            f'最低价: {min_price:.2f}\n'
+                            f'最新收盘: {latest_close:.2f}\n'
+                            f'总涨跌幅: {total_change:+.2f}%')
+                
+                ax.text(0.02, 0.98, stats_text, transform=ax.transAxes, 
+                       verticalalignment='top', bbox=dict(boxstyle='round', 
+                       facecolor='wheat', alpha=0.8), fontsize=10)
+                
+                # 调整布局并显示
+                plt.tight_layout()
+                plt.show()
+                
+                self._log(f"✓ 已显示{code}的完整K线图")
+                
+            except Exception as e:
+                self._log(f"❌ 绘制{code}K线图时出错: {str(e)}")
+                continue
+        
+        self._log("完成所有品种的K线图显示")
+    
+    def _core_pattern_validation(self, target_date, kline_data_row, kline_conditions):
+        """
+        统一的核心模式验证逻辑
+        
+        此函数包含两种模式共同的验证逻辑,消除代码重复。
+        无论是自动模式遍历所有K线,还是验证模式检查特定日期,
+        都使用相同的验证标准和计算方法。
+        
+        参数:
+        - target_date: 目标日期 (pandas Timestamp)
+        - kline_data_row: 单根K线数据 (pandas Series,包含open、high、low、close、volume)
+        - kline_conditions: 验证条件配置字典
+        
+        返回:
+        - dict: 包含验证结果和详细诊断信息的字典
+        """
+        
+        # 提取OHLC数据
+        open_price = kline_data_row['open']
+        close_price = kline_data_row['close']
+        high_price = kline_data_row['high']
+        low_price = kline_data_row['low']
+        volume = kline_data_row['volume']
+        
+        # 计算模式指标
+        price_diff_pct = abs(close_price - open_price) / open_price
+        high_gain_pct = (high_price - open_price) / open_price
+        
+        # 验证条件A: 开盘价与收盘价差异
+        condition_a = price_diff_pct <= kline_conditions['max_open_close_diff']
+        condition_a_threshold = kline_conditions['max_open_close_diff']
+        
+        # 验证条件B: 最高价相对开盘价涨幅
+        condition_b = high_gain_pct >= kline_conditions['min_high_open_gain']
+        condition_b_threshold = kline_conditions['min_high_open_gain']
+        
+        # 整体验证结果
+        validation_passed = condition_a and condition_b
+        
+        # 构建详细的验证结果
+        result = {
+            'date': target_date,
+            'validation_passed': validation_passed,
+            'ohlc_data': {
+                'open': round(open_price, 2),
+                'high': round(high_price, 2),
+                'low': round(low_price, 2),
+                'close': round(close_price, 2),
+                'volume': volume
+            },
+            'indicators': {
+                'price_diff_pct': {
+                    'actual_value': round(price_diff_pct * 100, 2),
+                    'threshold': round(condition_a_threshold * 100, 2),
+                    'unit': '%',
+                    'condition': '≤',
+                    'passed': condition_a,
+                    'description': '开盘价与收盘价差异率'
+                },
+                'high_gain_pct': {
+                    'actual_value': round(high_gain_pct * 100, 2),
+                    'threshold': round(condition_b_threshold * 100, 2),
+                    'unit': '%',
+                    'condition': '≥',
+                    'passed': condition_b,
+                    'description': '最高价相对开盘价涨幅'
+                }
+            }
+        }
+        
+        # 生成失败原因
+        if not validation_passed:
+            failed_conditions = []
+            if not condition_a:
+                failed_conditions.append(f"开收差异率{price_diff_pct*100:.2f}% > 阈值{condition_a_threshold*100:.1f}%")
+            if not condition_b:
+                failed_conditions.append(f"最高涨幅{high_gain_pct*100:.2f}% < 阈值{condition_b_threshold*100:.1f}%")
+            
+            result['failure_reason'] = "; ".join(failed_conditions)
+        
+        return result
+    
+    def validate_manual_patterns(self, manual_config, weekly_data):
+        """
+        手动验证模式功能
+        
+        参数:
+        - manual_config: 手动配置字典,格式:
+          {'B':['2022-07-22','2024-08-23'], 'A':['2023-01-06','2023-06-09']}
+        - weekly_data: 周线数据字典
+        
+        返回:
+        - dict: 验证结果,包含成功和失败的详细信息
+        """
+        self._log("\n=== 手动模式验证 ===")
+        self._log(f"配置的模式类型数: {len(manual_config)}")
+        
+        validation_results = {
+            'successful_validations': [],
+            'failed_validations': [],
+            'summary': {}
+        }
+        
+        kline_conditions = self.config['filter_conditions']['kline_conditions']
+        
+        for pattern_type, date_list in manual_config.items():
+            self._log(f"\n处理模式类型 '{pattern_type}': {len(date_list)} 个日期")
+            
+            type_results = {
+                'pattern_type': pattern_type,
+                'total_dates': len(date_list),
+                'successful_count': 0,
+                'failed_count': 0,
+                'validations': []
+            }
+            
+            for date_str in date_list:
+                try:
+                    # 转换日期格式
+                    target_date = pd.to_datetime(date_str)
+                    self._log(f"  验证日期: {target_date.strftime('%Y-%m-%d')}")
+                    
+                    # 验证这个日期
+                    validation_result = self._validate_single_date(
+                        target_date, pattern_type, weekly_data, kline_conditions
+                    )
+                    
+                    type_results['validations'].append(validation_result)
+                    
+                    if validation_result['validation_passed']:
+                        type_results['successful_count'] += 1
+                        validation_results['successful_validations'].append(validation_result)
+                        self._log(f"    ✓ 验证通过")
+                    else:
+                        type_results['failed_count'] += 1
+                        validation_results['failed_validations'].append(validation_result)
+                        self._log(f"    ❌ 验证失败: {validation_result['failure_reason']}")
+                        
+                except Exception as e:
+                    error_result = {
+                        'pattern_type': pattern_type,
+                        'target_date': date_str,
+                        'validation_passed': False,
+                        'failure_reason': f'日期处理错误: {str(e)}',
+                        'error': True
+                    }
+                    type_results['validations'].append(error_result)
+                    type_results['failed_count'] += 1
+                    validation_results['failed_validations'].append(error_result)
+                    self._log(f"    ❌ 日期处理错误: {str(e)}")
+            
+            validation_results['summary'][pattern_type] = type_results
+        
+        # 打印总体统计
+        total_validations = sum(result['total_dates'] for result in validation_results['summary'].values())
+        total_successful = len(validation_results['successful_validations'])
+        total_failed = len(validation_results['failed_validations'])
+        
+        self._log(f"\n=== 验证结果汇总 ===")
+        self._log(f"总验证数: {total_validations}")
+        self._log(f"成功验证: {total_successful}")
+        self._log(f"失败验证: {total_failed}")
+        self._log(f"成功率: {(total_successful/total_validations*100) if total_validations > 0 else 0:.1f}%")
+        
+        # 显示失败详情
+        if validation_results['failed_validations']:
+            self._display_validation_failures(validation_results['failed_validations'])
+        
+        return validation_results
+    
+    def _validate_single_date(self, target_date, pattern_type, weekly_data, kline_conditions):
+        """验证单个日期的模式 - 使用统一的核心验证逻辑"""
+        
+        validation_result = {
+            'pattern_type': pattern_type,
+            'target_date': target_date.strftime('%Y-%m-%d'),
+            'validation_passed': False,
+            'failure_reason': '',
+            'diagnostics': {}
+        }
+        
+        # 查找包含目标日期的数据
+        found_data = None
+        
+        for code, data_info in weekly_data.items():
+            data = data_info['data']
+            contract_code = data_info['contract_code']
+            
+            # 检查目标日期是否在数据范围内
+            if target_date >= data.index[0] and target_date <= data.index[-1]:
+                # 找到最接近的周线数据
+                closest_idx = data.index.get_indexer([target_date], method='nearest')[0]
+                if closest_idx >= 0 and closest_idx < len(data):
+                    found_data = data.iloc[closest_idx]
+                    validation_result['contract_code'] = contract_code
+                    validation_result['closest_date'] = data.index[closest_idx].strftime('%Y-%m-%d')
+                    break
+        
+        if found_data is None:
+            validation_result['failure_reason'] = '未找到匹配的数据'
+            return validation_result
+        
+        # 使用统一的核心验证逻辑
+        core_result = self._core_pattern_validation(target_date, found_data, kline_conditions)
+        
+        # 整合核心结果到验证结果
+        validation_result['validation_passed'] = core_result['validation_passed']
+        validation_result['diagnostics']['ohlc_data'] = core_result['ohlc_data']
+        validation_result['diagnostics']['indicators'] = core_result['indicators']
+        
+        if 'failure_reason' in core_result:
+            validation_result['failure_reason'] = core_result['failure_reason']
+        
+        return validation_result
+    
+    def _display_validation_failures(self, failed_validations):
+        """显示详细的验证失败信息"""
+        self._log("\n=== 详细失败诊断 ===")
+        
+        for i, failure in enumerate(failed_validations, 1):
+            if failure.get('error'):
+                continue  # 跳过错误情况
+                
+            self._log(f"\n失败 #{i}: 模式类型 '{failure['pattern_type']}' - 日期 {failure['target_date']}")
+            
+            if 'contract_code' in failure:
+                self._log(f"  合约: {failure['contract_code']}")
+                self._log(f"  最近日期: {failure['closest_date']}")
+            
+            # 显示OHLC数据
+            if 'ohlc_data' in failure['diagnostics']:
+                ohlc = failure['diagnostics']['ohlc_data']
+                self._log(f"  OHLC数据:")
+                self._log(f"    开盘: {ohlc['open']:>8}")
+                self._log(f"    最高: {ohlc['high']:>8}")
+                self._log(f"    最低: {ohlc['low']:>8}")
+                self._log(f"    收盘: {ohlc['close']:>8}")
+                self._log(f"    成交量: {ohlc['volume']}")
+            
+            # 显示指标验证详情
+            if 'indicators' in failure['diagnostics']:
+                self._log(f"  指标验证:")
+                for indicator_name, indicator_data in failure['diagnostics']['indicators'].items():
+                    status = "✓" if indicator_data['passed'] else "❌"
+                    self._log(f"    {status} {indicator_data['description']}:")
+                    self._log(f"        实际值: {indicator_data['actual_value']}{indicator_data['unit']}")
+                    self._log(f"        阈值: {indicator_data['condition']} {indicator_data['threshold']}{indicator_data['unit']}")
+                    self._log(f"        结果: {'通过' if indicator_data['passed'] else '失败'}")
+            
+            self._log(f"  失败原因: {failure['failure_reason']}")
+            
+    def analyze_future_performance(self, pattern_results, weekly_data):
+        """分析后续走势 - 逐周详细分析"""
+        self._log("\n=== 步骤6: 分析后续10周逐周表现 ===")
+        
+        performance_results = []
+        follow_up_weeks = self.config['analysis_params']['follow_up_weeks']
+        
+        for pattern in pattern_results:
+            code = pattern['品种代码']
+            pattern_date = pattern['日期']
+            pattern_idx = pattern['数据索引']
+            pattern_close_price = pattern['收盘价']
+            
+            if code not in weekly_data:
+                continue
+                
+            data = weekly_data[code]['data']
+            
+            # 基础信息
+            future_performance = {
+                '品种代码': code,
+                '形态日期': pattern_date,
+                '形态收盘价': pattern_close_price
+            }
+            
+            # 详细的逐周分析
+            print(f"\n{code} ({pattern_date.strftime('%Y-%m-%d')}) 后续10周详细分析:")
+            print("-" * 80)
+            print(f"{'周次':<4} {'日期':<12} {'开盘':<8} {'收盘':<8} {'最高':<8} {'最低':<8} "
+                  f"{'周涨跌幅':<10} {'最大跌幅':<10}")
+            print("-" * 80)
+            
+            # 逐周分析
+            for weeks in range(1, follow_up_weeks + 1):
+                future_idx = pattern_idx + weeks
+                
+                if future_idx < len(data):
+                    future_row = data.iloc[future_idx]
+                    week_date = data.index[future_idx]
+                    
+                    week_open = future_row['open']
+                    week_close = future_row['close']
+                    week_high = future_row['high']
+                    week_low = future_row['low']
+                    
+                    # a) 每周的涨跌幅:收盘价相对于开盘价的涨跌幅度
+                    weekly_change_pct = (week_close - week_open) / week_open * 100
+                    
+                    # b) 每周的最大跌幅:开盘价到当周最低价的最大跌幅
+                    max_decline_pct = (week_low - week_open) / week_open * 100
+                    
+                    # 存储详细数据
+                    future_performance[f'第{weeks}周_日期'] = week_date.strftime('%Y-%m-%d')
+                    future_performance[f'第{weeks}周_开盘价'] = round(week_open, 2)
+                    future_performance[f'第{weeks}周_收盘价'] = round(week_close, 2)
+                    future_performance[f'第{weeks}周_最高价'] = round(week_high, 2)
+                    future_performance[f'第{weeks}周_最低价'] = round(week_low, 2)
+                    future_performance[f'第{weeks}周_涨跌幅'] = round(weekly_change_pct, 2)
+                    future_performance[f'第{weeks}周_最大跌幅'] = round(max_decline_pct, 2)
+                    
+                    # 打印逐周详细信息
+                    print(f"{weeks:>2}   {week_date.strftime('%Y-%m-%d'):<12} "
+                          f"{week_open:<8.2f} {week_close:<8.2f} {week_high:<8.2f} {week_low:<8.2f} "
+                          f"{weekly_change_pct:>+8.2f}% {max_decline_pct:>+8.2f}%")
+                    
+                else:
+                    # 数据不足的情况
+                    for field in ['日期', '开盘价', '收盘价', '最高价', '最低价', '涨跌幅', '最大跌幅']:
+                        future_performance[f'第{weeks}周_{field}'] = None
+                    print(f"{weeks:>2}   {'无数据':<12} {'--':<8} {'--':<8} {'--':<8} {'--':<8} "
+                          f"{'--':<10} {'--':<10}")
+            
+            print("-" * 80)
+            
+            # 计算统计指标
+            weekly_changes = [future_performance[f'第{w}周_涨跌幅'] 
+                            for w in range(1, follow_up_weeks + 1) 
+                            if future_performance[f'第{w}周_涨跌幅'] is not None]
+            
+            max_declines = [future_performance[f'第{w}周_最大跌幅'] 
+                          for w in range(1, follow_up_weeks + 1) 
+                          if future_performance[f'第{w}周_最大跌幅'] is not None]
+            
+            if weekly_changes:
+                avg_weekly_change = sum(weekly_changes) / len(weekly_changes)
+                max_weekly_gain = max(weekly_changes)
+                max_weekly_loss = min(weekly_changes)
+                
+                future_performance['统计_平均周涨跌幅'] = round(avg_weekly_change, 2)
+                future_performance['统计_最大周涨幅'] = round(max_weekly_gain, 2)
+                future_performance['统计_最大周跌幅'] = round(max_weekly_loss, 2)
+                
+                print(f"统计汇总:")
+                print(f"  平均周涨跌幅: {avg_weekly_change:+.2f}%")
+                print(f"  最大周涨幅: {max_weekly_gain:+.2f}%")
+                print(f"  最大周跌幅: {max_weekly_loss:+.2f}%")
+            
+            if max_declines:
+                avg_max_decline = sum(max_declines) / len(max_declines)
+                worst_decline = min(max_declines)
+                
+                future_performance['统计_平均最大跌幅'] = round(avg_max_decline, 2)
+                future_performance['统计_最差跌幅'] = round(worst_decline, 2)
+                
+                print(f"  平均最大跌幅: {avg_max_decline:+.2f}%")
+                print(f"  最差单周跌幅: {worst_decline:+.2f}%")
+            
+            performance_results.append(future_performance)
+            print("=" * 80)
+        
+        return performance_results
+    
+    def create_visualizations(self, pattern_results, weekly_data):
+        """创建K线图可视化"""
+        if not self.config['output_settings']['generate_charts']:
+            return
+            
+        self._log("\n=== 步骤7: 生成K线图可视化 ===")
+        
+        max_charts = min(len(pattern_results), self.config['output_settings']['max_chart_count'])
+        
+        for i, pattern in enumerate(pattern_results[:max_charts]):
+            code = pattern['品种代码']
+            pattern_date = pattern['日期']
+            pattern_idx = pattern['数据索引']
+            
+            if code not in weekly_data:
+                continue
+                
+            data = weekly_data[code]['data']
+            
+            # 准备绘图数据(形态前后各5周)
+            start_idx = max(0, pattern_idx - 5)
+            end_idx = min(len(data), pattern_idx + 11)
+            plot_data = data.iloc[start_idx:end_idx]
+            
+            # 创建K线图
+            fig, ax = plt.subplots(figsize=(12, 8))
+            
+            # 绘制K线
+            for j, (date, row) in enumerate(plot_data.iterrows()):
+                open_price = row['open']
+                close_price = row['close']
+                high_price = row['high']
+                low_price = row['low']
+                
+                # K线颜色
+                color = 'red' if close_price > open_price else 'green'
+                
+                # 绘制影线
+                ax.plot([j, j], [low_price, high_price], color='black', linewidth=1)
+                
+                # 绘制实体
+                body_height = abs(close_price - open_price)
+                if close_price > open_price:
+                    rect = patches.Rectangle((j-0.3, open_price), 0.6, body_height, 
+                                           linewidth=1, edgecolor='red', facecolor='red', alpha=0.7)
+                else:
+                    rect = patches.Rectangle((j-0.3, close_price), 0.6, body_height, 
+                                           linewidth=1, edgecolor='green', facecolor='green', alpha=0.7)
+                ax.add_patch(rect)
+                
+                # 标记目标形态
+                if date == pattern_date:
+                    ax.scatter(j, high_price + (high_price - low_price) * 0.1, 
+                             color='yellow', s=100, marker='*', zorder=5)
+                    ax.text(j, high_price + (high_price - low_price) * 0.15, 
+                           '目标形态', ha='center', fontsize=10, color='red', fontweight='bold')
+            
+            # 设置图表
+            ax.set_title(f'{code} 周线K线图 - 目标形态分析\n形态日期: {pattern_date.strftime("%Y-%m-%d")}', 
+                        fontsize=14, fontweight='bold')
+            ax.set_xlabel('时间', fontsize=12)
+            ax.set_ylabel('价格', fontsize=12)
+            ax.grid(True, alpha=0.3)
+            
+            # 设置x轴标签
+            dates = [date.strftime('%m-%d') for date in plot_data.index]
+            ax.set_xticks(range(len(dates)))
+            ax.set_xticklabels(dates, rotation=45)
+            
+            # 显示图表
+            plt.tight_layout()
+            plt.show()  # 直接在界面中显示图表
+            self._log(f"显示图表: {code} {pattern_date.strftime('%Y-%m-%d')} K线形态分析")
+    
+    def generate_summary_report(self, decline_futures, pattern_results, performance_results):
+        """生成整体规律总结报告"""
+        self._log("\n=== 步骤8: 生成统计报告 ===")
+        
+        # 基础统计
+        total_decline_futures = len(decline_futures)
+        total_patterns = len(pattern_results)
+        total_analyzed = len(performance_results)
+        
+        print(f"\n=== 期货技术形态规律分析报告 ===")
+        print(f"分析期间: {self.start_date.strftime('%Y-%m-%d')} 至 {self.end_date.strftime('%Y-%m-%d')}")
+        print(f"总分析期货品种数: {len(self.future_codes)}")
+        print(f"大幅下跌品种数: {total_decline_futures}")
+        print(f"识别出的目标形态数: {total_patterns}")
+        print(f"完成后续走势分析数: {total_analyzed}")
+        
+        if total_analyzed == 0:
+            print("无有效数据进行统计分析")
+            return {}
+        
+        # 收益率统计
+        print(f"\n=== 后续走势统计分析 ===")
+        
+        # 提取所有统计指标
+        avg_weekly_changes = [r['统计_平均周涨跌幅'] for r in performance_results if '统计_平均周涨跌幅' in r]
+        max_weekly_gains = [r['统计_最大周涨幅'] for r in performance_results if '统计_最大周涨幅' in r]
+        max_weekly_losses = [r['统计_最大周跌幅'] for r in performance_results if '统计_最大周跌幅' in r]
+        avg_max_declines = [r['统计_平均最大跌幅'] for r in performance_results if '统计_平均最大跌幅' in r]
+        
+        if avg_weekly_changes:
+            print(f"平均周涨跌幅统计:")
+            print(f"  平均值: {np.mean(avg_weekly_changes):.2f}%")
+            print(f"  中位数: {np.median(avg_weekly_changes):.2f}%") 
+            print(f"  最大值: {max(avg_weekly_changes):.2f}%")
+            print(f"  最小值: {min(avg_weekly_changes):.2f}%")
+            
+            positive_weeks = sum(1 for r in avg_weekly_changes if r > 0)
+            print(f"  正收益形态比例: {positive_weeks}/{len(avg_weekly_changes)} ({positive_weeks/len(avg_weekly_changes)*100:.1f}%)")
+            
+        if max_weekly_gains:
+            print(f"\n最大周涨幅统计:")
+            print(f"  平均值: {np.mean(max_weekly_gains):.2f}%")
+            print(f"  中位数: {np.median(max_weekly_gains):.2f}%")
+            print(f"  最大值: {max(max_weekly_gains):.2f}%")
+            print(f"  最小值: {min(max_weekly_gains):.2f}%")
+            
+        if max_weekly_losses:
+            print(f"\n最大周跌幅统计:")
+            print(f"  平均值: {np.mean(max_weekly_losses):.2f}%")
+            print(f"  中位数: {np.median(max_weekly_losses):.2f}%")
+            print(f"  最大值: {max(max_weekly_losses):.2f}%") 
+            print(f"  最小值: {min(max_weekly_losses):.2f}%")
+            
+        if avg_max_declines:
+            print(f"\n平均最大跌幅统计:")
+            print(f"  平均值: {np.mean(avg_max_declines):.2f}%")
+            print(f"  中位数: {np.median(avg_max_declines):.2f}%")
+            print(f"  最大值: {max(avg_max_declines):.2f}%")
+            print(f"  最小值: {min(avg_max_declines):.2f}%")
+        
+        # 成功率统计 - 基于平均周涨跌幅是否为正
+        positive_performance_count = sum(1 for r in avg_weekly_changes if r > 0) if avg_weekly_changes else 0
+        success_rate = positive_performance_count / len(avg_weekly_changes) * 100 if avg_weekly_changes else 0
+        
+        # 基于最大周涨幅的成功率统计
+        success_threshold = self.config['analysis_params']['success_threshold'] * 100
+        high_gain_count = sum(1 for r in max_weekly_gains if r > success_threshold) if max_weekly_gains else 0
+        high_gain_rate = high_gain_count / len(max_weekly_gains) * 100 if max_weekly_gains else 0
+        
+        print(f"\n=== 交易成功率分析 ===")
+        print(f"正收益形态数量: {positive_performance_count}/{len(avg_weekly_changes) if avg_weekly_changes else 0}")
+        print(f"正收益成功率: {success_rate:.1f}%")
+        print(f"")
+        print(f"高收益标准: 最大周涨幅 > {success_threshold}%")
+        print(f"高收益形态数量: {high_gain_count}/{len(max_weekly_gains) if max_weekly_gains else 0}")
+        print(f"高收益成功率: {high_gain_rate:.1f}%")
+        
+        return {
+            'total_futures': len(self.future_codes),
+            'decline_futures': total_decline_futures,
+            'pattern_count': total_patterns,
+            'analyzed_count': total_analyzed,
+            'avg_weekly_change': np.mean(avg_weekly_changes) if avg_weekly_changes else 0,
+            'avg_max_gain': np.mean(max_weekly_gains) if max_weekly_gains else 0,
+            'avg_max_loss': np.mean(max_weekly_losses) if max_weekly_losses else 0,
+            'avg_max_decline': np.mean(avg_max_declines) if avg_max_declines else 0,
+            'success_rate': success_rate,
+            'high_gain_rate': high_gain_rate,
+            'positive_count': positive_performance_count,
+            'high_gain_count': high_gain_count
+        }
+    
+    def save_results(self, decline_futures, pattern_results, performance_results, summary_stats):
+        """保存结果到CSV文件"""
+        self._log("\n=== 保存分析结果 ===")
+        
+        timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
+        encoding = self.config['output_settings']['csv_encoding']
+        
+        # 保存大幅下跌期货品种
+        if decline_futures:
+            decline_df = pd.DataFrame(decline_futures)
+            decline_file = f'future_major_decline_{timestamp}.csv'
+            decline_df.to_csv(decline_file, index=False, encoding=encoding)
+            self._log(f"大幅下跌期货保存至: {decline_file}")
+        
+        # 保存识别的K线形态
+        if pattern_results:
+            pattern_df = pd.DataFrame(pattern_results)
+            pattern_file = f'future_kline_patterns_{timestamp}.csv'
+            pattern_df.to_csv(pattern_file, index=False, encoding=encoding)
+            self._log(f"K线形态数据保存至: {pattern_file}")
+        
+        # 保存后续走势分析
+        if performance_results:
+            performance_df = pd.DataFrame(performance_results)
+            performance_file = f'future_pattern_performance_{timestamp}.csv'
+            performance_df.to_csv(performance_file, index=False, encoding=encoding)
+            self._log(f"后续走势分析保存至: {performance_file}")
+        
+        # 保存汇总报告
+        if summary_stats:
+            summary_df = pd.DataFrame([summary_stats])
+            summary_file = f'future_pattern_summary_{timestamp}.csv'
+            summary_df.to_csv(summary_file, index=False, encoding=encoding)
+            self._log(f"汇总报告保存至: {summary_file}")
+    
+    def run_analysis(self, mode=1, manual_config=None):
+        """
+        统一的分析运行入口
+        
+        参数:
+        - mode: 执行模式
+          1 = 原始/自动模式:自动搜索符合条件的技术形态
+          2 = 验证模式:验证指定日期是否符合模式条件
+        - manual_config: 手动配置字典,仅在mode=2时使用
+        
+        返回:
+        - dict: 分析结果
+        """
+        
+        if mode == 1:
+            return self._run_original_mode()
+        elif mode == 2:
+            if manual_config is None:
+                raise ValueError("验证模式(mode=2)需要提供manual_config参数")
+            return self._run_validation_mode(manual_config)
+        else:
+            raise ValueError("mode参数必须为1(原始模式)或2(验证模式)")
+    
+    def _run_original_mode(self):
+        """原始/自动模式:自动搜索符合条件的技术形态"""
+        print("开始期货技术形态规律分析... [原始模式]")
+        
+        # 构建期货品种基础数据
+        self.build_future_codes()
+        
+        # 筛选大幅下跌的期货品种
+        decline_futures = self.find_major_decline_futures()
+        
+        if not decline_futures:
+            print("未找到符合条件的大幅下跌期货品种")
+            return {}
+        
+        # 创建8888加权合约映射
+        contract_mapping = self.get_simplified_contract_mapping(decline_futures)
+        
+        if not contract_mapping:
+            print("未能创建8888合约映射")
+            return {}
+        else:
+            print(f"创建8888合约映射成功,共{len(contract_mapping)}个合约")
+            for contract_code, time_range in contract_mapping.items():
+                start_date, end_date = time_range
+                print(f"  {contract_code}: {start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}")
+        
+        # 获取周线K线数据
+        weekly_data = self.get_weekly_kline_data(contract_mapping)
+        
+        if not weekly_data:
+            print("未能获取周线数据")
+            return {}
+        
+        # 识别目标K线形态
+        pattern_results = self.identify_target_pattern(weekly_data, decline_futures)
+        
+        if not pattern_results:
+            print("未识别出目标K线形态")
+            return {}
+        
+        # 分析后续走势
+        performance_results = self.analyze_future_performance(pattern_results, weekly_data)
+        
+        # 创建可视化
+        self.create_visualizations(pattern_results, weekly_data)
+        
+        # 生成汇总报告
+        summary_stats = self.generate_summary_report(decline_futures, pattern_results, performance_results)
+        
+        print(f"\n=== 分析完成 ===")
+        print(f"共分析了{len(decline_futures)}个大幅下跌期货品种")
+        print(f"识别出{len(pattern_results)}个目标K线形态")
+        print(f"完成{len(performance_results)}个形态的后续走势分析")
+        
+        return {
+            'mode': 1,
+            'decline_futures': decline_futures,
+            'pattern_results': pattern_results,
+            'performance_results': performance_results,
+            'summary_stats': summary_stats
+        }
+    
+    def _run_validation_mode(self, manual_config):
+        """验证模式:验证指定日期是否符合模式条件"""
+        print("开始期货技术形态验证分析... [验证模式]")
+        
+        # 构建期货品种基础数据(仅构建需要的品种)
+        self.build_future_codes()
+        
+        # 从配置中提取需要验证的品种代码
+        config_codes = list(manual_config.keys())
+        print(f"配置的验证目标: {config_codes}")
+        
+        # 为每个配置的品种创建数据获取范围
+        sample_futures = []
+        
+        for code in config_codes:
+            if code in self.future_codes:
+                contract_8888 = self.future_codes[code]
+                date_list = manual_config[code]
+                
+                # 基于配置的日期计算数据获取范围
+                config_dates = [pd.to_datetime(date_str) for date_str in date_list]
+                earliest_date = min(config_dates)
+                latest_date = max(config_dates)
+                
+                # 为了确保有足够的数据进行验证,在日期范围前后各扩展一些时间
+                buffer_days = 90  # 3个月缓冲
+                start_date = earliest_date - pd.Timedelta(days=buffer_days)
+                end_date = latest_date + pd.Timedelta(days=buffer_days)
+                
+                sample_futures.append({
+                    '品种代码': code,
+                    '合约代码': contract_8888,
+                    '高点日期': start_date,
+                    '低点日期': end_date
+                })
+                
+                print(f"  ✓ {code} ({contract_8888}): {start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}")
+            else:
+                print(f"  ⚠️ 未找到品种 '{code}' 对应的合约代码")
+        
+        print(f"实际处理的品种数: {len(sample_futures)}")
+        
+        if not sample_futures:
+            print("❌ 配置中的品种代码都无法找到对应的合约")
+            return {}
+        
+        # 创建简化的合约映射
+        contract_mapping = self.get_simplified_contract_mapping(sample_futures)
+        
+        if not contract_mapping:
+            print("❌ 无法创建合约映射")
+            return {}
+        
+        # 获取周线数据
+        weekly_data = self.get_weekly_kline_data(contract_mapping)
+        
+        if not weekly_data:
+            print("❌ 无法获取周线数据")
+            return {}
+        
+        print(f"✓ 成功获取 {len(weekly_data)} 个品种的数据")
+        
+        # 执行手动验证
+        validation_results = self.validate_manual_patterns(manual_config, weekly_data)
+        validation_results['mode'] = 2
+        
+        return validation_results
+
+# =====================================================================================
+# 第三部分:主程序运行函数
+# 提供统一的程序入口和运行选项
+# =====================================================================================
+
+def run_pattern_analysis(mode=1, manual_config=None):
+    """
+    统一的期货技术形态分析入口
+    
+    参数:
+    - mode: 执行模式
+      1 = 原始/自动模式:自动搜索符合条件的技术形态
+      2 = 验证模式:验证指定日期是否符合模式条件
+    - manual_config: 手动配置字典,仅在mode=2时使用
+    
+    返回:
+    - dict: 分析结果
+    """
+    print("期货技术形态规律分析工具")
+    print("="*60)
+    
+    # 打印配置信息
+    AnalysisConfig.print_config()
+    print("="*60)
+    
+    mode_name = "原始模式" if mode == 1 else "验证模式"
+    print(f"开始分析... [{mode_name}]")
+    
+    # 创建分析器并运行
+    analyzer = FuturePatternAnalyzer(
+        AnalysisConfig.START_DATE, 
+        AnalysisConfig.END_DATE
+    )
+    
+    results = analyzer.run_analysis(mode=mode, manual_config=manual_config)
+    return results
+    
+# =====================================================================================
+# 第四部分:辅助工具函数
+# 用于处理合约切换日志和数据导出
+# =====================================================================================
+
+def parse_contract_switch_log_from_text(log_text):
+    """
+    从文本格式的合约切换日志中解析合约信息
+    
+    参数:
+    - log_text: 日志文本,格式示例:
+      "A2203.XDCE:2022年1月26日至2022年2月22日
+       A2207.XDCE:2022年2月23日至2022年6月21日"
+    
+    返回:
+    - list: 标准化的合约切换日志
+    """
+    import re
+    
+    contracts = []
+    lines = log_text.strip().split('\n')
+    
+    for line in lines:
+        line = line.strip()
+        if not line:
+            continue
+            
+        # 正则匹配合约代码和日期范围
+        pattern = r'([A-Z0-9]+\.[A-Z]+):(\d{4})年(\d{1,2})月(\d{1,2})日至(\d{4})年(\d{1,2})月(\d{1,2})日'
+        match = re.search(pattern, line)
+        
+        if match:
+            contract_code = match.group(1)
+            start_year = int(match.group(2))
+            start_month = int(match.group(3))
+            start_day = int(match.group(4))
+            end_year = int(match.group(5))
+            end_month = int(match.group(6))
+            end_day = int(match.group(7))
+            
+            contracts.append({
+                'contract': contract_code,
+                'start_date': f'{start_year}-{start_month:02d}-{start_day:02d}',
+                'end_date': f'{end_year}-{end_month:02d}-{end_day:02d}'
+            })
+    
+    return contracts
+
+def export_contract_weekly_data(weekly_data_dict, filename_prefix='contract_weekly_data'):
+    """
+    导出合约周线数据到CSV文件
+    
+    参数:
+    - weekly_data_dict: get_weekly_data_by_contract_log返回的数据字典
+    - filename_prefix: 文件名前缀
+    
+    注意:导出的数据是直接从get_bars API获取的原生周线数据,
+          相比日线转换的数据具有更高的精度和准确性
+    """
+    import datetime
+    
+    timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
+    
+    for contract_code, contract_data in weekly_data_dict.items():
+        # 清理文件名中的特殊字符
+        safe_contract_name = contract_code.replace('.', '_')
+        filename = f'{filename_prefix}_{safe_contract_name}_{timestamp}.csv'
+        
+        # 准备数据
+        df = contract_data['data'].copy()
+        df['contract'] = contract_code
+        df['original_start'] = contract_data['original_start'].strftime('%Y-%m-%d')
+        df['original_end'] = contract_data['original_end'].strftime('%Y-%m-%d')
+        
+        # 保存到CSV
+        df.to_csv(filename, encoding='utf-8-sig')
+        print(f"合约 {contract_code} 的周线数据已保存至: {filename}")
+
+def run_manual_pattern_validation(manual_config, analyzer=None):
+    """
+    运行手动模式验证的便捷函数
+    
+    此函数只会处理manual_config中明确指定的品种和时间范围,
+    不会查询额外的品种数据。现在使用统一的分析框架。
+    
+    参数:
+    - manual_config: 手动配置字典,格式:
+      {'A':['2023-01-06','2023-06-09'], 'B':['2022-07-22','2024-08-23']}
+      键: 期货品种代码(如'A'、'B'、'RB'等)
+      值: 需要验证的日期列表('YYYY-MM-DD'格式)
+    - analyzer: 可选的分析器实例,如果为None则创建新实例
+    
+    返回:
+    - dict: 验证结果,包含成功和失败的详细信息
+    
+    注意:
+    - 系统只会获取配置中指定品种的数据
+    - 数据获取范围基于每个品种配置的日期范围自动计算
+    - 不会处理未在配置中明确指定的品种
+    - 现在使用统一的核心验证逻辑,确保与原始模式的一致性
+    """
+    print("\n=== 手动模式验证工具 ===")
+    print("此功能用于验证指定日期是否符合预定义的技术形态模式")
+    print("使用统一的验证逻辑,与原始模式保持完全一致")
+    
+    # 创建分析器实例(如果没有提供)
+    if analyzer is None:
+        analyzer = FuturePatternAnalyzer()
+    
+    # 使用统一的分析入口 - 验证模式
+    validation_results = analyzer.run_analysis(mode=2, manual_config=manual_config)
+    
+    return validation_results
+
+def demo_manual_validation():
+    """演示手动模式验证功能"""
+    print("\n=== 手动模式验证演示 ===")
+    
+    # 示例配置
+    demo_config = {
+        'A': ['2023-01-06', '2023-06-09', '2024-07-26'],
+        'B': ['2022-07-22', '2024-08-23', '2025-01-03']
+    }
+    
+    print("使用演示配置:")
+    for pattern_type, dates in demo_config.items():
+        print(f"  模式 '{pattern_type}': {len(dates)} 个日期")
+        for date in dates:
+            print(f"    - {date}")
+    
+    try:
+        # 运行验证
+        results = run_manual_pattern_validation(demo_config)
+        
+        if results:
+            print("\n✅ 验证完成")
+            return results
+        else:
+            print("\n⚠️ 验证未能完成")
+            return {}
+            
+    except Exception as e:
+        print(f"\n❌ 验证过程中出错: {str(e)}")
+        import traceback
+        traceback.print_exc()
+        return {}
+
+def get_contract_weekly_data(contract_switch_log, extend_weeks=10, export_data=False):
+    """
+    基于合约切换日志获取周线数据的正式方法
+    
+    参数:
+    - contract_switch_log: 合约切换日志,可以是文本格式或列表格式
+    - extend_weeks: 延长周数,默认10周
+    - export_data: 是否导出数据到CSV文件,默认False
+    
+    返回:
+    - dict: 每个合约的周线数据
+    """
+    print("\n=== 基于合约切换日志获取周线数据 ===")
+    
+    # 如果是文本格式,先解析
+    if isinstance(contract_switch_log, str):
+        print("解析合约切换日志:")
+        contracts = parse_contract_switch_log_from_text(contract_switch_log)
+        for contract in contracts:
+            print(f"   {contract['contract']}: {contract['start_date']} 至 {contract['end_date']}")
+    else:
+        contracts = contract_switch_log
+        print(f"处理{len(contracts)}个合约的切换日志")
+    
+    print(f"\n创建分析器并获取周线数据 (延长{extend_weeks}周):")
+    analyzer = FuturePatternAnalyzer()
+    
+    try:
+        # 获取周线数据
+        weekly_data = analyzer.get_weekly_data_by_contract_log(contracts, extend_weeks=extend_weeks)
+        
+        if weekly_data:
+            print("   ✓ 获取成功!")
+            for contract_code, data_info in weekly_data.items():
+                print(f"   {contract_code}: {data_info['total_weeks']}周数据")
+                print(f"     原始范围: {data_info['original_start'].strftime('%Y-%m-%d')} 至 {data_info['original_end'].strftime('%Y-%m-%d')}")
+                print(f"     延长范围: {data_info['extended_start'].strftime('%Y-%m-%d')} 至 {data_info['extended_end'].strftime('%Y-%m-%d')}")
+            
+            # 导出数据
+            if export_data:
+                export_contract_weekly_data(weekly_data)
+                
+            return weekly_data
+        else:
+            print("   ⚠️ 未获取到数据")
+            return {}
+            
+    except Exception as e:
+        print(f"   ❌ 获取失败: {str(e)}")
+        import traceback
+        traceback.print_exc()
+        return {}
+
+# =====================================================================================
+# 第五部分:程序主入口 - 自动运行分析
+# =====================================================================================
+
+print("期货市场技术形态规律分析工具")
+print("本工具专门研究期货品种在大幅下跌后出现特定K线形态的规律")  
+print("适用于聚宽在线研究平台")
+print("="*80)
+
+# =====================================================================================
+# 基于合约切换日志的周线数据获取功能使用说明
+# =====================================================================================
+# 
+# 使用示例1:处理文本格式的合约切换日志
+# sample_log = """
+# A2203.XDCE:2022年1月26日至2022年2月22日
+# A2207.XDCE:2022年2月23日至2022年6月21日
+# A2209.XDCE:2022年6月22日至2022年8月30日
+# """
+# weekly_data = get_contract_weekly_data(sample_log, extend_weeks=10, export_data=True)
+#
+# 使用示例2:处理列表格式的合约切换日志
+# contract_list = [
+#     {'contract': 'A2203.XDCE', 'start_date': '2022-01-26', 'end_date': '2022-02-22'},
+#     {'contract': 'A2207.XDCE', 'start_date': '2022-02-23', 'end_date': '2022-06-21'}
+# ]
+# weekly_data = get_contract_weekly_data(contract_list, extend_weeks=15, export_data=False)
+#
+# 新增功能1:模式识别失败时的完整K线图显示
+# 当identify_target_pattern函数未识别到任何目标形态时,系统会自动:
+# 1. 显示所有可用品种的完整烛台图
+# 2. 包含OHLC数据的专业K线图表现
+# 3. 添加统计信息(最高价、最低价、总涨跌幅等)
+# 4. 直接在界面中显示,不保存到本地文件
+#
+# 新增功能2:手动模式验证系统
+# 使用示例3:手动验证指定日期的模式匹配
+# 现在使用统一的入口函数,通过mode参数控制执行模式
+manual_config = {
+    'A': ['2023-01-06', '2023-06-09', '2024-07-26'],
+    'B': ['2022-07-22', '2024-08-23', '2025-01-03']
+}
+
+# 方法1: 使用统一入口函数
+validation_results = run_pattern_analysis(mode=2, manual_config=manual_config)
+
+# 方法2: 或者使用便捷函数(内部调用统一入口)
+# validation_results = run_manual_pattern_validation(manual_config)
+# 或运行演示: demo_manual_validation()
+#
+# 重构后的统一模式控制系统:
+# 1. mode=1(原始模式):自动搜索符合条件的技术形态,遍历所有期货品种
+# 2. mode=2(验证模式):验证指定日期是否符合模式条件,只处理配置品种
+# 3. 统一的核心验证逻辑:_core_pattern_validation()消除了代码重复
+# 4. 独立的执行路径:两种模式不再共享步骤1,完全独立执行
+# 5. 一致的验证标准:两种模式使用完全相同的模式识别条件和计算方法
+#
+# 数据获取特点:
+# 1. 直接使用get_bars API获取原生周线数据,避免日线转换的精度损失
+# 2. 自动延长时间范围(每个合约前后各延长指定周数)
+# 3. 确保获取充足的历史数据(最少60周作为背景)
+# 4. 支持数据导出到CSV文件便于后续分析
+# 5. 简化结构:直接使用8888加权合约,无需处理主力合约切换
+# 
+# 简化后的工作流程:
+# 1. 识别符合条件的大幅下跌期货品种
+# 2. 为每个品种创建8888合约映射(单级字典结构)
+# 3. 直接从8888合约获取周线数据,避免主力合约复杂性
+# 4. 进行技术形态识别和后续走势分析
+# 5. 当未识别到目标形态时,自动显示完整的烛台图(K线图)
+# =====================================================================================
+
+# 直接运行分析
+# print("\n开始运行完整分析...")
+# try:
+#     results = run_pattern_analysis()
+# except Exception as e:
+#     print(f"运行过程中出现错误: {str(e)}")
+#     import traceback
+#     traceback.print_exc()
+#     results = None
+
+# if results:
+#     print("\n✅ 分析完成!")
+#     if isinstance(results, dict) and 'summary_stats' in results:
+#         summary = results['summary_stats']
+#         print(f"\n📊 快速统计:")
+#         print(f"   - 识别到技术形态: {summary.get('pattern_count', 0)}个")
+#         print(f"   - 正收益成功率: {summary.get('success_rate', 0):.1f}%")
+#         print(f"   - 高收益成功率: {summary.get('high_gain_rate', 0):.1f}%")
+#         print(f"   - 平均周涨跌幅: {summary.get('avg_weekly_change', 0):+.2f}%")
+# else:
+#     print("\n❌ 分析未能完成,请检查错误信息。")

+ 226 - 0
Lib/research/get_dominant_future.py

@@ -0,0 +1,226 @@
+# 主力合约跟踪工具
+# 用于获取指定时间范围内某个或某些标的的主力合约变化信息
+
+from jqdata import *
+import pandas as pd
+from datetime import datetime, timedelta
+import warnings
+warnings.filterwarnings('ignore')
+
+def get_dominant_contracts_history(symbol_list, time_range):
+    """
+    获取指定时间范围内某个或某些标的的主力合约历史变化
+    
+    参数:
+        symbol_list (list): 标的编码列表,如 ['FU', 'BU']
+        time_range (list): 时间范围,格式为 ['20240101', '20250101']
+    
+    返回:
+        pandas.DataFrame: 包含以下列的数据框
+            - symbol: 原始标的编码
+            - dominant_contract: 主力合约完整编码
+            - start_date: 主力合约开始日期
+            - end_date: 主力合约结束日期
+    """
+    
+    # 验证输入参数
+    if not isinstance(symbol_list, list) or len(symbol_list) == 0:
+        raise ValueError("symbol_list必须是非空列表")
+    
+    if not isinstance(time_range, list) or len(time_range) != 2:
+        raise ValueError("time_range必须是包含两个日期的列表")
+    
+    # 转换日期格式
+    try:
+        start_date = datetime.strptime(time_range[0], '%Y%m%d').date()
+        end_date = datetime.strptime(time_range[1], '%Y%m%d').date()
+    except ValueError:
+        raise ValueError("日期格式错误,请使用YYYYMMDD格式")
+    
+    if start_date >= end_date:
+        raise ValueError("开始日期必须早于结束日期")
+    
+    print(f"开始获取主力合约数据...")
+    print(f"标的列表: {symbol_list}")
+    print(f"时间范围: {start_date} 至 {end_date}")
+    
+    # 存储所有结果的列表
+    all_results = []
+    
+    # 逐个处理每个标的
+    for symbol in symbol_list:
+        print(f"\n处理标的: {symbol}")
+        
+        try:
+            # 获取该标的在指定时间范围内的主力合约历史
+            symbol_results = _get_symbol_dominant_history(symbol, start_date, end_date)
+            all_results.extend(symbol_results)
+            
+        except Exception as e:
+            print(f"处理标的 {symbol} 时发生错误: {str(e)}")
+            continue
+    
+    # 将结果转换为DataFrame
+    if all_results:
+        result_df = pd.DataFrame(all_results)
+        result_df = result_df.sort_values(['symbol', 'start_date']).reset_index(drop=True)
+        
+        print(f"\n成功获取 {len(result_df)} 条主力合约记录")
+        return result_df
+    else:
+        print("\n未获取到任何主力合约数据")
+        return pd.DataFrame(columns=['symbol', 'dominant_contract', 'start_date', 'end_date'])
+
+
+def _get_symbol_dominant_history(symbol, start_date, end_date):
+    """
+    获取单个标的的主力合约历史变化
+    
+    参数:
+        symbol (str): 标的编码
+        start_date (date): 开始日期
+        end_date (date): 结束日期
+    
+    返回:
+        list: 主力合约记录列表
+    """
+    
+    results = []
+    current_date = start_date
+    current_dominant = None
+    period_start = None
+    
+    # 按日遍历时间范围
+    while current_date <= end_date:
+        
+        # 跳过非交易日
+        if not _is_trading_day(current_date):
+            current_date += timedelta(days=1)
+            continue
+        
+        try:
+            # 获取当日主力合约
+            dominant_contract = get_dominant_future(symbol, current_date)
+            
+            if dominant_contract is None:
+                current_date += timedelta(days=1)
+                continue
+                
+            # 如果是新的主力合约
+            if dominant_contract != current_dominant:
+                
+                # 如果不是第一个合约,结束上一个合约的记录
+                if current_dominant is not None and period_start is not None:
+                    results.append({
+                        'symbol': symbol,
+                        'dominant_contract': current_dominant,
+                        'start_date': period_start,
+                        'end_date': current_date - timedelta(days=1)
+                    })
+                
+                # 开始新合约的记录
+                current_dominant = dominant_contract
+                period_start = current_date
+                
+                print(f"  {current_date}: 主力合约变更为 {dominant_contract}")
+            
+        except Exception as e:
+            print(f"  获取 {current_date} 的主力合约时出错: {str(e)}")
+        
+        current_date += timedelta(days=1)
+    
+    # 处理最后一个合约
+    if current_dominant is not None and period_start is not None:
+        results.append({
+            'symbol': symbol,
+            'dominant_contract': current_dominant,
+            'start_date': period_start,
+            'end_date': end_date
+        })
+    
+    return results
+
+
+def _is_trading_day(check_date):
+    """
+    检查是否为交易日
+    
+    参数:
+        check_date (date): 要检查的日期
+    
+    返回:
+        bool: 是否为交易日
+    """
+    try:
+        # 使用聚宽API检查交易日
+        trade_days = get_trade_days(end_date=check_date, count=1)
+        if len(trade_days) > 0:
+            return trade_days[0].date() == check_date
+        return False
+    except:
+        # 如果API调用失败,简单判断是否为工作日
+        return check_date.weekday() < 5
+
+
+def get_dominant_contracts_summary(symbol_list, time_range):
+    """
+    获取主力合约变化的汇总信息
+    
+    参数:
+        symbol_list (list): 标的编码列表
+        time_range (list): 时间范围
+    
+    返回:
+        dict: 汇总信息
+    """
+    
+    df = get_dominant_contracts_history(symbol_list, time_range)
+    
+    if df.empty:
+        return {}
+    
+    summary = {}
+    
+    for symbol in symbol_list:
+        symbol_data = df[df['symbol'] == symbol]
+        
+        if not symbol_data.empty:
+            summary[symbol] = {
+                '主力合约数量': len(symbol_data),
+                '合约列表': symbol_data['dominant_contract'].tolist(),
+                '平均持续天数': (symbol_data['end_date'] - symbol_data['start_date']).dt.days.mean()
+            }
+        else:
+            summary[symbol] = {
+                '主力合约数量': 0,
+                '合约列表': [],
+                '平均持续天数': 0
+            }
+    
+    return summary
+
+
+def export_to_csv(symbol_list, time_range, filename=None):
+    """
+    导出主力合约数据到CSV文件
+    
+    参数:
+        symbol_list (list): 标的编码列表
+        time_range (list): 时间范围
+        filename (str): 导出文件名,如果为None则自动生成
+    
+    返回:
+        str: 导出的文件路径
+    """
+    
+    df = get_dominant_contracts_history(symbol_list, time_range)
+    
+    if filename is None:
+        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
+        symbols_str = '_'.join(symbol_list)
+        filename = f"dominant_contracts_{symbols_str}_{time_range[0]}_{time_range[1]}_{timestamp}.csv"
+    
+    df.to_csv(filename, index=False, encoding='utf-8-sig')
+    print(f"数据已导出到: {filename}")
+    
+    return filename