optz
This commit is contained in:
53
labs/analysis/etf/universe.py
Normal file
53
labs/analysis/etf/universe.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""加标的 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}")
|
||||
Reference in New Issue
Block a user