Pārlūkot izejas kodu

调整了日志和合约到期移仓日期限制

maxfeng 2 mēneši atpakaļ
vecāks
revīzija
56148011b3
1 mainītis faili ar 54 papildinājumiem un 29 dzēšanām
  1. 54 29
      Lib/Options/deep_itm_bull_spread_strategy.py

+ 54 - 29
Lib/Options/deep_itm_bull_spread_strategy.py

@@ -34,8 +34,8 @@ class DeepITMBullSpreadStrategy:
             'min_days_to_expiry': 15,  # 最少开仓日期(距离到期日)
             'call_time_value_threshold': 0.015,  # 买购时间价值阈值(README中为0.015)
             'put_close_premium_threshold': 0.005,  # 卖购平仓权利金阈值(README中为50单位,转换为0.005)
-            'max_days_before_expiry': 7,  # 合约到期移仓日期最大(交易日)
-            'min_days_before_expiry': 2,  # 合约到期移仓日期最小(交易日)
+            'max_days_before_expiry': 3,  # 合约到期移仓日期最大(交易日)
+            'min_days_before_expiry': 1,  # 合约到期移仓日期最小(交易日)
             'add_position_threshold': {'510300': 0.2, '510050': 0.1, '159915': 0.15},  # 加仓窗口阈值
             'max_add_positions': 2,  # 加仓次数上限
             'max_profit_close_threshold': 0.83
@@ -62,12 +62,12 @@ class DeepITMBullSpreadStrategy:
         self.month_split = list(self.trade_days.resample('M', label='left').mean().index) + [pd.to_datetime(end_date)]
         
         # 调试输出:显示月份分割点
-        print(f"Month split 计算结果 ({len(self.month_split)}个分割点):")
+        # print(f"Month split 计算结果 ({len(self.month_split)}个分割点):")
         for i, split_date in enumerate(self.month_split):
-            print(f"  索引{i}: {split_date.strftime('%Y-%m-%d')}")
+            # print(f"  索引{i}: {split_date.strftime('%Y-%m-%d')}")
             if i < len(self.month_split) - 1:
                 next_split = self.month_split[i + 1]
-                print(f"    月份{i}覆盖范围: {split_date.strftime('%Y-%m-%d')} 到 {next_split.strftime('%Y-%m-%d')}")
+                # print(f"    月份{i}覆盖范围: {split_date.strftime('%Y-%m-%d')} 到 {next_split.strftime('%Y-%m-%d')}")
         
         # 用于存储前一日ETF价格,计算当日涨幅
         self.previous_etf_price = None
@@ -116,7 +116,7 @@ class DeepITMBullSpreadStrategy:
         query_date = trade_date.date() if hasattr(trade_date, 'date') else trade_date
         current_year = query_date.year
         current_month = query_date.month
-        print(f"  期权筛选调试: month_idx={month_idx}, query_date={query_date}, 当前年月={current_year}-{current_month:02d}")
+        # print(f"  期权筛选调试: month_idx={month_idx}, query_date={query_date}, 当前年月={current_year}-{current_month:02d}")
         
         # 计算目标月份
         if month_idx == 0:
@@ -145,7 +145,7 @@ class DeepITMBullSpreadStrategy:
             else:
                 end_date = pd.to_datetime(f'{target_year}-{target_month + 1:02d}-01').date() - pd.Timedelta(days=1)
             
-            print(f"  月份索引{month_idx}({target_year}-{target_month:02d})期权筛选范围: {start_date} 到 {end_date}")
+            # print(f"  月份索引{month_idx}({target_year}-{target_month:02d})期权筛选范围: {start_date} 到 {end_date}")
         
         q_contract_info = query(
             opt.OPT_CONTRACT_INFO.code,
@@ -175,17 +175,18 @@ class DeepITMBullSpreadStrategy:
     
     def get_monthly_option_candidates(self, trade_date, month_idx, silent=False):
         """获取指定月份的所有认购期权候选信息
-        返回: (contract_info, month_info) - contract_info为期权列表,month_info为月份信息
+        返回: (contract_info, month_info, failure_reason) - contract_info为期权列表,month_info为月份信息,failure_reason为失败原因(成功时为None)
         """
-        if not silent:
-            print(f"{trade_date.strftime('%Y-%m-%d')} 获取月份索引 {month_idx} 的认购期权候选信息")
+        # if not silent:
+        #     print(f"{trade_date.strftime('%Y-%m-%d')} 获取月份索引 {month_idx} 的认购期权候选信息")
         
         # 获取期权合约信息
         contract_info = self.get_option_contracts(trade_date, month_idx, 'CO')
         
         if contract_info.empty:
+            failure_reason = "无可用认购期权合约"
             if not silent:
-                print(f"  月份索引 {month_idx} 无可用认购期权合约")
+                print(f"  月份索引 {month_idx} {failure_reason}")
                 # 增加调试信息,显示查询的时间范围
                 if month_idx < len(self.month_split) - 1:
                     start_date = self.month_split[month_idx].date() if hasattr(self.month_split[month_idx], 'date') else self.month_split[month_idx]
@@ -194,7 +195,7 @@ class DeepITMBullSpreadStrategy:
                     print(f"    查询日期: {trade_date}")
                     print(f"    标的代码: {self.get_underlying_code()}")
                     print(f"    month_split总长度: {len(self.month_split)}")
-            return None, None
+            return None, None, failure_reason
         
         # 获取月份信息
         underlying_code = self.get_underlying_code()
@@ -204,8 +205,13 @@ class DeepITMBullSpreadStrategy:
         query_date = trade_date.date() if hasattr(trade_date, 'date') else trade_date
         candidates = []
         
-        if not silent:
-            print(f"  查询到 {len(contract_info)} 个认购期权合约,开始获取价格信息:")
+        # 统计失败原因
+        expiry_rejected_count = 0
+        price_missing_count = 0
+        price_error_count = 0
+        
+        # if not silent:
+        #     print(f"  查询到 {len(contract_info)} 个认购期权合约,开始获取价格信息:")
         
         for idx, contract in contract_info.iterrows():
             # 检查到期日
@@ -214,6 +220,7 @@ class DeepITMBullSpreadStrategy:
             days_to_expiry = len(get_trade_days(trade_date_obj, expiry_date.date())) - 1
             
             if days_to_expiry < min_days_to_expiry:
+                expiry_rejected_count += 1
                 # if not silent:
                 #     print(f"    行权价 {contract['exercise_price']:.3f}: 到期时间不足 ({days_to_expiry} < {min_days_to_expiry})")
                 continue
@@ -226,6 +233,7 @@ class DeepITMBullSpreadStrategy:
                 )
                 price_result = opt.run_query(q_price)
                 if price_result.empty:
+                    price_missing_count += 1
                     if not silent:
                         print(f"    行权价 {contract['exercise_price']:.3f}: 无价格数据")
                     continue
@@ -246,14 +254,30 @@ class DeepITMBullSpreadStrategy:
                 #     print(f"    行权价 {contract['exercise_price']:.3f}: 期权价格 {option_price:.4f}, 剩余天数 {days_to_expiry}")
                     
             except Exception as e:
+                price_error_count += 1
                 if not silent:
                     print(f"    行权价 {contract['exercise_price']:.3f}: 价格查询失败 ({str(e)})")
                 continue
         
         if not candidates:
-            if not silent:
-                print(f"  月份索引 {month_idx} 无符合基本条件的期权候选")
-            return None, None
+            # 构建详细的失败原因
+            total_contracts = len(contract_info)
+            failure_parts = []
+            if expiry_rejected_count > 0:
+                failure_parts.append(f"到期时间不足{expiry_rejected_count}个")
+            if price_missing_count > 0:
+                failure_parts.append(f"无价格数据{price_missing_count}个")
+            if price_error_count > 0:
+                failure_parts.append(f"价格查询失败{price_error_count}个")
+            
+            if failure_parts:
+                failure_reason = f"共{total_contracts}个期权合约,但均不符合条件:" + "、".join(failure_parts)
+            else:
+                failure_reason = f"共{total_contracts}个期权合约,但均不符合基本条件"
+            
+            # if not silent:
+            #     print(f"  月份索引 {month_idx} 无符合基本条件的期权候选")
+            return None, None, failure_reason
         
         month_info = {
             'month_idx': month_idx,
@@ -266,7 +290,7 @@ class DeepITMBullSpreadStrategy:
             print(f"  成功获取 {len(candidates)} 个有效期权候选")
             # print(f"  月份索引 {month_idx} 的认购期权候选信息: {candidates}")
         
-        return candidates, month_info
+        return candidates, month_info, None
     
     def select_sell_call_from_candidates(self, candidates, etf_price, min_premium, silent=False):
         """从候选期权中选择卖购期权(虚值期权)
@@ -398,9 +422,9 @@ class DeepITMBullSpreadStrategy:
             print(f"  权利金阈值: {min_premium:.4f},时间价值阈值: {time_value_threshold:.4f}")
         
         # 1. 获取月份期权候选
-        candidates, month_info = self.get_monthly_option_candidates(trade_date, month_idx, silent)
+        candidates, month_info, failure_reason = self.get_monthly_option_candidates(trade_date, month_idx, silent)
         if not candidates:
-            reason = f"{month_type}无可用期权候选"
+            reason = f"{month_type}期权候选获取失败:{failure_reason}"
             if not silent:
                 print(f"  {month_type}牛差策略失败:{reason}")
             return None, None, reason
@@ -521,9 +545,6 @@ class DeepITMBullSpreadStrategy:
     def try_bull_spread_strategy(self, trade_date, etf_price, month_idx, position_type='main', silent=False, save_to_csv=True):
         """尝试牛差策略:深度实值买购+卖购期权"""
         
-        if not silent:
-            print(f"{trade_date.strftime('%Y-%m-%d')} 开始尝试牛差策略")
-        
         # 1. 先尝试当月牛差策略
         buy_call, sell_call, reason = self.try_bull_spread_for_month(trade_date, etf_price, month_idx, is_current_month=True, silent=silent)
         
@@ -588,6 +609,10 @@ class DeepITMBullSpreadStrategy:
             if trade_date >= month_date:
                 month_idx = i
         
+        # 先输出开始尝试的日志,确保顺序正确
+        if not silent:
+            print(f"{trade_date.strftime('%Y-%m-%d')} 开始尝试牛差策略")
+        
         # 尝试牛差策略(深度实值买购+卖购)
         result, reason = self.try_bull_spread_strategy(trade_date, etf_price, month_idx, position_type, silent, save_to_csv)
         if result is not None:
@@ -1239,9 +1264,9 @@ class StrategyConfig:
     def __init__(self):
         # ETF标的配置字典
         self.etf_symbols = {
-            '50ETF': '510050.XSHG'    # 上证50ETF
-            # '300ETF': '510300.XSHG',   # 沪深300ETF
-            # '创业板ETF': '159915.XSHE'    # 创业板ETF
+            '50ETF': '510050.XSHG',    # 上证50ETF
+            '300ETF': '510300.XSHG',   # 沪深300ETF
+            '创业板ETF': '159915.XSHE'    # 创业板ETF
         }
         
         # 时间范围配置
@@ -1892,8 +1917,8 @@ class MultiUnderlyingBullSpreadManager:
                             
                             print(f"  {etf_code}: 开仓主仓位({strategy_desc}),ETF价格: {etf_price:.4f}, 合约数量: {contract_size}张, {profit_desc}")
                             daily_has_activity = True
-                        else:
-                            print(f"  {etf_code}: 开仓失败,无法找到合适的期权组合,ETF价格: {etf_price:.4f}")
+                        # else:
+                        #     print(f"  {etf_code}: 开仓失败,无法找到合适的期权组合,ETF价格: {etf_price:.4f}")
                     
                     # 检查是否需要加仓
                     elif strategy.should_add_position(trade_date, etf_price):
@@ -1994,7 +2019,7 @@ def run_deep_itm_bull_spread_example():
     # 可以自定义配置
     config.time_config = {
         'start_date': '2025-06-15',
-        'end_date': '2025-08-15'
+        'end_date': '2025-08-25'
     }
     
     config.capital_config.update({