Răsfoiți Sursa

更新多均线穿越突破策略,添加跟踪均线止损机制及相关配置,完善文档说明,增加跳空检查功能,优化交易逻辑

maxfeng 4 luni în urmă
părinte
comite
7e8f0aeda3

+ 530 - 147
Lib/future/MultiMABreakoutStrategy_v001.py

@@ -44,42 +44,107 @@ def initialize(context):
     g.max_ma_crosses = 4  # 最大允许的均线交叉数量
     g.ma_cross_check_days = 10  # 检查均线交叉的天数
     
-    # 定义默认的保证金比例
-    g.margin_rates = {
-        'long': {'A': 0.07, 'AG': 0.04, 'AL': 0.05, 'AO': 0.05, 'AP': 0.08, 'AU': 0.04, 'B': 0.05,
-        'BC': 0.13, 'BR': 0.07, 'BU': 0.04, 'C': 0.07, 'CF': 0.05, 'CJ': 0.07, 'CS': 0.07,
-        'CU': 0.05, 'CY': 0.05, 'EB': 0.12, 'EC': 0.12, 'EG': 0.05, 'FG': 0.05, 'FU': 0.08,
-        'HC': 0.04, 'I': 0.1, 'J': 0.22, 'JD': 0.08, 'JM': 0.22, 
-        'L': 0.07, 'LC': 0.05, 'LH': 0.1, 'LR': 0.05, 'LU': 0.15, 'M': 0.07, 'MA': 0.05, 'NI': 0.05, 'NR': 0.13, 'OI': 0.05,
-        'P': 0.05, 'PB': 0.05, 'PF': 0.1, 'PG': 0.05, 'PK': 0.05,
-        'PP': 0.07, 'RB': 0.05, 'RI': 0.05, 'RM': 0.05, 'RU': 0.05,
-        'SA': 0.05, 'SC': 0.12, 'SF': 0.05, 'SH': 0.05, 'SI': 0.13, 'SM': 0.05, 'SN': 0.05, 'SP': 0.1, 'SR': 0.05,
-        'SS': 0.05, 'TA': 0.05, 'UR': 0.09, 'V': 0.07,
-        'Y': 0.05, 'ZC': 0.05, 'ZN': 0.05}, 
-        'short': {'A': 0.07, 'AG': 0.04, 'AL': 0.05, 'AO': 0.05, 'AP': 0.08, 'AU': 0.04, 'B': 0.05,
-        'BC': 0.13, 'BR': 0.07, 'BU': 0.04, 'C': 0.07, 'CF': 0.05, 'CJ': 0.07, 'CS': 0.07,
-        'CU': 0.05, 'CY': 0.05, 'EB': 0.12, 'EC': 0.12, 'EG': 0.05, 'FG': 0.05, 'FU': 0.08,
-        'HC': 0.04, 'I': 0.1, 'J': 0.22, 'JD': 0.08, 'JM': 0.22, 
-        'L': 0.07, 'LC': 0.05, 'LH': 0.1, 'LR': 0.05, 'LU': 0.15, 'M': 0.07, 'MA': 0.05, 'NI': 0.05, 'NR': 0.13, 'OI': 0.05,
-        'P': 0.05, 'PB': 0.05, 'PF': 0.1, 'PG': 0.05, 'PK': 0.05,
-        'PP': 0.07, 'RB': 0.05, 'RI': 0.05, 'RM': 0.05, 'RU': 0.05,
-        'SA': 0.05, 'SC': 0.12, 'SF': 0.05, 'SH': 0.05, 'SI': 0.13, 'SM': 0.05, 'SN': 0.05, 'SP': 0.1, 'SR': 0.05,
-        'SS': 0.05, 'TA': 0.05, 'UR': 0.09, 'V': 0.07,
-        'Y': 0.05, 'ZC': 0.05, 'ZN': 0.05}
+    # 止损止盈策略参数
+    g.gap_ratio_threshold = 0.002  # 跳空比例:0.2%
+    g.market_close_times = ["14:55:00"]  # 盘尾时间
+    g.profit_thresholds = [5000, 15000]  # 价格分区
+    g.stop_ratios = [0.0025, 0.005, 0.01, 0.02]  # 止盈止损比例:[0.25%, 0.5%, 1%, 2%]
+    
+    # 期货品种完整配置字典
+    g.futures_config = {
+        # 贵金属
+        'AU': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 1000},
+        'AG': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 15},
+        
+        # 有色金属
+        'CU': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'AL': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'ZN': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'PB': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'NI': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 1},
+        'SN': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 1},
+        'SS': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        
+        # 黑色系
+        'RB': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'HC': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, '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},
+        'AO': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
+        '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},
+        'BC': {'has_night_session': True, 'margin_rate': {'long': 0.13, 'short': 0.13}, 'multiplier': 5},
+        
+        # 化工
+        '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},
+        'CY': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'SH': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 30},
+        'PX': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        
+        # 农产品
+        '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},
+        '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},
+        'PR': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
+        'AD': {'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},
+        'EC': {'has_night_session': False, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 50},
+        'SF': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'SM': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'UR': {'has_night_session': False, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 20},
+        'AP': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 10},
+        'CJ': {'has_night_session': False, 'margin_rate': {'long': 0.07, 'short': 0.07}, '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.08, 'short': 0.08}, 'multiplier': 5},
+        'LH': {'has_night_session': False, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 16},
+        'SI': {'has_night_session': False, 'margin_rate': {'long': 0.13, 'short': 0.13}, 'multiplier': 5},
+        'LC': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 1},
+        'PS': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
+        'LG': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5}
     }
     
-    g.multiplier = {
-        'A': 10, 'AG': 15, 'AL': 5, 'AO': 20, 'AP': 10, 'AU': 1000, 'B': 10,
-        'BC': 5, 'BR': 5, 'BU': 10, 'C': 10, 'CF': 5, 'CJ': 5, 'CS': 10,
-        'CU': 5, 'CY': 5, 'EB': 5, 'EC': 50, 'EG': 10, 'FG': 20, 'FU': 10,
-        'HC': 10, 'I': 100, 'J': 60, 'JD': 5, 'JM': 100, 
-        'L': 5, 'LC': 1, 'LH': 16, 'LR': 0.05, 'LU': 10, 'M': 10, 'MA': 10, 'NI': 1, 'NR': 10, 'OI': 10,
-        'P': 10, 'PB': 5, 'PF': 5, 'PG': 20, 'PK': 5,
-        'PP': 5, 'RB': 10, 'RI': 0.05, 'RM': 10, 'RU': 10,
-        'SA': 20, 'SC': 1000, 'SF': 5, 'SH': 30, 'SI': 5, 'SM': 5, 'SN': 1, 'SP': 10, 'SR': 10,
-        'SS': 5, 'TA': 5, 'UR': 20, 'V': 5,
-        'Y': 10, 'ZC': 0.05, 'ZN': 5
-    }
+    # 当前策略关注的标的列表(可以根据需要调整,为空则考虑所有品种)
+    g.strategy_focus_symbols = ['RM']
+    
+    # 如果关注列表为空,则使用所有配置的品种
+    if not g.strategy_focus_symbols:
+        g.strategy_focus_symbols = list(g.futures_config.keys())
+        log.info("策略关注品种列表为空,将考虑所有配置的品种")
+    
+    # 打印配置摘要
+    log.info(f"策略关注品种总数: {len(g.strategy_focus_symbols)}")
     
     # 交易记录和数据存储
     g.trade_history = {}
@@ -88,8 +153,9 @@ def initialize(context):
     g.minute_data_cache = {}  # 存储今日分钟数据缓存
     g.ma_data_cache = {}  # 存储均线数据缓存
     g.ma_cross_filtered_futures = {}  # 存储通过均线交叉检查的品种(每日缓存)
+    g.gap_check_results = {}  # 存储跳空检查结果(每日缓存)
     
-    # 保证金比例管理(g.margin_rates会根据实际交易校准)
+    # 保证金比例管理(g.futures_config中的保证金比例会根据实际交易校准)
     g.margin_rate_history = {}  # 保证金比例变化历史记录
     g.today_trades = []  # 当日交易记录
     
@@ -236,19 +302,23 @@ def task_1_get_tradable_futures(context):
     """任务1: 获取所有可交易品种(分白天和晚上)"""
     # log.info("执行任务1: 获取可交易品种")
     
-    # 夜盘品种
-    # potential_night_list = ['NI', 'CF', 'PF', 'Y', 'M', 'B', 'SN', 'RM', 'RB', 'HC', 'I', 'J', 'JM']
-    potential_night_list = ['PF', 'Y', 'SN']
-    # 日盘品种  
-    potential_day_list = ['PK']
-    # potential_day_list = ['JD', 'UR', 'AP', 'CJ', 'PK']
-    
     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 = []
+    
     if current_time in ('21', '22'):
-        potential_icon_list = potential_night_list
-        log.info(f"夜盘时间,可交易品种: {potential_night_list}")
+        # 夜盘时间:只考虑有夜盘的品种
+        for symbol in focus_symbols:
+            if get_futures_config(symbol, 'has_night_session', False):
+                potential_icon_list.append(symbol)
+        log.info(f"夜盘时间,可交易品种: {potential_icon_list}")
     else:
-        potential_icon_list = potential_day_list + potential_night_list
+        # 日盘时间:所有关注的品种都可以交易
+        potential_icon_list = focus_symbols[:]
         log.info(f"日盘时间,可交易品种: {potential_icon_list}")
     
     potential_future_list = []
@@ -349,14 +419,28 @@ def task_3_check_ma_crosses(context):
     return filtered_futures
 
 def task_4_update_realtime_data(context):
-    """任务4: 获取所有可交易品种今天所需的分钟数据作为今天数据,和2中的数据合并出最新的均线数据,并保存到内存中"""
-    # log.info("执行任务4: 更新实时数据和均线")
+    """任务4: 获取所有可交易品种和持仓品种今天所需的分钟数据作为今天数据,和2中的数据合并出最新的均线数据,并保存到内存中"""
+    log.info("执行任务4: 更新实时数据和均线")
     
-    if not hasattr(g, 'tradable_futures'):
+    # 收集需要更新数据的品种
+    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:
+        log.info("没有需要更新的品种")
         return
     
-    for future_code in g.tradable_futures:
-        log.info(f"任务4 future_code: {future_code}")
+    today_date = context.current_dt.date()
+    
+    for future_code in update_symbols:
+        # log.info(f"任务4 future_code: {future_code}")
         try:
             # 获取今日分钟数据
             minute_data = get_today_minute_data(context, future_code)
@@ -364,11 +448,35 @@ def task_4_update_realtime_data(context):
             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, 50, '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
+                        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
             
+            # 检查跳空(只在第一次获取今日数据时检查)
+            if future_code not in g.gap_check_results:
+                gap_result = check_gap_opening(historical_data, minute_data)
+                g.gap_check_results[future_code] = gap_result
+                if gap_result['has_gap']:
+                    log.info(f"{future_code} 检测到跳空开盘,跳空比例: {gap_result['gap_ratio']:.3%}")
+            
             # 合并数据并计算均线
             combined_data = combine_and_calculate_ma(historical_data, minute_data)
             if combined_data is not None:
@@ -379,7 +487,7 @@ def task_4_update_realtime_data(context):
             log.warning(f"更新{future_code}实时数据时出错: {str(e)}")
             continue
     
-    # log.info(f"实时数据更新完成,共更新 {len(g.ma_data_cache)} 个品种")
+    log.info(f"实时数据更新完成,共更新 {len(g.ma_data_cache)} 个品种(可交易: {len(g.tradable_futures) if hasattr(g, 'tradable_futures') else 0},持仓: {len(g.trade_history) if hasattr(g, 'trade_history') else 0})")
 
 def task_5_analyze_ma_crosses(context):
     """任务5: 根据交易品种的今天数据和均线数据,判断是否出现了上穿或者下穿的情况"""
@@ -473,7 +581,9 @@ def task_7_execute_trades(context, filtered_signals):
         if success:
             # 获取实际保证金(只有在开仓成功时才能获取)
             actual_margin = g.trade_history[symbol]['actual_margin']
-            log.info(f"成功开仓 {symbol} {direction} 金额: {order_value}, 实际保证金: {actual_margin:.0f}")
+            # 获取成交价格
+            actual_price = g.trade_history[symbol]['entry_price']
+            log.info(f"成功开仓 {symbol} {direction}, 成交价格: {actual_price:.2f}, 金额: {order_value}, 实际保证金: {actual_margin:.0f}")
         else:
             log.warning(f"开仓失败 {symbol} {direction}")
 
@@ -512,14 +622,7 @@ def task_9_check_stop_loss_profit(context):
 
 def check_has_night_session(underlying_symbol):
     """检查品种是否有夜盘"""
-    # 有夜盘的品种列表
-    night_session_symbols = {
-        'NI', 'CF', 'PF', 'Y', 'M', 'B', 'SN', 'RM', 'RB', 'HC', 'I', 'J', 'JM',
-        'A', 'AG', 'AL', 'AU', 'BU', 'C', 'CU', 'FU', 'L', 'P', 'PB', 'RU', 
-        'SC', 'SP', 'SS', 'TA', 'V', 'ZC', 'ZN', 'MA', 'SR', 'OI', 'CF', 'RM',
-        'FG', 'SA', 'UR', 'NR', 'LU', 'BC', 'EC', 'PP', 'EB', 'EG', 'PG'
-    }
-    return underlying_symbol in night_session_symbols
+    return get_futures_config(underlying_symbol, 'has_night_session', False)
 
 def get_today_minute_data(context, future_code):
     """获取今日分钟数据"""
@@ -540,12 +643,12 @@ def get_today_minute_data(context, future_code):
         if minute_data is None or len(minute_data) == 0:
             return None
         
-        # log.debug(f"原始分钟数据范围: {minute_data.index[0]} 到 {minute_data.index[-1]}")
+        log.debug(f"原始分钟数据范围: {minute_data.index[0]} 到 {minute_data.index[-1]}")
         
         # 提取所有日期(年月日维度)
         minute_data['date'] = minute_data.index.date
         unique_dates = sorted(minute_data['date'].unique())
-        # log.debug(f"数据包含的日期: {unique_dates}")
+        log.debug(f"数据包含的日期: {unique_dates}")
         
         if has_night_session:
             # 有夜盘的品种:需要找到前一交易日的21:00作为今日开盘起点
@@ -553,13 +656,13 @@ def get_today_minute_data(context, future_code):
             
             # 找到今天之前的最后一个交易日
             previous_trading_dates = [d for d in unique_dates if d < today_date]
-            # log.debug(f"夜盘标的 today_date: {today_date}, previous_trading_dates: {previous_trading_dates}")
+            log.debug(f"夜盘标的 today_date: {today_date}, previous_trading_dates: {previous_trading_dates}")
             if not previous_trading_dates:
-                # log.warning(f"找不到{future_code}的前一交易日数据")
+                log.warning(f"找不到{future_code}的前一交易日数据")
                 return minute_data
             
             previous_trading_date = max(previous_trading_dates)
-            # log.debug(f"前一交易日: {previous_trading_date}")
+            log.debug(f"前一交易日: {previous_trading_date}")
             
             # 找到前一交易日21:00:00的数据作为开盘起点
             previous_day_data = minute_data[minute_data['date'] == previous_trading_date]
@@ -569,10 +672,10 @@ def get_today_minute_data(context, future_code):
                 # 从前一交易日21:00开始的所有数据
                 start_time = night_21_data.index[0]  # 21:00:00的时间点
                 filtered_data = minute_data[minute_data.index >= start_time]
-                # log.debug(f"夜盘品种,从{start_time}开始,数据量: {len(filtered_data)}")
+                log.debug(f"夜盘品种,从{start_time}开始,数据量: {len(filtered_data)}")
                 return filtered_data.drop(columns=['date'])
             else:
-                # log.warning(f"找不到{future_code}前一交易日21:00的数据")
+                log.warning(f"找不到{future_code}前一交易日21:00的数据")
                 # 备选方案:使用今天9:00开始的数据
                 today_data = minute_data[minute_data['date'] == today_date]
                 day_9_data = today_data[today_data.index.hour >= 9]
@@ -589,10 +692,10 @@ def get_today_minute_data(context, future_code):
             day_9_data = today_data[today_data.index.hour >= 9]
             
             if len(day_9_data) > 0:
-                # log.debug(f"日盘品种,从今天9:00开始,数据量: {len(day_9_data)}")
+                log.debug(f"日盘品种,从今天9:00开始,数据量: {len(day_9_data)}")
                 return day_9_data.drop(columns=['date'])
             else:
-                # log.warning(f"找不到{future_code}今天9:00的数据")
+                log.warning(f"找不到{future_code}今天9:00的数据")
                 return today_data.drop(columns=['date']) if len(today_data) > 0 else minute_data.drop(columns=['date'])
         
     except Exception as e:
@@ -662,6 +765,40 @@ def aggregate_minute_to_daily(minute_data):
         log.warning(f"聚合分钟数据时出错: {str(e)}")
         return None
 
+def check_gap_opening(historical_data, minute_data):
+    """
+    检查开盘是否跳空
+    :param historical_data: 历史日线数据
+    :param minute_data: 今日分钟数据
+    :return: 跳空检查结果字典
+    """
+    try:
+        if historical_data is None or len(historical_data) == 0 or minute_data is None or len(minute_data) == 0:
+            return {'has_gap': False, 'gap_ratio': 0.0}
+        
+        # 获取前一交易日收盘价
+        previous_close = historical_data['close'].iloc[-1]
+        
+        # 获取今日开盘价
+        today_open = minute_data['open'].iloc[0]
+        
+        # 计算跳空比例
+        gap_ratio = abs(today_open - previous_close) / previous_close
+        
+        # 判断是否跳空
+        has_gap = gap_ratio >= g.gap_ratio_threshold
+        
+        return {
+            'has_gap': has_gap,
+            'gap_ratio': gap_ratio,
+            'previous_close': previous_close,
+            'today_open': today_open
+        }
+        
+    except Exception as e:
+        log.warning(f"检查跳空开盘时出错: {str(e)}")
+        return {'has_gap': False, 'gap_ratio': 0.0}
+
 ############################ 原有函数保持不变 ###################################
 
 def check_latest_multi_ma_cross(data, future_code):
@@ -672,8 +809,8 @@ def check_latest_multi_ma_cross(data, future_code):
     # 获取最新两天的数据
     today = data.iloc[-1]
     yesterday = data.iloc[-2]
-    # log.info(f"today: {today}")
-    # log.info(f"yesterday: {yesterday}")
+    log.debug(f"today: {today}")
+    log.debug(f"yesterday: {yesterday}")
     
     # 检查多均线穿越
     cross_result = check_multi_ma_cross_single_day(today)
@@ -717,38 +854,85 @@ def check_multi_ma_cross_single_day(row):
     if open_price == close_price:
         return None
     
-    crossed_mas = []
-    
-    # 上涨(阳线),检查上穿
-    if close_price > open_price:
+    # 1. 统计开盘价和均线的高低关系
+    open_above_count = 0  # 开盘价高于均线的数量
+    open_below_count = 0  # 开盘价低于均线的数量
+    open_above_mas = []   # 开盘价高于的均线列表
+    open_below_mas = []   # 开盘价低于的均线列表
+    
+    for ma_name, ma_value in ma_values:
+        if open_price > ma_value:
+            open_above_count += 1
+            open_above_mas.append((ma_name, ma_value))
+        elif open_price < ma_value:
+            open_below_count += 1
+            open_below_mas.append((ma_name, ma_value))
+    
+    # 2. 统计收盘价和均线的高低关系
+    close_above_count = 0  # 收盘价高于均线的数量
+    close_below_count = 0  # 收盘价低于均线的数量
+    close_above_mas = []   # 收盘价高于的均线列表
+    close_below_mas = []   # 收盘价低于的均线列表
+    
+    for ma_name, ma_value in ma_values:
+        if close_price > ma_value:
+            close_above_count += 1
+            close_above_mas.append((ma_name, ma_value))
+        elif close_price < ma_value:
+            close_below_count += 1
+            close_below_mas.append((ma_name, ma_value))
+    
+    # 3. 计算穿越情况
+    # 上穿:收盘价高于的数量比开盘价高于的数量增加了
+    upward_cross_count = close_above_count - open_above_count
+    # 下穿:收盘价低于的数量比开盘价低于的数量增加了
+    downward_cross_count = close_below_count - open_below_count
+    
+    log.debug(f"开盘价高于均线数量: {open_above_count}, 收盘价高于均线数量: {close_above_count}")
+    log.debug(f"开盘价低于均线数量: {open_below_count}, 收盘价低于均线数量: {close_below_count}")
+    log.debug(f"上穿数量: {upward_cross_count}, 下穿数量: {downward_cross_count}")
+    
+    # 4. 判断是否满足最少穿越条件并找到临界线
+    if upward_cross_count >= g.min_cross_mas:
+        # 上穿:找到被穿越的均线中值最大的一条作为临界线
+        # 被穿越的均线是那些开盘价低于但收盘价高于的均线
+        crossed_mas = []
+        crossed_ma_names = []
         for ma_name, ma_value in ma_values:
-            if open_price < ma_value and close_price > ma_value:
+            if open_price <= ma_value and close_price > ma_value:
                 crossed_mas.append((ma_name, ma_value))
+                crossed_ma_names.append(ma_name)
         
-        if len(crossed_mas) >= g.min_cross_mas:
-            # 找到最大的穿越线作为临界线
+        if len(crossed_mas) > 0:
+            # 找到值最大的被穿越均线
             critical_ma = max(crossed_mas, key=lambda x: x[1])
             return {
                 'direction': 'up',
                 'critical_ma_name': critical_ma[0],
                 'critical_ma_value': critical_ma[1],
-                'crossed_count': len(crossed_mas)
+                'crossed_count': upward_cross_count,
+                'crossed_ma_names': crossed_ma_names  # 添加被穿越的均线名称列表
             }
     
-    # 下跌(阴线),检查下穿        
-    elif open_price > close_price:
+    elif downward_cross_count >= g.min_cross_mas:
+        # 下穿:找到被穿越的均线中值最小的一条作为临界线
+        # 被穿越的均线是那些开盘价高于但收盘价低于的均线
+        crossed_mas = []
+        crossed_ma_names = []
         for ma_name, ma_value in ma_values:
-            if open_price > ma_value and close_price < ma_value:
+            if open_price >= ma_value and close_price < ma_value:
                 crossed_mas.append((ma_name, ma_value))
+                crossed_ma_names.append(ma_name)
         
-        if len(crossed_mas) >= g.min_cross_mas:
-            # 找到最小的穿越线作为临界线
+        if len(crossed_mas) > 0:
+            # 找到值最小的被穿越均线
             critical_ma = min(crossed_mas, key=lambda x: x[1])
             return {
                 'direction': 'down',
                 'critical_ma_name': critical_ma[0],
                 'critical_ma_value': critical_ma[1],
-                'crossed_count': len(crossed_mas)
+                'crossed_count': downward_cross_count,
+                'crossed_ma_names': crossed_ma_names  # 添加被穿越的均线名称列表
             }
     
     return None
@@ -804,7 +988,7 @@ def open_position(context, security, value, direction, signal):
         cash_before = context.portfolio.available_cash
         
         order = order_target_value(security, value, side=direction)
-        log.info(f"order: {order}")
+        log.debug(f"order: {order}")
         
         if order is not None and order.filled > 0:
             # 记录交易后的可用资金
@@ -819,7 +1003,7 @@ def open_position(context, security, value, direction, signal):
             
             # 计算实际保证金比例
             underlying_symbol = security.split('.')[0][:-4]
-            multiplier = g.multiplier.get(underlying_symbol, 10)
+            multiplier = get_multiplier(underlying_symbol)
             
             # 单笔保证金 = 资金变化 / 数量
             single_margin = cash_change / order_amount if order_amount > 0 else 0
@@ -829,7 +1013,7 @@ def open_position(context, security, value, direction, signal):
             actual_margin_rate = single_margin / contract_value if contract_value > 0 else 0
             
             # 校准保证金比例(只有变化大于1%时才更新)
-            current_rate = g.margin_rates.get(direction, {}).get(underlying_symbol, 0.10)
+            current_rate = get_margin_rate(underlying_symbol, direction)
             rate_change = abs(actual_margin_rate - current_rate) / current_rate if current_rate > 0 else 0
             
             if rate_change > 0.01:  # 变化大于1%
@@ -847,35 +1031,36 @@ def open_position(context, security, value, direction, signal):
                     'security': security
                 })
                 
-                # 直接更新默认保证金比例
-                g.margin_rates[direction][underlying_symbol] = actual_margin_rate
+                # 直接更新配置字典中的保证金比例
+                if underlying_symbol in g.futures_config:
+                    g.futures_config[underlying_symbol]['margin_rate'][direction] = actual_margin_rate
                 
-                log.info(f"保证金比例校准: {underlying_symbol}_{direction} {current_rate:.4f} -> {actual_margin_rate:.4f} (变化{rate_change*100:.1f}%)")
+                log.debug(f"保证金比例校准: {underlying_symbol}_{direction} {current_rate:.4f} -> {actual_margin_rate:.4f} (变化{rate_change*100:.1f}%)")
             
             # 记录当日交易
             g.today_trades.append({
-                'security': security,
-                'underlying_symbol': underlying_symbol,
-                'direction': direction,
-                'order_amount': order_amount,
-                'order_price': order_price,
-                'cash_change': cash_change,
-                'actual_margin_rate': actual_margin_rate,
-                'time': context.current_dt
+                'security': security, # 交易标的
+                'underlying_symbol': underlying_symbol, # 标的字母
+                'direction': direction, # 方向
+                'order_amount': order_amount, # 开仓数量
+                'order_price': order_price, # 开仓金额
+                'cash_change': cash_change, # 现金变化
+                'actual_margin_rate': actual_margin_rate, # 实际保证金率
+                'time': context.current_dt # 成交日期
             })
             
             # 记录交易信息
             g.trade_history[security] = {
-                'entry_price': order_price,
-                'position_value': value,
-                'actual_margin': cash_change,
-                'direction': direction, 
-                'entry_time': context.current_dt,
-                'signal_info': signal
+                'entry_price': order_price, # 成交价格
+                'position_value': value, # 开仓金额
+                'actual_margin': cash_change, # 实际保证金
+                'direction': direction, # 方向
+                'entry_time': context.current_dt, # 开仓时间
+                'signal_info': signal # 信号信息
             }
             
-            log.info(f"开仓成功 - 品种: {underlying_symbol}, 手数: {order_amount}, 订单价格: {order_price:.2f}")
-            log.info(f"资金变化: {cash_change:.0f}, 实际保证金比例: {actual_margin_rate:.4f}")
+            log.debug(f"开仓成功 - 品种: {underlying_symbol}, 手数: {order_amount}, 订单价格: {order_price:.2f}")
+            log.debug(f"资金变化: {cash_change:.0f}, 实际保证金比例: {actual_margin_rate:.4f}")
             return True
             
     except Exception as e:
@@ -923,50 +1108,249 @@ def check_stop_loss_profit(context, position):
         return False
     
     trade_info = g.trade_history[security]
-    entry_price = trade_info['entry_price']
-    current_price = position.price
-    direction = trade_info['direction']
-    
-    # 计算盈亏
-    if direction == 'long':
-        pnl_ratio = (current_price - entry_price) / entry_price
-    else:
-        pnl_ratio = (entry_price - current_price) / entry_price
     
-    # 止损条件
-    stop_loss_ratio = -0.03  # 3%止损
-    # 止盈条件
-    take_profit_ratio = 0.05  # 5%止盈
-    
-    if pnl_ratio <= stop_loss_ratio:
-        log.info(f"触发止损 {security} 盈亏比例: {pnl_ratio:.2%}")
-        close_position(context, security, direction)
-        return True
-    elif pnl_ratio >= take_profit_ratio:
-        log.info(f"触发止盈 {security} 盈亏比例: {pnl_ratio:.2%}")
-        close_position(context, security, direction) 
+    # 跟踪均线止损止盈
+    tracking_stop_result = check_tracking_ma_stop(context, security, position, trade_info)
+    if tracking_stop_result:
         return True
     
     return False
 
+def check_tracking_ma_stop(context, security, position, trade_info):
+    """
+    检查跟踪均线止损止盈
+    :param context: 上下文对象
+    :param security: 标的代码
+    :param position: 持仓对象
+    :param trade_info: 交易信息
+    :return: 是否触发止损止盈
+    """
+    try:
+        direction = trade_info['direction']
+        entry_price = trade_info['entry_price']
+        current_price = position.price
+        
+        # 获取最新的均线数据
+        if security not in g.ma_data_cache:
+            return False
+        
+        ma_data = g.ma_data_cache[security]
+        if len(ma_data) == 0:
+            return False
+        
+        latest_data = ma_data.iloc[-1]
+        
+        # 获取四条均线价格和今日最高最低价
+        ma5 = latest_data['MA5']
+        ma10 = latest_data['MA10']
+        ma20 = latest_data['MA20']
+        ma30 = latest_data['MA30']
+        today_high = latest_data['high']
+        today_low = latest_data['low']
+        
+        # 检查是否有NaN值
+        if pd.isna(ma5) or pd.isna(ma10) or pd.isna(ma20) or pd.isna(ma30):
+            return False
+        
+        # 获取开仓时被穿越的均线信息
+        signal_info = trade_info.get('signal_info', {})
+        crossed_ma_names = signal_info.get('crossed_ma_names', ['MA5', 'MA10', 'MA20', 'MA30'])
+        
+        # 根据方向确定止损均线,需要过滤掉不符合条件的均线
+        mas = [ma5, ma10, ma20, ma30]
+        ma_names = ['MA5', 'MA10', 'MA20', 'MA30']
+        
+        # 第一步:只考虑被穿越的均线
+        crossed_mas = []
+        crossed_ma_prices = []
+        for i, ma_name in enumerate(ma_names):
+            if ma_name in crossed_ma_names:
+                crossed_mas.append(ma_name)
+                crossed_ma_prices.append(mas[i])
+        
+        log.debug(f"开仓时被穿越的均线: {crossed_ma_names}")
+        
+        if direction == 'long':
+            # 多仓:在被穿越的均线中,选择价格低于今日最高价的均线
+            valid_mas = []
+            valid_ma_names = []
+            for i, ma_name in enumerate(crossed_mas):
+                ma_price = crossed_ma_prices[i]
+                if ma_price <= today_high:
+                    valid_mas.append(ma_price)
+                    valid_ma_names.append(ma_name)
+            
+            if not valid_mas:
+                # 如果被穿越的均线都高于今日最高价,使用被穿越均线中的最低价
+                if crossed_ma_prices:
+                    stop_ma_price = min(crossed_ma_prices)
+                    stop_ma_name = crossed_mas[crossed_ma_prices.index(stop_ma_price)]
+                    log.warning(f"多仓被穿越均线都高于今日最高价{today_high:.2f},使用被穿越均线中最低的")
+                else:
+                    # 如果没有被穿越均线信息,使用所有均线中的最低价
+                    stop_ma_price = min(mas)
+                    stop_ma_name = ma_names[mas.index(stop_ma_price)]
+                    log.warning(f"多仓无被穿越均线信息,使用所有均线中最低的")
+            else:
+                # 多仓跟踪符合条件的被穿越均线中价格最高的
+                stop_ma_price = max(valid_mas)
+                stop_ma_name = valid_ma_names[valid_mas.index(stop_ma_price)]
+        else:
+            # 空仓:在被穿越的均线中,选择价格高于今日最低价的均线
+            valid_mas = []
+            valid_ma_names = []
+            for i, ma_name in enumerate(crossed_mas):
+                ma_price = crossed_ma_prices[i]
+                if ma_price >= today_low:
+                    valid_mas.append(ma_price)
+                    valid_ma_names.append(ma_name)
+            
+            if not valid_mas:
+                # 如果被穿越的均线都低于今日最低价,使用被穿越均线中的最高价
+                if crossed_ma_prices:
+                    stop_ma_price = max(crossed_ma_prices)
+                    stop_ma_name = crossed_mas[crossed_ma_prices.index(stop_ma_price)]
+                    log.warning(f"空仓被穿越均线都低于今日最低价{today_low:.2f},使用被穿越均线中最高的")
+                else:
+                    # 如果没有被穿越均线信息,使用所有均线中的最高价
+                    stop_ma_price = max(mas)
+                    stop_ma_name = ma_names[mas.index(stop_ma_price)]
+                    log.warning(f"空仓无被穿越均线信息,使用所有均线中最高的")
+            else:
+                # 空仓跟踪符合条件的被穿越均线中价格最低的
+                stop_ma_price = min(valid_mas)
+                stop_ma_name = valid_ma_names[valid_mas.index(stop_ma_price)]
+        
+        log.debug(f"今日高低价: {today_high:.2f}/{today_low:.2f}, 被穿越均线: {crossed_ma_names}, 选择止损均线: {stop_ma_name}({stop_ma_price:.2f})")
+        log.debug(f"所有均线: MA5={ma5:.2f}, MA10={ma10:.2f}, MA20={ma20:.2f}, MA30={ma30:.2f}")
+        # 计算止损均线的收益水平
+        underlying_symbol = security.split('.')[0][:-4]
+        multiplier = get_multiplier(underlying_symbol)
+        position_value = abs(position.total_amount) * stop_ma_price * multiplier
+        
+        if direction == 'long':
+            ma_profit = (stop_ma_price - entry_price) * multiplier * abs(position.total_amount)
+        else:
+            ma_profit = (entry_price - stop_ma_price) * multiplier * abs(position.total_amount)
+        
+        # 获取跳空信息
+        gap_info = g.gap_check_results.get(security, {'has_gap': False})
+        has_gap = gap_info['has_gap']
+        
+        # 判断是否盘尾
+        current_time = context.current_dt.strftime('%H:%M:%S')
+        is_market_close = current_time in g.market_close_times
+        
+        # 确定止损比例
+        stop_ratio = get_tracking_stop_ratio(ma_profit, has_gap, is_market_close)
+        
+        # 计算止损价格
+        if direction == 'long':
+            stop_price = stop_ma_price * (1 - stop_ratio)
+            should_stop = current_price <= stop_price
+        else:
+            stop_price = stop_ma_price * (1 + stop_ratio)
+            should_stop = current_price >= stop_price
+        
+        if should_stop:
+            log.info(f"触发跟踪均线止损 {security} {direction}")
+            log.info(f"止损均线价格: {stop_ma_price:.2f}, 方向: {direction}, 收益: {ma_profit:.0f}, 跳空: {has_gap}, 盘尾: {is_market_close}")
+            log.info(f"止损比例: {stop_ratio:.4f}, 止损价格: {stop_price:.2f}, 当前价格: {current_price:.2f}")
+            close_position(context, security, direction)
+            return True
+        
+        return False
+        
+    except Exception as e:
+        log.warning(f"检查跟踪均线止损时出错 {security}: {str(e)}")
+        return False
+
+def get_tracking_stop_ratio(ma_profit, has_gap, is_market_close):
+    """
+    根据均线收益、跳空情况、盘中盘尾确定止损比例
+    :param ma_profit: 止损均线的收益水平
+    :param has_gap: 是否跳空
+    :param is_market_close: 是否盘尾
+    :return: 止损比例
+    """
+    # 根据收益水平分区
+    if ma_profit < g.profit_thresholds[0]:  # 收益 < 5000
+        if is_market_close:  # 盘尾
+            if has_gap:
+                return g.stop_ratios[0]  # 0.25%
+            else:
+                return g.stop_ratios[1]  # 0.5%
+        else:  # 盘中
+            if has_gap:
+                return g.stop_ratios[1]  # 0.5%
+            else:
+                return g.stop_ratios[2]  # 1%
+    elif ma_profit < g.profit_thresholds[1]:  # 5000 <= 收益 < 15000
+        if is_market_close:  # 盘尾
+            return g.stop_ratios[1]  # 0.5%
+        else:  # 盘中
+            return g.stop_ratios[2]  # 1%
+    else:  # 收益 >= 15000
+        if is_market_close:  # 盘尾
+            return g.stop_ratios[2]  # 1%
+        else:  # 盘中
+            return g.stop_ratios[3]  # 2%
+
 ############################ 辅助函数 ###################################
 
+def get_futures_config(underlying_symbol, config_key=None, default_value=None):
+    """
+    获取期货品种配置信息的辅助函数
+    :param underlying_symbol: 品种符号,如 'AU', 'PF' 等
+    :param config_key: 配置键,如 'multiplier', 'has_night_session' 等
+    :param default_value: 默认值
+    :return: 配置值或整个配置字典
+    """
+    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):
+    """
+    获取保证金比例的辅助函数
+    :param underlying_symbol: 品种符号
+    :param direction: 方向 'long' 或 'short'
+    :param default_rate: 默认保证金比例
+    :return: 保证金比例
+    """
+    return g.futures_config.get(underlying_symbol, {}).get('margin_rate', {}).get(direction, default_rate)
+
+def get_multiplier(underlying_symbol, default_multiplier=10):
+    """
+    获取合约乘数的辅助函数
+    :param underlying_symbol: 品种符号
+    :param default_multiplier: 默认合约乘数
+    :return: 合约乘数
+    """
+    return g.futures_config.get(underlying_symbol, {}).get('multiplier', default_multiplier)
+
 def calculate_order_value(context, security, direction):
     """计算开仓金额"""
     current_price = get_current_data()[security].last_price
     underlying_symbol = security.split('.')[0][:-4]
     
     # 使用保证金比例(已经过校准)
-    margin_rate = g.margin_rates.get(direction, {}).get(underlying_symbol, 0.10)
+    margin_rate = get_margin_rate(underlying_symbol, direction)
     
-    multiplier = g.multiplier.get(underlying_symbol, 10)
+    multiplier = get_multiplier(underlying_symbol)
     
     # 计算单手保证金
     single_hand_margin = current_price * multiplier * margin_rate
     
     # 还要考虑可用资金限制
     available_cash = context.portfolio.available_cash * g.usage_percentage
-    log.info(f"可用资金: {available_cash:.0f}")
+    log.debug(f"可用资金: {available_cash:.0f}")
     
     # 根据单个标的最大持仓保证金限制计算开仓数量
     max_margin = g.max_margin_per_position
@@ -986,7 +1370,7 @@ def calculate_order_value(context, security, direction):
         # 计算订单金额
         order_value = actual_margin
         
-        log.info(f"单手保证金: {single_hand_margin:.0f}, 最大手数(保证金限制): {max_hands}, 最大手数(资金限制): {max_hands_by_cash}, 实际开仓手数: {actual_hands}, 实际保证金: {actual_margin:.0f}")
+        log.debug(f"单手保证金: {single_hand_margin:.0f}, 最大手数(保证金限制): {max_hands}, 最大手数(资金限制): {max_hands_by_cash}, 实际开仓手数: {actual_hands}, 实际保证金: {actual_margin:.0f}")
     else:
         # 如果单手保证金超过最大限制,默认开仓1手
         actual_hands = 1
@@ -995,9 +1379,9 @@ def calculate_order_value(context, security, direction):
         # 计算订单金额
         order_value = actual_margin
         
-        log.info(f"单手保证金: {single_hand_margin:.0f} 超过最大限制: {max_margin}, 默认开仓1手, 实际保证金: {actual_margin:.0f}")
+        log.debug(f"单手保证金: {single_hand_margin:.0f} 超过最大限制: {max_margin}, 默认开仓1手, 实际保证金: {actual_margin:.0f}")
     
-    log.info(f"计算结果 - 品种: {underlying_symbol}, 开仓手数: {actual_hands}, 订单金额: {order_value:.0f}, 实际保证金: {actual_margin:.0f}")
+    log.debug(f"计算结果 - 品种: {underlying_symbol}, 开仓手数: {actual_hands}, 订单金额: {order_value:.0f}, 实际保证金: {actual_margin:.0f}")
     
     return order_value
 
@@ -1006,8 +1390,8 @@ def calculate_required_margin(context, symbol):
     current_price = get_current_data()[symbol].last_price
     underlying_symbol = symbol.split('.')[0][:-4]
     
-    margin_rate = g.margin_rates.get('long', {}).get(underlying_symbol, 0.10)
-    multiplier = g.multiplier.get(underlying_symbol, 10)
+    margin_rate = get_margin_rate(underlying_symbol, 'long')
+    multiplier = get_multiplier(underlying_symbol)
     
     return current_price * multiplier * margin_rate
 
@@ -1033,7 +1417,6 @@ def after_market_close(context):
         # 清空当日交易记录
         g.today_trades = []
     
-    log.info('A day ends')
     log.info('##############################################################')
 
 def print_daily_trading_summary(context):
@@ -1041,34 +1424,34 @@ def print_daily_trading_summary(context):
     if not g.today_trades:
         return
     
-    log.info("\n=== 当日交易汇总 ===")
+    log.debug("\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']}手 "
+            log.debug(f"开仓 {trade['underlying_symbol']} {trade['direction']} {trade['order_amount']}手 "
                   f"价格:{trade['order_price']:.2f} 保证金:{trade['cash_change']:.0f} "
                   f"比例:{trade['actual_margin_rate']:.4f}")
             total_margin += trade['cash_change']
         else:  # 平仓
-            log.info(f"平仓 {trade['underlying_symbol']} {trade['direction']} {abs(trade['order_amount'])}手 "
+            log.debug(f"平仓 {trade['underlying_symbol']} {trade['direction']} {abs(trade['order_amount'])}手 "
                   f"价格:{trade['order_price']:.2f}")
     
-    log.info(f"当日保证金占用: {total_margin:.0f}")
-    log.info("==================\n")
+    log.debug(f"当日保证金占用: {total_margin:.0f}")
+    log.debug("==================\n")
 
 def print_margin_rate_changes(context):
     """打印保证金比例变化记录"""
     if not g.margin_rate_history:
         return
     
-    log.info("\n=== 保证金比例变化记录 ===")
+    log.debug("\n=== 保证金比例变化记录 ===")
     for key, history in g.margin_rate_history.items():
-        log.info(f"{key}:")
+        log.debug(f"{key}:")
         for record in history:
-            log.info(f"  {record['date']} {record['time']}: {record['old_rate']:.4f} -> {record['new_rate']:.4f} "
+            log.debug(f"  {record['date']} {record['time']}: {record['old_rate']:.4f} -> {record['new_rate']:.4f} "
                   f"(变化{record['change_pct']:.1f}%)")
-    log.info("========================\n")
+    log.debug("========================\n")
 
 ########################## 自动移仓换月函数 #################################
 def position_auto_switch(context,pindex=0,switch_func=None, callback=None):

+ 27 - 7
Lib/future/README.md

@@ -194,7 +194,7 @@
   - 滑点:按照品种特性设置
   - 保证金:按照交易所要求设置
 
-## 穿越均线趋势交易策略
+## 多均线穿越突破策略 v001
 
 ### 核心思路
 该策略基于K线实体在同一天内穿越多条均线来识别建仓时机。策略同时结合止损和移仓换月机制来控制风险。
@@ -273,12 +273,32 @@
 #### 4. 风险控制
 
 ##### 4.1 止损机制
-- 固定止损:
-  - 初始止损额度:**initial_loss_limit**(默认-4000)
-  - 每日调整:**loss_increment_per_day**(默认200)
-- 均线止损:
-  - 监控均线偏离度
-  - 突破重要均线立即止损
+- **跟踪均线止损**:
+  - **多仓**:跟踪价格最高的均线(MA5、MA10、MA20、MA30中的最高值)
+  - **空仓**:跟踪价格最低的均线(MA5、MA10、MA20、MA30中的最低值)
+  - **跳空检查**:当天开盘价与前一交易日收盘价差异超过0.2%即为跳空
+  - **时间分类**:14:55:00为盘尾,其他时间为盘中
+  - **动态止损比例**:根据收益水平、跳空情况、交易时段确定0.25%-2%的止损比例
+
+##### 4.1.1 详细止损规则
+| 收益水平 | 跳空情况 | 时段 | 止损比例 | 说明 |
+|---------|---------|------|----------|------|
+| < 5000 | 无跳空 | 盘中 | 1.0% | 基础止损 |
+| < 5000 | 有跳空 | 盘中 | 0.5% | 跳空风险较大,收紧止损 |
+| 5000-15000 | 不限 | 盘中 | 1.0% | 中等收益,标准止损 |
+| ≥ 15000 | 不限 | 盘中 | 2.0% | 高收益,放宽止损 |
+| < 5000 | 有跳空 | 盘尾 | 0.25% | 最严格止损 |
+| < 5000 | 无跳空 | 盘尾 | 0.5% | 盘尾收紧止损 |
+| 5000-15000 | 不限 | 盘尾 | 0.5% | 中等收益盘尾止损 |
+| ≥ 15000 | 不限 | 盘尾 | 1.0% | 高收益盘尾止损 |
+
+##### 4.1.2 配置参数
+```python
+g.gap_ratio_threshold = 0.002      # 跳空比例:0.2%
+g.market_close_times = ["14:55:00"] # 盘尾时间
+g.profit_thresholds = [5000, 15000] # 价格分区
+g.stop_ratios = [0.0025, 0.005, 0.01, 0.02] # 止损比例
+```
 
 ##### 4.2 特殊情况处理
 - 涨跌停板:

BIN
Lib/future/__pycache__/MultiMABreakoutStrategy_v001.cpython-38.pyc