advanced_optimization_v2.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 创业板50 T+1 高级优化策略V2
  5. 探索: 移动止损、分批止盈、多因子评分、市场环境自适应
  6. """
  7. import pandas as pd
  8. import numpy as np
  9. from datetime import datetime, timedelta
  10. import warnings
  11. from itertools import product
  12. warnings.filterwarnings('ignore')
  13. # 导入原策略组件
  14. from cyb50_30min_dual_direction import (
  15. ConfigManager,
  16. IntradayDataFetcher,
  17. DualDirectionSignalGenerator,
  18. DualDirectionExecutor
  19. )
  20. from t1_converter import simulate_t1_trades
  21. def load_local_data(csv_file='cyb50_30min_2023_to_20260325.csv'):
  22. """加载本地数据"""
  23. print(f"📊 加载数据 {csv_file}...")
  24. df = pd.read_csv(csv_file)
  25. df['DateTime'] = pd.to_datetime(df['DateTime'])
  26. df.set_index('DateTime', inplace=True)
  27. df.sort_index(inplace=True)
  28. if 'Open' not in df.columns and 'o' in df.columns:
  29. df.rename(columns={'o':'Open','h':'High','l':'Low','c':'Close','v':'Volume','a':'Amount'}, inplace=True)
  30. for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
  31. if col in df.columns:
  32. df[col] = pd.to_numeric(df[col], errors='coerce')
  33. df['Returns'] = df['Close'].pct_change()
  34. df['High_Low_Pct'] = (df['High'] - df['Low']) / df['Close'].shift(1)
  35. df['Close_Open_Pct'] = (df['Close'] - df['Open']) / df['Open']
  36. df.ffill(inplace=True)
  37. df.dropna(inplace=True)
  38. print(f"✅ 数据加载完成: {len(df)}条K线")
  39. return df
  40. def calculate_advanced_indicators(df):
  41. """计算高级技术指标"""
  42. print("📈 计算高级指标...")
  43. # RSI多种周期
  44. for period in [6, 14, 21]:
  45. delta = df['Close'].diff()
  46. gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
  47. loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
  48. rs = gain / loss
  49. df[f'RSI_{period}'] = 100 - (100 / (1 + rs))
  50. # 动量
  51. for period in [3, 5, 10]:
  52. df[f'Momentum_{period}'] = (df['Close'] / df['Close'].shift(period) - 1) * 100
  53. # 均线
  54. for period in [5, 20, 60]:
  55. df[f'EMA_{period}'] = df['Close'].ewm(span=period, adjust=False).mean()
  56. # 趋势评分
  57. df['Trend_Score'] = 0
  58. df.loc[df['Close'] > df['EMA_5'], 'Trend_Score'] += 1
  59. df.loc[df['Close'] > df['EMA_20'], 'Trend_Score'] += 1
  60. df.loc[df['Close'] > df['EMA_60'], 'Trend_Score'] += 1
  61. # 波动率
  62. df['Volatility'] = df['Returns'].rolling(20).std() * np.sqrt(48)
  63. df['Vol_MA'] = df['Volatility'].rolling(20).mean()
  64. df['Vol_Regime'] = '正常'
  65. df.loc[df['Volatility'] > df['Vol_MA'] * 1.5, 'Vol_Regime'] = '高波动'
  66. df.loc[df['Volatility'] < df['Vol_MA'] * 0.5, 'Vol_Regime'] = '低波动'
  67. # 成交量
  68. df['Volume_MA20'] = df['Volume'].rolling(20).mean()
  69. df['Volume_Ratio'] = df['Volume'] / df['Volume_MA20']
  70. # MACD
  71. exp1 = df['Close'].ewm(span=12, adjust=False).mean()
  72. exp2 = df['Close'].ewm(span=26, adjust=False).mean()
  73. df['MACD'] = exp1 - exp2
  74. df['MACD_Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
  75. df['MACD_Hist'] = df['MACD'] - df['MACD_Signal']
  76. # 布林带
  77. df['BB_Middle'] = df['Close'].rolling(20).mean()
  78. df['BB_Std'] = df['Close'].rolling(20).std()
  79. df['BB_Upper'] = df['BB_Middle'] + 2 * df['BB_Std']
  80. df['BB_Lower'] = df['BB_Middle'] - 2 * df['BB_Std']
  81. df['BB_Position'] = (df['Close'] - df['BB_Lower']) / (df['BB_Upper'] - df['BB_Lower'])
  82. df.dropna(inplace=True)
  83. return df
  84. class AdvancedOptimizedStrategy:
  85. """高级优化策略V2"""
  86. def __init__(self, params=None):
  87. self.initial_capital = 1000000
  88. self.current_capital = 1000000
  89. self.trades = []
  90. # 默认参数
  91. self.params = params or {
  92. 'rsi_low': 30,
  93. 'rsi_high': 60,
  94. 'momentum_min': -2,
  95. 'trend_min': 0,
  96. 'avoid_hour': 13,
  97. 'max_daily_trades': 3,
  98. 'position_base': 0.7,
  99. 'stop_loss': 0.008,
  100. 'take_profit': 0.025,
  101. 'trailing_stop': True,
  102. 'trailing_activation': 0.01,
  103. 'trailing_distance': 0.005,
  104. 'partial_exit': True,
  105. 'partial_ratio': 0.5,
  106. 'partial_target': 0.015,
  107. }
  108. def calculate_multi_factor_score(self, row):
  109. """多因子评分系统 (0-100分)"""
  110. score = 50 # 基础分
  111. # RSI因子 (0-20分)
  112. rsi = row['RSI_14']
  113. if 40 <= rsi <= 50:
  114. score += 20
  115. elif 35 <= rsi <= 55:
  116. score += 15
  117. elif 30 <= rsi <= 60:
  118. score += 10
  119. else:
  120. score -= 10
  121. # 趋势因子 (0-20分)
  122. trend = row['Trend_Score']
  123. score += trend * 5
  124. # 动量因子 (0-15分)
  125. mom = row['Momentum_5']
  126. if mom > 2:
  127. score += 15
  128. elif mom > 0:
  129. score += 10
  130. elif mom > -1:
  131. score += 5
  132. else:
  133. score -= 5
  134. # 波动率因子 (0-15分)
  135. if row['Vol_Regime'] == '低波动':
  136. score += 15
  137. elif row['Vol_Regime'] == '正常':
  138. score += 10
  139. else:
  140. score -= 5
  141. # MACD因子 (0-15分)
  142. if row['MACD_Hist'] > 0 and row['MACD'] > 0:
  143. score += 15
  144. elif row['MACD_Hist'] > 0:
  145. score += 10
  146. elif row['MACD'] > row['MACD_Signal']:
  147. score += 5
  148. # 布林带因子 (0-15分)
  149. bb_pos = row['BB_Position']
  150. if 0.2 <= bb_pos <= 0.4:
  151. score += 15
  152. elif 0.4 <= bb_pos <= 0.6:
  153. score += 10
  154. elif bb_pos < 0.2:
  155. score += 5
  156. return min(max(score, 0), 100)
  157. def should_trade(self, timestamp, row, daily_count):
  158. """判断是否应该交易"""
  159. # 时间过滤
  160. if timestamp.hour == self.params['avoid_hour']:
  161. return False, "避开13点"
  162. # T+1过滤
  163. if timestamp.time() > datetime.strptime('14:30', '%H:%M').time():
  164. return False, "避开T+1"
  165. # 每日交易次数限制
  166. date = timestamp.date()
  167. if daily_count.get(date, 0) >= self.params['max_daily_trades']:
  168. return False, "已达日交易上限"
  169. # 基础RSI过滤
  170. rsi = row['RSI_14']
  171. if not (self.params['rsi_low'] <= rsi <= self.params['rsi_high']):
  172. return False, f"RSI {rsi:.1f} 不在范围内"
  173. # 动量过滤
  174. if row['Momentum_5'] < self.params['momentum_min']:
  175. return False, f"动量 {row['Momentum_5']:.2f} 不足"
  176. return True, "通过"
  177. def simulate_advanced_exit(self, entry_price, exit_price, high_price, low_price,
  178. entry_time, exit_time, position_size):
  179. """模拟高级退出策略(移动止损+分批止盈)"""
  180. pnl_pct = (exit_price - entry_price) / entry_price
  181. # 计算期间最大盈利(用于移动止损)
  182. if high_price > entry_price:
  183. max_profit_pct = (high_price - entry_price) / entry_price
  184. else:
  185. max_profit_pct = 0
  186. exit_reason = '正常平仓'
  187. actual_pnl_pct = pnl_pct
  188. # 1. 硬止损检查
  189. if pnl_pct <= -self.params['stop_loss']:
  190. actual_pnl_pct = -self.params['stop_loss']
  191. exit_reason = '止损'
  192. return actual_pnl_pct, exit_reason
  193. # 2. 分批止盈检查
  194. if self.params['partial_exit'] and max_profit_pct >= self.params['partial_target']:
  195. # 达到分批止盈条件,假设50%仓位止盈
  196. partial_pnl = self.params['partial_target'] * self.params['partial_ratio']
  197. remaining_pnl = pnl_pct * (1 - self.params['partial_ratio'])
  198. actual_pnl_pct = partial_pnl + remaining_pnl
  199. exit_reason = '分批止盈'
  200. # 3. 移动止损检查
  201. elif self.params['trailing_stop'] and max_profit_pct >= self.params['trailing_activation']:
  202. # 激活移动止损
  203. trailing_stop_price = high_price * (1 - self.params['trailing_distance'])
  204. trailing_stop_pct = (trailing_stop_price - entry_price) / entry_price
  205. if pnl_pct <= trailing_stop_pct:
  206. actual_pnl_pct = trailing_stop_pct
  207. exit_reason = '移动止损'
  208. # 4. 目标止盈检查
  209. elif pnl_pct >= self.params['take_profit']:
  210. actual_pnl_pct = self.params['take_profit']
  211. exit_reason = '止盈'
  212. return actual_pnl_pct, exit_reason
  213. def backtest(self, data, trades_df):
  214. """执行回测"""
  215. print("\n🚀 开始高级优化策略回测")
  216. t1_trades = trades_df[trades_df['交易方向'] == '做多'].copy()
  217. t1_trades['开仓时间'] = pd.to_datetime(t1_trades['开仓时间'])
  218. t1_trades['平仓时间'] = pd.to_datetime(t1_trades['平仓时间'])
  219. daily_count = {}
  220. for idx, trade in t1_trades.iterrows():
  221. try:
  222. mask = data.index <= trade['开仓时间']
  223. if not mask.any():
  224. continue
  225. current_idx = data.index[mask][-1]
  226. current_data = data.loc[current_idx]
  227. should_trade, reason = self.should_trade(trade['开仓时间'], current_data, daily_count)
  228. if should_trade:
  229. position_size = self.params['position_base']
  230. # 多因子评分调整仓位
  231. factor_score = self.calculate_multi_factor_score(current_data)
  232. if factor_score >= 80:
  233. position_size = min(position_size * 1.2, 1.0)
  234. elif factor_score < 50:
  235. position_size = position_size * 0.7
  236. # 获取期间高低点(简化处理)
  237. exit_mask = (data.index >= trade['开仓时间']) & (data.index <= trade['平仓时间'])
  238. if exit_mask.any():
  239. period_data = data.loc[exit_mask]
  240. high_price = period_data['High'].max()
  241. low_price = period_data['Low'].min()
  242. else:
  243. high_price = trade['平仓价格']
  244. low_price = trade['平仓价格']
  245. pnl_pct, exit_reason = self.simulate_advanced_exit(
  246. trade['开仓价格'],
  247. trade['平仓价格'],
  248. high_price,
  249. low_price,
  250. trade['开仓时间'],
  251. trade['平仓时间'],
  252. position_size
  253. )
  254. position_value = self.current_capital * position_size
  255. pnl_amount = pnl_pct * position_value
  256. self.trades.append({
  257. '开仓时间': trade['开仓时间'],
  258. '平仓时间': trade['平仓时间'],
  259. '仓位': position_size,
  260. '因子评分': factor_score,
  261. '盈亏比例': pnl_pct,
  262. '实际盈亏': pnl_amount,
  263. '退出原因': exit_reason,
  264. })
  265. self.current_capital += pnl_amount
  266. date = trade['开仓时间'].date()
  267. daily_count[date] = daily_count.get(date, 0) + 1
  268. except Exception as e:
  269. continue
  270. return pd.DataFrame(self.trades)
  271. def report(self):
  272. """生成报告"""
  273. if len(self.trades) == 0:
  274. return {"error": "无交易记录"}
  275. df = pd.DataFrame(self.trades)
  276. total_trades = len(df)
  277. wins = df[df['实际盈亏'] > 0]
  278. losses = df[df['实际盈亏'] < 0]
  279. win_rate = len(wins) / total_trades * 100
  280. total_pnl = df['实际盈亏'].sum()
  281. total_return = (self.current_capital - self.initial_capital) / self.initial_capital * 100
  282. profits = wins['实际盈亏'].sum() if len(wins) > 0 else 0
  283. loss_amount = abs(losses['实际盈亏'].sum()) if len(losses) > 0 else 1
  284. profit_factor = profits / loss_amount
  285. return {
  286. '总交易次数': total_trades,
  287. '胜率': win_rate,
  288. '总盈亏': total_pnl,
  289. '总收益率': total_return,
  290. '盈亏比': profit_factor,
  291. '最终资金': self.current_capital,
  292. }
  293. def grid_search_optimization(data, trades_df):
  294. """网格搜索最优参数"""
  295. print("\n" + "="*60)
  296. print("🔍 网格搜索最优参数")
  297. print("="*60)
  298. # 参数搜索空间
  299. param_grid = {
  300. 'stop_loss': [0.005, 0.008, 0.01, 0.015],
  301. 'take_profit': [0.02, 0.025, 0.03, 0.035],
  302. 'trailing_activation': [0.008, 0.01, 0.015],
  303. 'trailing_distance': [0.003, 0.005, 0.008],
  304. }
  305. results = []
  306. for sl in param_grid['stop_loss']:
  307. for tp in param_grid['take_profit']:
  308. for ta in param_grid['trailing_activation']:
  309. for td in param_grid['trailing_distance']:
  310. params = {
  311. 'rsi_low': 30,
  312. 'rsi_high': 60,
  313. 'momentum_min': -2,
  314. 'trend_min': 0,
  315. 'avoid_hour': 13,
  316. 'max_daily_trades': 3,
  317. 'position_base': 0.7,
  318. 'stop_loss': sl,
  319. 'take_profit': tp,
  320. 'trailing_stop': True,
  321. 'trailing_activation': ta,
  322. 'trailing_distance': td,
  323. 'partial_exit': False,
  324. }
  325. strategy = AdvancedOptimizedStrategy(params)
  326. strategy.backtest(data, trades_df)
  327. result = strategy.report()
  328. if 'error' not in result and result['总交易次数'] >= 20:
  329. results.append({
  330. '止损': sl,
  331. '止盈': tp,
  332. '移动激活': ta,
  333. '移动距离': td,
  334. '交易数': result['总交易次数'],
  335. '胜率': result['胜率'],
  336. '总盈亏': result['总盈亏'],
  337. '盈亏比': result['盈亏比'],
  338. '收益率': result['总收益率'],
  339. })
  340. results_df = pd.DataFrame(results)
  341. if len(results_df) > 0:
  342. print("\n【总盈亏TOP10参数组合】")
  343. top10 = results_df.nlargest(10, '总盈亏')
  344. print(top10.to_string(index=False))
  345. print("\n【胜率TOP5参数组合】(至少30笔)")
  346. winrate_top = results_df[results_df['交易数'] >= 30].nlargest(5, '胜率')
  347. print(winrate_top.to_string(index=False))
  348. print("\n【综合评分TOP5】(盈亏比>1.5且胜率>45%)")
  349. filtered = results_df[(results_df['盈亏比'] > 1.5) & (results_df['胜率'] > 45)]
  350. if len(filtered) > 0:
  351. print(filtered.nlargest(5, '总盈亏').to_string(index=False))
  352. else:
  353. print("无满足条件的参数组合")
  354. return results_df
  355. return None
  356. def compare_strategies(data, trades_df):
  357. """对比不同策略"""
  358. print("\n" + "="*60)
  359. print("📊 策略对比分析")
  360. print("="*60)
  361. strategies = {
  362. '原策略(固定SL0.8% TP2%)': {
  363. 'stop_loss': 0.008, 'take_profit': 0.02,
  364. 'trailing_stop': False, 'partial_exit': False
  365. },
  366. '优化V1(固定SL0.8% TP2.5%)': {
  367. 'stop_loss': 0.008, 'take_profit': 0.025,
  368. 'trailing_stop': False, 'partial_exit': False
  369. },
  370. '优化V2(移动止损)': {
  371. 'stop_loss': 0.008, 'take_profit': 0.025,
  372. 'trailing_stop': True, 'trailing_activation': 0.01,
  373. 'trailing_distance': 0.005, 'partial_exit': False
  374. },
  375. '优化V3(分批止盈)': {
  376. 'stop_loss': 0.008, 'take_profit': 0.03,
  377. 'trailing_stop': False, 'partial_exit': True,
  378. 'partial_ratio': 0.5, 'partial_target': 0.015
  379. },
  380. '优化V4(移动+分批)': {
  381. 'stop_loss': 0.01, 'take_profit': 0.035,
  382. 'trailing_stop': True, 'trailing_activation': 0.015,
  383. 'trailing_distance': 0.008, 'partial_exit': True,
  384. 'partial_ratio': 0.5, 'partial_target': 0.02
  385. },
  386. }
  387. results = []
  388. for name, params in strategies.items():
  389. full_params = {
  390. 'rsi_low': 30, 'rsi_high': 60, 'momentum_min': -2,
  391. 'trend_min': 0, 'avoid_hour': 13, 'max_daily_trades': 3,
  392. 'position_base': 0.7, **params
  393. }
  394. strategy = AdvancedOptimizedStrategy(full_params)
  395. strategy.backtest(data, trades_df)
  396. result = strategy.report()
  397. if 'error' not in result:
  398. results.append({
  399. '策略': name,
  400. '交易数': result['总交易次数'],
  401. '胜率': f"{result['胜率']:.1f}%",
  402. '总盈亏': f"{result['总盈亏']:+.0f}",
  403. '盈亏比': f"{result['盈亏比']:.2f}",
  404. '收益率': f"{result['总收益率']:+.2f}%",
  405. })
  406. results_df = pd.DataFrame(results)
  407. print("\n" + results_df.to_string(index=False))
  408. def main():
  409. """主函数"""
  410. print("="*60)
  411. print("创业板50 T+1 高级优化策略V2")
  412. print("="*60)
  413. # 加载数据
  414. raw_data = load_local_data('cyb50_30min_2023_to_20260325.csv')
  415. data = calculate_advanced_indicators(raw_data)
  416. # 获取原策略交易
  417. print("\n📡 生成原策略交易信号...")
  418. config_manager = ConfigManager('config.json')
  419. fetcher = IntradayDataFetcher(config_manager)
  420. signal_generator = DualDirectionSignalGenerator()
  421. executor = DualDirectionExecutor(initial_capital=1000000)
  422. data_with_indicators = fetcher.calculate_intraday_indicators(data)
  423. signals_df = signal_generator.generate_dual_direction_signals(data_with_indicators)
  424. results_df, trades_df = executor.execute_dual_direction_trades(signals_df)
  425. # 策略对比
  426. compare_strategies(data, trades_df)
  427. # 网格搜索最优参数
  428. grid_results = grid_search_optimization(data, trades_df)
  429. # 使用最优参数最终回测
  430. if grid_results is not None and len(grid_results) > 0:
  431. best_params = grid_results.loc[grid_results['总盈亏'].idxmax()]
  432. print(f"\n" + "="*60)
  433. print("🏆 最优参数最终回测")
  434. print("="*60)
  435. print(f"止损: {best_params['止损']}")
  436. print(f"止盈: {best_params['止盈']}")
  437. print(f"移动激活: {best_params['移动激活']}")
  438. print(f"移动距离: {best_params['移动距离']}")
  439. print(f"预期盈亏: {best_params['总盈亏']:,.0f}元")
  440. print(f"预期收益率: {best_params['收益率']:+.2f}%")
  441. print("\n" + "="*60)
  442. print("✅ 分析完成")
  443. print("="*60)
  444. if __name__ == '__main__':
  445. main()