68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
"""在指定关键词范围内,按"最近 120 根无价格断层 + 规模/成交额"挑选 ETF。
|
|
|
|
用法:
|
|
py -3.14 -B labs/analysis/etf/pick_sector.py 通信
|
|
py -3.14 -B labs/analysis/etf/pick_sector.py 半导体 芯片
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import time
|
|
import urllib.parse
|
|
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 CACHE, DAILY_URL # noqa: E402
|
|
from screen_etf import fetch_all, code_of, size_yi, turnover_yi # noqa: E402
|
|
|
|
|
|
def window_clean(code: str) -> tuple[bool, float, str]:
|
|
"""最近 120 根内最大单日跳空;<=15% 视为干净。"""
|
|
path = CACHE / f"{code}.json"
|
|
try:
|
|
if path.exists():
|
|
rows = json.loads(path.read_text(encoding="utf-8"))
|
|
else:
|
|
request = urllib.request.Request(f"{DAILY_URL}?code={code}",
|
|
headers={"User-Agent": "Mozilla/5.0"})
|
|
with urllib.request.urlopen(request, timeout=30) as response:
|
|
rows = json.load(response)
|
|
CACHE.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(rows), encoding="utf-8")
|
|
except Exception as exc:
|
|
return False, 999.0, f"抓取失败:{exc}"
|
|
rows = sorted(rows, key=lambda r: str(r["trade_date"]))[-120:]
|
|
worst, day = 0.0, ""
|
|
for prev, cur in zip(rows, rows[1:]):
|
|
a, b = float(prev["close"]), float(cur["close"])
|
|
if a > 0 and abs(b / a - 1) * 100 > worst:
|
|
worst, day = abs(b / a - 1) * 100, str(cur["trade_date"])
|
|
return worst <= 15, worst, day
|
|
|
|
|
|
def main() -> int:
|
|
keywords = sys.argv[1:] or ["通信"]
|
|
wanted = {c: r for c, r in ((code_of(r), r) for r in fetch_all())
|
|
if any(k in str(r.get("f14") or "") for k in keywords)}
|
|
rows = sorted(wanted.values(), key=lambda r: -size_yi(r))
|
|
print(f"关键词 {keywords}:命中 {len(rows)} 只,按规模前 12 只做断层检查")
|
|
print(f"{'代码':11} {'名称':26} {'规模(亿)':>9} {'成交额(亿)':>10} {'窗口跳空':>9} 判定")
|
|
for row in rows[:12]:
|
|
code = code_of(row)
|
|
clean, worst, day = window_clean(code)
|
|
time.sleep(0.2)
|
|
print(f"{code:11} {str(row.get('f14')):26} {size_yi(row):9.2f} {turnover_yi(row):10.3f} "
|
|
f"{worst:8.1f}% {'可用' if clean else '有断层(' + day + ')'}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|