analyze_deep_dive.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 创业板50 T+1 多维度深度分析
  5. """
  6. import pandas as pd
  7. import numpy as np
  8. from datetime import datetime, timedelta
  9. import warnings
  10. import sys
  11. warnings.filterwarnings('ignore')
  12. # Redirect stdout
  13. from io import StringIO
  14. old_stdout = sys.stdout
  15. sys.stdout = StringIO()
  16. from cyb50_30min_dual_direction import ConfigManager, IntradayDataFetcher, DualDirectionSignalGenerator, DualDirectionExecutor
  17. from t1_converter import simulate_t1_trades
  18. def load_local_data(csv_file='cyb50_30min_2023_to_20260325.csv'):
  19. df = pd.read_csv(csv_file)
  20. df['DateTime'] = pd.to_datetime(df['DateTime'])
  21. df.set_index('DateTime', inplace=True)
  22. df.sort_index(inplace=True)
  23. if 'Open' not in df.columns and 'o' in df.columns:
  24. df.rename(columns={'o':'Open','h':'High','l':'Low','c':'Close','v':'Volume','a':'Amount'}, inplace=True)
  25. for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
  26. if col in df.columns:
  27. df[col] = pd.to_numeric(df[col], errors='coerce')
  28. df['Returns'] = df['Close'].pct_change()
  29. df['High_Low_Pct'] = (df['High'] - df['Low']) / df['Close'].shift(1)
  30. df['Close_Open_Pct'] = (df['Close'] - df['Open']) / df['Open']
  31. df.ffill(inplace=True)
  32. df.dropna(inplace=True)
  33. return df
  34. # 运行回测
  35. initial_capital = 1000000
  36. raw_data = load_local_data('cyb50_30min_2023_to_20260325.csv')
  37. config_manager = ConfigManager('config.json')
  38. fetcher = IntradayDataFetcher(config_manager)
  39. data_with_indicators = fetcher.calculate_intraday_indicators(raw_data)
  40. signal_generator = DualDirectionSignalGenerator()
  41. signals_df = signal_generator.generate_dual_direction_signals(data_with_indicators)
  42. executor = DualDirectionExecutor(initial_capital=initial_capital)
  43. results_df, trades_df = executor.execute_dual_direction_trades(signals_df)
  44. long_trades = trades_df[trades_df['交易方向'] == '做多'].copy()
  45. t1_trades = simulate_t1_trades(data_with_indicators, long_trades, initial_capital)
  46. # Restore stdout
  47. sys.stdout = old_stdout
  48. print('='*80)
  49. print('创业板50 T+1 多维度深度分析')
  50. print('='*80)
  51. # 准备数据
  52. t1_trades['开仓时间'] = pd.to_datetime(t1_trades['开仓时间'])
  53. t1_trades['平仓时间'] = pd.to_datetime(t1_trades['平仓时间'])
  54. t1_trades['持仓小时'] = (t1_trades['平仓时间'] - t1_trades['开仓时间']).dt.total_seconds() / 3600
  55. t1_trades['持仓周期'] = ((t1_trades['平仓时间'] - t1_trades['开仓时间']).dt.total_seconds() / 1800).astype(int)
  56. t1_trades['是否盈利'] = t1_trades['盈亏金额'] > 0
  57. t1_trades['盈亏比例'] = t1_trades['盈亏金额'] / initial_capital * 100
  58. t1_trades['开仓年份'] = t1_trades['开仓时间'].dt.year
  59. # 将交易与原始数据合并,获取更多维度
  60. for idx, trade in t1_trades.iterrows():
  61. try:
  62. # 获取开仓时的市场数据
  63. mask = data_with_indicators.index <= trade['开仓时间']
  64. if mask.any():
  65. current_idx = data_with_indicators.index[mask][-1]
  66. current_data = data_with_indicators.loc[current_idx]
  67. # 计算价格位置(过去20日高低点中的位置)
  68. past_20d = data_with_indicators.loc[:current_idx].tail(20*8) # 20天,每天8个30分钟
  69. if len(past_20d) > 0:
  70. high_20d = past_20d['High'].max()
  71. low_20d = past_20d['Low'].min()
  72. current_price = current_data['Close']
  73. t1_trades.loc[idx, '价格位置_20d'] = (current_price - low_20d) / (high_20d - low_20d) * 100 if high_20d > low_20d else 50
  74. # 获取技术指标
  75. for col in ['RSI_14', 'MACD', 'MACD_signal', 'KDJ_K', 'KDJ_D', '布林带宽度']:
  76. if col in current_data.index:
  77. t1_trades.loc[idx, col] = current_data[col]
  78. except:
  79. pass
  80. # ========== 1. 持仓时长分析 ==========
  81. print('\n' + '='*80)
  82. print('【1】持仓时长深度分析')
  83. print('='*80)
  84. # 按持仓周期分组
  85. period_stats = t1_trades.groupby('持仓周期').agg({
  86. '盈亏金额': ['count', 'sum', 'mean'],
  87. '是否盈利': 'sum'
  88. }).round(2)
  89. period_stats.columns = ['交易次数', '总盈亏', '平均盈亏', '盈利次数']
  90. period_stats['胜率'] = (period_stats['盈利次数'] / period_stats['交易次数'] * 100).round(1)
  91. print('\n按持仓周期统计(30分钟为单位):')
  92. print(period_stats[period_stats['交易次数'] >= 5].to_string())
  93. # 持仓时长分布
  94. print('\n持仓时长分布:')
  95. t1_trades['持仓时长分类'] = pd.cut(t1_trades['持仓小时'],
  96. bins=[0, 1, 2, 4, 8, 16, 100],
  97. labels=['<1小时', '1-2小时', '2-4小时', '4-8小时', '8-16小时', '>16小时'])
  98. duration_stats = t1_trades.groupby('持仓时长分类').agg({
  99. '盈亏金额': ['count', 'sum', 'mean'],
  100. '是否盈利': 'sum'
  101. }).round(2)
  102. duration_stats.columns = ['交易次数', '总盈亏', '平均盈亏', '盈利次数']
  103. duration_stats['胜率'] = (duration_stats['盈利次数'] / duration_stats['交易次数'] * 100).round(1)
  104. print(duration_stats.to_string())
  105. # ========== 2. 价格位置分析 ==========
  106. print('\n' + '='*80)
  107. print('【2】开仓价格位置分析')
  108. print('='*80)
  109. # 将价格位置分箱
  110. t1_trades['价格位置分类'] = pd.cut(t1_trades['价格位置_20d'],
  111. bins=[0, 20, 40, 60, 80, 100],
  112. labels=['极低(0-20%)', '低位(20-40%)', '中位(40-60%)', '高位(60-80%)', '极高(80-100%)'])
  113. position_stats = t1_trades.groupby('价格位置分类').agg({
  114. '盈亏金额': ['count', 'sum', 'mean'],
  115. '是否盈利': 'sum'
  116. }).round(2)
  117. position_stats.columns = ['交易次数', '总盈亏', '平均盈亏', '盈利次数']
  118. position_stats['胜率'] = (position_stats['盈利次数'] / position_stats['交易次数'] * 100).round(1)
  119. print('\n按20日价格位置统计:')
  120. print(position_stats.to_string())
  121. # T+1调整与价格位置的关系
  122. print('\nT+1调整与价格位置的关系:')
  123. position_t1 = pd.crosstab(t1_trades['价格位置分类'], t1_trades['T+1调整'],
  124. values=t1_trades['盈亏金额'], aggfunc='mean')
  125. print(position_t1.round(2).to_string())
  126. # ========== 3. 止盈止损分析 ==========
  127. print('\n' + '='*80)
  128. print('【3】止盈止损触发分析')
  129. print('='*80)
  130. # 解析退出原因
  131. exit_reasons = t1_trades['退出原因'].str.extract(r'(.*?)(?:触发|$)')
  132. exit_reasons.columns = ['退出类型']
  133. exit_reasons['退出类型'] = exit_reasons['退出类型'].str.strip()
  134. t1_trades['退出类型'] = exit_reasons['退出类型']
  135. exit_stats = t1_trades.groupby('退出类型').agg({
  136. '盈亏金额': ['count', 'sum', 'mean'],
  137. '是否盈利': 'sum'
  138. }).round(2)
  139. exit_stats.columns = ['交易次数', '总盈亏', '平均盈亏', '盈利次数']
  140. exit_stats['胜率'] = (exit_stats['盈利次数'] / exit_stats['交易次数'] * 100).round(1)
  141. print('\n按退出类型统计:')
  142. print(exit_stats.to_string())
  143. # ========== 4. 波动率分析 ==========
  144. print('\n' + '='*80)
  145. print('【4】市场波动率与交易表现')
  146. print('='*80)
  147. # 计算开仓时的波动率
  148. t1_trades['开仓波动率'] = np.nan
  149. for idx, trade in t1_trades.iterrows():
  150. try:
  151. mask = data_with_indicators.index <= trade['开仓时间']
  152. if mask.sum() >= 20:
  153. recent_data = data_with_indicators.loc[mask].tail(20)
  154. volatility = recent_data['Returns'].std() * np.sqrt(48) # 年化波动率,每天48个30分钟
  155. t1_trades.loc[idx, '开仓波动率'] = volatility
  156. except:
  157. pass
  158. # 波动率分箱
  159. t1_trades['波动率分类'] = pd.cut(t1_trades['开仓波动率'],
  160. bins=[0, 0.15, 0.25, 0.35, 0.5, 2.0],
  161. labels=['低波动(<15%)', '中低波动(15-25%)', '中等波动(25-35%)', '高波动(35-50%)', '极高波动(>50%)'])
  162. vol_stats = t1_trades.groupby('波动率分类').agg({
  163. '盈亏金额': ['count', 'sum', 'mean'],
  164. '是否盈利': 'sum'
  165. }).round(2)
  166. vol_stats.columns = ['交易次数', '总盈亏', '平均盈亏', '盈利次数']
  167. vol_stats['胜率'] = (vol_stats['盈利次数'] / vol_stats['交易次数'] * 100).round(1)
  168. print('\n按市场波动率统计:')
  169. print(vol_stats.to_string())
  170. # ========== 5. 交易量分析 ==========
  171. print('\n' + '='*80)
  172. print('【5】交易量分析')
  173. print('='*80)
  174. # 计算开仓时的相对成交量
  175. t1_trades['相对成交量'] = np.nan
  176. for idx, trade in t1_trades.iterrows():
  177. try:
  178. mask = data_with_indicators.index <= trade['开仓时间']
  179. if mask.sum() >= 20:
  180. recent_data = data_with_indicators.loc[mask].tail(20)
  181. current_vol = recent_data['Volume'].iloc[-1]
  182. avg_vol = recent_data['Volume'].mean()
  183. t1_trades.loc[idx, '相对成交量'] = current_vol / avg_vol if avg_vol > 0 else 1
  184. except:
  185. pass
  186. t1_trades['成交量分类'] = pd.cut(t1_trades['相对成交量'],
  187. bins=[0, 0.5, 1.0, 1.5, 2.0, 10.0],
  188. labels=['缩量(<0.5x)', '正常(0.5-1x)', '放量(1-1.5x)', '大量(1.5-2x)', '巨量(>2x)'])
  189. volume_stats = t1_trades.groupby('成交量分类').agg({
  190. '盈亏金额': ['count', 'sum', 'mean'],
  191. '是否盈利': 'sum'
  192. }).round(2)
  193. volume_stats.columns = ['交易次数', '总盈亏', '平均盈亏', '盈利次数']
  194. volume_stats['胜率'] = (volume_stats['盈利次数'] / volume_stats['交易次数'] * 100).round(1)
  195. print('\n按相对成交量统计:')
  196. print(volume_stats.to_string())
  197. # ========== 6. 入场信号分析 ==========
  198. print('\n' + '='*80)
  199. print('【6】入场信号分析')
  200. print('='*80)
  201. # 解析入场信号
  202. if '入场信号' in t1_trades.columns:
  203. # 统计各类信号出现的频率
  204. signal_types = []
  205. for signals in t1_trades['入场信号'].dropna():
  206. if isinstance(signals, str):
  207. for s in signals.split(','):
  208. signal_types.append(s.strip())
  209. from collections import Counter
  210. signal_counter = Counter(signal_types)
  211. print('\n入场信号频率统计:')
  212. for signal, count in signal_counter.most_common(15):
  213. print(f' {signal}: {count}次')
  214. # 分析特定信号的表现
  215. print('\n关键信号组合分析:')
  216. for signal in ['MACD金叉', 'RSI超卖', 'KDJ金叉', '放量突破']:
  217. mask = t1_trades['入场信号'].str.contains(signal, na=False)
  218. if mask.sum() > 0:
  219. subset = t1_trades[mask]
  220. win_rate = (subset['盈亏金额'] > 0).mean() * 100
  221. avg_pnl = subset['盈亏金额'].mean()
  222. print(f' {signal}: {mask.sum()}笔, 胜率{win_rate:.1f}%, 平均盈亏{avg_pnl:+,.0f}元')
  223. # ========== 7. 盈亏分布分析 ==========
  224. print('\n' + '='*80)
  225. print('【7】盈亏分布特征')
  226. print('='*80)
  227. profit_trades = t1_trades[t1_trades['盈亏金额'] > 0]
  228. loss_trades = t1_trades[t1_trades['盈亏金额'] < 0]
  229. print('\n盈利交易特征:')
  230. print(f' 总盈利: {profit_trades["盈亏金额"].sum():,.0f}元')
  231. print(f' 平均盈利: {profit_trades["盈亏金额"].mean():,.0f}元')
  232. print(f' 最大单笔: {profit_trades["盈亏金额"].max():,.0f}元')
  233. print(f' 中位数: {profit_trades["盈亏金额"].median():,.0f}元')
  234. print(f' 25分位: {profit_trades["盈亏金额"].quantile(0.25):,.0f}元')
  235. print(f' 75分位: {profit_trades["盈亏金额"].quantile(0.75):,.0f}元')
  236. print('\n亏损交易特征:')
  237. print(f' 总亏损: {loss_trades["盈亏金额"].sum():,.0f}元')
  238. print(f' 平均亏损: {loss_trades["盈亏金额"].mean():,.0f}元')
  239. print(f' 最大单笔: {loss_trades["盈亏金额"].min():,.0f}元')
  240. print(f' 中位数: {loss_trades["盈亏金额"].median():,.0f}元')
  241. print(f' 25分位: {loss_trades["盈亏金额"].quantile(0.25):,.0f}元')
  242. print(f' 75分位: {loss_trades["盈亏金额"].quantile(0.75):,.0f}元')
  243. # 盈亏比分析
  244. total_profit = profit_trades['盈亏金额'].sum()
  245. total_loss = abs(loss_trades['盈亏金额'].sum())
  246. profit_factor = total_profit / total_loss if total_loss > 0 else 0
  247. print(f'\n盈亏比: {profit_factor:.2f}')
  248. print(f'盈亏比(单笔平均): {abs(profit_trades["盈亏金额"].mean() / loss_trades["盈亏金额"].mean()):.2f}')
  249. # ========== 8. 资金曲线分析 ==========
  250. print('\n' + '='*80)
  251. print('【8】资金曲线与回撤分析')
  252. print('='*80)
  253. # 计算资金曲线
  254. t1_trades_sorted = t1_trades.sort_values('平仓时间')
  255. cumulative = [initial_capital]
  256. for pnl in t1_trades_sorted['盈亏金额']:
  257. cumulative.append(cumulative[-1] + pnl)
  258. # 计算最大回撤
  259. max_drawdown = 0
  260. max_drawdown_pct = 0
  261. peak = initial_capital
  262. peak_idx = 0
  263. drawdown_start = 0
  264. drawdown_end = 0
  265. for i, capital in enumerate(cumulative):
  266. if capital > peak:
  267. peak = capital
  268. peak_idx = i
  269. else:
  270. dd = peak - capital
  271. dd_pct = dd / peak * 100
  272. if dd_pct > max_drawdown_pct:
  273. max_drawdown = dd
  274. max_drawdown_pct = dd_pct
  275. drawdown_start = peak_idx
  276. drawdown_end = i
  277. print(f'\n资金曲线统计:')
  278. print(f' 期初资金: {initial_capital:,.0f}元')
  279. print(f' 期末资金: {cumulative[-1]:,.0f}元')
  280. print(f' 峰值资金: {max(cumulative):,.0f}元')
  281. print(f' 谷值资金: {min(cumulative):,.0f}元')
  282. print(f' 最大回撤: {max_drawdown:,.0f}元 ({max_drawdown_pct:.2f}%)')
  283. print(f' 回撤开始: 第{drawdown_start}笔交易')
  284. print(f' 回撤结束: 第{drawdown_end}笔交易')
  285. print(f' 恢复耗时: {drawdown_end - drawdown_start}笔交易')
  286. # 回撤期间分析
  287. if drawdown_start < len(t1_trades_sorted) and drawdown_end < len(t1_trades_sorted):
  288. dd_period = t1_trades_sorted.iloc[drawdown_start:drawdown_end+1]
  289. print(f'\n最大回撤期间特征:')
  290. print(f' 交易次数: {len(dd_period)}笔')
  291. print(f' 期间盈亏: {dd_period["盈亏金额"].sum():,.0f}元')
  292. print(f' 盈利笔数: {(dd_period["盈亏金额"] > 0).sum()}笔')
  293. print(f' 亏损笔数: {(dd_period["盈亏金额"] < 0).sum()}笔')
  294. print(f' 期间胜率: {(dd_period["盈亏金额"] > 0).mean()*100:.1f}%')
  295. # ========== 9. 多维度交叉分析 ==========
  296. print('\n' + '='*80)
  297. print('【9】多维度交叉分析')
  298. print('='*80)
  299. # 时间 + T+1调整
  300. cross_analysis = t1_trades.groupby(['开仓年份', 'T+1调整']).agg({
  301. '盈亏金额': ['count', 'sum', 'mean'],
  302. '持仓小时': 'mean'
  303. }).round(2)
  304. cross_analysis.columns = ['交易次数', '总盈亏', '平均盈亏', '平均持仓']
  305. print('\n年度 x T+1调整:')
  306. print(cross_analysis.to_string())
  307. # 价格位置 + 持仓时长
  308. if t1_trades['价格位置分类'].notna().sum() > 0 and t1_trades['持仓时长分类'].notna().sum() > 0:
  309. print('\n价格位置 x 持仓时长 (平均盈亏):')
  310. cross_pnl = pd.pivot_table(t1_trades, values='盈亏金额',
  311. index='价格位置分类',
  312. columns='持仓时长分类',
  313. aggfunc='mean')
  314. print(cross_pnl.round(0).to_string())
  315. # 波动率 + T+1调整
  316. if t1_trades['波动率分类'].notna().sum() > 0:
  317. print('\n波动率 x T+1调整 (平均盈亏):')
  318. cross_vol = pd.pivot_table(t1_trades, values='盈亏金额',
  319. index='波动率分类',
  320. columns='T+1调整',
  321. aggfunc='mean')
  322. print(cross_vol.round(0).to_string())
  323. # ========== 10. 亏损根因分析 ==========
  324. print('\n' + '='*80)
  325. print('【10】亏损根因深度分析')
  326. print('='*80)
  327. # 找出最差的交易
  328. worst_trades = t1_trades.nsmallest(10, '盈亏金额')
  329. print('\n亏损TOP10交易特征:')
  330. for idx, row in worst_trades.iterrows():
  331. print(f"\n #{idx+1} 亏损{row['盈亏金额']:,.0f}元")
  332. print(f" 时间: {row['开仓时间']} → {row['平仓时间']}")
  333. print(f" 持仓: {row['持仓小时']:.1f}小时")
  334. print(f" 退出: {row['退出原因']}")
  335. print(f" T+1调整: {row['T+1调整']}")
  336. # 亏损交易共性分析
  337. print('\n\n亏损交易共性:')
  338. loss_trades = t1_trades[t1_trades['盈亏金额'] < 0]
  339. # 退出类型分布
  340. loss_exit = loss_trades['退出类型'].value_counts()
  341. print('\n 退出类型分布:')
  342. for exit_type, count in loss_exit.head(5).items():
  343. pct = count / len(loss_trades) * 100
  344. avg_loss = loss_trades[loss_trades['退出类型'] == exit_type]['盈亏金额'].mean()
  345. print(f' {exit_type}: {count}笔 ({pct:.1f}%), 平均亏损{avg_loss:,.0f}元')
  346. # T+1调整分布
  347. loss_t1 = loss_trades['T+1调整'].value_counts()
  348. print('\n T+1调整分布:')
  349. for t1_type, count in loss_t1.items():
  350. pct = count / len(loss_trades) * 100
  351. avg_loss = loss_trades[loss_trades['T+1调整'] == t1_type]['盈亏金额'].mean()
  352. print(f' {t1_type}: {count}笔 ({pct:.1f}%), 平均亏损{avg_loss:,.0f}元')
  353. # 持仓时长分布
  354. loss_duration = loss_trades['持仓时长分类'].value_counts()
  355. print('\n 亏损交易持仓时长分布:')
  356. for duration, count in loss_duration.items():
  357. if pd.notna(duration):
  358. pct = count / len(loss_trades) * 100
  359. print(f' {duration}: {count}笔 ({pct:.1f}%)')
  360. # ========== 11. 盈利因子分析 ==========
  361. print('\n' + '='*80)
  362. print('【11】盈利交易成功因子')
  363. print('='*80)
  364. profit_trades = t1_trades[t1_trades['盈亏金额'] > 0]
  365. print('\n盈利交易特征:')
  366. # 退出类型分布
  367. profit_exit = profit_trades['退出类型'].value_counts()
  368. print('\n 退出类型分布:')
  369. for exit_type, count in profit_exit.head(5).items():
  370. pct = count / len(profit_trades) * 100
  371. avg_profit = profit_trades[profit_trades['退出类型'] == exit_type]['盈亏金额'].mean()
  372. print(f' {exit_type}: {count}笔 ({pct:.1f}%), 平均盈利{avg_profit:,.0f}元')
  373. # T+1调整分布
  374. profit_t1 = profit_trades['T+1调整'].value_counts()
  375. print('\n T+1调整分布:')
  376. for t1_type, count in profit_t1.items():
  377. pct = count / len(profit_trades) * 100
  378. avg_profit = profit_trades[profit_trades['T+1调整'] == t1_type]['盈亏金额'].mean()
  379. print(f' {t1_type}: {count}笔 ({pct:.1f}%), 平均盈利{avg_profit:,.0f}元')
  380. # 持仓时长分布
  381. profit_duration = profit_trades['持仓时长分类'].value_counts()
  382. print('\n 盈利交易持仓时长分布:')
  383. for duration, count in profit_duration.items():
  384. if pd.notna(duration):
  385. pct = count / len(profit_trades) * 100
  386. print(f' {duration}: {count}笔 ({pct:.1f}%)')
  387. # ========== 12. 综合改进建议 ==========
  388. print('\n' + '='*80)
  389. print('【12】综合改进建议')
  390. print('='*80)
  391. print("""
  392. 基于多维度分析,提出以下改进策略:
  393. 【A. 时间维度优化】
  394. 1. 避开13:00-14:00开仓 (胜率22.9%)
  395. 2. 周五减少或停止交易 (胜率32.8%)
  396. 3. 优选周二、周三交易
  397. 【B. 持仓管理优化】
  398. 1. 限制持仓时长 > 16小时的交易 (过夜风险)
  399. 2. T+1调整后增加风控: 若当日已盈利,尾盘不平仓
  400. 3. 高波动期(>35%)降低仓位50%
  401. 【C. 价格位置过滤】
  402. 1. 避免在极高位置(>80%)开仓做多
  403. 2. 优先在低位(<40%)开仓
  404. 3. 结合20日高低点判断趋势
  405. 【D. 止盈止损优化】
  406. 1. 盈利交易平均持仓较短,可考虑收紧止盈
  407. 2. 亏损交易多因止损触发,检查止损位置是否合理
  408. 3. 考虑移动止损保护利润
  409. 【E. 信号质量提升】
  410. 1. 过滤低成交量信号(<0.5倍均量)
  411. 2. 增加趋势确认信号,避免逆势交易
  412. 3. 连续亏损3笔后暂停交易
  413. 【F. 风险管理强化】
  414. 1. 单日亏损超过3万当日停止
  415. 2. 连续2天亏损后降低仓位至30%
  416. 3. 月度亏损超过10万暂停策略复盘
  417. """)
  418. print('\n' + '='*80)
  419. print('分析完成')
  420. print('='*80)