Forráskód Böngészése

新增烛台影线形态反向交易策略及相关分析工具,包含完整的策略逻辑、参数配置和交易信号生成。同时更新README文档以说明新功能和使用方法,增强了对未来价格走势的预测能力。

maxfeng 1 hónapja
szülő
commit
2b7e0def4c

+ 1339 - 0
Lib/future/CandlestickHatchReverseStrategy_v001.py

@@ -0,0 +1,1339 @@
+# 导入函数库
+from jqdata import *
+from jqdata import finance
+import pandas as pd
+import numpy as np
+from datetime import date, datetime, timedelta
+import re
+
+# 烛台影线形态反向交易策略 v001
+# 基于烛台形态检测,按照影线相反方向进行交易的策略
+
+# 设置以便完整打印 DataFrame
+pd.set_option('display.max_rows', None)
+pd.set_option('display.max_columns', None)
+pd.set_option('display.width', None)
+pd.set_option('display.max_colwidth', 20)
+
+## 初始化函数,设定基准等等
+def initialize(context):
+    # 设定沪深300作为基准
+    set_benchmark('000300.XSHG')
+    # 开启动态复权模式(真实价格)
+    set_option('use_real_price', True)
+    # 输出内容到日志
+    log.info('烛台影线形态反向交易策略初始化开始')
+
+    ### 期货相关设定 ###
+    # 设定账户为金融账户
+    set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type='index_futures')])
+    # 期货类每笔交易时的手续费是: 买入时万分之0.23,卖出时万分之0.23,平今仓为万分之23
+    set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023, close_today_commission=0.0023), type='index_futures')
+    
+    # 设置期货交易的滑点
+    set_slippage(StepRelatedSlippage(2))
+    
+    # 初始化全局变量
+    g.usage_percentage = 0.8  # 最大资金使用比例
+    g.max_margin_per_position = 20000  # 单个标的最大持仓保证金(元)
+    
+    # 烛台形态检测参数(与研究文件保持一致)
+    g.hatch_to_body_ratio = 1.2  # 影线与实体长度比率阈值
+    g.opposite_hatch_ratio = 0.5  # 相反方向影线与实体长度比率阈值
+    g.historical_days = 365  # 历史数据天数,用于计算平均实体长度阈值(与研究文件保持一致)
+    
+    # 全局阈值缓存(每月更新一次)
+    g.monthly_body_threshold_cache = {}  # 存储每月计算的实体长度阈值
+    g.last_threshold_update_month = None  # 记录上次更新阈值的月份
+    
+    # 止损止盈策略参数
+    g.fixed_stop_loss_rate = 0.01  # 固定止损比率(1%)
+    g.trailing_stop_thresholds = [0.05, 0.10]  # 动态追踪止损触发条件(5%, 10%)
+    g.trailing_stop_rates = [0.02, 0.03, 0.04]  # 动态追踪止损比率(2%, 3%, 4%)
+    
+    # 开仓方向配置参数
+    g.reverse_direction_symbols = ['JD']  # 使用反向逻辑的品种列表(上影线做空,下影线做多)
+    # 不在此列表中的品种使用正向逻辑(上影线做多,下影线做空)
+    
+    # 期货品种完整配置字典
+    g.futures_config = {
+        # 贵金属
+        'AU': {'has_night_session': True, 'margin_rate': {'long': 0.14, 'short': 0.14}, 'multiplier': 1000},
+        'AG': {'has_night_session': True, 'margin_rate': {'long': 0.14, 'short': 0.14}, 'multiplier': 15},
+        
+        # 有色金属
+        'CU': {'has_night_session': True, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 5},
+        'AL': {'has_night_session': True, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 5},
+        'ZN': {'has_night_session': True, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 5},
+        'PB': {'has_night_session': True, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 5},
+        'NI': {'has_night_session': True, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 1},
+        'SN': {'has_night_session': True, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 1},
+        'SS': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
+        
+        # 黑色系
+        'RB': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'HC': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'I': {'has_night_session': True, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 100},
+        'JM': {'has_night_session': True, 'margin_rate': {'long': 0.22, 'short': 0.22}, 'multiplier': 100},
+        'J': {'has_night_session': True, 'margin_rate': {'long': 0.22, 'short': 0.22}, 'multiplier': 60},
+        
+        # 能源化工
+        'SP': {'has_night_session': True, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 10},
+        'FU': {'has_night_session': True, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 10},
+        'BU': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 10},
+        'RU': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'BR': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
+        'SC': {'has_night_session': True, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 1000},
+        'NR': {'has_night_session': True, 'margin_rate': {'long': 0.13, 'short': 0.13}, 'multiplier': 10},
+        'LU': {'has_night_session': True, 'margin_rate': {'long': 0.15, 'short': 0.15}, 'multiplier': 10},
+        'LC': {'has_night_session': False, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 1},
+        
+        # 化工
+        'FG': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
+        'TA': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'MA': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'SA': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
+        'L': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
+        'V': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
+        'EG': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'PP': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
+        'EB': {'has_night_session': True, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 5},
+        'PG': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
+        
+        # 农产品
+        'RM': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'OI': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'CF': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'SR': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'PF': {'has_night_session': True, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 5},
+        'C': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'CS': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'CY': {'has_night_session': True, 'margin_rate': {'long': 0.15, 'short': 0.15}, 'multiplier': 5},
+        'A': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'B': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'M': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'Y': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'P': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        
+        # 无夜盘品种
+        'IF': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 300},
+        'IH': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 300},
+        'IC': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 200},
+        'IM': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 200},
+        'AP': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 10},
+        'CJ': {'has_night_session': False, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 5},
+        'PK': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'JD': {'has_night_session': False, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
+        'LH': {'has_night_session': False, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 16}
+    }
+    
+    # 策略品种选择策略配置
+    # 方案1:全品种策略 - 考虑所有配置的期货品种
+    # g.strategy_focus_symbols = []  # 空列表表示考虑所有品种
+    
+    # 方案2:精选品种策略 - 只交易流动性较好的特定品种(如需使用请取消下行注释)
+    g.strategy_focus_symbols = ['RM', 'CJ', 'CY', 'JD', 'L', 'LC', 'SF', 'SI']
+    
+    log.info(f"品种选择策略: {'全品种策略(覆盖所有配置品种)' if not g.strategy_focus_symbols else '精选品种策略(' + str(len(g.strategy_focus_symbols)) + '个品种)'}")
+    
+    # 输出开仓方向配置信息
+    log.info("=" * 50)
+    log.info("开仓方向配置:")
+    log.info(f"反向逻辑品种: {g.reverse_direction_symbols} (上影线做空,下影线做多)")
+    log.info("正向逻辑品种: 所有其他品种 (上影线做多,下影线做空)")
+    log.info("=" * 50)
+    
+    # 交易记录和数据存储
+    g.trade_history = {}
+    g.candlestick_signals = {}  # 存储烛台形态信号
+    g.daily_data_cache = {}  # 存储历史日线数据缓存
+    g.minute_data_cache = {}  # 存储今日分钟数据缓存
+    g.body_threshold_cache = {}  # 存储实体长度阈值缓存
+    
+    # 保证金比例管理
+    g.margin_rate_history = {}  # 保证金比例变化历史记录
+    g.today_trades = []  # 当日交易记录
+    
+    # 定时任务设置
+    # 每月第1个交易日更新阈值
+    run_monthly(monthly_update_thresholds, 1, 'before_open', reference_security='IF1808.CCFX')
+    
+    # 夜盘开始 - 仅止损止盈检查
+    run_daily(main_trading_stop_only, time='21:05:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='21:35:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='22:05:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='22:35:00', reference_security='IF1808.CCFX')
+    
+    # 日盘开始 - 仅止损止盈检查
+    run_daily(main_trading_stop_only, time='09:05:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='09:35:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='10:05:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='10:35:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='11:05:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='11:25:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='13:35:00', reference_security='IF1808.CCFX')
+    run_daily(main_trading_stop_only, time='14:05:00', reference_security='IF1808.CCFX')
+    
+    # 收盘前 - 14:55进行完整交易检查,14:35仅止损止盈
+    run_daily(main_trading_stop_only, time='14:35:00', reference_security='IF1808.CCFX')
+    
+    # 完整交易检查 - 仅在14:55执行(任务1, 2, 3, 4, 5, 6, 7)
+    run_daily(main_trading_complete, time='14:55:00', reference_security='IF1808.CCFX')
+    
+    # 收盘后
+    run_daily(after_market_close, time='15:30:00', reference_security='IF1808.CCFX')
+
+############################ 主程序执行函数 ###################################
+
+def monthly_update_thresholds(context):
+    """每月更新实体长度阈值"""
+    log.info("=" * 60)
+    log.info("每月阈值更新开始")
+    log.info("=" * 60)
+    
+    current_month = context.current_dt.strftime('%Y-%m')
+    
+    # 检查是否需要更新
+    if g.last_threshold_update_month == current_month:
+        log.info(f"本月阈值已更新,跳过更新")
+        return
+    
+    # 获取所有需要计算阈值的品种
+    focus_symbols = g.strategy_focus_symbols if g.strategy_focus_symbols else list(g.futures_config.keys())
+    
+    updated_count = 0
+    for symbol in focus_symbols:
+        try:
+            # 获取主力合约
+            dominant_future = get_dominant_future(symbol)
+            if not dominant_future:
+                continue
+            
+            # 获取365天历史数据
+            data = attribute_history(dominant_future, g.historical_days, '1d', 
+                                   ['open', 'close', 'high', 'low', 'volume'], 
+                                   df=True)
+            
+            if data is not None and len(data) > 30:  # 确保有足够数据
+                # 计算实体长度阈值
+                data['body_length'] = abs(data['close'] - data['open'])
+                body_threshold = data['body_length'].mean()
+                
+                # 存储到全局阈值缓存
+                g.monthly_body_threshold_cache[dominant_future] = body_threshold
+                updated_count += 1
+                
+                log.info(f"更新 {symbol}({dominant_future}) 实体长度阈值: {body_threshold:.4f}")
+            
+        except Exception as e:
+            log.warning(f"更新{symbol}阈值时出错: {str(e)}")
+            continue
+    
+    # 更新最后更新月份
+    g.last_threshold_update_month = current_month
+    
+    log.info(f"阈值更新完成,共更新 {updated_count} 个品种")
+    log.info("=" * 60)
+
+def main_trading_stop_only(context):
+    """仅进行止损止盈检查的交易时段"""
+    # 只进行止损止盈检查
+    task_7_check_stop_loss_profit(context)
+
+def main_trading_complete(context):
+    """14:55 完整交易检查 - 执行任务1, 2, 3, 4, 5, 6, 7"""
+    log.info("=" * 60)
+    log.info("14:55 烛台影线形态反向交易策略 - 完整交易检查")
+    log.info("=" * 60)
+    
+    # 任务1: 获取所有可交易品种
+    task_1_get_tradable_futures(context)
+    
+    # 任务2: 获取历史数据并处理阈值(包括单独计算缺失阈值)
+    task_2_load_historical_data_and_thresholds(context)
+    
+    # 任务3: 获取今日分钟数据
+    task_3_update_realtime_data(context)
+    
+    # 任务4: 检测烛台形态
+    task_4_detect_candlestick_patterns(context)
+    
+    # 任务5: 检查开仓条件(简化版)
+    filtered_signals = task_5_check_opening_conditions(context)
+
+    # 任务6: 执行交易
+    if filtered_signals:
+        task_6_execute_trades(context, filtered_signals)
+    
+    # 任务7: 检查止损止盈
+    task_7_check_stop_loss_profit(context)
+    
+    # 任务8: 检查换月移仓
+    task_8_check_position_switch(context)
+
+############################ 核心任务函数 ###################################
+
+def task_1_get_tradable_futures(context):
+    """任务1: 获取所有可交易品种(分白天和晚上)"""
+    log.info("执行任务1: 获取可交易品种")
+    
+    current_time = str(context.current_dt.time())[:2]
+    
+    # 从策略关注列表中筛选可交易品种
+    focus_symbols = g.strategy_focus_symbols if g.strategy_focus_symbols else list(g.futures_config.keys())
+    
+    potential_icon_list = []
+    night_session_symbols = []
+    day_only_symbols = []
+    
+    # 预先分类所有品种
+    for symbol in focus_symbols:
+        if get_futures_config(symbol, 'has_night_session', False):
+            night_session_symbols.append(symbol)
+        else:
+            day_only_symbols.append(symbol)
+    
+    if current_time in ('21', '22'):
+        # 夜盘时间:只考虑有夜盘的品种
+        potential_icon_list = night_session_symbols[:]
+        log.info(f"夜盘时间,有夜盘交易的品种: {len(night_session_symbols)}个")
+        log.info(f"夜盘可交易品种: {potential_icon_list[:10]}{'...' if len(potential_icon_list) > 10 else ''}")  # 显示前10个,避免日志过长
+    else:
+        # 日盘时间:所有品种都可以交易(包括有夜盘的和只有日盘的)
+        potential_icon_list = focus_symbols[:]
+        log.info(f"日盘时间,全部品种: {len(focus_symbols)}个(含夜盘品种: {len(night_session_symbols)}个,日盘专属品种: {len(day_only_symbols)}个)")
+        if day_only_symbols:
+            log.info(f"日盘专属品种: {day_only_symbols}")
+        log.info(f"日盘可交易品种: {potential_icon_list[:10]}{'...' if len(potential_icon_list) > 10 else ''}")  # 显示前10个,避免日志过长
+    
+    potential_future_list = []
+    for symbol in potential_icon_list:
+        dominant_future = get_dominant_future(symbol)
+        if dominant_future:
+            potential_future_list.append(dominant_future)
+    
+    # 过滤掉已有持仓的品种
+    existing_positions = set(g.trade_history.keys())
+    potential_future_list = [f for f in potential_future_list if not check_symbol_prefix_match(f, existing_positions)]
+    
+    # 存储到全局变量
+    g.tradable_futures = potential_future_list
+    log.info(f"最终可交易期货品种数量: {len(potential_future_list)}")
+    
+    return potential_future_list
+
+def task_2_load_historical_data_and_thresholds(context):
+    """任务2: 获取历史数据并处理阈值(包括单独计算缺失阈值)"""
+    log.info("执行任务2: 加载历史数据并处理阈值")
+    
+    if not hasattr(g, 'tradable_futures'):
+        return
+    
+    for future_code in g.tradable_futures:
+        try:
+            # 获取30天历史数据(用于分钟数据合并)
+            data = attribute_history(future_code, 30, '1d', 
+                                   ['open', 'close', 'high', 'low', 'volume'], 
+                                   df=True)
+            
+            if data is not None and len(data) > 0:
+                # 排除今天的数据
+                today = context.current_dt.date()
+                data = data[data.index.date < today]
+                g.daily_data_cache[future_code] = data
+                
+                # 处理阈值:优先使用月度阈值,缺失时单独计算
+                if future_code in g.monthly_body_threshold_cache:
+                    # 使用月度阈值缓存
+                    body_threshold = g.monthly_body_threshold_cache[future_code]
+                    g.body_threshold_cache[future_code] = body_threshold
+                    # log.info(f"已缓存 {future_code} 历史数据 {len(data)} 条,使用月度阈值: {body_threshold:.4f}")
+                else:
+                    # 单独计算缺失的阈值
+                    log.info(f"{future_code} 未找到月度阈值,开始单独计算")
+                    body_threshold = calculate_individual_threshold(context, future_code)
+                    if body_threshold is not None:
+                        g.body_threshold_cache[future_code] = body_threshold
+                        log.info(f"已为 {future_code} 单独计算阈值: {body_threshold:.4f}")
+                    else:
+                        log.warning(f"{future_code} 无法计算阈值,跳过")
+            
+        except Exception as e:
+            log.warning(f"加载{future_code}历史数据时出错: {str(e)}")
+            continue
+    
+    log.info(f"历史数据缓存完成,共缓存 {len(g.daily_data_cache)} 个品种")
+
+def calculate_individual_threshold(context, future_code):
+    """为单个工具计算实体长度阈值并自动缓存到月度阈值中"""
+    try:
+        log.info(f"为 {future_code} 单独计算365天历史阈值")
+        
+        # 获取365天历史数据
+        data = attribute_history(future_code, g.historical_days, '1d', 
+                               ['open', 'close', 'high', 'low', 'volume'], 
+                               df=True)
+        
+        if data is not None and len(data) > 30:  # 确保有足够数据
+            # 排除今天的数据
+            today = context.current_dt.date()
+            data = data[data.index.date < today]
+            
+            # 计算实体长度阈值
+            data['body_length'] = abs(data['close'] - data['open'])
+            body_threshold = data['body_length'].mean()
+            
+            # 🚀 优化1:将计算结果自动添加到月度阈值缓存中
+            g.monthly_body_threshold_cache[future_code] = body_threshold
+            log.info(f"✅ {future_code} 单独计算完成,阈值: {body_threshold:.4f},已添加到月度缓存")
+            log.info(f"当前月度缓存包含 {len(g.monthly_body_threshold_cache)} 个品种阈值")
+            
+            return body_threshold
+        else:
+            log.warning(f"{future_code} 历史数据不足,无法计算阈值")
+            return None
+            
+    except Exception as e:
+        log.warning(f"为 {future_code} 单独计算阈值时出错: {str(e)}")
+        return None
+
+def task_3_update_realtime_data(context):
+    """任务3: 获取今日分钟数据"""
+    # log.info("执行任务3: 更新实时数据")
+    
+    # 收集需要更新数据的品种
+    update_symbols = set()
+    
+    # 添加可交易品种
+    if hasattr(g, 'tradable_futures') and g.tradable_futures:
+        update_symbols.update(g.tradable_futures)
+    
+    # 添加持仓品种(用于止损止盈)
+    if hasattr(g, 'trade_history') and g.trade_history:
+        update_symbols.update(g.trade_history.keys())
+    
+    if not update_symbols:
+        return
+    
+    for future_code in update_symbols:
+        try:
+            # 获取今日分钟数据
+            minute_data = get_today_minute_data(context, future_code)
+            if minute_data is None:
+                continue
+            
+            # 获取历史数据
+            historical_data = g.daily_data_cache.get(future_code)
+            if historical_data is None:
+                # 为持仓品种临时获取历史数据
+                try:
+                    data = attribute_history(future_code, g.historical_days, '1d', 
+                                           ['open', 'close', 'high', 'low', 'volume'], 
+                                           df=True)
+                    if data is not None and len(data) > 0:
+                        today = context.current_dt.date()
+                        data = data[data.index.date < today]
+                        
+                        # 计算实体长度阈值
+                        data['body_length'] = abs(data['close'] - data['open'])
+                        body_threshold = data['body_length'].mean()
+                        
+                        g.daily_data_cache[future_code] = data
+                        g.body_threshold_cache[future_code] = body_threshold
+                        historical_data = data
+                        log.info(f"为持仓品种 {future_code} 临时获取历史数据 {len(data)} 条")
+                except Exception as e:
+                    log.warning(f"获取{future_code}历史数据失败: {str(e)}")
+                    continue
+            
+            if historical_data is None:
+                continue
+            
+            # 将今日数据合并为日线数据
+            today_data = aggregate_minute_to_daily(minute_data)
+            if today_data is not None:
+                # 合并历史数据和今日数据
+                combined_data = pd.concat([historical_data, today_data], sort=False)
+                g.minute_data_cache[future_code] = combined_data
+            
+        except Exception as e:
+            log.warning(f"更新{future_code}实时数据时出错: {str(e)}")
+            continue
+
+def task_4_detect_candlestick_patterns(context):
+    """任务4: 检测烛台形态"""
+    # log.info("执行任务4: 检测烛台形态")
+    
+    candlestick_signals = []
+    
+    # 获取已持仓的品种列表
+    existing_positions = set(g.trade_history.keys())
+    
+    for future_code, data in g.minute_data_cache.items():
+        try:
+            # 检查是否已有相似持仓,如果有则跳过分析
+            if check_symbol_prefix_match(future_code, existing_positions):
+                continue
+            
+            # 获取实体长度阈值
+            body_threshold = g.body_threshold_cache.get(future_code)
+            if body_threshold is None:
+                continue
+            
+            # 检查最新的烛台形态
+            latest_pattern = check_latest_candlestick_pattern(data, future_code, body_threshold)
+            if latest_pattern:
+                candlestick_signals.append(latest_pattern)
+                log.info(f"{future_code} 发现烛台形态: {latest_pattern['hatch_direction']}影线")
+                
+        except Exception as e:
+            log.warning(f"检测{future_code}烛台形态时出错: {str(e)}")
+            continue
+    
+    # 存储到全局变量
+    g.candlestick_signals = candlestick_signals
+    if len(candlestick_signals) > 0:
+        log.info(f"烛台形态检测完成,发现信号 {len(candlestick_signals)} 个")
+    
+    return candlestick_signals
+
+def task_5_check_opening_conditions(context):
+    """任务5: 检查开仓条件(简化版)"""
+    log.info("执行任务5: 检查开仓条件(简化版)")
+    
+    if not hasattr(g, 'candlestick_signals') or not g.candlestick_signals:
+        log.info("没有检测到烛台信号")
+        return []
+    
+    log.info(f"检测到 {len(g.candlestick_signals)} 个烛台信号,开始筛选")
+    
+    filtered_signals = []
+    
+    for i, signal in enumerate(g.candlestick_signals):
+        log.info(f"处理信号 {i+1}: {signal['symbol']} {signal['hatch_direction']}影线")
+        
+        # 仅检查是否已有相似持仓(避免重复开仓同一品种)
+        if check_symbol_prefix_match(signal['symbol'], set(g.trade_history.keys())):
+            log.info(f"{signal['symbol']} 已有相似持仓,跳过")
+            continue
+        
+        # 简化版:只要形态满足条件就接受,无额外验证
+        filtered_signals.append(signal)
+        log.info(f"{signal['symbol']} 烛台形态满足条件,接受交易信号")
+    
+    log.info(f"开仓条件检查完成,满足条件 {len(filtered_signals)} 个")
+    return filtered_signals
+
+def task_6_execute_trades(context, filtered_signals):
+    """任务6: 执行交易(简化版)"""
+    log.info("执行任务6: 执行交易(简化版)")
+    
+    if not filtered_signals:
+        log.info("没有满足条件的交易信号")
+        return
+    
+    log.info(f"开始执行 {len(filtered_signals)} 个交易信号")
+    
+    for i, signal in enumerate(filtered_signals):
+        symbol = signal['symbol']
+        log.info(f"执行交易 {i+1}: {symbol} {signal['hatch_direction']}影线形态")
+        
+        # 使用可配置的开仓方向逻辑
+        direction, logic_description = determine_trading_direction(symbol, signal['hatch_direction'])
+        
+        log.info(f"{symbol} 交易方向: {direction} ({logic_description})")
+        
+        try:
+            # 计算目标手数(优化版:直接返回手数)
+            target_hands = calculate_target_hands(context, symbol, direction)
+            log.info(f"计算目标手数: {symbol} {direction} 目标手数: {target_hands}")
+            
+            if target_hands > 0:
+                # 执行开仓
+                log.info(f"开始下单: {symbol} {direction} 目标手数: {target_hands}")
+                success = open_position(context, symbol, target_hands, direction, signal)
+                
+                if success:
+                    actual_margin = g.trade_history[symbol]['actual_margin']
+                    actual_price = g.trade_history[symbol]['entry_price']
+                    log.info(f"✅ 成功开仓 {symbol} {direction}, 成交价格: {actual_price:.2f}, 目标手数: {target_hands}, 实际保证金: {actual_margin:.0f}")
+                else:
+                    log.warning(f"❌ 开仓失败 {symbol} {direction}")
+            else:
+                log.warning(f"{symbol} 计算目标手数为0,跳过")
+        except Exception as e:
+            log.warning(f"{symbol} 交易执行出错: {str(e)}")
+            continue
+    
+    log.info("交易执行完成")
+
+def task_8_check_position_switch(context):
+    """任务8: 检查换月移仓"""
+    # log.info("执行任务8: 检查换月移仓")
+    
+    switch_result = position_auto_switch(context)
+    if switch_result:
+        log.info(f"执行了 {len(switch_result)} 次移仓换月")
+        for result in switch_result:
+            log.info(f"移仓: {result['before']} -> {result['after']}")
+
+def task_7_check_stop_loss_profit(context):
+    """任务7: 检查止损止盈(带交易时间验证)"""
+    # log.info("执行任务7: 检查止损止盈")
+    
+    # 获取当前时间
+    current_time = context.current_dt.strftime('%H:%M')
+    current_hour = int(current_time[:2])
+    
+    # 判断是否为夜盘时间(21:00-23:00 和 00:00-02:30)
+    is_night_session = (current_hour >= 21) or (current_hour <= 2)
+    
+    # 遍历所有持仓进行止损止盈检查
+    subportfolio = context.subportfolios[0]
+    long_positions = list(subportfolio.long_positions.values())
+    short_positions = list(subportfolio.short_positions.values())
+    
+    closed_count = 0
+    skipped_count = 0
+    
+    for position in long_positions + short_positions:
+        security = position.security
+        underlying_symbol = security.split('.')[0][:-4]
+        
+        # 检查交易时间适配性
+        has_night_session = get_futures_config(underlying_symbol, 'has_night_session', False)
+        
+        # 如果是夜盘时间,但品种不支持夜盘交易,则跳过
+        if is_night_session and not has_night_session:
+            skipped_count += 1
+            # log.info(f"跳过夜盘时间的日间品种: {underlying_symbol} ({current_time})")
+            continue
+        
+        # 执行止损止盈检查
+        if check_stop_loss_profit(context, position):
+            closed_count += 1
+    
+    if closed_count > 0:
+        log.info(f"执行了 {closed_count} 次止损止盈")
+    
+    if skipped_count > 0:
+        log.info(f"夜盘时间跳过 {skipped_count} 个日间品种的止损止盈检查")
+
+############################ 数据处理辅助函数 ###################################
+
+def check_has_night_session(underlying_symbol):
+    """检查品种是否有夜盘"""
+    return get_futures_config(underlying_symbol, 'has_night_session', False)
+
+def get_today_minute_data(context, future_code):
+    """获取今日分钟数据"""
+    try:
+        # 判断该品种是否有夜盘
+        underlying_symbol = future_code.split('.')[0][:-4]
+        has_night_session = check_has_night_session(underlying_symbol)
+        
+        end_time = context.current_dt
+        
+        # 获取足够的历史分钟数据
+        minute_data = attribute_history(future_code, 
+                                      count=800,  # 获取足够多的数据
+                                      unit='1m',
+                                      fields=['open', 'close', 'high', 'low', 'volume'],
+                                      df=True)
+        
+        if minute_data is None or len(minute_data) == 0:
+            return None
+        
+        # 提取所有日期(年月日维度)
+        minute_data['date'] = minute_data.index.date
+        unique_dates = sorted(minute_data['date'].unique())
+        
+        if has_night_session:
+            # 有夜盘的品种:需要找到前一交易日的21:00作为今日开盘起点
+            today_date = end_time.date()
+            
+            # 找到今天之前的最后一个交易日
+            previous_trading_dates = [d for d in unique_dates if d < today_date]
+            if not previous_trading_dates:
+                return minute_data
+            
+            previous_trading_date = max(previous_trading_dates)
+            
+            # 找到前一交易日21:00:00的数据作为开盘起点
+            previous_day_data = minute_data[minute_data['date'] == previous_trading_date]
+            night_21_data = previous_day_data[previous_day_data.index.hour == 21]
+            
+            if len(night_21_data) > 0:
+                # 从前一交易日21:00开始的所有数据
+                start_time = night_21_data.index[0]  # 21:00:00的时间点
+                filtered_data = minute_data[minute_data.index >= start_time]
+                return filtered_data.drop(columns=['date'])
+            else:
+                # 备选方案:使用今天9:00开始的数据
+                today_data = minute_data[minute_data['date'] == today_date]
+                day_9_data = today_data[today_data.index.hour >= 9]
+                if len(day_9_data) > 0:
+                    return day_9_data.drop(columns=['date'])
+                else:
+                    return minute_data.drop(columns=['date'])
+        else:
+            # 没有夜盘的品种:从今天9:00:00开始
+            today_date = end_time.date()
+            today_data = minute_data[minute_data['date'] == today_date]
+            
+            # 找到今天9:00:00开始的数据
+            day_9_data = today_data[today_data.index.hour >= 9]
+            
+            if len(day_9_data) > 0:
+                return day_9_data.drop(columns=['date'])
+            else:
+                return today_data.drop(columns=['date']) if len(today_data) > 0 else minute_data.drop(columns=['date'])
+        
+    except Exception as e:
+        log.warning(f"获取{future_code}今日分钟数据时出错: {str(e)}")
+    
+    return None
+
+def aggregate_minute_to_daily(minute_data):
+    """将分钟数据聚合为日数据"""
+    try:
+        if minute_data is None or len(minute_data) == 0:
+            return None
+        
+        # 获取今日日期(使用最后一条数据的日期作为今日日期)
+        today_date = minute_data.index[-1].date()
+        
+        # 聚合为日数据
+        daily_data = pd.DataFrame({
+            'open': [minute_data['open'].iloc[0]],  # 今日交易开始时的开盘价
+            'close': [minute_data['close'].iloc[-1]],  # 当前收盘价,实时更新
+            'high': [minute_data['high'].max()],
+            'low': [minute_data['low'].min()],
+            'volume': [minute_data['volume'].sum()]
+        }, index=[pd.Timestamp(today_date)])
+        
+        return daily_data
+        
+    except Exception as e:
+        log.warning(f"聚合分钟数据时出错: {str(e)}")
+        return None
+
+def check_latest_candlestick_pattern(data, future_code, body_threshold):
+    """检查最新的烛台形态"""
+    if len(data) < 1:
+        return None
+        
+    # 获取最新一天的数据
+    today = data.iloc[-1]
+    
+    # 检查烛台形态
+    pattern_result = check_candlestick_pattern_single_day(today, body_threshold)
+    if not pattern_result:
+        return None
+    
+    return {
+        'symbol': future_code,
+        'date': today.name,
+        'hatch_direction': pattern_result['hatch_direction'],
+        'open': today['open'],
+        'close': today['close'],
+        'high': today['high'],
+        'low': today['low'],
+        'body_length': pattern_result['body_length'],
+        'hatch_length': pattern_result['hatch_length']
+    }
+
+def check_candlestick_pattern_single_day(row, body_threshold):
+    """检查单日烛台形态是否符合影线条件"""
+    open_price = row['open']
+    close_price = row['close']
+    high_price = row['high']
+    low_price = row['low']
+    
+    # 计算实体长度
+    body_length = abs(close_price - open_price)
+    
+    # 检查实体长度是否满足阈值
+    if body_length < body_threshold:
+        return None
+    
+    # 计算影线长度
+    upper_hatch = high_price - max(open_price, close_price)
+    lower_hatch = min(open_price, close_price) - low_price
+    
+    # 检查影线条件
+    hatch_direction = None
+    hatch_length = 0
+    
+    # 检查上影线条件:上影线长度符合要求 且 下影线长度小于实体长度的一半
+    if (upper_hatch >= g.hatch_to_body_ratio * body_length and 
+        lower_hatch < g.opposite_hatch_ratio * body_length):
+        hatch_direction = 'up'
+        hatch_length = upper_hatch
+    # 检查下影线条件:下影线长度符合要求 且 上影线长度小于实体长度的一半
+    elif (lower_hatch >= g.hatch_to_body_ratio * body_length and 
+          upper_hatch < g.opposite_hatch_ratio * body_length):
+        hatch_direction = 'down'
+        hatch_length = lower_hatch
+    
+    # 如果满足影线条件,返回形态信息
+    if hatch_direction is not None:
+        return {
+            'hatch_direction': hatch_direction,
+            'body_length': body_length,
+            'hatch_length': hatch_length
+        }
+    
+    return None
+
+############################ 动态保证金率调整函数 ###################################
+
+def detect_and_update_margin_rates(context, symbol, actual_hands, cash_change, direction):
+    """
+    检测并更新保证金率配置
+    
+    参数:
+    symbol: 期货合约代码
+    actual_hands: 实际成交手数
+    cash_change: 实际资金变化(保证金使用)
+    direction: 交易方向
+    """
+    try:
+        underlying_symbol = symbol.split('.')[0][:-4]
+        
+        if underlying_symbol not in g.futures_config:
+            return
+        
+        current_price = get_current_data()[symbol].last_price
+        multiplier = get_multiplier(underlying_symbol)
+        
+        # 计算实际保证金率
+        contract_value = current_price * multiplier * actual_hands
+        actual_margin_rate = cash_change / contract_value if contract_value > 0 else 0
+        
+        # 获取配置中的保证金率
+        config_margin_rate = g.futures_config[underlying_symbol]['margin_rate'][direction]
+        
+        # 计算差异百分比
+        rate_diff_percentage = abs(actual_margin_rate - config_margin_rate) / config_margin_rate * 100 if config_margin_rate > 0 else 100
+        
+        # 如果差异超过10%,更新配置
+        if rate_diff_percentage > 10:
+            log.info(f"🔍 检测到{underlying_symbol}保证金率差异: 配置={config_margin_rate:.3f}, 实际={actual_margin_rate:.3f}, 差异={rate_diff_percentage:.1f}%")
+            
+            # 更新配置
+            g.futures_config[underlying_symbol]['margin_rate'][direction] = actual_margin_rate
+            
+            # 如果是双向更新(多空保证金率通常相同)
+            if g.futures_config[underlying_symbol]['margin_rate']['long'] == g.futures_config[underlying_symbol]['margin_rate']['short']:
+                g.futures_config[underlying_symbol]['margin_rate']['long'] = actual_margin_rate
+                g.futures_config[underlying_symbol]['margin_rate']['short'] = actual_margin_rate
+                log.info(f"✅ 已更新{underlying_symbol}保证金率为 {actual_margin_rate:.3f} (双向)")
+            else:
+                log.info(f"✅ 已更新{underlying_symbol} {direction}保证金率为 {actual_margin_rate:.3f}")
+                
+    except Exception as e:
+        log.warning(f"检测保证金率时出错 {symbol}: {str(e)}")
+
+############################ 机会性仓位调整函数 ###################################
+
+def opportunistic_position_increase(context, symbol, direction, signal):
+    """
+    机会性仓位调整:在保证金使用量低于限制时自动增加仓位
+    
+    参数:
+    symbol: 期货合约代码
+    direction: 交易方向
+    signal: 交易信号
+    """
+    try:
+        if symbol not in g.trade_history:
+            return False
+            
+        underlying_symbol = symbol.split('.')[0][:-4]
+        current_price = get_current_data()[symbol].last_price
+        margin_rate = get_margin_rate(underlying_symbol, direction)
+        multiplier = get_multiplier(underlying_symbol)
+        
+        # 获取当前持仓信息
+        current_actual_margin = g.trade_history[symbol]['actual_margin']
+        current_hands = g.trade_history[symbol]['actual_hands']
+        
+        # 计算剩余保证金容量
+        max_margin = g.max_margin_per_position
+        remaining_margin_capacity = max_margin - current_actual_margin
+        
+        # 检查是否有足够的剩余容量增加至少1手
+        single_hand_margin = current_price * multiplier * margin_rate
+        
+        if remaining_margin_capacity >= single_hand_margin:
+            # 计算可以增加的最大手数
+            additional_hands = int(remaining_margin_capacity / single_hand_margin)
+            
+            # 同时考虑可用资金限制
+            available_cash = context.portfolio.available_cash * g.usage_percentage
+            max_hands_by_cash = int(available_cash / single_hand_margin)
+            
+            # 取较小值
+            additional_hands = min(additional_hands, max_hands_by_cash)
+            
+            if additional_hands >= 1:
+                log.info(f"🚀 机会性增仓机会: {symbol} 当前{current_hands}手(保证金:{current_actual_margin:.0f}), 可增加{additional_hands}手")
+                
+                # 计算新的目标手数
+                new_target_hands = current_hands + additional_hands
+                
+                # 执行增仓
+                order = order_target(symbol, new_target_hands, side=direction)
+                
+                if order and order.filled > 0:
+                    # 计算实际增仓使用的保证金
+                    cash_after = context.portfolio.available_cash
+                    additional_margin_used = order.filled * single_hand_margin  # 估算使用的保证金
+                    
+                    # 更新交易记录
+                    new_total_hands = current_hands + order.filled
+                    new_total_margin = current_actual_margin + additional_margin_used
+                    
+                    g.trade_history[symbol]['actual_hands'] = new_total_hands
+                    g.trade_history[symbol]['target_hands'] = new_target_hands
+                    g.trade_history[symbol]['actual_margin'] = new_total_margin
+                    
+                    log.info(f"✅ 成功增仓 {symbol} {order.filled}手, 总手数: {new_total_hands}手, 总保证金: {new_total_margin:.0f}")
+                    
+                    # 记录增仓交易
+                    g.today_trades.append({
+                        'security': symbol,
+                        'underlying_symbol': underlying_symbol,
+                        'direction': direction,
+                        'order_amount': order.filled,
+                        'order_price': order.avg_cost if order.avg_cost else order.price,
+                        'cash_change': additional_margin_used,
+                        'time': context.current_dt,
+                        'trade_type': 'increase'  # 标记为增仓
+                    })
+                    
+                    return True
+                    
+        return False
+        
+    except Exception as e:
+        log.warning(f"机会性增仓时出错 {symbol}: {str(e)}")
+        return False
+
+############################ 交易执行函数 ###################################
+
+def open_position(context, security, target_hands, direction, signal):
+    """开仓(优化版:使用order_target按手数开仓)"""
+    try:
+        # 记录交易前的可用资金
+        cash_before = context.portfolio.available_cash
+        
+        # 使用order_target按手数开仓,自动处理持仓差额
+        order = order_target(security, target_hands, side=direction)
+        
+        if order is not None and order.filled > 0:
+            # 记录交易后的可用资金
+            cash_after = context.portfolio.available_cash
+            
+            # 计算实际资金变化
+            cash_change = cash_before - cash_after
+            
+            # 获取订单价格和数量
+            order_price = order.avg_cost if order.avg_cost else order.price
+            order_amount = order.filled
+            
+            # 记录当日交易
+            underlying_symbol = security.split('.')[0][:-4]
+            g.today_trades.append({
+                'security': security,
+                'underlying_symbol': underlying_symbol,
+                'direction': direction,
+                'order_amount': order_amount,
+                'order_price': order_price,
+                'cash_change': cash_change,
+                'time': context.current_dt
+            })
+            
+            # 记录交易信息,包含止损止盈相关信息
+            g.trade_history[security] = {
+                'entry_price': order_price,
+                'target_hands': target_hands,
+                'actual_hands': order_amount,
+                'actual_margin': cash_change,
+                'direction': direction,
+                'entry_time': context.current_dt,
+                'signal_info': signal,
+                'max_profit': 0.0,  # 初始化最大利润
+                'max_profit_price': order_price  # 记录最大利润时的价格
+            }
+            
+            # 🚀 优化2:动态保证金率调整
+            detect_and_update_margin_rates(context, security, order_amount, cash_change, direction)
+            
+            # 🚀 优化3:机会性仓位调整
+            opportunistic_position_increase(context, security, direction, signal)
+            
+            return True
+            
+    except Exception as e:
+        log.warning(f"开仓失败 {security}: {str(e)}")
+    
+    return False
+
+def close_position(context, security, direction):
+    """平仓(优化版:使用order_target平仓到0手)"""
+    try:
+        # 使用order_target平仓到0手,自动处理持仓清零
+        order = order_target(security, 0, side=direction)
+        
+        if order is not None and order.filled > 0:
+            underlying_symbol = security.split('.')[0][:-4]
+            
+            # 记录当日交易(平仓)
+            g.today_trades.append({
+                'security': security,
+                'underlying_symbol': underlying_symbol,
+                'direction': direction,
+                'order_amount': -order.filled,  # 负数表示平仓
+                'order_price': order.avg_cost if order.avg_cost else order.price,
+                'cash_change': 0,  # 平仓不计算保证金变化
+                'time': context.current_dt
+            })
+            
+            log.info(f"平仓成功 - 品种: {underlying_symbol}, 平仓手数: {order.filled}")
+            
+            # 从交易历史中移除
+            if security in g.trade_history:
+                del g.trade_history[security]
+            return True
+            
+    except Exception as e:
+        log.warning(f"平仓失败 {security}: {str(e)}")
+    
+    return False
+
+def check_stop_loss_profit(context, position):
+    """检查止损止盈"""
+    security = position.security
+    
+    if security not in g.trade_history:
+        return False
+    
+    trade_info = g.trade_history[security]
+    direction = trade_info['direction']
+    entry_price = trade_info['entry_price']
+    current_price = position.price
+    
+    # 计算当前盈亏比率
+    if direction == 'long':
+        profit_rate = (current_price - entry_price) / entry_price
+    else:
+        profit_rate = (entry_price - current_price) / entry_price
+    
+    # 更新最大利润记录
+    if profit_rate > trade_info['max_profit']:
+        trade_info['max_profit'] = profit_rate
+        trade_info['max_profit_price'] = current_price
+        g.trade_history[security] = trade_info
+    
+    # 检查固定止损
+    if profit_rate <= -g.fixed_stop_loss_rate:
+        log.info(f"触发固定止损 {security} {direction}, 当前亏损率: {profit_rate:.3%}, 成本价: {entry_price:.2f}, 当前价格: {current_price:.2f}")
+        close_position(context, security, direction)
+        return True
+    
+    # 检查动态追踪止盈
+    max_profit = trade_info['max_profit']
+    if max_profit > 0:
+        # 确定追踪止损比率
+        if max_profit <= g.trailing_stop_thresholds[0]:  # ≤5%
+            trailing_rate = g.trailing_stop_rates[0]  # 1%
+        elif max_profit <= g.trailing_stop_thresholds[1]:  # 5%-10%
+            trailing_rate = g.trailing_stop_rates[1]  # 2%
+        else:  # >10%
+            trailing_rate = g.trailing_stop_rates[2]  # 3%
+        
+        # 检查是否触发追踪止损
+        profit_drawdown = max_profit - profit_rate
+        if profit_drawdown >= trailing_rate:
+            log.info(f"触发动态追踪止损 {security} {direction}")
+            log.info(f"最大利润: {max_profit:.3%}, 当前利润: {profit_rate:.3%}, 回撤: {profit_drawdown:.3%}, 触发阈值: {trailing_rate:.3%}")
+            close_position(context, security, direction)
+            return True
+    
+    return False
+
+############################ 辅助函数 ###################################
+
+def determine_trading_direction(symbol, hatch_direction):
+    """
+    确定开仓方向(支持可配置的正向/反向逻辑)
+    
+    参数:
+    symbol: 期货合约代码 (如 'JD2510.XDCE')
+    hatch_direction: 影线方向 ('up' 或 'down')
+    
+    返回:
+    (direction, logic_description): 交易方向和逻辑说明的元组
+    """
+    # 提取基础品种代码
+    underlying_symbol = symbol.split('.')[0][:-4]  # 如 'JD2510.XDCE' -> 'JD'
+    
+    # 检查是否在反向逻辑列表中
+    use_reverse_logic = underlying_symbol in g.reverse_direction_symbols
+    
+    if use_reverse_logic:
+        # 反向逻辑:上影线做空,下影线做多(当前逻辑)
+        if hatch_direction == 'up':
+            direction = 'short'
+            logic_description = f"反向逻辑:{underlying_symbol}上影线做空"
+        else:
+            direction = 'long'
+            logic_description = f"反向逻辑:{underlying_symbol}下影线做多"
+    else:
+        # 正向逻辑:上影线做多,下影线做空(新逻辑)
+        if hatch_direction == 'up':
+            direction = 'long'
+            logic_description = f"正向逻辑:{underlying_symbol}上影线做多"
+        else:
+            direction = 'short'
+            logic_description = f"正向逻辑:{underlying_symbol}下影线做空"
+    
+    return direction, logic_description
+
+def get_futures_config(underlying_symbol, config_key=None, default_value=None):
+    """获取期货品种配置信息的辅助函数"""
+    if underlying_symbol not in g.futures_config:
+        if config_key and default_value is not None:
+            return default_value
+        return {}
+    
+    if config_key is None:
+        return g.futures_config[underlying_symbol]
+    
+    return g.futures_config[underlying_symbol].get(config_key, default_value)
+
+def get_margin_rate(underlying_symbol, direction, default_rate=0.10):
+    """获取保证金比例的辅助函数"""
+    return g.futures_config.get(underlying_symbol, {}).get('margin_rate', {}).get(direction, default_rate)
+
+def get_multiplier(underlying_symbol, default_multiplier=10):
+    """获取合约乘数的辅助函数"""
+    return g.futures_config.get(underlying_symbol, {}).get('multiplier', default_multiplier)
+
+def calculate_target_hands(context, security, direction):
+    """计算目标开仓手数(优化版:直接返回手数用于order_target)"""
+    current_price = get_current_data()[security].last_price
+    underlying_symbol = security.split('.')[0][:-4]
+    
+    # 使用保证金比例
+    margin_rate = get_margin_rate(underlying_symbol, direction)
+    multiplier = get_multiplier(underlying_symbol)
+    
+    # 计算单手保证金
+    single_hand_margin = current_price * multiplier * margin_rate
+    
+    # 还要考虑可用资金限制
+    available_cash = context.portfolio.available_cash * g.usage_percentage
+    
+    # 根据单个标的最大持仓保证金限制计算开仓数量
+    max_margin = g.max_margin_per_position
+    
+    if single_hand_margin <= max_margin:
+        # 如果单手保证金不超过最大限制,计算最大可开仓手数
+        max_hands = int(max_margin / single_hand_margin)
+        max_hands_by_cash = int(available_cash / single_hand_margin)
+        
+        # 取两者较小值
+        actual_hands = min(max_hands, max_hands_by_cash)
+        
+        # 确保至少开1手
+        actual_hands = max(1, actual_hands)
+        
+        # 实际保证金
+        actual_margin = single_hand_margin * actual_hands
+        
+        log.info(f"单手保证金: {single_hand_margin:.0f}, 目标开仓手数: {actual_hands}, 预计保证金: {actual_margin:.0f}")
+        
+        # 直接返回手数,用于order_target()
+        return actual_hands
+    else:
+        # 如果单手保证金超过最大限制,默认开仓1手
+        actual_hands = 1
+        actual_margin = single_hand_margin * actual_hands
+        
+        log.info(f"单手保证金: {single_hand_margin:.0f} 超过最大限制: {max_margin}, 默认开仓1手, 预计保证金: {actual_margin:.0f}")
+        
+        # 直接返回手数,用于order_target()
+        return actual_hands
+
+def check_sufficient_capital(context, symbol):
+    """检查资金是否充足"""
+    try:
+        current_price = get_current_data()[symbol].last_price
+        underlying_symbol = symbol.split('.')[0][:-4]
+        
+        margin_rate = get_margin_rate(underlying_symbol, 'long')
+        multiplier = get_multiplier(underlying_symbol)
+        
+        # 计算单手保证金
+        single_hand_margin = current_price * multiplier * margin_rate
+        
+        # 使用实际可用资金(考虑资金使用比例)
+        available_cash = context.portfolio.available_cash * g.usage_percentage
+        
+        # 检查是否有足够资金开仓至少1手
+        return available_cash >= single_hand_margin
+    except:
+        return False
+
+def check_price_and_liquidity(signal):
+    """检查价格合理性和流动性"""
+    try:
+        # 简单的合理性检查
+        symbol = signal['symbol']
+        current_data = get_current_data()[symbol]
+        
+        log.info(f"{symbol} 价格检查 - 当前价: {current_data.last_price:.2f}, 涨停: {current_data.high_limit:.2f}, 跌停: {current_data.low_limit:.2f}, 成交量: {current_data.volume}")
+        
+        # 检查是否处于涨跌停
+        if current_data.last_price <= current_data.low_limit:
+            log.warning(f"{symbol} 价格触及跌停板 ({current_data.last_price:.2f} <= {current_data.low_limit:.2f}),跳过")
+            return False
+            
+        if current_data.last_price >= current_data.high_limit:
+            log.warning(f"{symbol} 价格触及涨停板 ({current_data.last_price:.2f} >= {current_data.high_limit:.2f}),跳过")
+            return False
+        
+        # 检查成交量是否充足
+        if current_data.volume <= 0:
+            log.warning(f"{symbol} 无成交量 ({current_data.volume}),跳过")
+            return False
+        
+        log.info(f"{symbol} 价格和流动性检查通过")
+        return True
+    except Exception as e:
+        log.warning(f"{signal['symbol']} 价格检查时出错: {str(e)}")
+        return False
+
+def check_symbol_prefix_match(symbol, hold_symbols):
+    """检查是否有相似的持仓品种"""
+    symbol_prefix = symbol[:-9]
+    
+    for hold_symbol in hold_symbols:
+        hold_symbol_prefix = hold_symbol[:-9]
+        if symbol_prefix == hold_symbol_prefix:
+            return True
+    return False
+
+def after_market_close(context):
+    """收盘后运行函数"""
+    log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time())))
+    
+    # 只有当天有交易时才打印统计信息
+    if g.today_trades:
+        print_daily_trading_summary(context)
+        
+        # 清空当日交易记录
+        g.today_trades = []
+    
+    log.info('##############################################################')
+
+def print_daily_trading_summary(context):
+    """打印当日交易汇总"""
+    if not g.today_trades:
+        return
+    
+    log.info("\n=== 当日交易汇总 ===")
+    total_margin = 0
+    
+    for trade in g.today_trades:
+        if trade['order_amount'] > 0:  # 开仓
+            log.info(f"开仓 {trade['underlying_symbol']} {trade['direction']} {trade['order_amount']}手 "
+                  f"价格:{trade['order_price']:.2f} 保证金:{trade['cash_change']:.0f}")
+            total_margin += trade['cash_change']
+        else:  # 平仓
+            log.info(f"平仓 {trade['underlying_symbol']} {trade['direction']} {abs(trade['order_amount'])}手 "
+                  f"价格:{trade['order_price']:.2f}")
+    
+    log.info(f"当日保证金占用: {total_margin:.0f}")
+    log.info("==================\n")
+
+########################## 自动移仓换月函数 #################################
+def position_auto_switch(context, pindex=0, switch_func=None, callback=None):
+    """
+    期货自动移仓换月。默认使用市价单进行开平仓。
+    """
+    import re
+    subportfolio = context.subportfolios[pindex]
+    symbols = set(subportfolio.long_positions.keys()) | set(subportfolio.short_positions.keys())
+    switch_result = []
+    for symbol in symbols:
+        match = re.match(r"(?P<underlying_symbol>[A-Z]{1,})", symbol)
+        if not match:
+            raise ValueError("未知期货标的: {}".format(symbol))
+        else:
+            dominant = get_dominant_future(match.groupdict()["underlying_symbol"])
+            cur = get_current_data()
+            symbol_last_price = cur[symbol].last_price
+            dominant_last_price = cur[dominant].last_price
+            log.info(f'当前持仓合约: {symbol}, 当前主力合约: {dominant}')
+            
+            if dominant > symbol:
+                for positions_ in (subportfolio.long_positions, subportfolio.short_positions):
+                    if symbol not in positions_.keys():
+                        continue
+                    else :
+                        p = positions_[symbol]
+
+                    if switch_func is not None:
+                        switch_func(context, pindex, p, dominant)
+                    else:
+                        amount = p.total_amount
+                        # 跌停不能开空和平多,涨停不能开多和平空。
+                        if p.side == "long":
+                            symbol_low_limit = cur[symbol].low_limit
+                            dominant_high_limit = cur[dominant].high_limit
+                            if symbol_last_price <= symbol_low_limit:
+                                log.warning("标的{}跌停,无法平仓。移仓换月取消。".format(symbol))
+                                continue
+                            elif dominant_last_price >= dominant_high_limit:
+                                log.warning("标的{}涨停,无法开仓。移仓换月取消。".format(dominant))
+                                continue
+                            else:
+                                log.info("进行移仓换月: ({0},long) -> ({1},long)".format(symbol, dominant))
+                                order_old = order_target(symbol, 0, side='long')
+                                if order_old != None and order_old.filled > 0:
+                                    order_new = order_target(dominant, amount, side='long')
+                                    if order_new != None and order_new.filled > 0:
+                                        switch_result.append({"before": symbol, "after": dominant, "side": "long"})
+                                        # 换月中的买卖都成功了,则增加新的记录去掉旧的记录
+                                        g.trade_history[dominant] = g.trade_history[symbol]
+                                        del g.trade_history[symbol]
+                                    else:
+                                        log.warning("标的{}交易失败,无法开仓。移仓换月失败。".format(dominant))
+                        if p.side == "short":
+                            symbol_high_limit = cur[symbol].high_limit
+                            dominant_low_limit = cur[dominant].low_limit
+                            if symbol_last_price >= symbol_high_limit:
+                                log.warning("标的{}涨停,无法平仓。移仓换月取消。".format(symbol))
+                                continue
+                            elif dominant_last_price <= dominant_low_limit:
+                                log.warning("标的{}跌停,无法开仓。移仓换月取消。".format(dominant))
+                                continue
+                            else:
+                                log.info("进行移仓换月: ({0},short) -> ({1},short)".format(symbol, dominant))
+                                order_old = order_target(symbol, 0, side='short')
+                                if order_old != None and order_old.filled > 0:
+                                    order_new = order_target(dominant, amount, side='short')
+                                    if order_new != None and order_new.filled > 0:
+                                        switch_result.append({"before": symbol, "after": dominant, "side": "short"})
+                                        # 换月中的买卖都成功了,则增加新的记录去掉旧的记录
+                                        g.trade_history[dominant] = g.trade_history[symbol]
+                                        del g.trade_history[symbol]
+                                    else:
+                                        log.warning("标的{}交易失败,无法开仓。移仓换月失败。".format(dominant))
+                        if callback:
+                            callback(context, pindex, p, dominant)
+    return switch_result

+ 160 - 1
Lib/future/README.md

@@ -362,4 +362,163 @@ g.stop_ratios = [0.0025, 0.005, 0.01, 0.02] # 止损比例
 - 考虑交易成本:
   - 手续费:按照不同品种设置
   - 滑点:按照品种特性设置
-  - 保证金:按照交易所要求设置
+  - 保证金:按照交易所要求设置
+
+## 烛台影线形态反向交易策略 v001
+
+### 核心思路
+该策略基于烛台形态检测,专门识别带有明显影线的K线形态,并按照影线的相反方向进行交易。这是一种基于"结论1"逻辑的反向交易策略,即认为影线代表市场的假突破,应该按相反方向进行交易。
+
+1. [策略来源](https://www.joinquant.com)/ [本地](Lib\future\CandlestickHatchReverseStrategy_v001.py)
+2. 策略名称:烛台影线形态反向交易策略
+3. 实现理念:基于烛台形态的反向交易思路
+
+### 主要特点
+1. 烛台形态识别(上影线/下影线)
+2. 反向交易逻辑(影线相反方向)
+3. 固定止损保护(可配置)
+4. 动态追踪止盈机制
+5. 自动合约换月功能
+6. 支持日内和夜盘交易
+
+### 具体策略逻辑
+
+#### 1. 交易品种
+- 交易品种:流动性较好的商品期货和金融期货
+- 默认关注品种:RM、RB、AU、CU、M、Y、CF、SR
+- 夜盘品种:金属、能源、农产品等
+- 日盘品种:其他商品、股指期货等
+- 交易合约:主力合约
+
+#### 2. 形态识别
+
+##### 2.1 烛台形态条件
+- 实体长度阈值:使用历史30天平均实体长度作为基准
+- 影线与实体比率:影线长度 >= 1.2 × 实体长度(**g.hatch_to_body_ratio**)
+- 相反影线限制:相反方向影线 < 0.5 × 实体长度(**g.opposite_hatch_ratio**)
+
+##### 2.2 形态类型
+- **上影线形态**:
+  - 上影线长度 >= 1.2 × 实体长度
+  - 下影线长度 < 0.5 × 实体长度
+  - 交易方向:做空(反向交易)
+- **下影线形态**:
+  - 下影线长度 >= 1.2 × 实体长度
+  - 上影线长度 < 0.5 × 实体长度
+  - 交易方向:做多(反向交易)
+
+#### 3. 仓位管理
+
+##### 3.1 开仓规则
+- 计算开仓数量:
+  ```python
+  单手保证金 = 当前价格 × 合约乘数 × 保证金比率
+  可用资金 = 账户资金 × 资金使用比例(80%)
+  开仓手数 = min(最大保证金限制 / 单手保证金, 可用资金 / 单手保证金)
+  ```
+- 开仓限制:
+  1. 检查涨跌停限制
+  2. 考虑保证金要求(**g.max_margin_per_position = 20000**)
+  3. 检查流动性条件
+  4. 防止同品种重复开仓
+
+##### 3.2 持仓调整
+- 移仓换月条件:
+  1. 主力合约更换时自动执行
+  2. 确保新旧合约可交易
+  3. 保持原有持仓方向和止损止盈记录
+- 移仓流程:
+  1. 平掉旧合约
+  2. 开仓新合约
+  3. 更新交易记录
+
+#### 4. 风险控制
+
+##### 4.1 止损机制
+- **固定止损**:
+  - 固定止损率:1%(**g.fixed_stop_loss_rate = 0.01**)
+  - 当亏损达到1%时立即平仓
+  - 适用于所有交易,无条件执行
+
+##### 4.2 动态追踪止盈
+- **分级追踪止盈**:根据已实现的最大利润设置不同的追踪止损比率
+  - 若最大利润 ≤ 5%:使用1%追踪止损(**g.trailing_stop_rates[0] = 0.01**)
+  - 若最大利润在5%-10%之间:使用2%追踪止损(**g.trailing_stop_rates[1] = 0.02**)
+  - 若最大利润 > 10%:使用3%追踪止损(**g.trailing_stop_rates[2] = 0.03**)
+- **追踪机制**:
+  - 实时记录持仓的最大利润
+  - 当利润回撤超过对应阈值时触发止盈
+  - 例如:最大利润8%,当前利润5%,回撤3% > 2%,触发止盈
+
+##### 4.2.1 详细止盈规则
+| 最大利润水平 | 追踪止损率 | 触发条件 | 说明 |
+|-------------|----------|---------|------|
+| ≤ 5% | 1% | 利润回撤 ≥ 1% | 小额利润,保守止盈 |
+| 5%-10% | 2% | 利润回撤 ≥ 2% | 中等利润,适度止盈 |
+| > 10% | 3% | 利润回撤 ≥ 3% | 高额利润,宽松止盈 |
+
+##### 4.3 配置参数
+```python
+g.fixed_stop_loss_rate = 0.01  # 固定止损比率(1%)
+g.trailing_stop_thresholds = [0.05, 0.10]  # 动态追踪止损触发条件(5%, 10%)
+g.trailing_stop_rates = [0.01, 0.02, 0.03]  # 动态追踪止损比率(1%, 2%, 3%)
+g.hatch_to_body_ratio = 1.2  # 影线与实体长度比率阈值
+g.opposite_hatch_ratio = 0.5  # 相反方向影线与实体长度比率阈值
+```
+
+#### 5. 交易执行
+
+##### 5.1 交易时间
+- 日盘:09:05开始交易
+- 夜盘:21:05开始交易
+- 14:55最后一次检查
+
+##### 5.2 核心交易步骤
+1. **任务1**: 获取所有可交易品种(分白天和晚上)
+2. **任务2**: 获取历史数据并计算实体长度阈值(基于30天历史数据)
+3. **任务3**: 获取今日分钟数据并聚合为日数据
+4. **任务4**: 检测烛台影线形态
+5. **任务5**: 检查开仓条件(价格合理性、流动性等)
+6. **任务6**: 执行交易(按影线相反方向开仓)
+7. **任务7**: 检查止损止盈
+8. **任务8**: 检查换月移仓
+
+##### 5.3 时间点任务分配
+- **夜盘开始**:
+  - 21:05:任务1, 2, 3, 4, 5, 6, 7(完整流程)
+  - 21:35, 22:05, 22:35:任务3, 4, 5, 6, 7(常规检查)
+- **日盘开始**:
+  - 09:05:任务1, 2, 3, 4, 5, 6, 7(完整流程)
+  - 09:35, 10:05, 10:35, 11:05, 11:25, 13:35, 14:05:任务3, 4, 5, 6, 7(常规检查)
+- **收盘前**:
+  - 14:35, 14:55:任务3, 4, 5, 6, 7, 8(包含移仓检查)
+
+#### 6. 策略特色
+
+##### 6.1 反向交易逻辑
+- **上影线 → 做空**:认为上影线表示上方压力强劲,价格可能下跌
+- **下影线 → 做多**:认为下影线表示下方支撑强劲,价格可能上涨
+- 基于"影线代表假突破"的交易理念
+
+##### 6.2 智能风控系统
+- 固定止损保护本金
+- 分级追踪止盈锁定利润
+- 根据盈利水平动态调整止盈策略
+- 自动合约换月避免交割风险
+
+##### 6.3 品种选择策略
+- 重点关注流动性好的主流品种
+- 支持夜盘和日盘品种
+- 避免同品种重复开仓
+- 动态获取主力合约
+
+### 交易规则
+- 采用异步报单模式
+- 使用真实价格
+- 考虑交易成本:
+  - 开仓手续费:0.0023%
+  - 平仓手续费:0.0023%  
+  - 平今手续费:0.23%
+  - 滑点:2个价格单位
+- 保证金比例:按品种设置(4%-22%不等)
+- 最大单品种保证金:20000元

+ 181 - 0
Lib/research/README.md

@@ -160,6 +160,168 @@ results = analyzer.run_full_analysis()
 成功率: 75.0%
 ```
 
+### 4. 期货烛台影线形态分析 (`future_candlestick_hatch_analysis.py`)
+
+#### 功能描述
+分析期货合约中带有明显影线的K线,识别具备预测价值的烛台形态,并分析它们对未来价格走势的预测能力。
+
+#### 主要特点
+- **形态识别**: 识别带有明显影线且实体长度符合要求的K线
+- **双向过滤**: 主影线长度必须超过实体1.2倍,相反影线必须小于实体0.5倍
+- **交易信号**: 上影线做空信号,下影线做多信号
+- **未来跟踪**: 分析形态出现后N个交易日的价格表现
+- **主力合约**: 自动识别和使用主力合约数据
+
+#### 核心算法
+```python
+# 形态识别条件
+def check_hatch_pattern(row, body_threshold, hatch_ratio=1.2, opposite_ratio=0.5):
+    """检查K线是否符合影线形态条件"""
+    body_length = abs(close - open)
+    upper_hatch = high - max(open, close)
+    lower_hatch = min(open, close) - low
+    
+    # 条件1: 实体长度 >= 历史平均实体长度
+    # 条件2: 主影线长度 >= 1.2 × 实体长度  
+    # 条件3: 相反影线长度 < 0.5 × 实体长度
+```
+
+#### 交易逻辑
+- **上影线形态**: 做空信号
+  - 收益计算:(收盘价 - 未来最高价) / 收盘价
+  - 预期:价格向影线相反方向(下跌)移动
+- **下影线形态**: 做多信号  
+  - 收益计算:(未来最高价 - 收盘价) / 收盘价
+  - 预期:价格向影线相反方向(上涨)移动
+
+#### 配置参数
+```python
+class CandlestickHatchConfig:
+    ANALYSIS_START_DATE = datetime.datetime(2025, 8, 1)   # 分析开始日期
+    ANALYSIS_END_DATE = datetime.datetime(2025, 8, 20)    # 分析结束日期
+    HISTORICAL_DAYS = 365                                 # 历史数据回溯期
+    FORWARD_DAYS = 15                                     # 前向分析期
+    FUTURE_MONITOR_DAYS = 10                              # 未来表现跟踪期
+    HATCH_TO_BODY_RATIO = 1.2                            # 影线与实体长度比率阈值
+    OPPOSITE_HATCH_RATIO = 0.5                           # 相反影线与实体长度比率阈值
+    MAX_REVENUE_THRESHOLD = 0.02                         # 最大盈利阈值
+    MAX_LOSS_THRESHOLD = 0.005                           # 最大亏损阈值
+    MAX_ANALYSIS_CONTRACTS = 10                          # 最大分析合约数
+    # 交易仓位管理参数
+    INITIAL_TRADE_AMOUNT = 20000                         # 初始交易金额
+    LOSS_RATE = 0.01                                    # 亏损交易的亏损率
+    PROFIT_DRAWDOWN_RATE = 0.02                         # 盈利交易的回撤率
+```
+
+#### 数据结构
+```python
+# 主力合约数据结构 (dict1)
+contract_data = {
+    'L2601.XDCE': [开始日期, 结束日期],
+    'A2505.XDCE': [开始日期, 结束日期]
+}
+
+# 影线形态数据结构 (dict2)
+pattern_data = {
+    'L2601.XDCE': {
+        datetime.date(2025, 8, 5): {
+            'date': '2025-08-05',
+            'high': 6520.0, 'low': 6450.0, 'open': 6480.0, 'close': 6500.0,
+            'body_len': 20.0, 'hatch_dire': 'up', 'hatch_len': 50.0,
+            'max_revenue_date': '2025-08-08', 'max_revenue': 0.0234,
+            'min_revenue_date': '2025-08-06', 'min_revenue': -0.0156,
+            'conclusion': 'true',  # 形态有效性判断
+            'body_direction': 'up',  # 实体方向
+            'direction_consistency': 'false'  # 方向一致性
+        }
+    }
+}
+```
+
+#### 形态有效性判断(增强版)
+系统自动为每个识别的形态计算`conclusion`字段,判断逻辑:
+- **有效 (true)**:满足以下任一条件且无早期过度损失
+  1. 最大盈利日期 < 最大亏损日期 且 最大盈利 > 2%
+  2. 最大盈利日期 > 最大亏损日期 且 最大盈利 > 2% 且 最大亏损 < 0.5%
+- **增强条件**:如果在最大收益日期之前的任何一天,损失超过了最大亏损阈值(0.5%),则标记为无效
+- **无效 (false)**:不满足上述条件或存在早期过度损失
+
+#### 新增分析字段
+- **body_direction**: 烛台实体方向
+  - 'up': 收盘价 > 开盘价(阳线)
+  - 'down': 收盘价 < 开盘价(阴线) 
+  - 'flat': 收盘价 = 开盘价(十字星)
+- **direction_consistency**: 实体与影线方向一致性
+  - 'true': 实体方向与影线方向相反(符合预期)
+  - 'false': 实体方向与影线方向不相反
+- **动态仓位管理**: 基于交易结果的资金管理
+  - 初始交易金额:20,000(可配置)
+  - 亏损交易:按1%亏损率调整资金(可配置)
+  - 盈利交易:按最大利润减去2%回撤率调整资金(可配置)
+- **移动平均线分析**: 形态形成日的技术指标
+  - MA5、MA10、MA20、MA30:5日、10日、20日、30日移动平均线
+  - trend_strength:趋势强度评估(强趋势/中趋势/弱趋势/震荡/数据不足)
+- **hatch/body比率**: 影线与实体长度的比率,用于量化形态强度
+
+#### 使用方法
+```python
+# 基本使用
+results = run_candlestick_hatch_analysis()
+
+# 自定义配置
+class CustomConfig(CandlestickHatchConfig):
+    HATCH_TO_BODY_RATIO = 1.5       # 更严格的影线要求
+    OPPOSITE_HATCH_RATIO = 0.3      # 更严格的相反影线要求
+    MAX_REVENUE_THRESHOLD = 0.03    # 更高的盈利阈值要求
+    MAX_LOSS_THRESHOLD = 0.003      # 更低的亏损容忍度
+    FUTURE_MONITOR_DAYS = 15        # 更长的跟踪期
+
+results = run_candlestick_hatch_analysis(CustomConfig)
+```
+
+#### 输出结果
+- **CSV文件**: `future_candlestick_hatch_analysis_YYYYMMDD_HHMMSS.csv`
+- **包含字段**(按输出顺序): 
+  - contract_code:合约代码
+  - date:形态日期
+  - conclusion:有效性判断(true/false)
+  - trend_strength:趋势强度评估
+  - body_direction:实体方向
+  - direction_consistency:方向一致性
+  - body_len:实体长度
+  - hatch_len:影线长度  
+  - hatch/body:影线与实体长度比率
+  - close:收盘价
+  - open:开盘价
+  - high:最高价
+  - low:最低价
+  - ma5:5日移动平均线
+  - ma10:10日移动平均线
+  - ma20:20日移动平均线
+  - ma30:30日移动平均线
+  - max_revenue:最大收益率
+  - max_revenue_date:最大收益日期
+  - min_revenue:最大亏损率
+  - min_revenue_date:最大亏损日期
+  - profit_loss:单笔盈亏
+  - remaining_amount:剩余资金
+  - trade_amount:交易金额
+- **投资组合汇总**: 整体交易表现统计,包括成功率、总盈亏、总回报率等
+
+#### 统计指标
+- **检测形态总数**:识别出的符合条件的影线形态数量
+- **有效形态数**:conclusion为'true'的形态数量
+- **有效率**:有效形态占总形态的百分比
+
+#### 分析流程
+1. **构建主力合约数据结构**: 获取分析期间的主力合约变化
+2. **收集扩展数据**: 获取历史和未来的OHLC数据
+3. **计算实体长度阈值**: 使用历史数据计算平均实体长度
+4. **识别影线形态**: 筛选符合双重条件的K线
+5. **分析未来表现**: 跟踪形态后N天的价格走势
+6. **计算有效性判断**: 根据收益时序关系评估形态预测能力
+7. **生成分析结果**: 输出包含有效性判断的完整CSV数据文件
+
 ## 技术架构
 
 ### 数据源
@@ -204,6 +366,25 @@ import matplotlib.pyplot as plt  # 图表绘制
 
 ## 更新日志
 
+### v1.5 (2025-09)
+- **增强**: 期货烛台影线形态分析算法重大更新
+- **新增功能**:
+  - 动态仓位管理:基于交易结果的资金调整机制
+  - 移动平均线分析:增加MA5/MA10/MA20/MA30技术指标
+  - 趋势强度评估:6项比较的量化趋势分析
+  - 增强结论逻辑:早期过度损失检测机制
+- **字段优化**: 
+  - 移除重复的hatch_dire列
+  - 新增hatch/body比率列
+  - 按指定顺序重排CSV输出字段
+- **投资组合管理**: 完整的交易结果汇总和表现分析
+
+### v1.4 (2025-09)
+- **新增**: 期货烛台影线形态分析算法 (`future_candlestick_hatch_analysis.py`)
+- **算法特点**: 双向过滤条件、主力合约自动识别、未来表现跟踪
+- **配置优化**: 新增相反影线比率阈值等可配置参数
+- **文档完善**: 更新README文档包含新算法说明
+
 ### v1.3 (2025-09)
 - **重构**: 将期货技术形态分析整合为单一综合文件
 - **优化**: 改进代码结构,增强在线平台适配性

+ 1481 - 0
Lib/research/future_candlestick_hatch_analysis.py

@@ -0,0 +1,1481 @@
+"""
+期货烛台影线形态分析
+研究期货合约中带有明显影线的K线对未来价格走势的预测能力
+
+本程序实现完整的分析流程:
+1. 数据结构设置 - 获取主力期货合约数据
+2. 数据收集 - 扩展时间范围获取OHLC数据
+3. 实体长度计算 - 计算历史平均实体长度作为阈值
+4. 影线形态识别 - 识别满足条件的K线形态
+5. 未来表现分析 - 分析影线方向与价格走势的关系
+6. 结果输出 - 生成影线形态分析结果CSV
+
+注:程序使用动态获取的主力合约进行分析,确保分析结果基于真实的交易活动
+
+作者: jukuan研究团队
+日期: 2025-09
+适用平台: 聚宽在线研究平台
+"""
+
+import pandas as pd
+import numpy as np
+from jqdata import *
+import datetime
+import warnings
+warnings.filterwarnings('ignore')
+
+# =====================================================================================
+# 分析配置参数 - 集中配置部分
+# =====================================================================================
+
+class CandlestickHatchConfig:
+    """期货烛台影线形态分析配置参数"""
+    
+    # ==================== 时间范围设置 ====================
+    ANALYSIS_START_DATE = datetime.datetime(2025, 8, 1)  # 分析开始日期
+    ANALYSIS_END_DATE = datetime.datetime(2025, 8, 20)   # 分析结束日期
+    
+    # ==================== 时间扩展参数 ====================
+    HISTORICAL_DAYS = 365      # 历史数据回溯期:优势期前天数
+    FORWARD_DAYS = 15          # 正向分析期:优势期后天数
+    FUTURE_MONITOR_DAYS = 15   # 未来表现跟踪期:交易日数
+    
+    # ==================== 影线形态参数 ====================
+    HATCH_TO_BODY_RATIO = 1.2  # 影线与实体长度比率阈值
+    OPPOSITE_HATCH_RATIO = 0.5  # 相反方向影线与实体长度比率阈值
+    
+    # ==================== 形态验证阈值 ====================
+    MAX_REVENUE_THRESHOLD = 0.02   # 最大盈利阈值
+    MAX_LOSS_THRESHOLD = 0.01     # 最大亏损阈值
+    
+    # ==================== 分析规模控制 ====================
+    MAX_ANALYSIS_CONTRACTS = -1  # 最大分析合约数量限制
+    
+    # ==================== 输出设置 ====================
+    OUTPUT_ENCODING = 'utf-8-sig'  # 输出文件编码格式
+    VERBOSE_LOGGING = True          # 是否打印详细日志
+    
+    # ==================== 交易仓位管理 ====================
+    INITIAL_TRADE_AMOUNT = 20000    # 单笔交易初始金额
+    LOSS_RATE = 0.01               # 亏损交易的亏损率(可配置)
+    PROFIT_DRAWDOWN_RATE = 0.02    # 盈利交易的回撤率(可配置)
+    
+    @classmethod
+    def print_config(cls):
+        """打印当前配置信息"""
+        print("=== 期货烛台影线形态分析配置 ===")
+        print(f"分析时间范围: {cls.ANALYSIS_START_DATE.strftime('%Y-%m-%d')} 至 {cls.ANALYSIS_END_DATE.strftime('%Y-%m-%d')}")
+        print(f"历史数据回溯期: {cls.HISTORICAL_DAYS}天")
+        print(f"前向分析期: {cls.FORWARD_DAYS}天")
+        print(f"未来表现跟踪期: {cls.FUTURE_MONITOR_DAYS}个交易日")
+        print(f"影线与实体长度比率阈值: {cls.HATCH_TO_BODY_RATIO}")
+        print(f"相反方向影线比率阈值: {cls.OPPOSITE_HATCH_RATIO}")
+        print(f"最大盈利阈值: {cls.MAX_REVENUE_THRESHOLD}")
+        print(f"最大亏损阈值: {cls.MAX_LOSS_THRESHOLD}")
+        print(f"最大分析合约数: {cls.MAX_ANALYSIS_CONTRACTS}")
+        print(f"合约类型: 主力合约")
+        print(f"详细日志: {'开启' if cls.VERBOSE_LOGGING else '关闭'}")
+        print(f"初始交易金额: {cls.INITIAL_TRADE_AMOUNT}")
+        print(f"亏损交易亏损率: {cls.LOSS_RATE}")
+        print(f"盈利交易回撤率: {cls.PROFIT_DRAWDOWN_RATE}")
+        print("=" * 50)
+
+class FutureCandlestickHatchAnalyzer:
+    """期货烛台影线形态分析器"""
+    
+    def __init__(self, config=None):
+        """初始化分析器"""
+        # 使用配置类参数
+        if config is None:
+            config = CandlestickHatchConfig
+        
+        self.config = config
+        self.historical_days = config.HISTORICAL_DAYS
+        self.forward_days = config.FORWARD_DAYS  
+        self.future_monitor_days = config.FUTURE_MONITOR_DAYS
+        self.hatch_to_body_ratio = config.HATCH_TO_BODY_RATIO
+        self.opposite_hatch_ratio = config.OPPOSITE_HATCH_RATIO
+        self.max_revenue_threshold = config.MAX_REVENUE_THRESHOLD
+        self.max_loss_threshold = config.MAX_LOSS_THRESHOLD
+        self.analysis_start_date = config.ANALYSIS_START_DATE
+        self.analysis_end_date = config.ANALYSIS_END_DATE
+        self.max_analysis_contracts = config.MAX_ANALYSIS_CONTRACTS
+        self.output_encoding = config.OUTPUT_ENCODING
+        self.verbose_logging = config.VERBOSE_LOGGING
+        
+        # 交易仓位管理参数
+        self.initial_trade_amount = config.INITIAL_TRADE_AMOUNT
+        self.loss_rate = config.LOSS_RATE
+        self.profit_drawdown_rate = config.PROFIT_DRAWDOWN_RATE
+        
+        # 期货品种与交易所的映射关系
+        self.exchange_map = {
+            # 大连商品交易所 (XDCE)
+            'A': 'XDCE', 'B': 'XDCE', 'C': 'XDCE', 'CS': '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.contract_data = {}  # 主合约数据结构 - dict1
+        self.hatch_patterns = {}  # 影线形态数据 - dict2
+        
+        # 投资组合管理
+        self.portfolio_results = []  # 存储所有交易结果
+        
+        if self.verbose_logging:
+            print("初始化期货烛台影线形态分析器")
+            print(f"配置参数 - 历史数据回溯期: {self.historical_days}天, 前向分析期: {self.forward_days}天")
+            print(f"影线与实体长度比率阈值: {self.hatch_to_body_ratio}, 相反影线比率阈值: {self.opposite_hatch_ratio}")
+            print(f"最大盈利阈值: {self.max_revenue_threshold}, 最大亏损阈值: {self.max_loss_threshold}")
+            print(f"监控窗口: {self.future_monitor_days}个交易日")
+            print(f"最大分析合约数: {self.max_analysis_contracts}")
+            print(f"合约类型: 主力合约")
+    
+    def _is_trading_day(self, 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_symbol_dominant_history(self, 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
+        
+        # if self.verbose_logging:
+        #     print(f"  获取 {symbol} 主力合约历史...")
+        
+        # 按日遍历时间范围
+        while current_date <= end_date:
+            
+            # 跳过非交易日
+            if not self._is_trading_day(current_date):
+                current_date += datetime.timedelta(days=1)
+                continue
+            
+            try:
+                # 获取当日主力合约
+                dominant_contract = get_dominant_future(symbol, current_date)
+                
+                if dominant_contract is None:
+                    current_date += datetime.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 - datetime.timedelta(days=1)
+                        })
+                    
+                    # 开始新合约的记录
+                    current_dominant = dominant_contract
+                    period_start = current_date
+                    
+                    # if self.verbose_logging:
+                    #     print(f"    {current_date}: 主力合约变更为 {dominant_contract}")
+                
+            except Exception as e:
+                if self.verbose_logging:
+                    print(f"    获取 {current_date} 的主力合约时出错: {str(e)}")
+            
+            current_date += datetime.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 get_dominant_contracts_history(self, symbol_list, start_date, end_date):
+        """
+        获取指定时间范围内多个标的的主力合约历史变化
+        
+        参数:
+            symbol_list (list): 标的编码列表
+            start_date (date): 开始日期
+            end_date (date): 结束日期
+        
+        返回:
+            pandas.DataFrame: 包含主力合约变化历史的数据框
+        """
+        # if self.verbose_logging:
+        #     print(f"获取主力合约数据...")
+        #     print(f"标的列表: {symbol_list}")
+        #     print(f"时间范围: {start_date} 至 {end_date}")
+        
+        # 存储所有结果的列表
+        all_results = []
+        
+        # 逐个处理每个标的
+        for symbol in symbol_list:
+            # if self.verbose_logging:
+            #     print(f"\n处理标的: {symbol}")
+            
+            try:
+                # 获取该标的在指定时间范围内的主力合约历史
+                symbol_results = self._get_symbol_dominant_history(symbol, start_date, end_date)
+                all_results.extend(symbol_results)
+                
+            except Exception as e:
+                if self.verbose_logging:
+                    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)
+            
+            if self.verbose_logging:
+                print(f"\n成功获取 {len(result_df)} 条主力合约记录")
+            return result_df
+        else:
+            if self.verbose_logging:
+                print("\n未获取到任何主力合约数据")
+            return pd.DataFrame(columns=['symbol', 'dominant_contract', 'start_date', 'end_date'])
+
+    def build_contract_structure(self):
+        """
+        构建期货主力合约数据结构 (dict1)
+        返回结构:Key=完整合约代码, Value=[start_date, end_date]
+        """
+        if self.verbose_logging:
+            print("\n=== 步骤1: 构建期货主力合约数据结构 ===")
+        
+        # 分析时间范围
+        analysis_start = self.analysis_start_date
+        analysis_end = self.analysis_end_date
+        
+        if self.verbose_logging:
+            print(f"分析时间范围: {analysis_start.strftime('%Y-%m-%d')} 至 {analysis_end.strftime('%Y-%m-%d')}")
+            print(f"合约类型: 主力合约")
+        
+        # 构建主力合约数据结构
+        contract_dict = self._build_dominant_contract_structure(analysis_start, analysis_end)
+        
+        self.contract_data = contract_dict
+        if self.verbose_logging:
+            print(f"构建完成,共{len(contract_dict)}个期货合约")
+        return contract_dict
+    
+    def _build_dominant_contract_structure(self, analysis_start, analysis_end):
+        """构建基于主力合约的数据结构"""
+        if self.verbose_logging:
+            print("使用主力合约获取逻辑...")
+        
+        # 获取要分析的期货品种列表
+        if self.max_analysis_contracts > 0:
+            symbol_list = list(self.exchange_map.keys())[:self.max_analysis_contracts]
+        else:
+            symbol_list = list(self.exchange_map.keys())
+        
+        # 获取主力合约历史数据
+        dominant_df = self.get_dominant_contracts_history(
+            symbol_list, 
+            analysis_start.date(), 
+            analysis_end.date()
+        )
+        
+        contract_dict = {}
+        
+        if not dominant_df.empty:
+            # 将DataFrame转换为字典结构
+            for _, row in dominant_df.iterrows():
+                contract_code = row['dominant_contract']
+                start_date = row['start_date'] if isinstance(row['start_date'], datetime.date) else row['start_date'].date()
+                end_date = row['end_date'] if isinstance(row['end_date'], datetime.date) else row['end_date'].date()
+                
+                contract_dict[contract_code] = [start_date, end_date]
+                
+                # if self.verbose_logging:
+                #     print(f"  添加主力合约: {contract_code} ({start_date} 至 {end_date})")
+        
+        return contract_dict
+    
+    def collect_extended_data(self):
+        """
+        数据收集:为每个合约收集扩展时间范围的数据
+        扩展期限:从(start_date-365天)到(end_date+15天)
+        """
+        if self.verbose_logging:
+            print("\n=== 步骤2: 收集扩展时间范围的数据 ===")
+        
+        extended_data = {}
+        
+        for contract_code, period in self.contract_data.items():
+            start_date, end_date = period
+            
+            # 计算扩展期限
+            start_date_2 = start_date - datetime.timedelta(days=self.historical_days)
+            end_date_2 = end_date + datetime.timedelta(days=self.forward_days)
+            
+            # if self.verbose_logging:
+            #     print(f"获取 {contract_code} 数据")
+            #     print(f"  原始期间: {start_date} 至 {end_date}")
+            #     print(f"  扩展期间: {start_date_2} 至 {end_date_2}")
+            
+            try:
+                # 获取日线数据
+                data = get_price(
+                    contract_code,
+                    start_date=start_date_2,
+                    end_date=end_date_2,
+                    frequency='daily',
+                    fields=['open', 'close', 'high', 'low', 'volume'],
+                    skip_paused=False,
+                    panel=False
+                )
+                
+                if data is not None and len(data) > 0:
+                    extended_data[contract_code] = {
+                        'data': data,
+                        'original_start': start_date,
+                        'original_end': end_date,
+                        'extended_start': start_date_2,
+                        'extended_end': end_date_2
+                    }
+                    
+                #     if self.verbose_logging:
+                #         print(f"  成功获取{len(data)}条数据记录")
+                # else:
+                #     if self.verbose_logging:
+                #         print(f"  未获取到数据")
+                    
+            except Exception as e:
+                if self.verbose_logging:
+                    print(f"  获取数据失败: {str(e)}")
+                continue
+        
+        if self.verbose_logging:
+            print(f"数据收集完成,共{len(extended_data)}个合约有效数据")
+        return extended_data
+    
+    def calculate_body_thresholds(self, extended_data):
+        """
+        计算实体长度阈值:使用历史数据计算平均实体长度
+        """
+        if self.verbose_logging:
+            print("\n=== 步骤3: 计算实体长度阈值 ===")
+        
+        threshold_data = {}
+        
+        for contract_code, contract_info in extended_data.items():
+            data = contract_info['data']
+            start_date = contract_info['original_start']
+            start_date_2 = contract_info['extended_start']
+            
+            # if self.verbose_logging:
+            #     print(f"计算 {contract_code} 的实体长度阈值")
+            #     print(f"  历史期间: {start_date_2} 至 {start_date}")
+            
+            # 筛选历史数据:date >= start_date_2 且 date < start_date
+            historical_mask = (data.index.date >= start_date_2) & (data.index.date < start_date)
+            historical_data = data[historical_mask]
+            
+            if len(historical_data) == 0:
+                if self.verbose_logging:
+                    print(f"  历史数据无效,跳过")
+                continue
+            
+            # 计算实体长度
+            historical_data = historical_data.copy()
+            historical_data['body_length'] = abs(historical_data['close'] - historical_data['open'])
+            
+            # 计算平均实体长度作为阈值
+            body_threshold = historical_data['body_length'].mean()
+            
+            threshold_data[contract_code] = {
+                'contract_info': contract_info,
+                'body_threshold': body_threshold,
+                'historical_days': len(historical_data)
+            }
+            
+            # if self.verbose_logging:
+            #     print(f"  实体长度阈值: {body_threshold:.4f} (基于{len(historical_data)}个历史交易日)")
+        
+        if self.verbose_logging:
+            print("实体长度阈值计算完成")
+        return threshold_data
+    
+    def identify_hatch_patterns(self, threshold_data):
+        """
+        影线形态识别:扫描符合条件的K线
+        条件:实体长度 >= 阈值 且 影线长度 >= 1.2 × 实体长度
+        """
+        if self.verbose_logging:
+            print(f"\n=== 步骤4: 识别影线形态 ===")
+        
+        pattern_dict = {}  # dict2结构
+        
+        for contract_code, threshold_info in threshold_data.items():
+            contract_info = threshold_info['contract_info']
+            data = contract_info['data']
+            body_threshold = threshold_info['body_threshold']
+            start_date = contract_info['original_start']  # 使用原始开始日期
+            end_date = contract_info['original_end']
+            
+            # if self.verbose_logging:
+            #     print(f"分析 {contract_code} 的影线形态")
+            #     print(f"  扫描期间: {start_date} 至 {end_date}")
+            #     print(f"  实体长度阈值: {body_threshold:.4f}")
+            
+            # 筛选扫描数据:从start_date到end_date(含)
+            scan_mask = (data.index.date >= start_date) & (data.index.date <= end_date)
+            scan_data = data[scan_mask]
+            
+            contract_patterns = {}
+            pattern_count = 0
+            
+            for idx, (signal_date, row) in enumerate(scan_data.iterrows()):
+                # 计算K线各项数值
+                open_price = row['open']
+                close_price = row['close']
+                high_price = row['high']
+                low_price = row['low']
+                
+                # 计算实体长度
+                body_length = abs(close_price - open_price)
+                
+                # 检查实体长度是否满足阈值
+                if body_length < body_threshold:
+                    continue
+                
+                # 计算影线长度
+                upper_hatch = high_price - max(open_price, close_price)
+                lower_hatch = min(open_price, close_price) - low_price
+                
+                # 检查影线条件
+                hatch_direction = None
+                hatch_length = 0
+                
+                # 检查上影线条件:上影线长度符合要求 且 下影线长度小于实体长度的一半
+                if (upper_hatch >= self.hatch_to_body_ratio * body_length and 
+                    lower_hatch < self.opposite_hatch_ratio * body_length):
+                    hatch_direction = 'up'
+                    hatch_length = upper_hatch
+                # 检查下影线条件:下影线长度符合要求 且 上影线长度小于实体长度的一半
+                elif (lower_hatch >= self.hatch_to_body_ratio * body_length and 
+                      upper_hatch < self.opposite_hatch_ratio * body_length):
+                    hatch_direction = 'down'
+                    hatch_length = lower_hatch
+                
+                # 如果满足影线条件,记录形态
+                if hatch_direction is not None:
+                    pattern_count += 1
+                    
+                    # dict3结构
+                    pattern_info = {
+                        'date': signal_date.strftime('%Y-%m-%d'),
+                        'high': float(high_price),
+                        'low': float(low_price),
+                        'open': float(open_price),
+                        'close': float(close_price),  # 标准收盘价
+                        'body_len': float(body_length),
+                        'hatch_dire': hatch_direction,
+                        'hatch_len': float(hatch_length)
+                    }
+                    
+                    contract_patterns[signal_date] = pattern_info
+                    
+                    # if self.verbose_logging:
+                    #     opposite_hatch_len = lower_hatch if hatch_direction == 'up' else upper_hatch
+                        # print(f"    发现形态: {signal_date.strftime('%Y-%m-%d')} {hatch_direction}影线 实体:{body_length:.4f} 影线:{hatch_length:.4f} 相反影线:{opposite_hatch_len:.4f}")
+            
+            if contract_patterns:
+                pattern_dict[contract_code] = contract_patterns
+            #     if self.verbose_logging:
+            #         print(f"  检测到{pattern_count}个有效影线形态")
+            # else:
+            #     if self.verbose_logging:
+            #         print(f"  未检测到有效影线形态")
+        
+        # if self.verbose_logging:
+        #     total_patterns = sum(len(patterns) for patterns in pattern_dict.values())
+        #     print(f"\n影线形态识别完成,共{total_patterns}个形态")
+        
+        self.hatch_patterns = pattern_dict
+        return pattern_dict
+    
+    def analyze_future_performance(self, threshold_data, pattern_dict):
+        """
+        未来表现分析:分析影线形态对未来价格走势的预测能力
+        交易方向:上影线做空,下影线做多
+        """
+        if self.verbose_logging:
+            print(f"\n=== 步骤5: 分析未来表现 ===")
+        
+        enhanced_pattern_dict = {}
+        
+        for contract_code, contract_patterns in pattern_dict.items():
+            # 获取对应的数据信息
+            contract_info = threshold_data[contract_code]['contract_info']
+            data = contract_info['data']
+            end_date_2 = contract_info['extended_end']
+            
+            if self.verbose_logging:
+                print(f"\n分析 {contract_code} 的未来表现")
+                print(f"  监控窗口: {self.future_monitor_days}个交易日")
+            
+            enhanced_patterns = {}
+            
+            for signal_date, pattern_info in contract_patterns.items():
+                hatch_direction = pattern_info['hatch_dire']
+                standard_close = pattern_info['close']  # 标准收盘价
+                print(f"形态日期:{signal_date.strftime('%Y-%m-%d')}, 标准收盘价: {standard_close}")
+                
+                # 在数据中找到信号日期的位置
+                signal_idx = None
+                for idx, (date, row) in enumerate(data.iterrows()):
+                    if date.date() == signal_date.date():
+                        signal_idx = idx
+                        break
+                
+                if signal_idx is None:
+                    if self.verbose_logging:
+                        print(f"    未找到信号日期 {signal_date.strftime('%Y-%m-%d')} 在数据中")
+                    continue
+                
+                # 分析未来N天的表现
+                future_analysis = self._analyze_future_n_days(
+                    data, signal_idx, hatch_direction, standard_close, 
+                    self.future_monitor_days, end_date_2
+                )
+                
+                # 计算最大收益和最大风险(原逻辑:与影线方向相反交易)
+                max_revenue_info, min_revenue_info = self._calculate_max_revenue_risk(future_analysis, hatch_direction, standard_close)
+                
+                # 计算最大收益和最大风险(新逻辑:与影线方向相同交易)
+                max_revenue_info_2, min_revenue_info_2 = self._calculate_max_revenue_risk_2(future_analysis, hatch_direction, standard_close)
+                
+                # if self.verbose_logging:
+                #     print(f"      原逻辑最大收益: {max_revenue_info['revenue']:.4f} ({max_revenue_info['date']})")
+                #     print(f"      原逻辑最大亏损: {min_revenue_info['revenue']:.4f} ({min_revenue_info['date']})")
+                #     print(f"      新逻辑最大收益: {max_revenue_info_2['revenue']:.4f} ({max_revenue_info_2['date']})")
+                #     print(f"      新逻辑最大亏损: {min_revenue_info_2['revenue']:.4f} ({min_revenue_info_2['date']})")
+
+                # 计算conclusion字段(增强版,包含日损失检查)
+                conclusion = self._calculate_conclusion(max_revenue_info, min_revenue_info, future_analysis, hatch_direction, standard_close)
+                
+                # 计算conclusion2字段(新逻辑:与影线方向相同交易)
+                conclusion_2 = self._calculate_conclusion_2(max_revenue_info_2, min_revenue_info_2, future_analysis, hatch_direction, standard_close)
+                
+                # 计算新增字段
+                body_direction, direction_consistency = self._calculate_direction_fields(pattern_info, hatch_direction)
+                
+                # 计算影线与实体长度比率
+                hatch_to_body_ratio = pattern_info['hatch_len'] / pattern_info['body_len'] if pattern_info['body_len'] > 0 else 0
+                
+                # 计算移动平均线
+                ma_values = self._calculate_moving_averages(data, signal_idx)
+                
+                # 计算基于收盘价与5K移动平均线关系的价格方向
+                price_direction = self._calculate_price_direction(standard_close, ma_values['ma5'])
+                
+                # 计算趋势强度
+                trend_strength = self._calculate_trend_strength(ma_values)
+                
+                # 动态仓位计算
+                remaining_amount = self._calculate_dynamic_position(
+                    conclusion, max_revenue_info['revenue'], self.initial_trade_amount
+                )
+                
+                # 记录到投资组合(包含两种交易逻辑)
+                self._add_portfolio_trade(
+                    contract_code, 
+                    signal_date.strftime('%Y-%m-%d'),
+                    conclusion,
+                    conclusion_2,
+                    max_revenue_info['revenue'],
+                    max_revenue_info_2['revenue'],
+                    self.initial_trade_amount,
+                    remaining_amount
+                )
+                
+                # 增强pattern_info
+                enhanced_pattern = pattern_info.copy()
+                enhanced_pattern.update({
+                    'max_revenue_date': max_revenue_info['date'],
+                    'max_revenue': max_revenue_info['revenue'],
+                    'min_revenue_date': min_revenue_info['date'],
+                    'min_revenue': min_revenue_info['revenue'],
+                    'max_revenue_date2': max_revenue_info_2['date'],
+                    'max_revenue2': max_revenue_info_2['revenue'],
+                    'min_revenue_date2': min_revenue_info_2['date'],
+                    'min_revenue2': min_revenue_info_2['revenue'],
+                    'conclusion': conclusion,
+                    'conclusion2': conclusion_2,
+                    'body_direction': body_direction,
+                    'direction_consistency': direction_consistency,
+                    'price_direction': price_direction,  # 新增:基于收盘价与5K移动平均线关系的价格方向
+                    'trade_amount': float(self.initial_trade_amount),
+                    'remaining_amount': float(remaining_amount),
+                    'profit_loss': float(remaining_amount - self.initial_trade_amount),
+                    'hatch_body_ratio': float(hatch_to_body_ratio),
+                    'ma5': ma_values['ma5'],
+                    'ma10': ma_values['ma10'],
+                    'ma20': ma_values['ma20'],
+                    'ma30': ma_values['ma30'],
+                    'trend_strength': trend_strength
+                })
+                
+                enhanced_patterns[signal_date] = enhanced_pattern
+                
+                if self.verbose_logging:
+                    print(f"    {signal_date.strftime('%Y-%m-%d')} {hatch_direction}影线:")
+                    print(f"      原逻辑 - 最大收益: {max_revenue_info['revenue']:.4f} ({max_revenue_info['date']})")
+                    print(f"      原逻辑 - 最大亏损: {min_revenue_info['revenue']:.4f} ({min_revenue_info['date']})")
+                    print(f"      原逻辑 - 结论: {conclusion}")
+                    print(f"      新逻辑 - 最大收益: {max_revenue_info_2['revenue']:.4f} ({max_revenue_info_2['date']})")
+                    print(f"      新逻辑 - 最大亏损: {min_revenue_info_2['revenue']:.4f} ({min_revenue_info_2['date']})")
+                    print(f"      新逻辑 - 结论: {conclusion_2}")
+                    print(f"      实体方向: {body_direction}")
+                    print(f"      方向一致性: {direction_consistency}")
+                    print(f"      价格方向: {price_direction} (收盘价vs5K线)")
+            
+            if enhanced_patterns:
+                enhanced_pattern_dict[contract_code] = enhanced_patterns
+                if self.verbose_logging:
+                    print(f"  完成{len(enhanced_patterns)}个形态的未来表现分析")
+        
+        if self.verbose_logging:
+            total_patterns = sum(len(patterns) for patterns in enhanced_pattern_dict.values())
+            print(f"\n未来表现分析完成,共{total_patterns}个形态")
+        
+        return enhanced_pattern_dict
+    
+    def _analyze_future_n_days(self, data, signal_idx, hatch_direction, standard_close, monitor_days, end_date_2):
+        """分析信号后未来N天的价格表现"""
+        future_prices = []
+        data_list = list(data.iterrows())
+        
+        for i in range(1, monitor_days + 1):
+            future_idx = signal_idx + i
+            if future_idx < len(data_list):
+                future_date, future_row = data_list[future_idx]
+                
+                # 检查是否超出扩展结束日期
+                if future_date.date() > end_date_2:
+                    break
+                
+                future_prices.append({
+                    'date': future_date.strftime('%Y-%m-%d'),
+                    'high': float(future_row['high']),
+                    'low': float(future_row['low'])
+                })
+            else:
+                break
+        
+        return future_prices
+    
+    def _calculate_max_revenue_risk(self, future_analysis, hatch_direction, standard_close):
+        """计算最大收益和最大风险(原逻辑:与影线方向相反交易)"""
+        if not future_analysis:
+            return {'date': None, 'revenue': 0.0}, {'date': None, 'revenue': 0.0}
+        
+        max_revenue = float('-inf')
+        min_revenue = float('inf')
+        max_revenue_date = None
+        min_revenue_date = None
+        
+        for day_data in future_analysis:
+            date = day_data['date']
+            high = day_data['high']
+            low = day_data['low']
+            
+            if hatch_direction == 'up':
+                # 上影线做空:收益 = (标准收盘价 - 最低价) / 标准收盘价,风险 = (标准收盘价 - 最高价) / 标准收盘价
+                revenue = (standard_close - low) / standard_close
+                risk = (standard_close - high) / standard_close
+            else:
+                # 下影线做多:收益 = (最高价 - 标准收盘价) / 标准收盘价,风险 = (最低价 - 标准收盘价) / 标准收盘价
+                revenue = (high - standard_close) / standard_close
+                risk = (low - standard_close) / standard_close
+            
+            # 更新最大收益
+            if revenue > max_revenue:
+                max_revenue = round(revenue, 4)
+                max_revenue_date = date
+            
+            # 更新最大风险(最大亏损,即最小收益的相反数)
+            if risk < min_revenue:
+                min_revenue = round(risk, 4)
+                min_revenue_date = date
+        
+        return {'date': max_revenue_date, 'revenue': float(max_revenue)}, {'date': min_revenue_date, 'revenue': float(min_revenue)}
+    
+    def _calculate_max_revenue_risk_2(self, future_analysis, hatch_direction, standard_close):
+        """计算最大收益和最大风险(新逻辑:与影线方向相同交易)"""
+        if not future_analysis:
+            return {'date': None, 'revenue': 0.0}, {'date': None, 'revenue': 0.0}
+        
+        max_revenue_2 = float('-inf')
+        min_revenue_2 = float('inf')
+        max_revenue_date_2 = None
+        min_revenue_date_2 = None
+        
+        for day_data in future_analysis:
+            date = day_data['date']
+            high = day_data['high']
+            low = day_data['low']
+            
+            if hatch_direction == 'up':
+                # 上影线做多:收益 = (最高价 - 标准收盘价) / 标准收盘价,风险 = (最低价 - 标准收盘价) / 标准收盘价
+                revenue_2 = (high - standard_close) / standard_close
+                risk_2 = (low - standard_close) / standard_close
+            else:
+                # 下影线做空:收益 = (标准收盘价 - 最低价) / 标准收盘价,风险 = (标准收盘价 - 最高价) / 标准收盘价
+                revenue_2 = (standard_close - low) / standard_close
+                risk_2 = (standard_close - high) / standard_close
+            
+            # 更新最大收益
+            if revenue_2 > max_revenue_2:
+                max_revenue_2 = round(revenue_2, 4)
+                max_revenue_date_2 = date
+            
+            # 更新最大风险(最大亏损,即最小收益的相反数)
+            if risk_2 < min_revenue_2:
+                min_revenue_2 = round(risk_2, 4)
+                min_revenue_date_2 = date
+        
+        return {'date': max_revenue_date_2, 'revenue': float(max_revenue_2)}, {'date': min_revenue_date_2, 'revenue': float(min_revenue_2)}
+    
+    def _calculate_conclusion(self, max_revenue_info, min_revenue_info, future_analysis, hatch_direction, standard_close):
+        """
+        计算形态的结论:是否为有效的预测信号(增强版)
+        
+        逻辑:
+        - 若满足以下条件则返回 "true":
+        1. (最大盈利日期 < 最大亏损日期)且(最大盈利 > max_revenue_threshold)
+        2. 或者(最大盈利日期 > 最大亏损日期)且(最大盈利 > max_revenue_threshold)且(最大亏损 < max_loss_threshold)
+        - 增强条件:如果在最大收益日期之前的任何一天,损失超过了MAX_LOSS_THRESHOLD,则返回 "false"
+        - 否则返回 "false"
+        """
+        max_revenue_date = max_revenue_info['date']
+        max_revenue = max_revenue_info['revenue']
+        min_revenue_date = min_revenue_info['date']
+        min_revenue = min_revenue_info['revenue']
+        
+        # 如果日期为空,返回false
+        if max_revenue_date is None or min_revenue_date is None:
+            return "false"
+        
+        # 转换日期字符串为datetime对象进行比较
+        try:
+            from datetime import datetime
+            max_revenue_dt = datetime.strptime(max_revenue_date, '%Y-%m-%d')
+            min_revenue_dt = datetime.strptime(min_revenue_date, '%Y-%m-%d')
+        except:
+            return "false"
+        
+        # 增强检查:检查最大收益日期之前是否有任何一天的损失超过阈值
+        if future_analysis and self._check_early_excessive_loss(future_analysis, max_revenue_date, hatch_direction, standard_close):
+            return "false"
+        
+        # 条件1:(最大盈利日期 < 最大亏损日期)且(最大盈利 > 阈值)
+        condition1 = (max_revenue_dt < min_revenue_dt) and (max_revenue > self.max_revenue_threshold)
+        
+        # 条件2:(最大盈利日期 > 最大亏损日期)且(最大盈利 > 阈值)且(最大亏损 < 阈值)
+        condition2 = (max_revenue_dt > min_revenue_dt) and (max_revenue > self.max_revenue_threshold) and (abs(min_revenue) < self.max_loss_threshold)
+        
+        return "true" if (condition1 or condition2) else "false"
+    
+    def _check_early_excessive_loss(self, future_analysis, max_revenue_date, hatch_direction, standard_close):
+        """
+        检查在最大收益日期之前是否有任何一天的损失超过阈值(原逻辑:与影线方向相反交易)
+        
+        参数:
+            future_analysis: 未来价格分析数据
+            max_revenue_date: 最大收益日期
+            hatch_direction: 影线方向
+            standard_close: 标准收盘价
+            
+        返回:
+            bool: True表示存在早期过度损失
+        """
+        from datetime import datetime
+        try:
+            max_revenue_dt = datetime.strptime(max_revenue_date, '%Y-%m-%d')
+        except:
+            return False
+        
+        for day_data in future_analysis:
+            day_date_str = day_data['date']
+            try:
+                day_dt = datetime.strptime(day_date_str, '%Y-%m-%d')
+                
+                # 只检查最大收益日期之前的日期
+                if day_dt >= max_revenue_dt:
+                    continue
+                
+                high = day_data['high']
+                low = day_data['low']
+                
+                if hatch_direction == 'up':
+                    # 上影线做空:亏损 = (标准收盘价 - 最高价) / 标准收盘价,当价格上涨时为负值(亏损)
+                    loss_value = (standard_close - high) / standard_close
+                else:
+                    # 下影线做多:亏损 = (最低价 - 标准收盘价) / 标准收盘价,当价格下跌时为负值(亏损)
+                    loss_value = (low - standard_close) / standard_close
+                
+                # 检查亏损是否超过阈值:亏损为负值,阈值为正值,所以检查 abs(亏损) > 阈值
+                if loss_value < 0 and abs(loss_value) > self.max_loss_threshold:
+                    return True
+                    
+            except:
+                continue
+        
+        return False
+    
+    def _calculate_conclusion_2(self, max_revenue_info_2, min_revenue_info_2, future_analysis, hatch_direction, standard_close):
+        """
+        计算形态的结论2:是否为有效的预测信号(新逻辑:与影线方向相同交易)
+        
+        逻辑:
+        - 若满足以下条件则返回 "true":
+        1. (最大盈利日期 < 最大亏损日期)且(最大盈利 > max_revenue_threshold)
+        2. 或者(最大盈利日期 > 最大亏损日期)且(最大盈利 > max_revenue_threshold)且(最大亏损 < max_loss_threshold)
+        - 增强条件:如果在最大收益日期之前的任何一天,损失超过了MAX_LOSS_THRESHOLD,则返回 "false"
+        - 否则返回 "false"
+        """
+        max_revenue_date_2 = max_revenue_info_2['date']
+        max_revenue_2 = max_revenue_info_2['revenue']
+        min_revenue_date_2 = min_revenue_info_2['date']
+        min_revenue_2 = min_revenue_info_2['revenue']
+        
+        # 如果日期为空,返回false
+        if max_revenue_date_2 is None or min_revenue_date_2 is None:
+            return "false"
+        
+        # 转换日期字符串为datetime对象进行比较
+        try:
+            from datetime import datetime
+            max_revenue_dt_2 = datetime.strptime(max_revenue_date_2, '%Y-%m-%d')
+            min_revenue_dt_2 = datetime.strptime(min_revenue_date_2, '%Y-%m-%d')
+        except:
+            return "false"
+        
+        # 增强检查:检查最大收益日期之前是否有任何一天的损失超过阈值(新逻辑)
+        if future_analysis and self._check_early_excessive_loss_2(future_analysis, max_revenue_date_2, hatch_direction, standard_close):
+            return "false"
+        
+        # 条件1:(最大盈利日期 < 最大亏损日期)且(最大盈利 > 阈值)
+        condition1 = (max_revenue_dt_2 < min_revenue_dt_2) and (max_revenue_2 > self.max_revenue_threshold)
+        
+        # 条件2:(最大盈利日期 > 最大亏损日期)且(最大盈利 > 阈值)且(最大亏损 < 阈值)
+        condition2 = (max_revenue_dt_2 > min_revenue_dt_2) and (max_revenue_2 > self.max_revenue_threshold) and (abs(min_revenue_2) < self.max_loss_threshold)
+        
+        return "true" if (condition1 or condition2) else "false"
+    
+    def _check_early_excessive_loss_2(self, future_analysis, max_revenue_date_2, hatch_direction, standard_close):
+        """
+        检查在最大收益日期之前是否有任何一天的损失超过阈值(新逻辑:与影线方向相同交易)
+        
+        参数:
+            future_analysis: 未来价格分析数据
+            max_revenue_date_2: 最大收益日期
+            hatch_direction: 影线方向
+            standard_close: 标准收盘价
+            
+        返回:
+            bool: True表示存在早期过度损失
+        """
+        from datetime import datetime
+        try:
+            max_revenue_dt_2 = datetime.strptime(max_revenue_date_2, '%Y-%m-%d')
+        except:
+            return False
+        
+        for day_data in future_analysis:
+            day_date_str = day_data['date']
+            try:
+                day_dt = datetime.strptime(day_date_str, '%Y-%m-%d')
+                
+                # 只检查最大收益日期之前的日期
+                if day_dt >= max_revenue_dt_2:
+                    continue
+                
+                high = day_data['high']
+                low = day_data['low']
+                
+                if hatch_direction == 'up':
+                    # 上影线做多:亏损 = (最低价 - 标准收盘价) / 标准收盘价,当价格下跌时为负值(亏损)
+                    loss_value = (low - standard_close) / standard_close
+                else:
+                    # 下影线做空:亏损 = (标准收盘价 - 最高价) / 标准收盘价,当价格上涨时为负值(亏损)
+                    loss_value = (standard_close - high) / standard_close
+                
+                # 检查亏损是否超过阈值:亏损为负值,阈值为正值,所以检查 abs(亏损) > 阈值
+                if loss_value < 0 and abs(loss_value) > self.max_loss_threshold:
+                    return True
+                    
+            except:
+                continue
+        
+        return False
+    
+    def _calculate_direction_fields(self, pattern_info, hatch_direction):
+        """
+        计算body_direction和direction_consistency字段
+        
+        参数:
+            pattern_info: K线形态信息
+            hatch_direction: 影线方向 ('up' 或 'down')
+        
+        返回:
+            tuple: (body_direction, direction_consistency)
+        """
+        open_price = pattern_info['open']
+        close_price = pattern_info['close']
+        
+        # 计算body_direction:实体方向
+        if close_price > open_price:
+            body_direction = 'up'
+        elif close_price < open_price:
+            body_direction = 'down'
+        else:
+            body_direction = 'flat'  # 十字星情况
+        
+        # 计算direction_consistency:检查实体方向与影线方向是否相反
+        if body_direction == 'flat':
+            direction_consistency = 'false'  # 十字星情况无法判断一致性
+        elif (body_direction == 'up' and hatch_direction == 'down') or \
+             (body_direction == 'down' and hatch_direction == 'up'):
+            direction_consistency = 'true'   # 相反,符合预期
+        else:
+            direction_consistency = 'false'  # 不相反
+            
+        return body_direction, direction_consistency
+    
+    def _calculate_price_direction(self, close_price, ma5_value):
+        """
+        计算基于收盘价与5K移动平均线关系的价格方向
+        
+        参数:
+            close_price: 形态形成日的收盘价
+            ma5_value: 5日移动平均线价格
+            
+        返回:
+            str: 价格方向分类 ("上涨" 或 "下跌")
+        """
+        if ma5_value is None:
+            return "数据不足"  # 如果MA5数据不足,返回数据不足
+        
+        if close_price >= ma5_value:
+            return "上涨"
+        else:
+            return "下跌"
+    
+    def _calculate_dynamic_position(self, conclusion, max_revenue, trade_amount):
+        """
+        计算动态仓位调整后的剩余资金
+        
+        参数:
+            conclusion: 交易结论 ('true' 为正确, 'false' 为错误)
+            max_revenue: 最大盈利率
+            trade_amount: 当前交易金额
+            
+        返回:
+            float: 调整后的剩余资金
+        """
+        if conclusion == "false":  # 亏损交易(错误结果)
+            remaining_amount = trade_amount * (1 - self.loss_rate)
+            if self.verbose_logging:
+                print(f"      亏损交易,剩余资金: {remaining_amount:.2f} (亏损率: {self.loss_rate})")
+        else:  # 盈利交易(正确结果)
+            remaining_amount = trade_amount * (1 + max_revenue - self.profit_drawdown_rate)
+            if self.verbose_logging:
+                print(f"      盈利交易,剩余资金: {remaining_amount:.2f} (最大利润: {max_revenue:.4f}, 回撤率: {self.profit_drawdown_rate})")
+        
+        return remaining_amount
+    
+    def _add_portfolio_trade(self, contract_code, date, conclusion, conclusion_2, max_revenue, max_revenue_2, trade_amount, remaining_amount):
+        """
+        添加交易记录到投资组合(支持两种交易逻辑)
+        
+        参数:
+            contract_code: 合约代码
+            date: 交易日期
+            conclusion: 原逻辑交易结论
+            conclusion_2: 新逻辑交易结论
+            max_revenue: 原逻辑最大盈利率
+            max_revenue_2: 新逻辑最大盈利率
+            trade_amount: 交易金额
+            remaining_amount: 剩余金额(基于原逻辑计算)
+        """
+        # 计算新逻辑的剩余金额
+        remaining_amount_2 = self._calculate_dynamic_position(
+            conclusion_2, max_revenue_2, trade_amount
+        )
+        
+        trade_record = {
+            'contract_code': contract_code,
+            'date': date,
+            'conclusion': conclusion,
+            'conclusion2': conclusion_2,
+            'max_revenue': max_revenue,
+            'max_revenue2': max_revenue_2,
+            'trade_amount': trade_amount,
+            'remaining_amount': remaining_amount,
+            'remaining_amount2': remaining_amount_2,
+            'profit_loss': remaining_amount - trade_amount,
+            'profit_loss2': remaining_amount_2 - trade_amount
+        }
+        self.portfolio_results.append(trade_record)
+    
+    def _calculate_moving_averages(self, data, signal_idx):
+        """
+        计算形态形成日的移动平均线
+        
+        参数:
+            data: 价格数据DataFrame
+            signal_idx: 信号日期在数据中的索引
+            
+        返回:
+            dict: 包含各移动平均线的字典
+        """
+        if signal_idx < 29:  # 至少需要30天数据来计算30日移动平均
+            return {'ma5': None, 'ma10': None, 'ma20': None, 'ma30': None}
+        
+        # 获取到信号日期为止的收盘价数据
+        data_list = list(data.iterrows())
+        closes = []
+        for i in range(max(0, signal_idx - 29), signal_idx + 1):  # 取30天数据
+            if i < len(data_list):
+                _, row = data_list[i]
+                closes.append(row['close'])
+        
+        if len(closes) < 30:
+            return {'ma5': None, 'ma10': None, 'ma20': None, 'ma30': None}
+        
+        # 计算移动平均线(使用最后的N天数据)
+        ma5 = sum(closes[-5:]) / 5 if len(closes) >= 5 else None
+        ma10 = sum(closes[-10:]) / 10 if len(closes) >= 10 else None
+        ma20 = sum(closes[-20:]) / 20 if len(closes) >= 20 else None
+        ma30 = sum(closes[-30:]) / 30 if len(closes) >= 30 else None
+        
+        return {
+            'ma5': round(ma5, 2) if ma5 is not None else None,
+            'ma10': round(ma10, 2) if ma10 is not None else None,
+            'ma20': round(ma20, 2) if ma20 is not None else None,
+            'ma30': round(ma30, 2) if ma30 is not None else None
+        }
+    
+    def _calculate_trend_strength(self, ma_values):
+        """
+        计算趋势强度评估
+        
+        参数:
+            ma_values: 移动平均线字典
+            
+        返回:
+            str: 趋势强度分类
+        """
+        ma5 = ma_values['ma5']
+        ma10 = ma_values['ma10']
+        ma20 = ma_values['ma20']
+        ma30 = ma_values['ma30']
+        
+        # 如果有任何移动平均线为空,返回"数据不足"
+        if None in [ma5, ma10, ma20, ma30]:
+            return "数据不足"
+        
+        # 进行6项趋势比较
+        comparisons = [
+            ma5 >= ma10,  # 5K≥10K
+            ma10 >= ma20,  # 10K≥20K
+            ma20 >= ma30,  # 20K≥30K
+            ma5 >= ma20,   # 5K≥20K
+            ma10 >= ma30,  # 10K≥30K
+            ma5 >= ma30    # 5K≥30K
+        ]
+        
+        # 计算上涨趋势的数量
+        uptrend_count = sum(comparisons)
+        
+        # 根据上涨趋势数量进行分类
+        if uptrend_count == 6 or uptrend_count == 0:
+            return "强趋势"
+        elif uptrend_count == 3:
+            return "震荡"
+        elif uptrend_count == 5 or uptrend_count == 1:
+            return "中趋势"
+        elif uptrend_count == 4 or uptrend_count == 2:
+            return "弱趋势"
+        else:
+            return "未知"  # 理论上不应该到这里
+    
+    def calculate_portfolio_summary(self):
+        """
+        计算投资组合整体表现汇总(支持两种交易逻辑对比)
+        
+        返回:
+            dict: 投资组合汇总信息
+        """
+        if not self.portfolio_results:
+            return {
+                'total_trades': 0,
+                'logic1': {
+                    'successful_trades': 0,
+                    'failed_trades': 0,
+                    'success_rate': 0.0,
+                    'total_profit_loss': 0.0,
+                    'final_capital': self.initial_trade_amount,
+                    'total_return': 0.0
+                },
+                'logic2': {
+                    'successful_trades': 0,
+                    'failed_trades': 0,
+                    'success_rate': 0.0,
+                    'total_profit_loss': 0.0,
+                    'final_capital': self.initial_trade_amount,
+                    'total_return': 0.0
+                }
+            }
+        
+        total_trades = len(self.portfolio_results)
+        initial_capital = total_trades * self.initial_trade_amount
+        
+        # 原逻辑(与影线方向相反交易)统计
+        successful_trades_1 = sum(1 for trade in self.portfolio_results if trade['conclusion'] == 'true')
+        failed_trades_1 = total_trades - successful_trades_1
+        success_rate_1 = (successful_trades_1 / total_trades) * 100 if total_trades > 0 else 0
+        total_profit_loss_1 = sum(trade['profit_loss'] for trade in self.portfolio_results)
+        final_capital_1 = initial_capital + total_profit_loss_1
+        total_return_1 = (total_profit_loss_1 / initial_capital * 100) if initial_capital > 0 else 0
+        
+        # 新逻辑(与影线方向相同交易)统计
+        successful_trades_2 = sum(1 for trade in self.portfolio_results if trade['conclusion2'] == 'true')
+        failed_trades_2 = total_trades - successful_trades_2
+        success_rate_2 = (successful_trades_2 / total_trades) * 100 if total_trades > 0 else 0
+        total_profit_loss_2 = sum(trade['profit_loss2'] for trade in self.portfolio_results)
+        final_capital_2 = initial_capital + total_profit_loss_2
+        total_return_2 = (total_profit_loss_2 / initial_capital * 100) if initial_capital > 0 else 0
+        
+        portfolio_summary = {
+            'total_trades': total_trades,
+            'initial_capital': initial_capital,
+            'logic1': {
+                'name': '原逻辑(与影线方向相反交易)',
+                'successful_trades': successful_trades_1,
+                'failed_trades': failed_trades_1,
+                'success_rate': round(success_rate_1, 2),
+                'total_profit_loss': round(total_profit_loss_1, 2),
+                'final_capital': round(final_capital_1, 2),
+                'total_return': round(total_return_1, 2)
+            },
+            'logic2': {
+                'name': '新逻辑(与影线方向相同交易)',
+                'successful_trades': successful_trades_2,
+                'failed_trades': failed_trades_2,
+                'success_rate': round(success_rate_2, 2),
+                'total_profit_loss': round(total_profit_loss_2, 2),
+                'final_capital': round(final_capital_2, 2),
+                'total_return': round(total_return_2, 2)
+            }
+        }
+        
+        if self.verbose_logging:
+            print(f"\n=== 投资组合双向交易逻辑对比分析 ===")
+            print(f"总交易次数: {total_trades}")
+            print(f"初始资本: {initial_capital:.2f}")
+            print(f"\n--- 原逻辑(与影线方向相反交易)---")
+            print(f"成功交易: {successful_trades_1}")
+            print(f"失败交易: {failed_trades_1}")
+            print(f"成功率: {success_rate_1:.2f}%")
+            print(f"总盈亏: {total_profit_loss_1:.2f}")
+            print(f"最终资本: {final_capital_1:.2f}")
+            print(f"总回报率: {total_return_1:.2f}%")
+            print(f"\n--- 新逻辑(与影线方向相同交易)---")
+            print(f"成功交易: {successful_trades_2}")
+            print(f"失败交易: {failed_trades_2}")
+            print(f"成功率: {success_rate_2:.2f}%")
+            print(f"总盈亏: {total_profit_loss_2:.2f}")
+            print(f"最终资本: {final_capital_2:.2f}")
+            print(f"总回报率: {total_return_2:.2f}%")
+            print(f"\n--- 逻辑对比 ---")
+            print(f"成功率差异: {success_rate_2 - success_rate_1:.2f}% (新逻辑 - 原逻辑)")
+            print(f"回报率差异: {total_return_2 - total_return_1:.2f}% (新逻辑 - 原逻辑)")
+        
+        return portfolio_summary
+    
+    def generate_csv_output(self, enhanced_pattern_dict):
+        """
+        生成CSV输出文件
+        """
+        if self.verbose_logging:
+            print("\n=== 步骤6: 生成CSV输出文件 ===")
+        
+        # 准备CSV数据(按指定顺序排列字段)
+        csv_data = []
+        
+        for contract_code, patterns in enhanced_pattern_dict.items():
+            for signal_date, pattern_info in patterns.items():
+                csv_data.append({
+                    # 按更新的字段顺序:contract_code, date, conclusion, conclusion2, body_direction, direction_consistency, price_direction, body_len, hatch_len, hatch/body, close, open, high, low, max_revenue, max_revenue_date, min_revenue, min_revenue_date, max_revenue2, max_revenue_date2, min_revenue2, min_revenue_date2, profit_loss, remaining_amount, trade_amount
+                    'contract_code': contract_code,
+                    'date': pattern_info['date'],
+                    'conclusion': pattern_info['conclusion'],
+                    'conclusion2': pattern_info['conclusion2'],
+                    'body_direction': pattern_info['body_direction'],
+                    'direction_consistency': pattern_info['direction_consistency'],
+                    'price_direction': pattern_info['price_direction'],  # 新增:基于收盘价与5K移动平均线关系的价格方向
+                    'body_len': pattern_info['body_len'],
+                    'hatch_len': pattern_info['hatch_len'],
+                    'hatch/body': pattern_info['hatch_body_ratio'],
+                    'close': pattern_info['close'],
+                    'open': pattern_info['open'],
+                    'high': pattern_info['high'],
+                    'low': pattern_info['low'],
+                    'max_revenue': pattern_info['max_revenue'],
+                    'max_revenue_date': pattern_info['max_revenue_date'],
+                    'min_revenue': pattern_info['min_revenue'],
+                    'min_revenue_date': pattern_info['min_revenue_date'],
+                    'max_revenue2': pattern_info['max_revenue2'],
+                    'max_revenue_date2': pattern_info['max_revenue_date2'],
+                    'min_revenue2': pattern_info['min_revenue2'],
+                    'min_revenue_date2': pattern_info['min_revenue_date2'],
+                    'profit_loss': pattern_info['profit_loss'],
+                    'remaining_amount': pattern_info['remaining_amount'],
+                    'trade_amount': pattern_info['trade_amount']
+                })
+        
+        if csv_data:
+            # 创建DataFrame并保存为CSV,严格按照用户指定的列顺序
+            df = pd.DataFrame(csv_data)
+            
+            # 定义更新的确切列顺序(包含新的price_direction字段)
+            column_order = [
+                'contract_code', 'date', 'conclusion', 'conclusion2', 'body_direction', 
+                'direction_consistency', 'price_direction', 'body_len', 'hatch_len', 'hatch/body', 
+                'close', 'open', 'high', 'low', 'max_revenue', 'max_revenue_date', 
+                'min_revenue', 'min_revenue_date', 'max_revenue2', 'max_revenue_date2', 
+                'min_revenue2', 'min_revenue_date2', 'profit_loss', 'remaining_amount', 'trade_amount'
+            ]
+            
+            # 重新排列DataFrame的列顺序
+            df = df[column_order]
+            
+            timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
+            filename = f'future_candlestick_hatch_analysis_{timestamp}.csv'
+            df.to_csv(filename, index=False, encoding=self.output_encoding)
+            
+            if self.verbose_logging:
+                print(f"分析结果已保存至: {filename}")
+                print(f"总计 {len(csv_data)} 条记录")
+            
+            return filename
+        else:
+            if self.verbose_logging:
+                print("没有数据可以输出")
+            return None
+    
+    def run_complete_analysis(self):
+        """执行完整的分析流程"""
+        if self.verbose_logging:
+            print("开始执行期货烛台影线形态分析")
+            print("=" * 60)
+        
+        try:
+            # 步骤1: 构建合约数据结构
+            contract_structure = self.build_contract_structure()
+            if not contract_structure:
+                if self.verbose_logging:
+                    print("未获取到有效合约,分析终止")
+                return None
+            
+            # 步骤2: 收集扩展数据
+            extended_data = self.collect_extended_data()
+            if not extended_data:
+                if self.verbose_logging:
+                    print("未获取到有效数据,分析终止")
+                return None
+            
+            # 步骤3: 计算实体长度阈值
+            threshold_data = self.calculate_body_thresholds(extended_data)
+            if not threshold_data:
+                if self.verbose_logging:
+                    print("未能计算有效阈值,分析终止")
+                return None
+            
+            # 步骤4: 识别影线形态
+            pattern_dict = self.identify_hatch_patterns(threshold_data)
+            if not pattern_dict:
+                if self.verbose_logging:
+                    print("未检测到有效影线形态,分析终止")
+                return None
+            
+            # 步骤5: 分析未来表现
+            enhanced_pattern_dict = self.analyze_future_performance(threshold_data, pattern_dict)
+            
+            # 步骤6: 生成CSV输出
+            output_filename = self.generate_csv_output(enhanced_pattern_dict)
+            
+            # 步骤7: 计算投资组合汇总
+            portfolio_summary = self.calculate_portfolio_summary()
+            
+            # 分析汇总
+            total_contracts = len(contract_structure)
+            valid_contracts = len(threshold_data)
+            total_patterns = sum(len(patterns) for patterns in enhanced_pattern_dict.values())
+            
+            # 计算模式有效性指标
+            effective_patterns = 0
+            for contract_patterns in enhanced_pattern_dict.values():
+                for pattern_info in contract_patterns.values():
+                    if pattern_info.get('conclusion') == 'true':
+                        effective_patterns += 1
+            
+            effective_rate = (effective_patterns / total_patterns * 100) if total_patterns > 0 else 0
+            
+            if self.verbose_logging:
+                print("\n" + "=" * 60)
+                print("分析完成汇总:")
+                print(f"总合约数: {total_contracts}")
+                print(f"有效合约数: {valid_contracts}")
+                print(f"检测形态数: {total_patterns}")
+                print(f"有效形态数: {effective_patterns}")
+                print(f"有效率: {effective_rate:.1f}%")
+                print(f"输出文件: {output_filename}")
+            
+            return {
+                'contract_structure': contract_structure,
+                'threshold_data': threshold_data,
+                'pattern_results': enhanced_pattern_dict,
+                'output_filename': output_filename,
+                'portfolio_summary': portfolio_summary,
+                'summary': {
+                    'total_contracts': total_contracts,
+                    'valid_contracts': valid_contracts,
+                    'total_patterns': total_patterns,
+                    'effective_patterns': effective_patterns,
+                    'effective_rate': effective_rate
+                }
+            }
+            
+        except Exception as e:
+            if self.verbose_logging:
+                print(f"分析过程中出现错误: {str(e)}")
+                import traceback
+                traceback.print_exc()
+            return None
+
+
+# =====================================================================================
+# 主程序入口
+# =====================================================================================
+
+def run_candlestick_hatch_analysis(config=None):
+    """运行期货烛台影线形态分析"""
+    if config is None:
+        config = CandlestickHatchConfig
+    
+    # 打印配置信息
+    config.print_config()
+    
+    # 创建分析器并运行
+    analyzer = FutureCandlestickHatchAnalyzer(config)
+    results = analyzer.run_complete_analysis()
+    return results
+
+# 执行分析
+if __name__ == "__main__":
+    print("期货烛台影线形态双向交易分析工具")
+    print("研究带有明显影线的K线对未来价格走势的预测能力")
+    print("双向交易逻辑对比测试:")
+    print("  原逻辑:上影线做空,下影线做多(与影线方向相反交易)")
+    print("  新逻辑:上影线做多,下影线做空(与影线方向相同交易)")
+    print("适用于聚宽在线研究平台")
+    
+    results = run_candlestick_hatch_analysis()
+    
+    if results:
+        print("\n✅ 分析执行成功!")
+        summary = results['summary']
+        portfolio = results['portfolio_summary']
+        print(f"📊 结果摘要:")
+        print(f"   - 分析合约数: {summary['total_contracts']}")
+        print(f"   - 有效数据合约: {summary['valid_contracts']}")
+        print(f"   - 检测形态总数: {summary['total_patterns']}")
+        print(f"   - 有效形态数: {summary['effective_patterns']}")
+        print(f"   - 有效率: {summary['effective_rate']:.1f}%")
+        print(f"   - 输出文件: {results['output_filename']}")
+        print(f"\n💰 双向交易逻辑对比分析:")
+        print(f"   - 总交易次数: {portfolio['total_trades']}")
+        print(f"   - 初始资本: {portfolio['initial_capital']:.2f}")
+        print(f"\n   📈 原逻辑(与影线方向相反交易):")
+        print(f"       成功率: {portfolio['logic1']['success_rate']:.2f}%")
+        print(f"       总盈亏: {portfolio['logic1']['total_profit_loss']:.2f}")
+        print(f"       总回报率: {portfolio['logic1']['total_return']:.2f}%")
+        print(f"\n   📉 新逻辑(与影线方向相同交易):")
+        print(f"       成功率: {portfolio['logic2']['success_rate']:.2f}%")
+        print(f"       总盈亏: {portfolio['logic2']['total_profit_loss']:.2f}")
+        print(f"       总回报率: {portfolio['logic2']['total_return']:.2f}%")
+        print(f"\n   🔍 逻辑对比:")
+        success_diff = portfolio['logic2']['success_rate'] - portfolio['logic1']['success_rate']
+        return_diff = portfolio['logic2']['total_return'] - portfolio['logic1']['total_return']
+        print(f"       成功率差异: {success_diff:+.2f}% (新逻辑 - 原逻辑)")
+        print(f"       回报率差异: {return_diff:+.2f}% (新逻辑 - 原逻辑)")
+    else:
+        print("\n❌ 分析执行失败,请检查错误信息")