Files
big-qmt/labs/analysis/etf/compare.py
2026-09-19 19:45:43 +08:00

105 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""旧配置 vs 新配置etc/_etf.yaml对照并给出新配置下的资金需求与规模敏感性。
用法: py -3.14 -B analysis/etf/compare.py
"""
import sys
from dataclasses import replace
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
from backtest import ( # noqa: E402
REPO_DEFAULTS, SYMBOLS, SYMBOL_PARAMS, EtfSymbolConfig, analyze, fetch_daily,
precondition, simulate,
)
data = {code: fetch_daily(code) for code in SYMBOLS}
# 旧配置(本报告第一版的 etc/_etf.yaml三只都是 1000 股 / 10000 股上限)
OLD_PARAMS = {
"588000.SH": dict(is_t0=False, buy_shares=1000, max_shares=10000, atr_multiplier=0.5, inner_step=0.9),
"510300.SH": dict(is_t0=False, buy_shares=1000, max_shares=10000, atr_multiplier=1.0, inner_step=0.7),
"518880.SH": dict(is_t0=True, buy_shares=1000, max_shares=10000, atr_multiplier=1.0, inner_step=0.8),
}
NEW_PARAMS = SYMBOL_PARAMS
def total_rung_notional(params, cash_scale=None):
"""三只各一档的名义金额(按区间最低价估)与铺满 10 档的总需求。"""
one = sum(p["buy_shares"] * min(b["low"] for b in data[c]) for c, p in params.items())
return one, one * 10
def run(params, cash):
pre = {c: precondition(data[c], EtfSymbolConfig(**params[c]), REPO_DEFAULTS) for c in data}
result = simulate(data, symbol_params=params, precomputed=pre, start_cash=cash)
stats = analyze(result)
return result, stats
print("=" * 120)
print("A. 两版配置的资金需求(按各标的区间最低价估算)")
print("=" * 120)
for label, params in (("1000 股/档)", OLD_PARAMS), ("10000/4000/2000 股/档)", NEW_PARAMS)):
one, full = total_rung_notional(params)
detail = " ".join(
f"{c[:6]}={p['buy_shares']}股×{min(b['low'] for b in data[c]):.2f}{p['buy_shares'] * min(b['low'] for b in data[c]):,.0f}"
for c, p in params.items()
)
print(f"{label:28} 三只各 1 档 ≈ {one:>10,.0f} 铺满 10 档 ≈ {full:>10,.0f}")
print(f"{'':28} {detail}")
print()
print("=" * 120)
print("B. 旧 vs 新:同一资金 50 万,同一天数据、同一套逻辑")
print("=" * 120)
print(f"{'配置':24} {'权益变动':>10} {'收益率':>7} {'最大回撤':>8} {'轮次':>5} {'胜率':>6} "
f"{'单轮均利':>8} {'平均占用':>8} {'峰值占用':>8} {'佣金':>7} {'名义周转':>8}")
for label, params in (("1000 股/档)", OLD_PARAMS), ("新(现 _etf.yaml", NEW_PARAMS)):
result, stats = run(params, 500_000.0)
trips = [f for f in result["fills"] if f.kind == "exit"]
delta = stats["final_equity"] - 500_000.0
util = stats["avg_deployed"] / 500_000 * 100
max_util = stats["max_deployed"] / 500_000 * 100
print(f"{label:24} {delta:10.2f} {stats['return_pct']:6.2f}% {stats['max_dd_pct']:7.2f}% "
f"{len(trips):5} {'':>6} {'':>8} {util:7.2f}% {max_util:7.2f}% {stats['fees']:7.2f} "
f"{stats['buy_amount'] / 500_000:7.2f}x")
print(f"{'':24} 已了结盈亏={stats['net']:>10.2f} 买入名义={stats['buy_amount']:>10.2f} "
f"占用ROI={delta / stats['avg_deployed'] * 100 if stats['avg_deployed'] else 0:.2f}%")
print()
print("=" * 120)
print("C. 新配置:绝对盈亏与账户规模无关(按固定股数下单),但收益率会摊薄")
print("=" * 120)
print(f"{'起始资金':>10} {'权益变动':>10} {'收益率':>7} {'最大回撤':>8} {'平均占用':>8} {'峰值占用':>8} {'底仓':>4} {'补仓':>4}")
for cash in (200_000, 300_000, 500_000, 800_000, 1_200_000):
result, stats = run(NEW_PARAMS, cash)
print(f"{cash:10,.0f} {stats['final_equity'] - cash:10.2f} {stats['return_pct']:6.2f}% "
f"{stats['max_dd_pct']:7.2f}% {stats['avg_deployed'] / cash * 100:7.2f}% "
f"{stats['max_deployed'] / cash * 100:7.2f}% "
f"{sum(1 for f in result['fills'] if f.kind == 'base'):4} "
f"{sum(1 for f in result['fills'] if f.kind == 'add'):4}")
print()
print("=" * 120)
print("D. 新配置下再放大/缩小单档股数(资金 50 万不变)")
print("=" * 120)
print(f"{'场景':26} {'权益变动':>10} {'收益率':>7} {'最大回撤':>8} {'平均占用':>8} {'峰值占用':>8} {'占用ROI':>8} {'收益/回撤':>8}")
for factor in (0.25, 0.5, 1.0, 2.0, 4.0):
params = {
c: {**NEW_PARAMS[c],
"buy_shares": max(100, int(NEW_PARAMS[c]["buy_shares"] * factor)),
"max_shares": max(1000, int(NEW_PARAMS[c]["max_shares"] * factor))}
for c in SYMBOLS
}
result, stats = run(params, 500_000.0)
delta = stats["final_equity"] - 500_000.0
roi = delta / stats["avg_deployed"] * 100 if stats["avg_deployed"] else 0.0
ratio = stats["return_pct"] / stats["max_dd_pct"] if stats["max_dd_pct"] > 0.01 else 0.0
label = "基准(现 _etf.yaml" if factor == 1.0 else f"buy_shares×{factor}"
print(f"{label:26} {delta:10.2f} {stats['return_pct']:6.2f}% {stats['max_dd_pct']:7.2f}% "
f"{stats['avg_deployed'] / 500_000 * 100:7.2f}% {stats['max_deployed'] / 500_000 * 100:7.2f}% "
f"{roi:7.2f}% {ratio:8.2f}")