fix(live): 废单告警补第二肢治柜台级拒单盲区——08-31 T+1探针实锤:柜台拒单不进QMT当日订单API(query_stock_orders 15笔全SUCCEEDED不含被拒卖单),订单状态永停非终态,原告警等柜台报rejected结构性等不到;新肢=当日自提交订单EOD仍非终态且零成交→疑似废单(提交侧自证,不依赖QMT报状态;正常撤单已到终态不计,部分成交残量仍归守恒管);两肢合并入rejected_orders带why字段区分;+2测试(疑似单命中/撤单与跨日不误报);全量1368绿 [vps]
This commit is contained in:
@@ -310,22 +310,34 @@ def eod_reconcile(engine: Any, ledger: Any, db: str, account_id: int,
|
||||
"order_volume": ov, "ledger_volume": lv, "gap": ov - lv,
|
||||
})
|
||||
|
||||
# 当日废单告警(filled=0 的 rejected/failed):08-28 600818 实锤——盘中
|
||||
# 废单只有 ERROR 日志躺在文件里,复盘才发现当日目标缺仓 ~8 万;EOD 汇总
|
||||
# 让它当天可见。部分成交残量不进此清单(守恒/补插路径已覆盖)。
|
||||
# 当日废单告警:08-28 600818 实锤——盘中废单只有 ERROR 日志躺在文件里,
|
||||
# 复盘才发现当日目标缺仓 ~8 万;EOD 汇总让它当天可见。部分成交残量不进
|
||||
# 此清单(守恒/补插路径已覆盖)。两肢:
|
||||
# ① 柜台报 rejected/failed/error(交易所级拒单,QMT 订单视图可见);
|
||||
# ② EOD 仍非终态且零成交的当日自提交订单 = 疑似废单——柜台级拒单
|
||||
# 不进 QMT 当日订单 API(09-01 前实测 08-31 T+1 探针:query_stock_orders
|
||||
# 15 笔全 SUCCEEDED,被拒的卖单根本不在列表),状态永停非终态,等
|
||||
# 柜台报状态等不到,只能提交侧自证:下过的单收盘零成交又没到终态,
|
||||
# 就是废单/挂死(当日目标缺仓同形态;正常撤单已到终态不计)。
|
||||
rejected_orders = []
|
||||
for o in own_by_broker.values():
|
||||
if _status_str(getattr(o, "status", None)) not in ("rejected", "failed", "error"):
|
||||
continue
|
||||
st = _status_str(getattr(o, "status", None))
|
||||
o_dt = getattr(o, "add_time", None)
|
||||
if o_dt is not None and not str(o_dt).startswith(today):
|
||||
continue
|
||||
if int(getattr(o, "filled", 0) or 0) > 0:
|
||||
continue
|
||||
if st in ("rejected", "failed", "error"):
|
||||
why = "废单(柜台报rejected)"
|
||||
elif st not in _TERMINAL:
|
||||
why = "疑似废单(EOD非终态零成交)"
|
||||
else:
|
||||
continue
|
||||
rejected_orders.append({
|
||||
"symbol": str(getattr(o, "security", "") or "")[:6],
|
||||
"side": "buy" if bool(getattr(o, "is_buy", True)) else "sell",
|
||||
"amount": int(getattr(o, "amount", 0) or 0)})
|
||||
"amount": int(getattr(o, "amount", 0) or 0),
|
||||
"why": why})
|
||||
|
||||
summary = {"qmt_trades": len(trades_all), "ours": ours,
|
||||
"backfilled": backfilled,
|
||||
@@ -363,8 +375,8 @@ def eod_reconcile(engine: Any, ledger: Any, db: str, account_id: int,
|
||||
rsig = "%s|%s" % (today, rejected_orders)
|
||||
if rsig != _LAST_REJ_LOG_SIG:
|
||||
logger.warning(
|
||||
"[live-reconcile] 当日废单 %d 笔 (account=%s): %s——当日目标缺仓,"
|
||||
"查柜台废单原因(600818 形态)",
|
||||
"[live-reconcile] 当日废单/疑似废单 %d 笔 (account=%s): %s——当日"
|
||||
"目标缺仓,查柜台废单原因(600818/柜台拒单不进API 两形态)",
|
||||
len(rejected_orders), account_id, rejected_orders)
|
||||
_LAST_REJ_LOG_SIG = rsig
|
||||
return summary
|
||||
|
||||
@@ -354,7 +354,8 @@ class TestEodReconcile:
|
||||
with caplog.at_level("WARNING"):
|
||||
summary = eod_reconcile(eng, led, db, 19, "momentum")
|
||||
assert summary["rejected_orders"] == [
|
||||
{"symbol": "600818", "side": "buy", "amount": 10900}]
|
||||
{"symbol": "600818", "side": "buy", "amount": 10900,
|
||||
"why": "废单(柜台报rejected)"}]
|
||||
assert any("废单" in r.message for r in caplog.records)
|
||||
|
||||
def test_rejected_partial_or_crossday_not_in_alarm(self, db, caplog):
|
||||
@@ -370,6 +371,36 @@ class TestEodReconcile:
|
||||
assert summary["rejected_orders"] == []
|
||||
assert not any("废单" in r.message for r in caplog.records)
|
||||
|
||||
def test_stale_order_suspected_alarm(self, db, caplog):
|
||||
"""09-01 盲区补第二肢:当日订单 EOD 非终态+零成交 = 疑似废单。
|
||||
|
||||
08-31 T+1 探针实锤:柜台级拒单不进 QMT 当日订单 API,订单状态
|
||||
永停非终态——等柜台报 rejected 等不到,只能提交侧自证。
|
||||
"""
|
||||
led = LiveInstanceLedger()
|
||||
eng = _engine(
|
||||
[_order(status="submitted", filled=0, amount=100, is_buy=False,
|
||||
security="300059.XSHE", broker_oid="p1")], [])
|
||||
with caplog.at_level("WARNING"):
|
||||
summary = eod_reconcile(eng, led, db, 18, "channel_test")
|
||||
assert summary["rejected_orders"] == [
|
||||
{"symbol": "300059", "side": "sell", "amount": 100,
|
||||
"why": "疑似废单(EOD非终态零成交)"}]
|
||||
assert any("废单" in r.message for r in caplog.records)
|
||||
|
||||
def test_cancelled_or_crossday_stale_not_suspected(self, db, caplog):
|
||||
"""正常撤单(零成交但已到终态)与昨日挂死单都不进疑似清单。"""
|
||||
led = LiveInstanceLedger()
|
||||
eng = _engine(
|
||||
[_order(status="cancelled", filled=0, broker_oid="c1"),
|
||||
_order(status="submitted", filled=0, broker_oid="c2",
|
||||
add_time_="2020-01-01 10:00:00")],
|
||||
[])
|
||||
with caplog.at_level("WARNING"):
|
||||
summary = eod_reconcile(eng, led, db, 18, "s")
|
||||
assert summary["rejected_orders"] == []
|
||||
assert not any("废单" in r.message for r in caplog.records)
|
||||
|
||||
def test_yesterday_order_no_conservation_gap(self, db, caplog):
|
||||
"""08-31 守恒误报回归:引擎跨交易日存活(进程不死)时,**昨日**的
|
||||
终态足额订单不得进当日守恒对比——台账只记当日行,历史订单计入
|
||||
|
||||
Reference in New Issue
Block a user