105 lines
5.0 KiB
Python
105 lines
5.0 KiB
Python
"""旧配置 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}")
|