""" 读取 full_comparison_result.json 并生成多维度排序对比表 用法: source .venv/bin/activate && python example/analyze_comparison.py """ import json import sys from pathlib import Path _project_root = Path(__file__).resolve().parent.parent.parent if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) JSON_PATH = _project_root / "engine" / "example" / "full_comparison_result.json" with open(JSON_PATH, "r", encoding="utf-8") as f: data = json.load(f) results = data["results"] cfg = data["config"] # ── 通用排序列 ── METRICS = [ "总收益%", "年化收益%", "夏普比率", "最大回撤%", "胜率%", "盈亏比", "交易次数", "平均盈亏", "最佳盈亏", "最差盈亏", "卡尔玛比率", ] # 哪些指标越大越好(正序),哪些越小越好(倒序) DESCENDING = {"总收益%", "年化收益%", "夏普比率", "胜率%", "盈亏比", "平均盈亏", "最佳盈亏", "卡尔玛比率"} ASCENDING = {"最大回撤%", "交易次数", "最差盈亏", "耗时s"} def sort_results(items, key, descending=True): """排序,返回 top N""" return sorted(items, key=lambda x: x.get(key, -9999) if key in DESCENDING else -x.get(key, 9999), reverse=descending) def print_table(title, rows, fields, col_widths): """打印格式化表格""" print() print("═" * len(title)) print(title) print("═" * len(title)) print() # header header = "" for i, f in enumerate(fields): header += f" {f:<{col_widths[i]}}" print(header) # separator sep = " " + "─" * (sum(col_widths) + sum(c - len(str(fields[i])) for i, c in enumerate(col_widths))) print(sep) for r in rows: line = "" for i, f in enumerate(fields): val = r.get(f, "") if isinstance(val, float): if abs(val) >= 1000: val_str = f"{val:>{col_widths[i]}.0f}" elif abs(val) >= 100: val_str = f"{val:>{col_widths[i]}.1f}" elif abs(val) >= 10: val_str = f"{val:>{col_widths[i]}.2f}" else: val_str = f"{val:>{col_widths[i]}.3f}" else: val_str = f"{str(val):<{col_widths[i]}}" line += f" {val_str}" print(line) print() # ════════════════════════════════════════════════════════ # 表1:全局排名 — 按年化收益 Top 30 # ════════════════════════════════════════════════════════ top_annual = sort_results(results, "年化收益%", True)[:30] print_table( f" 全局排名 — 按年化收益 Top 30(共{len(results)}组) | 本金 {cfg['initial_capital']:,.0f} USDT", top_annual, fields=["策略名", "币种", "时间级别", "数据量", "总收益%", "年化收益%", "夏普比率", "最大回撤%", "胜率%", "盈亏比", "交易次数", "日期范围"], col_widths=[22, 10, 8, 8, 9, 9, 8, 8, 7, 7, 6, 24], ) # ════════════════════════════════════════════════════════ # 表2:按夏普比率 Top 30 # ════════════════════════════════════════════════════════ top_sharpe = sort_results(results, "夏普比率", True)[:30] print_table( " 全局排名 — 按夏普比率 Top 30", top_sharpe, fields=["策略名", "币种", "时间级别", "数据量", "年化收益%", "夏普比率", "最大回撤%", "胜率%", "盈亏比", "交易次数", "日期范围"], col_widths=[22, 10, 8, 8, 9, 8, 8, 7, 7, 6, 24], ) # ════════════════════════════════════════════════════════ # 表3:近半年+近一年(近期真实表现)按年化 Top 30 # ════════════════════════════════════════════════════════ recent = [r for r in results if r["数据量"] in ("近半年", "近一年")] recent_top = sort_results(recent, "年化收益%", True)[:30] print_table( " 近期现实表现 — 近半年+近一年 — 按年化收益 Top 30", recent_top, fields=["策略名", "币种", "时间级别", "数据量", "总收益%", "年化收益%", "夏普比率", "最大回撤%", "胜率%", "盈亏比", "交易次数", "日期范围"], col_widths=[22, 10, 8, 8, 9, 9, 8, 8, 7, 7, 6, 24], ) # ════════════════════════════════════════════════════════ # 表4:全量数据(历史长期)按年化 Top 20 # ════════════════════════════════════════════════════════ full_data = [r for r in results if r["数据量"] == "全量"] full_top = sort_results(full_data, "年化收益%", True)[:20] print_table( " 全量数据 — 按年化收益 Top 20", full_top, fields=["策略名", "币种", "时间级别", "总收益%", "年化收益%", "夏普比率", "最大回撤%", "胜率%", "盈亏比", "交易次数", "日期范围"], col_widths=[22, 10, 8, 9, 9, 8, 8, 7, 7, 6, 24], ) # ════════════════════════════════════════════════════════ # 表5:各策略在4h+1d上的近一年表现(最实用的中长线) # ════════════════════════════════════════════════════════ mid_long = [r for r in recent if r["时间级别"] in ("4h", "1d")] mid_sorted = sort_results(mid_long, "年化收益%", True)[:30] print_table( " 中长线(4h/1d)近期表现 — 按年化收益 Top 30", mid_sorted, fields=["策略名", "币种", "时间级别", "数据量", "总收益%", "年化收益%", "夏普比率", "最大回撤%", "胜率%", "盈亏比", "交易次数", "日期范围"], col_widths=[22, 10, 8, 8, 9, 9, 8, 8, 7, 7, 6, 24], ) # ════════════════════════════════════════════════════════ # 表6:策略×币种×时间级别 盈利能力矩阵(近一年年化) # ════════════════════════════════════════════════════════ one_year = [r for r in results if r["数据量"] == "近一年"] print() print("═" * 120) print(" 近一年盈利能力矩阵 — 策略 × 币种 × 时间级别(年化收益%)") print("═" * 120) for tf in cfg["timeframes"]: tf_data = [r for r in one_year if r["时间级别"] == tf] if not tf_data: continue print(f"\n ▲ 时间级别: {tf}") print(f" {'策略名':<24}", end="") for sym in cfg["symbols"]: print(f" {sym:>12}", end="") print() print(" " + "─" * 80) strategies_seen = set() for r in sort_results(tf_data, "年化收益%", True): if r["策略名"] not in strategies_seen: strategies_seen.add(r["策略名"]) print(f" {r['策略名']:<24}", end="") for sym in cfg["symbols"]: match = [x for x in tf_data if x["策略名"] == r["策略名"] and x["币种"] == sym] if match: val = match[0]["年化收益%"] color = "+" if val > 0 else "" print(f" {color}{val:>11.1f}%", end="") else: print(f" {'—':>12}", end="") print() print() # ════════════════════════════════════════════════════════ # 表7:每组(时间级别+数据量)下的最佳策略 # ════════════════════════════════════════════════════════ print("═" * 160) print(" 每组(时间级别 + 数据量)下的最佳策略 — 按年化收益") print("═" * 160) print() for tf in cfg["timeframes"]: for period in cfg["periods"]: subset = [r for r in results if r["时间级别"] == tf and r["数据量"] == period] if not subset: continue subset_sorted = sort_results(subset, "年化收益%", True) print(f" ▲ {tf} | {period}") print(f" {'排名':<5} {'策略名':<24} {'币种':<10} {'总收益%':>9} {'年化%':>9} {'夏普':>7} {'回撤%':>8} {'胜率%':>7} {'盈亏比':>7} {'交易':>6} {'卡尔玛':>7} {'日期范围':<24}") print(" " + "─" * 155) for i, r in enumerate(subset_sorted[:8]): rank = ["🥇1", "🥈2", "🥉3", " 4", " 5", " 6", " 7", " 8"][i] print(f" {rank:<5} {r['策略名']:<24} {r['币种']:<10} {r['总收益%']:>8.1f}% {r['年化收益%']:>8.1f}% {r['夏普比率']:>7.2f} {r['最大回撤%']:>7.1f}% {r['胜率%']:>6.1f}% {r['盈亏比']:>7.2f} {r['交易次数']:>6} {r['卡尔玛比率']:>7.2f} {r['日期范围']:<24}") print() # ════════════════════════════════════════════════════════ # 表8:策略总览 — 每个策略在所有组合中的盈利比例 # ════════════════════════════════════════════════════════ print("═" * 120) print(" 策略胜率统计 — 每个策略在所有回测组合中的盈利比例") print("═" * 120) print() strategy_stats = {} for r in results: sn = r["策略名"] if sn not in strategy_stats: strategy_stats[sn] = {"total": 0, "positive": 0, "positive_annual": 0, "sum_return": 0, "sum_sharpe": 0} strategy_stats[sn]["total"] += 1 if r["总收益%"] > 0: strategy_stats[sn]["positive"] += 1 if r["年化收益%"] > 0: strategy_stats[sn]["positive_annual"] += 1 strategy_stats[sn]["sum_return"] += r["年化收益%"] strategy_stats[sn]["sum_sharpe"] += r["夏普比率"] print(f" {'策略名':<24} {'总回测':>6} {'总收益>0':>9} {'年化>0':>8} {'平均年化%':>10} {'平均夏普':>8}") print(" " + "─" * 75) for sn, st in sorted(strategy_stats.items(), key=lambda x: x[1]["positive_annual"] / x[1]["total"], reverse=True): avg_ret = st["sum_return"] / st["total"] avg_sharpe = st["sum_sharpe"] / st["total"] pos_pct = st["positive_annual"] / st["total"] * 100 print(f" {sn:<24} {st['total']:>6} {st['positive']:>8} ({st['positive']/st['total']*100:>5.1f}%) {st['positive_annual']:>7} ({pos_pct:>5.1f}%) {avg_ret:>9.1f}% {avg_sharpe:>8.2f}") print() print("═" * 120)