| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- 分层分析:区分"下跌趋势低波"与其他市场状态后,各指标的真实表现
- """
- import sys, io
- if sys.platform == 'win32':
- sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
- sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
- import pandas as pd
- import numpy as np
- import warnings
- warnings.filterwarnings('ignore')
- df = pd.read_csv(
- 'D:/work/project/cyb50-quant/cat-fly/t1/t1_trades_with_environment_20260327_141655.csv',
- encoding='utf-8-sig'
- )
- cols = [
- '交易方向','开仓时间','平仓时间','开仓价格','平仓价格','仓位',
- '盈亏金额','盈亏百分比','退出原因','持仓周期数','持仓小时数',
- 'T1调整','原平仓时间','原平仓价格','原盈亏','盈亏变化',
- '入场信号','开仓市值','平仓时资金','市场状态',
- '趋势短期','趋势中期','趋势强度','波动率分位','波动率水平',
- '成交量分位','布林带位置','布林带区域','RSI分位','RSI区域',
- '1日动量','入场价格'
- ]
- df.columns = cols
- df['开仓时间'] = pd.to_datetime(df['开仓时间'])
- df['年份'] = df['开仓时间'].dt.year
- df['盈利'] = df['盈亏金额'] > 0
- # ── 核心分层 ──────────────────────────────────────────────
- death_zone = df['市场状态'] == '下跌趋势低波'
- good_zone = ~death_zone
- dz = df[death_zone] # 死亡区:下跌趋势低波
- gz = df[good_zone] # 其余环境
- SEP = '=' * 70
- def show(title):
- print(f'\n{SEP}')
- print(f' {title}')
- print(SEP)
- def breakdown(sub, name):
- n = len(sub)
- wr = sub['盈利'].mean() if n > 0 else 0
- avg = sub['盈亏金额'].mean() if n > 0 else 0
- total = sub['盈亏金额'].sum()
- print(f' {name}: {n}笔 | 胜率{wr:.1%} | 均盈亏{avg:+,.0f}元 | 总盈亏{total:+,.0f}元')
- # ── 总览 ─────────────────────────────────────────────────
- show('总览:下跌趋势低波 vs 其余环境')
- breakdown(df, '全部')
- breakdown(dz, '下跌趋势低波 (死亡区)')
- breakdown(gz, '其余市场状态 (非死亡区)')
- print()
- print(' 年份分布:')
- for y in sorted(df['年份'].unique()):
- total_y = len(df[df['年份'] == y])
- dz_y = len(dz[dz['年份'] == y])
- pct = dz_y / total_y if total_y > 0 else 0
- gz_sub = df[(df['年份'] == y) & good_zone]
- gz_wr = gz_sub['盈利'].mean() if len(gz_sub) > 0 else 0
- print(f' {y}年: 死亡区占比{pct:.1%}({dz_y}/{total_y}笔) | 非死亡区胜率{gz_wr:.1%}({len(gz_sub)}笔)')
- # ── 在死亡区内,各指标表现 ───────────────────────────────
- show('死亡区内各指标表现(理解为何之前的规则误导)')
- for col in ['波动率水平', 'RSI区域', 'T1调整']:
- print(f'\n [{col}] 在死亡区:')
- for val in dz[col].dropna().unique():
- sub = dz[dz[col] == val]
- if len(sub) < 3:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- print(f' {val}: {len(sub)}笔, 胜率{wr:.1%}, 均盈亏{avg:+,.0f}元')
- # ── 在非死亡区,各指标真实表现 ───────────────────────────
- show('非死亡区内各指标表现(剔除死亡区噪音后的真实信号)')
- for col in ['波动率水平', 'RSI区域', 'T1调整', '市场状态']:
- print(f'\n [{col}] 在非死亡区:')
- results = []
- for val in gz[col].dropna().unique():
- sub = gz[gz[col] == val]
- if len(sub) < 5:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- results.append((val, len(sub), wr, avg))
- results.sort(key=lambda x: x[2], reverse=True)
- for val, n, wr, avg in results:
- print(f' {val}: {n}笔, 胜率{wr:.1%}, 均盈亏{avg:+,.0f}元')
- # ── 非死亡区内 波动率分位 精确阈值 ─────────────────────
- show('非死亡区内 波动率分位 阈值扫描')
- valid = gz[gz['波动率分位'].notna()].copy()
- thresholds = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
- print(f'\n 波动率分位区间 笔数 胜率 均盈亏')
- bins = [(0, 0.3), (0.3, 0.5), (0.5, 0.7), (0.7, 0.85), (0.85, 1.01)]
- for lo, hi in bins:
- sub = valid[(valid['波动率分位'] >= lo) & (valid['波动率分位'] < hi)]
- if len(sub) < 3:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- print(f' [{lo:.2f}, {hi:.2f}): {len(sub):>4}笔 {wr:.1%} {avg:>+8,.0f}元')
- # ── 非死亡区内 RSI分位 精确阈值 ────────────────────────
- show('非死亡区内 RSI分位 阈值扫描')
- valid2 = gz[gz['RSI分位'].notna()].copy()
- print(f'\n RSI分位区间 笔数 胜率 均盈亏')
- bins2 = [(0, 0.05), (0.05, 0.1), (0.1, 0.2), (0.2, 0.35), (0.35, 0.6), (0.6, 1.01)]
- for lo, hi in bins2:
- sub = valid2[(valid2['RSI分位'] >= lo) & (valid2['RSI分位'] < hi)]
- if len(sub) < 3:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- print(f' [{lo:.2f}, {hi:.2f}): {len(sub):>4}笔 {wr:.1%} {avg:>+8,.0f}元')
- # ── 非死亡区内 双指标组合 ────────────────────────────────
- show('非死亡区内 双指标组合(市场状态 × 波动率水平)')
- combos = []
- for ms in gz['市场状态'].dropna().unique():
- for vl in gz['波动率水平'].dropna().unique():
- sub = gz[(gz['市场状态'] == ms) & (gz['波动率水平'] == vl)]
- if len(sub) < 5:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- combos.append((f'{ms} × {vl}', len(sub), wr, avg))
- combos.sort(key=lambda x: x[2], reverse=True)
- print(f'\n {"组合":<25} 笔数 胜率 均盈亏')
- for cond, n, wr, avg in combos[:15]:
- print(f' {cond:<25} {n:>4} {wr:.1%} {avg:>+8,.0f}元')
- # ── 非死亡区内 RSI × 波动率 ────────────────────────────
- show('非死亡区内 RSI区域 × 波动率水平 组合')
- combos2 = []
- for rs in gz['RSI区域'].dropna().unique():
- for vl in gz['波动率水平'].dropna().unique():
- sub = gz[(gz['RSI区域'] == rs) & (gz['波动率水平'] == vl)]
- if len(sub) < 5:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- combos2.append((f'{rs} × {vl}', len(sub), wr, avg))
- combos2.sort(key=lambda x: x[2], reverse=True)
- print(f'\n {"组合":<28} 笔数 胜率 均盈亏')
- for cond, n, wr, avg in combos2[:15]:
- print(f' {cond:<28} {n:>4} {wr:.1%} {avg:>+8,.0f}元')
- # ── 非死亡区内 趋势强度 分段 ─────────────────────────────
- show('非死亡区内 趋势强度 分段')
- valid3 = gz[gz['趋势强度'].notna()].copy()
- bins3 = [(0, 1.0), (1.0, 1.5), (1.5, 2.5), (2.5, 4.0), (4.0, 10)]
- print(f'\n 趋势强度区间 笔数 胜率 均盈亏')
- for lo, hi in bins3:
- sub = valid3[(valid3['趋势强度'] >= lo) & (valid3['趋势强度'] < hi)]
- if len(sub) < 3:
- continue
- wr = sub['盈利'].mean()
- avg = sub['盈亏金额'].mean()
- print(f' [{lo:.1f}, {hi:.1f}): {len(sub):>4}笔 {wr:.1%} {avg:>+8,.0f}元')
- # ── 验证:用"仅排除死亡区"作为过滤,各年表现 ─────────────
- show('仅排除死亡区后的各年绩效(最简化规则的效果)')
- for y in sorted(df['年份'].unique()):
- all_y = df[df['年份'] == y]
- keep_y = gz[gz['年份'] == y]
- drop_y = dz[dz['年份'] == y]
- kn, kwr, ktotal = len(keep_y), keep_y['盈利'].mean() if len(keep_y) else 0, keep_y['盈亏金额'].sum()
- dn = len(drop_y)
- print(f' {y}年: 保留{kn}笔 胜率{kwr:.1%} {ktotal:+,.0f}元 | 过滤{dn}笔')
- print()
- print(SEP)
- print(' 结论摘要')
- print(SEP)
- print()
- print(' "下跌趋势低波"是唯一应该被完全排除的市场状态。')
- print(' 在此之外的所有环境,包括极高波动、RSI超卖,策略依然有效甚至更好。')
- print()
- print(' 非死亡区内的辅助加分指标(待从上方组合数据中提炼):')
- print(' 详见上方各维度组合分析结果。')
|