"""名单决策依据: 1) 逐标的对组合的净贡献(了结 + 未实现) 2) 规模/成交额(流动性)是否够网格用 3) 是否还有更贴题的品类 ETF(如真正的"存储") 4) 剔除弱标的后的组合结果对照 用法: py -3.14 -B labs/analysis/etf/decide_list.py """ import sys import urllib.request from pathlib import Path HERE = Path(__file__).resolve().parent sys.path.insert(0, str(HERE)) try: sys.stdout.reconfigure(encoding="utf-8", errors="replace") except Exception: pass from backtest import SYMBOLS, SYMBOL_PARAMS, analyze, fetch_daily, simulate # noqa: E402 from screen_etf import code_of, fetch_all, size_yi, turnover_yi # noqa: E402 CASH = 600_000.0 data = {code: fetch_daily(code) for code in SYMBOLS} # ---------- 1) 逐标的贡献 ---------- result = simulate(data, start_cash=CASH, fill_mode="touch") print("== 逐标的净贡献(了结 + 未实现,按市价;账户 60 万)==") print(f"{'代码':11} {'了结净额':>10} {'未了结股数':>9} {'未实现':>9} {'净额':>10} {'买入名义':>10}") contrib = {} for code in SYMBOLS: book = result["books"][code] fills = [f for f in result["fills"] if f.code == code] closed = sum((f.price * f.volume if f.side == "SELL" else -f.price * f.volume) - f.fee for f in fills) cost = book.avg_cost * book.volume close = data[code][-1]["close"] unreal = close * book.volume - cost if book.volume else 0.0 contrib[code] = closed + unreal print(f"{code:11} {closed:10,.0f} {book.volume:9} {unreal:9,.0f} {closed + unreal:10,.0f} " f"{sum(f.price * f.volume for f in fills if f.side == 'BUY'):10,.0f}") # ---------- 2) 规模/成交额 ---------- rows = fetch_all() info = {code_of(r): r for r in rows} print() print("== 流动性与规模 ==") print(f"{'代码':11} {'规模(亿)':>9} {'成交额(亿)':>10} {'名称':26} 评价") for code in SYMBOLS: row = info.get(code) if row is None: print(f"{code:11} {'?':>9} {'?':>10} 未在东财列表找到") continue size, turn = size_yi(row), turnover_yi(row) if size < 10 or turn < 0.3: verdict = "⚠️ 规模/成交偏小,网格成交与冲击成本风险高" elif size < 30 or turn < 1.0: verdict = "△ 中等,可接受但优先级靠后" else: verdict = "✓ 流动性充足" print(f"{code:11} {size:9.2f} {turn:10.3f} {str(row.get('f14')):26} {verdict}") # ---------- 3) 剔除弱标的后的对照 ---------- weak = [c for c in SYMBOLS if c in info and (size_yi(info[c]) < 10 or turnover_yi(info[c]) < 0.3)] if weak: print() print(f"== 剔除弱流动性标的 {weak} 后的组合 ==") kept = {c: data[c] for c in SYMBOLS if c not in weak} for label, universe in (("全部 16 只", data), (f"剔除 {len(weak)} 只 → {len(kept)} 只", kept)): res = simulate(universe, start_cash=CASH, fill_mode="touch") st = analyze(res) print(f" {label:24} 权益变动 {st['final_equity'] - CASH:10,.2f} " f"收益率 {st['return_pct']:6.2f}% 回撤 {st['max_dd_pct']:5.2f}% " f"底仓 {sum(1 for f in res['fills'] if f.kind == 'base'):4} " f"补仓 {sum(1 for f in res['fills'] if f.kind == 'add'):3} " f"佣金 {st['fees']:8,.2f}") else: print() print("== 没有规模/成交额低于阈值的标的 ==") # ---------- 4) 更贴题的品类 ETF(存储) ---------- print() print("== 存储/内存相关 ETF 全量搜索 ==") hits = [r for r in rows if any(k in str(r.get("f14") or "") for k in ("存储", "内存", "存储器"))] hits.sort(key=lambda r: -size_yi(r)) for row in hits[:10]: print(f" {code_of(row):11} {str(row.get('f14')):26} 规模={size_yi(row):8.2f}亿 " f"成交额={turnover_yi(row):7.3f}亿") print(f" 共 {len(hits)} 只名称含存储/内存的 ETF")