"""校验 py-client/etc/_etf.yaml:用策略自己的 config.load 解析,并核对每只标的的数据可用性。 用法: py -3.14 -B labs/analysis/etf/validate_config.py """ import sys import tempfile from pathlib import Path HERE = Path(__file__).resolve().parent REPO = HERE.parents[2] sys.path.insert(0, str(HERE)) sys.path.insert(0, str(REPO / "py-client")) try: sys.stdout.reconfigure(encoding="utf-8", errors="replace") except Exception: pass import yaml # noqa: E402 import config # noqa: E402 from backtest import CACHE, fetch_daily # noqa: E402 ETC = REPO / "py-client" / "etc" # 1) 用策略自身的校验逻辑加载(除了 _global/account 这两层,直接调 _etf_config) cfg = config._etf_config(ETC / "_etf.yaml") if cfg is None: print("FAIL: _etf_config 返回 None(文件不存在?)") raise SystemExit(1) print(f"配置解析通过:{len(cfg.symbols)} 只标的,顺序 = 资金优先级") print(f"{'#':>3} {'代码':11} {'每档':>6} {'上限':>7} {'T+0':>5} {'atr×':>5} {'inner':>6} 数据") ok = True for index, code in enumerate(cfg.codes, 1): symbol = cfg.symbols[code] try: bars = fetch_daily(code) first, last = bars[0]["date"], bars[-1]["date"] data = f"{len(bars)} 根 {first}..{last}" if len(bars) < 61: data += " ← 不足 61 根,策略会跳过" ok = False except Exception as exc: data = f"抓取失败:{exc}" ok = False print(f"{index:3} {code:11} {symbol.buy_shares:6} {symbol.max_shares:7} " f"{str(symbol.is_t0):>5} {symbol.atr_multiplier:5.1f} {symbol.inner_step:6.1f} {data}") # 2) 断言:inner_grids × inner_step < min_profit_pct,否则副出口不可能触发 grids, step = cfg.defaults.inner_grids, min(s.inner_step for s in cfg.symbols.values()) profit = cfg.defaults.min_profit_pct print() print(f"副出口可达性:inner_grids({grids}) × inner_step({step}) = {grids * step:.2f}% " f"vs min_profit_pct {profit}% → {'可达' if grids * step < profit else '不可达(会被主出口压制)'}") # 3) 估算资金需求 need_one = sum(cfg.symbols[c].buy_shares * fetch_daily(c)[-1]["close"] for c in cfg.codes) print(f"资金需求:16 只各铺 1 档 ≈ {need_one:,.0f} 元;各铺满 10 档 ≈ {need_one * 10:,.0f} 元") print() print("校验结果:", "通过" if ok else "存在问题(见上)") raise SystemExit(0 if ok else 1)