diagnose_strategy.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 诊断脚本 - 检查交易策略的bug
  5. """
  6. import pandas as pd
  7. import numpy as np
  8. import sys
  9. sys.path.insert(0, '/root/.openclaw/workspace/cat-fly')
  10. sys.path.insert(0, '/root/.openclaw/workspace/quant')
  11. def diagnose_strategy():
  12. """诊断策略问题"""
  13. print("="*60)
  14. print("🔍 策略诊断报告")
  15. print("="*60)
  16. # 加载数据
  17. df = pd.read_csv('/root/.openclaw/workspace/quant/cyb50_baostock.csv')
  18. df['date'] = pd.to_datetime(df['date'])
  19. df = df.set_index('date').sort_index()
  20. for col in ['open', 'high', 'low', 'close', 'volume']:
  21. df[col] = pd.to_numeric(df[col], errors='coerce')
  22. # 计算指标
  23. df['ma10'] = df['close'].rolling(window=10).mean()
  24. df['ma30'] = df['close'].rolling(window=30).mean()
  25. df['high_20'] = df['high'].rolling(window=20).max()
  26. df['low_20'] = df['low'].rolling(window=20).min()
  27. df['ret_10'] = df['close'].pct_change(periods=10)
  28. # 买入条件
  29. buy_cond = (
  30. (df['close'] > df['ma10']) &
  31. (df['ma10'] > df['ma30']) &
  32. (df['close'] >= df['high_20'] * 0.995) &
  33. (df['ret_10'] > 0.02)
  34. )
  35. # 卖出条件
  36. sell_cond = (
  37. (df['close'] < df['ma30']) |
  38. (df['close'] <= df['low_20'] * 1.005)
  39. )
  40. print("\n📊 信号统计:")
  41. print(f" 买入信号天数: {buy_cond.sum()}")
  42. print(f" 卖出信号天数: {sell_cond.sum()}")
  43. # 检查同一天买卖冲突
  44. both_cond = buy_cond & sell_cond
  45. print(f" ⚠️ 同一天买卖冲突: {both_cond.sum()} 天")
  46. if both_cond.sum() > 0:
  47. print("\n 冲突日期示例:")
  48. for date in df[both_cond].index[:5]:
  49. print(f" {date.date()}: 买={buy_cond.loc[date]}, 卖={sell_cond.loc[date]}")
  50. # 模拟回测,检查交易
  51. print("\n📈 交易明细检查:")
  52. df['signal'] = 0
  53. df.loc[buy_cond, 'signal'] = 1
  54. df.loc[sell_cond, 'signal'] = -1
  55. position = 0
  56. entry_price = 0
  57. peak_price = 0
  58. capital = 1000000
  59. trades = []
  60. issues = []
  61. for i in range(30, len(df)):
  62. date = df.index[i]
  63. price = df['close'].iloc[i]
  64. signal = df['signal'].iloc[i]
  65. # 检查移动止损
  66. stop_loss_triggered = False
  67. if position > 0:
  68. if price > peak_price:
  69. peak_price = price
  70. if price < peak_price * 0.90:
  71. signal = -1
  72. stop_loss_triggered = True
  73. # 买入
  74. if signal == 1 and position == 0:
  75. position = 1
  76. entry_price = price
  77. peak_price = price
  78. trades.append({
  79. 'date': date,
  80. 'action': 'BUY',
  81. 'price': price,
  82. 'capital': capital,
  83. 'stop_loss': peak_price * 0.90
  84. })
  85. # 卖出
  86. elif signal == -1 and position == 1:
  87. pnl = (price / entry_price - 1) * capital
  88. capital += pnl
  89. exit_reason = '止损' if stop_loss_triggered else '信号'
  90. hold_days = (date - trades[-1]['date']).days if trades else 0
  91. trades.append({
  92. 'date': date,
  93. 'action': 'SELL',
  94. 'price': price,
  95. 'capital': capital,
  96. 'pnl': pnl,
  97. 'return_pct': (price / entry_price - 1) * 100,
  98. 'reason': exit_reason,
  99. 'hold_days': hold_days
  100. })
  101. # 检查问题
  102. if hold_days == 0:
  103. issues.append(f"⚠️ {date.date()}: 同日买卖 (T+0交易)")
  104. elif hold_days == 1:
  105. issues.append(f"⚠️ {date.date()}: 隔日交易 (T+1卖出)")
  106. position = 0
  107. print(f"\n 总交易次数: {len([t for t in trades if t['action'] == 'SELL'])}")
  108. print(f" 发现问题: {len(issues)}")
  109. if issues:
  110. print("\n 问题列表:")
  111. for issue in issues[:10]:
  112. print(f" {issue}")
  113. # 检查连续信号
  114. print("\n🔍 连续信号检查:")
  115. signal_changes = df['signal'].diff().abs()
  116. consecutive_days = 0
  117. max_consecutive = 0
  118. for i in range(30, len(df)):
  119. if df['signal'].iloc[i] == 1:
  120. consecutive_days += 1
  121. max_consecutive = max(max_consecutive, consecutive_days)
  122. else:
  123. consecutive_days = 0
  124. print(f" 最大连续买入信号: {max_consecutive} 天")
  125. # 显示最近5次交易
  126. print("\n📋 最近5次完整交易:")
  127. complete_trades = []
  128. for i in range(0, len(trades)-1, 2):
  129. if i+1 < len(trades):
  130. buy = trades[i]
  131. sell = trades[i+1]
  132. complete_trades.append({
  133. 'entry': buy['date'].strftime('%Y-%m-%d'),
  134. 'exit': sell['date'].strftime('%Y-%m-%d'),
  135. 'entry_price': buy['price'],
  136. 'exit_price': sell['price'],
  137. 'return': sell['return_pct'],
  138. 'hold_days': sell['hold_days']
  139. })
  140. for t in complete_trades[-5:]:
  141. print(f" 买入: {t['entry']} @ {t['entry_price']:.2f}")
  142. print(f" 卖出: {t['exit']} @ {t['exit_price']:.2f}")
  143. print(f" 收益: {t['return']:+.2f}%, 持仓: {t['hold_days']}天")
  144. print()
  145. print("="*60)
  146. print(f"最终资产: {capital:,.0f}元, 收益率: {(capital/1000000-1)*100:+.2f}%")
  147. print("="*60)
  148. if __name__ == "__main__":
  149. diagnose_strategy()