cyb50_ultimate.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 创业板50指数 - 终极高收益策略
  5. 核心:追涨杀跌 + 杠杆思维 + 择时精准
  6. """
  7. import pandas as pd
  8. import numpy as np
  9. import matplotlib
  10. matplotlib.use('Agg')
  11. import matplotlib.pyplot as plt
  12. import warnings
  13. warnings.filterwarnings('ignore')
  14. def generate_realistic_data():
  15. """基于创业板真实历史特征生成数据"""
  16. np.random.seed(42)
  17. dates = pd.date_range(start='2017-01-01', end='2025-12-31', freq='D')
  18. dates = dates[dates.dayofweek < 5]
  19. n = len(dates)
  20. # 生成带有肥尾和趋势特征的收益率
  21. returns = np.random.standard_t(df=4, size=n) * 0.012 # t分布,肥尾
  22. # 按年份调整(基于创业板真实历史)
  23. year_returns = {
  24. 2017: -0.0003, 2018: -0.0015, 2019: 0.0018,
  25. 2020: 0.0022, 2021: 0.0008, 2022: -0.0012,
  26. 2023: -0.0006, 2024: 0.0010, 2025: 0.0008
  27. }
  28. for i, date in enumerate(dates):
  29. year = date.year
  30. if year in year_returns:
  31. returns[i] += year_returns[year]
  32. # 添加动量自相关(趋势延续)
  33. for i in range(1, len(returns)):
  34. returns[i] += returns[i-1] * 0.15 # 15%动量延续
  35. price = 1800
  36. prices = [price]
  37. for r in returns:
  38. price *= (1 + r)
  39. prices.append(price)
  40. df = pd.DataFrame(index=dates)
  41. df['close'] = prices[1:]
  42. df['open'] = df['close'].shift(1) * (1 + np.random.normal(0, 0.003, n))
  43. df['high'] = df[['open', 'close']].max(axis=1) * (1 + np.abs(np.random.normal(0, 0.008, n)))
  44. df['low'] = df[['open', 'close']].min(axis=1) * (1 - np.abs(np.random.normal(0, 0.008, n)))
  45. df = df.dropna()
  46. return df
  47. class UltimateStrategy:
  48. """
  49. 终极策略:三重过滤 + 动态杠杆
  50. """
  51. def __init__(self):
  52. self.position = 0
  53. self.cash = 1.0
  54. self.holdings = 0
  55. self.entry_price = 0
  56. self.peak_price = 0
  57. self.state = "EMPTY"
  58. def signal(self, data):
  59. """三重过滤信号"""
  60. c = data['close']
  61. # 1. 趋势过滤(三均线多头排列)
  62. ma5 = c.rolling(5).mean().iloc[-1]
  63. ma20 = c.rolling(20).mean().iloc[-1]
  64. ma60 = c.rolling(60).mean().iloc[-1]
  65. trend_ok = (c.iloc[-1] > ma5) and (ma5 > ma20) and (ma20 > ma60)
  66. # 2. 动量过滤(20日涨幅为正且加速)
  67. ret20 = (c.iloc[-1] / c.iloc[-20] - 1)
  68. ret10 = (c.iloc[-1] / c.iloc[-10] - 1)
  69. momentum_ok = (ret20 > 0.05) and (ret10 > ret20 * 0.6) # 动量加速
  70. # 3. 波动率过滤(低波动时重仓)
  71. volatility = c.pct_change().rolling(20).std().iloc[-1] * np.sqrt(252)
  72. vol_ok = volatility < 0.45 # 年化波动小于45%
  73. # 综合信号
  74. if trend_ok and momentum_ok and vol_ok:
  75. return "FULL", 1.0
  76. elif trend_ok and momentum_ok:
  77. return "HALF", 0.5
  78. elif trend_ok:
  79. return "QUARTER", 0.25
  80. else:
  81. return "EMPTY", 0.0
  82. def manage_risk(self, current_price):
  83. """风险管理"""
  84. if self.position <= 0:
  85. return self.position
  86. # 更新峰值
  87. if current_price > self.peak_price:
  88. self.peak_price = current_price
  89. # 回撤控制
  90. drawdown = (current_price - self.peak_price) / self.peak_price
  91. # 从高点回撤12%清仓
  92. if drawdown < -0.12:
  93. return 0.0
  94. # 从高点回撤8%减半
  95. if drawdown < -0.08 and self.position >= 0.5:
  96. return self.position * 0.5
  97. # 入场后亏损8%止损
  98. if self.entry_price > 0:
  99. loss = (current_price - self.entry_price) / self.entry_price
  100. if loss < -0.08:
  101. return 0.0
  102. return self.position
  103. def generate_signal(self, data):
  104. """主信号生成"""
  105. signal, pos = self.signal(data)
  106. current_price = data['close'].iloc[-1]
  107. # 先应用风险管理
  108. pos = self.manage_risk(current_price)
  109. # 状态更新
  110. if pos > self.position and self.position == 0:
  111. # 新开仓
  112. self.entry_price = current_price
  113. self.peak_price = current_price
  114. self.state = "ENTRY"
  115. elif pos == 0 and self.position > 0:
  116. # 清仓
  117. self.entry_price = 0
  118. self.peak_price = 0
  119. self.state = "EXIT"
  120. elif pos == 1.0:
  121. self.state = "FULL"
  122. elif pos == 0.5:
  123. self.state = "HALF"
  124. elif pos == 0:
  125. self.state = "EMPTY"
  126. else:
  127. self.state = "PARTIAL"
  128. self.position = pos
  129. return pos, self.state
  130. def backtest(data, strategy, start_date, end_date, warmup=60):
  131. """回测"""
  132. data = data[data.index >= start_date]
  133. data = data[data.index <= end_date]
  134. results = []
  135. nav = 1.0
  136. for i in range(warmup, len(data)):
  137. curr = data.iloc[:i+1]
  138. pos, state = strategy.generate_signal(curr)
  139. if i > warmup:
  140. daily_ret = data['close'].iloc[i] / data['close'].iloc[i-1] - 1
  141. # 使用前一个时刻的仓位计算当日收益
  142. prev_pos = results[-1]['position']
  143. nav *= (1 + daily_ret * prev_pos)
  144. results.append({
  145. 'date': data.index[i],
  146. 'position': pos,
  147. 'nav': nav,
  148. 'state': state,
  149. 'close': data['close'].iloc[i]
  150. })
  151. df = pd.DataFrame(results).set_index('date')
  152. df['index_nav'] = df['close'] / df['close'].iloc[0]
  153. return df
  154. def metrics(nav, index_nav):
  155. """计算指标"""
  156. s_ret = nav.pct_change().dropna()
  157. total = nav.iloc[-1] - 1
  158. days = len(nav)
  159. annual = (1 + total) ** (252/days) - 1
  160. idx_total = index_nav.iloc[-1] - 1
  161. idx_annual = (1 + idx_total) ** (252/days) - 1
  162. # 最大回撤
  163. running_max = nav.expanding().max()
  164. dd = ((nav - running_max) / running_max).min()
  165. # 夏普
  166. vol = s_ret.std() * np.sqrt(252)
  167. sharpe = (annual - 0.03) / vol if vol > 0 else 0
  168. # 卡玛
  169. calmar = annual / abs(dd) if dd != 0 else 0
  170. return {
  171. 'annual': annual,
  172. 'idx_annual': idx_annual,
  173. 'excess': annual - idx_annual,
  174. 'max_dd': dd,
  175. 'sharpe': sharpe,
  176. 'calmar': calmar,
  177. 'total': total,
  178. 'idx_total': idx_total
  179. }
  180. def plot(df, title, filename):
  181. """绘图"""
  182. fig, ax = plt.subplots(2, 1, figsize=(14, 8))
  183. ax[0].plot(df.index, df['nav'], 'r-', linewidth=2, label='Strategy')
  184. ax[0].plot(df.index, df['index_nav'], 'gray', linewidth=1, alpha=0.7, label='Index')
  185. ax[0].set_title(title, fontsize=14)
  186. ax[0].legend()
  187. ax[0].grid(True, alpha=0.3)
  188. ax[1].fill_between(df.index, 0, df['position'], alpha=0.5, color='green')
  189. ax[1].set_ylim(0, 1.1)
  190. ax[1].set_ylabel('Position')
  191. ax[1].grid(True, alpha=0.3)
  192. plt.tight_layout()
  193. plt.savefig(filename, dpi=150)
  194. print(f" 图表: {filename}")
  195. def main():
  196. print("="*60)
  197. print("创业板50 - 终极高收益策略")
  198. print("="*60)
  199. # 数据
  200. print("\n[1] 加载数据...")
  201. data = generate_realistic_data()
  202. print(f" {data.index[0].date()} ~ {data.index[-1].date()}")
  203. # 训练
  204. print("\n[2] 训练集 (2018-2023)...")
  205. s = UltimateStrategy()
  206. train = backtest(data, s, '2018-01-01', '2023-12-31')
  207. m = metrics(train['nav'], train['index_nav'])
  208. print(f"\n ╔════════════════════════════════╗")
  209. print(f" ║ 训 练 集 结 果 ║")
  210. print(f" ╠════════════════════════════════╣")
  211. print(f" ║ 策略收益: {m['total']*100:7.1f}% ║")
  212. print(f" ║ 指数收益: {m['idx_total']*100:7.1f}% ║")
  213. print(f" ║ 年化收益: {m['annual']*100:7.1f}% ║")
  214. print(f" ║ 超额收益: {m['excess']*100:7.1f}% ║")
  215. print(f" ║ 最大回撤: {m['max_dd']*100:7.1f}% ║")
  216. print(f" ║ 夏普比率: {m['sharpe']:7.2f} ║")
  217. print(f" ║ 卡玛比率: {m['calmar']:7.2f} ║")
  218. print(f" ╚════════════════════════════════╝")
  219. plot(train, "Training (2018-2023)", "train_ultimate.png")
  220. # 验证
  221. print("\n[3] 验证集 (2024-2025)...")
  222. s2 = UltimateStrategy()
  223. val = backtest(data, s2, '2024-01-01', '2025-12-31')
  224. m2 = metrics(val['nav'], val['index_nav'])
  225. print(f"\n ╔════════════════════════════════╗")
  226. print(f" ║ 验 证 集 结 果 ║")
  227. print(f" ╠════════════════════════════════╣")
  228. print(f" ║ 策略收益: {m2['total']*100:7.1f}% ║")
  229. print(f" ║ 指数收益: {m2['idx_total']*100:7.1f}% ║")
  230. print(f" ║ 年化收益: {m2['annual']*100:7.1f}% ║")
  231. print(f" ║ 超额收益: {m2['excess']*100:7.1f}% ║")
  232. print(f" ║ 最大回撤: {m2['max_dd']*100:7.1f}% ║")
  233. print(f" ║ 夏普比率: {m2['sharpe']:7.2f} ║")
  234. print(f" ╚════════════════════════════════╝")
  235. plot(val, "Validation (2024-2025)", "val_ultimate.png")
  236. # 评估
  237. print("\n[4] 策略评估:")
  238. if m['annual'] > 0.20 and m['calmar'] > 0.5:
  239. print(" ✅ 训练集表现优秀,具备盈利潜力")
  240. else:
  241. print(" ⚠️ 训练集收益一般")
  242. decay = (m['annual'] - m2['annual']) / m['annual'] if m['annual'] > 0 else 0
  243. print(f" 年化收益衰减: {decay*100:.0f}%")
  244. if m2['annual'] > 0.15:
  245. print(" ✅ 验证集收益优秀")
  246. elif m2['annual'] > 0:
  247. print(" ⚠️ 验证集收益一般")
  248. else:
  249. print(" ❌ 验证集亏损")
  250. print("\n" + "="*60)
  251. if __name__ == "__main__":
  252. main()