dita_v2(bingx_venue): make _publish_telemetry TOTAL — telemetry cannot raise into the order path
The pre-existing guard covered only publish(). The attribute/coercion block above it (int()/str()/.metadata.get() on intent+order) was UNGUARDED, and kwarg binding happens before the body runs at all — so a signature skew sailed straight past a defence that existed precisely to prevent this, and orphaned 6 live positions. Everything that can raise now lives inside one try. Failures log loud and are swallowed; the order path is never affected. Proven against: exploding plane, poisoned intent coercion (the formerly-unguarded region), and absent plane. ARCHITECTURAL DEBT (raised by HJ, not fixed here): telemetry has no business being called inline on the exec path at all. Correct shape = fire-and-forget enqueue drained by the M6 journal lane. This commit makes it harmless, not absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -302,29 +302,39 @@ class BingxVenueAdapter(VenueAdapter):
|
||||
client_order_id: str = "",
|
||||
details: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
plane = self._telemetry_plane
|
||||
publish = getattr(plane, "publish_venue", None) if plane is not None else None
|
||||
if publish is None:
|
||||
return
|
||||
slot_id = 0
|
||||
trade_id = ""
|
||||
asset = ""
|
||||
side = TradeSide.FLAT
|
||||
action = ""
|
||||
intent_id = ""
|
||||
if intent is not None:
|
||||
slot_id = int(getattr(intent, "slot_id", 0) or 0)
|
||||
trade_id = str(getattr(intent, "trade_id", "") or "")
|
||||
asset = str(getattr(intent, "asset", "") or "")
|
||||
side = getattr(intent, "side", TradeSide.FLAT)
|
||||
action = str(getattr(intent, "action", "") or "")
|
||||
intent_id = str(getattr(intent, "intent_id", "") or "")
|
||||
if order is not None:
|
||||
slot_id = int(order.metadata.get("slot_id", slot_id) or slot_id)
|
||||
trade_id = str(order.internal_trade_id or trade_id)
|
||||
asset = str(order.metadata.get("asset") or asset)
|
||||
side = order.side or side
|
||||
# TOTAL: this method cannot raise. It runs INLINE on the order path —
|
||||
# submit()/submit_async() call it immediately after the venue has already
|
||||
# accepted the order. An exception escaping here reaches the caller's
|
||||
# submit guard, which synthesises a REJECTED event and rolls the FSM back
|
||||
# while the venue keeps the position (orphan; 6 of them on 2026-07-13).
|
||||
#
|
||||
# The guard used to cover only publish(), leaving the attribute/coercion
|
||||
# block below it unprotected — which is how a signature skew orphaned live
|
||||
# positions past a defence that existed precisely to prevent it. Everything
|
||||
# that can raise now lives inside the try. Observability never vetoes a fill.
|
||||
try:
|
||||
plane = self._telemetry_plane
|
||||
publish = getattr(plane, "publish_venue", None) if plane is not None else None
|
||||
if publish is None:
|
||||
return
|
||||
slot_id = 0
|
||||
trade_id = ""
|
||||
asset = ""
|
||||
side = TradeSide.FLAT
|
||||
action = ""
|
||||
intent_id = ""
|
||||
if intent is not None:
|
||||
slot_id = int(getattr(intent, "slot_id", 0) or 0)
|
||||
trade_id = str(getattr(intent, "trade_id", "") or "")
|
||||
asset = str(getattr(intent, "asset", "") or "")
|
||||
side = getattr(intent, "side", TradeSide.FLAT)
|
||||
action = str(getattr(intent, "action", "") or "")
|
||||
intent_id = str(getattr(intent, "intent_id", "") or "")
|
||||
if order is not None:
|
||||
slot_id = int(order.metadata.get("slot_id", slot_id) or slot_id)
|
||||
trade_id = str(order.internal_trade_id or trade_id)
|
||||
asset = str(order.metadata.get("asset") or asset)
|
||||
side = order.side or side
|
||||
publish(
|
||||
VenueTelemetrySnapshot(
|
||||
phase=phase,
|
||||
@@ -350,8 +360,14 @@ class BingxVenueAdapter(VenueAdapter):
|
||||
details=dict(details or {}),
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as exc:
|
||||
import logging as _log
|
||||
|
||||
_log.getLogger(__name__).warning(
|
||||
"FIX(bingx_venue): telemetry publish failed (phase=%s) — swallowed, "
|
||||
"order path unaffected: %s",
|
||||
phase, exc,
|
||||
)
|
||||
|
||||
def _call_backend(self, method_name: str, *args: Any, **kwargs: Any) -> Any:
|
||||
method = getattr(self.backend, method_name, None)
|
||||
|
||||
Reference in New Issue
Block a user