tradingSystem/scripts/probe_bionic.py

120 lines
5.7 KiB
Python
Raw Normal View History

2026-08-03 12:49:01 +08:00
# -*- 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()