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

54 lines
2.7 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 加仓位:同一引擎、按比例缩放资金,隔离"标的数量"的影响。
注意:把 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}")