regime_final.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. """
  2. 最终修正版Regime策略 - 正确使用backtrader指标历史访问
  3. """
  4. import backtrader as bt
  5. import pandas as pd
  6. import numpy as np
  7. class FinalRegimeStrategy(bt.Strategy):
  8. """
  9. 状态感知策略 - 最终修正版
  10. 关键点: 在backtrader中,用(1)而不是[-1]获取历史值
  11. """
  12. params = (
  13. ('fast', 20),
  14. ('slow', 60),
  15. ('printlog', False),
  16. )
  17. def __init__(self):
  18. self.dataclose = self.datas[0].close
  19. self.order = None
  20. # 均线
  21. self.sma_fast = bt.indicators.SMA(period=self.p.fast)
  22. self.sma_slow = bt.indicators.SMA(period=self.p.slow)
  23. # 交易计数
  24. self.trade_count = 0
  25. def next(self):
  26. if self.order:
  27. return
  28. # 必须至少有2个数据点才能判断交叉
  29. if len(self) < 2:
  30. return
  31. # 获取当前和前一周期的均线值
  32. # 在backtrader中: [0]是当前, [-1]是前一个周期
  33. fast_now = self.sma_fast[0]
  34. fast_prev = self.sma_fast[-1]
  35. slow_now = self.sma_slow[0]
  36. slow_prev = self.sma_slow[-1]
  37. # 检查是否有有效值
  38. if np.isnan(fast_now) or np.isnan(slow_now):
  39. return
  40. # 检测金叉: 前一周fast<=slow, 这一周fast>slow
  41. golden_cross = fast_prev <= slow_prev and fast_now > slow_now
  42. # 检测死叉: 前一周fast>=slow, 这一周fast<slow
  43. death_cross = fast_prev >= slow_prev and fast_now < slow_now
  44. # 买入逻辑: 金叉 + 空仓
  45. if golden_cross and not self.position:
  46. size = int(self.broker.getcash() / self.dataclose[0] / 100) * 100
  47. if size > 0:
  48. self.order = self.buy(size=size)
  49. self.trade_count += 1
  50. if self.p.printlog:
  51. self.log(f'BUY #{self.trade_count} @ {self.dataclose[0]:.2f}')
  52. # 卖出逻辑: 死叉 + 有持仓
  53. elif death_cross and self.position:
  54. self.order = self.close()
  55. if self.p.printlog:
  56. self.log(f'SELL @ {self.dataclose[0]:.2f}')
  57. def notify_order(self, order):
  58. if order.status in [order.Submitted, order.Accepted]:
  59. return
  60. if order.status in [order.Completed]:
  61. if order.isbuy():
  62. self.log(f'BUY EXECUTED @ {order.executed.price:.2f}')
  63. else:
  64. self.log(f'SELL EXECUTED @ {order.executed.price:.2f}')
  65. self.order = None
  66. def log(self, txt, dt=None):
  67. if not self.p.printlog:
  68. return
  69. dt = dt or self.datas[0].datetime.date(0)
  70. print(f'{dt.isoformat()} {txt}')
  71. def stop(self):
  72. roi = (self.broker.getvalue() / self.broker.startingcash - 1) * 100
  73. print(f'\n=== 最终收益: {roi:.2f}% ===')
  74. print(f'总交易次数: {self.trade_count}')
  75. def run_final_regime(csv_file="chinext50.csv", cash=100000.0):
  76. """运行最终修正版Regime策略"""
  77. cerebro = bt.Cerebro()
  78. df = pd.read_csv(csv_file, parse_dates=['datetime'], index_col='datetime')
  79. data = bt.feeds.PandasData(dataname=df)
  80. cerebro.adddata(data)
  81. cerebro.addstrategy(FinalRegimeStrategy, printlog=False)
  82. cerebro.broker.setcash(cash)
  83. cerebro.broker.setcommission(commission=0.001)
  84. cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe', riskfreerate=0.02)
  85. cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
  86. cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
  87. cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
  88. print('=== 创业板50 Regime策略回测 ===')
  89. print(f'初始资金: {cerebro.broker.getvalue():.2f}')
  90. results = cerebro.run()
  91. strat = results[0]
  92. print(f'最终资金: {cerebro.broker.getvalue():.2f}')
  93. returns = strat.analyzers.returns.get_analysis()
  94. print(f"年化收益: {returns.get('rnorm100', 0):.2f}%")
  95. sharpe = strat.analyzers.sharpe.get_analysis()
  96. sharpe_val = sharpe.get('sharperatio', 0)
  97. if sharpe_val:
  98. print(f"夏普比率: {sharpe_val:.3f}")
  99. else:
  100. print("夏普比率: N/A")
  101. drawdown = strat.analyzers.drawdown.get_analysis()
  102. print(f"最大回撤: {drawdown.get('max', {}).get('drawdown', 0):.2f}%")
  103. trades = strat.analyzers.trades.get_analysis()
  104. if trades and trades.get('total'):
  105. total = trades['total'].get('total', 0)
  106. won = trades['won'].get('total', 0) if trades.get('won') else 0
  107. print(f"总交易: {total}, 盈利: {won}")
  108. if total > 0:
  109. print(f"胜率: {won/total:.1%}")
  110. return cerebro, strat
  111. if __name__ == "__main__":
  112. run_final_regime()