# -*- coding: utf-8 -*- """ 决策系统 (bionic) 两个 PMS 接口的实机探活 —— 只读, 不落任何表, 不产生任何指令 ============================================================================== 运行 (factorevaluation, 需 .env 能连参数表; --base 显式给地址则连参数表也不用): docker compose run --rm --no-deps pms-web python scripts/probe_bionic.py docker compose run --rm --no-deps pms-web python scripts/probe_bionic.py \ --base http://192.168.16.178:38000 --code 600000.SH 探两件事 (待办 #9 / #10 的接通自证, 契约见 BIONIC_PMS_INTERFACE.md): 1. /api/intraday/pms_exec 择时研判: 送一份手造的现场快照, 看回不回 FIRE/WAIT/UNAVAILABLE 2. /api/intraday/pms_judge 研判闸: 送一份手造的 DCA 候选, 看回不回 PASS/REJECT (研判走 LLM, 30~90 秒是正常的; --skip-judge 可只探择时) 判读: * exec 回 UNAVAILABLE 时看原因——「无实时价」(收盘后正常)、「没有昨夜结论/已过期」 (该股不在决策系统每晚分析范围, 换一只探) 都是正常降级不是故障; 盘中探到 FIRE/WAIT 才算全通。 * judge 回 UNAVAILABLE —— 看 reason: 队列超时说明 brain_queue 拥堵 (考虑独立队列), 连接拒绝说明地址/网络不通。 * 两个都通了: 页面把 PMS_JUDGE_API_BASE 填上 (研判闸即生效), 要切择时A再把 PMS_EXEC_IMPL 改成 A。 """ import argparse import json import os import sys import time sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) def main(): ap = argparse.ArgumentParser() ap.add_argument("--base", default="", help="bionic 根地址; 缺省读参数 " "PMS_EXEC_API_BASE / PMS_JUDGE_API_BASE") ap.add_argument("--code", default="600000.SH", help="探活用股票代码 (点式)") ap.add_argument("--skip-judge", action="store_true", help="只探择时, 不打 LLM") ap.add_argument("--judge-timeout", type=int, default=120) args = ap.parse_args() import requests base = args.base.rstrip("/") if not base: from app.services import exec_advisor, judge base = exec_advisor.base_url() or judge.base_url() if not base: print("[FAIL] 没有可用地址: --base 未给, PMS_EXEC_API_BASE / " "PMS_JUDGE_API_BASE 也都为空") sys.exit(2) print(f"目标: {base} 探活代码: {args.code}") # ---- 1. 择时研判 (纯代码, 应当秒回) ---- exec_payload = { "direction": "PMS_EXEC", "ts_code": args.code, "side": "buy", "action": "OPEN", "qty_left": 500, "is_last_day": False, "tdays_left": 3, "now": time.strftime("%H:%M"), "day": {"price": None, "vwap": None, "open": None, "high": None, "low": None, "day_chg_from_open": None, "bars": 0}, # 现场快照留空 → 由 bionic 自取实时价 "refs": {"support": None, "pressure": None, "stop": None, "source": None}, "position": {"total_qty": 0, "avail_qty": 0, "avg_cost": None, "cushion_pct": None}, } t0 = time.time() try: r = requests.post(f"{base}/api/intraday/pms_exec", json=exec_payload, timeout=15) body = r.json() ms = (time.time() - t0) * 1000 verdict = str(body.get("verdict") or "?") ok = verdict in ("FIRE", "WAIT", "UNAVAILABLE") print(f"[{'OK' if ok else 'FAIL'}] pms_exec {r.status_code} {ms:.0f}ms → {verdict}: " f"{body.get('reason')}") print(" " + json.dumps({k: body.get(k) for k in ("limit_price", "valid_min", "confidence", "missing", "advisor")}, ensure_ascii=False)) exec_ok = ok except Exception as e: print(f"[FAIL] pms_exec 打不通: {type(e).__name__}: {e}") exec_ok = False # ---- 2. 研判闸 (LLM, 慢是正常) ---- judge_ok = None if not args.skip_judge: judge_payload = { "direction": "PMS_JUDGE", "action": "DCA", "ts_code": args.code, "qty": 200, "reason": "[探活] 浮亏 -8.5% 触发第一档补仓评估", "hard_numbers": {"cushion_pct": -0.085, "stage": 1, "price": None}, "context": {"position": {"ts_code": args.code, "avg_cost": None, "total_qty": 1000, "cushion_pct": -0.085}, "recent_ledger": [], "probe": True}, "must_answer": ["下跌是杀逻辑还是杀情绪"], } print(f"研判闸探活中 (走 LLM, 最多等 {args.judge_timeout}s)...") t0 = time.time() try: r = requests.post(f"{base}/api/intraday/pms_judge", json=judge_payload, timeout=args.judge_timeout) body = r.json() sec = time.time() - t0 verdict = str(body.get("verdict") or "?") judge_ok = verdict in ("PASS", "REJECT") tag = "OK" if judge_ok else ("WARN" if verdict == "UNAVAILABLE" else "FAIL") print(f"[{tag}] pms_judge {r.status_code} {sec:.1f}s → {verdict} " f"(confidence={body.get('confidence')}): {body.get('reason')}") except Exception as e: print(f"[FAIL] pms_judge 打不通: {type(e).__name__}: {e}") judge_ok = False print("-" * 60) if exec_ok and judge_ok: print("两个接口都通。下一步: 页面填 PMS_JUDGE_API_BASE=" + base + " (研判闸即生效); 要切择时A再把 PMS_EXEC_IMPL 改成 A") elif exec_ok and judge_ok is None: print("择时接口通 (--skip-judge 未探研判闸)") sys.exit(0 if exec_ok and judge_ok is not False else 1) if __name__ == "__main__": main()