This commit is contained in:
2026-09-19 19:45:43 +08:00
parent 7183cb45f8
commit 8131b158b4
60 changed files with 7669 additions and 909 deletions

View File

@@ -0,0 +1,104 @@
"""旧配置 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}")