#!/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(' 详见上方各维度组合分析结果。')