"""加标的 vs 加仓位:同一引擎、按比例缩放资金,隔离"标的数量"的影响。 注意:把 universe 缩成 N 只、资金缩到 N/3 只能近似"同时持有更多标的", 但它同时缩短了白名单,所以结论按"资金可部署机会数"来读,而不是精确预测。 用法: py -3.14 -B analysis/etf/universe.py """ import sys from pathlib import Path HERE = Path(__file__).resolve().parent sys.path.insert(0, str(HERE)) from backtest import ( # noqa: E402 MIN_CASH_RATIO, START_CASH, SYMBOLS, SYMBOL_PARAMS, EtfDefaults, EtfSymbolConfig, analyze, fetch_daily, precondition, simulate, ) BASE = EtfDefaults() FULL = {code: fetch_daily(code) for code in SYMBOLS} # (白名单, 起始资金, 说明) CASES = [] for size in (1, 2, 3): codes = SYMBOLS[:size] CASES.append((f"白名单 {size} 只({'+'.join(c[:6] for c in codes)}),资金按 {size}/3 缩放", codes, START_CASH * size / 3)) # 同样 20 万资金,但只放 1 只 / 2 只 → 观察"钱多而标的少"是否浪费 CASES.append(("白名单 1 只,但仍是 20 万资金", SYMBOLS[:1], START_CASH)) CASES.append(("白名单 2 只,但仍是 20 万资金", SYMBOLS[:2], START_CASH)) CASES.append(("白名单 3 只,20 万(基准)", SYMBOLS[:3], START_CASH)) # 每档放大到 5000 股,资金同步放大到 100 万 → 检验"标的不变、仓位变大"的天花板 CASES.append(("3 只 + buy_shares=5000,资金 100 万", SYMBOLS[:3], 1_000_000.0)) CASES.append(("3 只 + buy_shares=10000,资金 200 万", SYMBOLS[:3], 2_000_000.0)) print(f"{'场景':52} {'资金':>10} {'权益变动':>10} {'收益率':>7} {'回撤':>6} " f"{'平均占用':>8} {'峰值占用':>8} {'ROI/占用':>8} {'底仓':>4} {'补仓':>4}") for label, codes, cash in CASES: data = {c: FULL[c] for c in codes} shares = 5000 if "5000" in label else 10000 if "10000" in label else SYMBOL_PARAMS[codes[0]]["buy_shares"] params = {c: {**SYMBOL_PARAMS[c], "buy_shares": shares, "max_shares": shares * 10} for c in codes} pre = {c: precondition(data[c], EtfSymbolConfig(**params[c]), BASE) for c in codes} result = simulate(data, symbol_params=params, precomputed=pre, start_cash=cash) stats = analyze(result) delta = stats["final_equity"] - cash util = stats["avg_deployed"] / cash * 100 max_util = stats["max_deployed"] / cash * 100 roi = delta / stats["avg_deployed"] * 100 if stats["avg_deployed"] else 0.0 print(f"{label:52} {cash:10.0f} {delta:10.2f} {stats['return_pct']:6.2f}% " f"{stats['max_dd_pct']:5.2f}% {util:7.2f}% {max_util:7.2f}% {roi: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}")