tradingSystem/scripts/test_batch32_units.py

237 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""盘中确认包 (2026-09-14 方案第三节, 台账 012)。全部离线, 不连库。
给技术面这一票加盘中确认, 只用于入场。时段与量比与解锁重问同源 (台账 003)。
A intraday_confirm 纯函数: 相位无关原样/收口突破要价与量同时满足/突破开关关不升/取不到按等待/
开口确认要均价与量/时段外开口进观察。
B 同源哨兵与映射带上轨: in_window 是 reask_rules 那一个; synthesize base 与 _compact 带 boll_upper。
C 接线 (proposal_service._apply_intraday): 突破改立场重合议为放行、硬数字带 intraday;
开口未确认路由改观察带 wait_confirm; 总闸关掉 _intraday_cfg 为 None (逐字回旧)。
E 参数登记与范围。
"""
import unit_test_guard # noqa: F401,单独运行也禁止联网
import os
import sys
import traceback
from datetime import datetime
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.core import tech_rules as tr # noqa: E402
from app.core import reask_rules as rr # noqa: E402
from app.services import param_store as ps # noqa: E402
RESULTS = []
def case(name):
def deco(fn):
RESULTS.append((name, fn))
return fn
return deco
def _st(phase, **kw):
"""一个技术面立场块 (synthesize 的紧凑态)。"""
d = {"stance": "中性", "strength": None, "phase": phase, "sar_side": "多",
"boll_upper": 10.0, "reason": "x"}
d.update(kw)
return d
NOW = datetime(2026, 9, 15, 10, 0) # 在 0945-1430 时段内
# ================================================================ A intraday_confirm 纯函数
@case("A 相位无关 (趋势多) → 原样返回, kind 空, 不改")
def _():
r = tr.intraday_confirm(_st("趋势多"), price=11, vwap=10, upper_prev=10,
vol_ratio=3, now=NOW)
assert r["kind"] is None and r["changed"] is False
@case("A 收口等待·价站上昨日上轨且量足 → breakout (看多/强/盘中开口向上)")
def _():
r = tr.intraday_confirm(_st("收口等待"), price=10.5, vwap=10.0, upper_prev=10.2,
vol_ratio=2.0, now=NOW, vol_min=1.5)
assert r["kind"] == "breakout" and r["changed"] is True
assert r["state"]["stance"] == "看多" and r["state"]["strength"] == "强"
assert r["state"]["phase"] == "盘中开口向上"
@case("A 收口等待·价没过上轨 → 不升 (原样, kind 空)")
def _():
r = tr.intraday_confirm(_st("收口等待"), price=10.1, vwap=10.0, upper_prev=10.2,
vol_ratio=2.0, now=NOW, vol_min=1.5)
assert r["kind"] is None and r["changed"] is False
@case("A 收口等待·量不足 → 不升")
def _():
r = tr.intraday_confirm(_st("收口等待"), price=10.5, vwap=10.0, upper_prev=10.2,
vol_ratio=1.2, now=NOW, vol_min=1.5)
assert r["kind"] is None
@case("A 收口等待·突破开关关掉 → 不升 (breakout_on 假)")
def _():
r = tr.intraday_confirm(_st("收口等待"), price=10.5, vwap=10.0, upper_prev=10.2,
vol_ratio=2.0, now=NOW, vol_min=1.5, breakout_on=False)
assert r["kind"] is None and r["changed"] is False
@case("A 收口等待·昨日上轨取不到 → 不升 (取不到不放行)")
def _():
r = tr.intraday_confirm(_st("收口等待"), price=10.5, vwap=10.0, upper_prev=None,
vol_ratio=2.0, now=NOW, vol_min=1.5)
assert r["kind"] is None
@case("A 开口向上·价≥均价且量足 → opened_ok, 块不变")
def _():
r = tr.intraday_confirm(_st("开口向上", stance="看多"), price=10.5, vwap=10.0,
upper_prev=10.2, vol_ratio=2.0, now=NOW, vol_min=1.5)
assert r["kind"] == "opened_ok" and r["changed"] is False
assert r["state"]["phase"] == "开口向上" and r["state"]["stance"] == "看多"
@case("A 开口向上·价低于均价 → opened_wait")
def _():
r = tr.intraday_confirm(_st("开口向上", stance="看多"), price=9.8, vwap=10.0,
upper_prev=10.2, vol_ratio=2.0, now=NOW, vol_min=1.5)
assert r["kind"] == "opened_wait" and r["changed"] is False
@case("A 开口向上·量不足 → opened_wait")
def _():
r = tr.intraday_confirm(_st("开口向上", stance="看多"), price=10.5, vwap=10.0,
upper_prev=10.2, vol_ratio=1.0, now=NOW, vol_min=1.5)
assert r["kind"] == "opened_wait"
@case("A 开口向上·读数取不到 (现价 None) → opened_wait (取不到按等待)")
def _():
r = tr.intraday_confirm(_st("开口向上", stance="看多"), price=None, vwap=10.0,
upper_prev=10.2, vol_ratio=2.0, now=NOW, vol_min=1.5)
assert r["kind"] == "opened_wait"
@case("A 时段外·开口向上 → outside_window (进观察)")
def _():
early = datetime(2026, 9, 15, 9, 40) # 早于 09:45
r = tr.intraday_confirm(_st("开口向上", stance="看多"), price=10.5, vwap=10.0,
upper_prev=10.2, vol_ratio=2.0, now=early, window="0945-1430")
assert r["kind"] == "outside_window" and r["changed"] is False
@case("A 时段外·收口等待 → 原样 (kind 空, 不动)")
def _():
early = datetime(2026, 9, 15, 9, 40)
r = tr.intraday_confirm(_st("收口等待"), price=10.5, vwap=10.0, upper_prev=10.2,
vol_ratio=2.0, now=early, window="0945-1430")
assert r["kind"] is None and r["changed"] is False
# ================================================================ B 同源哨兵与映射带上轨
@case("B 同源哨兵·tech_rules 的 in_window 就是 reask_rules 那一个函数对象")
def _():
assert tr.in_window is rr.in_window
@case("B 映射带上轨·synthesize base 与 _compact 都带 boll_upper")
def _():
from app.services import tech_service as ts
rows = [{"data_date": 20260914, "reanchored": 1, "quality": "OK", "sar_side": "多",
"boll_pos": 0.6, "boll_upper": 12.34, "boll_squeeze": 1, "sar_flip_days": 5,
"bbi_state": "多头区", "sar_value": 11.0, "boll_bw_pct": 3.0}]
st = tr.synthesize(rows)
assert st.get("boll_upper") == 12.34
assert "boll_upper" in ts._compact(st) and ts._compact(st)["boll_upper"] == 12.34
# ================================================================ C 接线 (proposal_service._apply_intraday)
def _cfg(**kw):
c = {"breakout_on": True, "vol_min": 1.5, "window": "0945-1430",
"cparams": {"fund_required": True, "consensus_route": True, "weak_confirm": True}}
c.update(kw)
return c
@case("C 突破·改立场重合议为放行, 硬数字 tech 带 intraday")
def _():
from app.services import proposal_service as pp
blocks = {"fund": {"stance": "看多"}, "timing": {"stance": "中性"},
"tech": {"phase": "收口等待", "boll_upper": 10.2, "stance": "中性"},
"consensus": {"route": "观察", "direction": "中性", "route_reason": "等技术面开口"}}
cache = {"600000.SH": (10.5, 10.0, 2.0)} # 价站上上轨、量足
kind = pp._apply_intraday("600000.SH", blocks, _cfg(), NOW, cache)
assert kind == "breakout"
assert blocks["tech"]["stance"] == "看多" and blocks["tech"]["phase"] == "盘中开口向上"
assert blocks["tech"]["intraday"]["kind"] == "breakout"
assert blocks["consensus"]["direction"] == "看多" and blocks["consensus"]["route"] == "放行"
@case("C 开口未确认·路由改观察带 wait_confirm, 硬数字带 intraday")
def _():
from app.services import proposal_service as pp
from app.core import action_engine as ae
blocks = {"fund": {"stance": "看多"}, "timing": {"stance": "看多"},
"tech": {"phase": "开口向上", "boll_upper": 10.2, "stance": "看多"},
"consensus": {"route": "放行", "direction": "看多"}}
cache = {"600000.SH": (9.5, 10.0, 2.0)} # 价低于均价 → 未确认
kind = pp._apply_intraday("600000.SH", blocks, _cfg(), NOW, cache)
assert kind == "opened_wait"
assert blocks["consensus"]["route"] == "观察"
assert blocks["consensus"]["disp"] == ae.DISP_WAIT_CONFIRM
assert blocks["tech"]["intraday"]["kind"] == "opened_wait"
@case("C 总闸关掉·_intraday_cfg 返回 None (逐字回旧)")
def _():
from app.services import proposal_service as pp
o = pp.param_store.get_bool
pp.param_store.get_bool = lambda k, d=False: (False if k == "PMS_TECH_INTRADAY_CONFIRM"
else o(k, d))
try:
assert pp._intraday_cfg() is None
finally:
pp.param_store.get_bool = o
# ================================================================ E 参数
@case("E 参数·盘中确认四键登记, 默认值对")
def _():
assert ps.RUNTIME_EXTRA["PMS_TECH_INTRADAY_CONFIRM"][0] is True
assert ps.RUNTIME_EXTRA["PMS_TECH_INTRADAY_BREAKOUT"][0] is True
assert abs(ps.RUNTIME_EXTRA["PMS_TECH_VOL_CONFIRM"][0] - 1.5) < 1e-9
assert ps.RUNTIME_EXTRA["PMS_TECH_INTRADAY_WINDOW"][0] == "0945-1430"
@case("E 参数·量比阈值范围校验 0.5 到 10")
def _():
assert ps._range_check("PMS_TECH_VOL_CONFIRM", 0.3) # 低于下限 → 报错串
assert ps._range_check("PMS_TECH_VOL_CONFIRM", 20) # 高于上限 → 报错串
assert ps._range_check("PMS_TECH_VOL_CONFIRM", 1.5) is None
def main():
ok = 0
for name, fn in RESULTS:
try:
fn()
ok += 1
print(" ok " + name)
except Exception:
print(" FAIL " + name)
traceback.print_exc()
print("-" * 60)
if ok == len(RESULTS):
print("ALL PASS (%d cases)" % ok)
return 0
print("FAILED %d/%d" % (len(RESULTS) - ok, len(RESULTS)))
return 1
if __name__ == "__main__":
sys.exit(main())