Add flip execution diagnostics
This commit is contained in:
@@ -52,6 +52,7 @@ class ExecutionEngine(
|
|||||||
_flip_cooldown_seconds = 45
|
_flip_cooldown_seconds = 45
|
||||||
_loss_flip_confidence = 0.75
|
_loss_flip_confidence = 0.75
|
||||||
_last_flip_block_key: str | None = None
|
_last_flip_block_key: str | None = None
|
||||||
|
_last_flip_diagnostic_key: str | None = None
|
||||||
_runtime_action_cooldown_seconds = 30
|
_runtime_action_cooldown_seconds = 30
|
||||||
_last_runtime_action_key: str | None = None
|
_last_runtime_action_key: str | None = None
|
||||||
_emergency_halt_drawdown_usd = 250.0
|
_emergency_halt_drawdown_usd = 250.0
|
||||||
@@ -107,6 +108,18 @@ class ExecutionEngine(
|
|||||||
if protection_decision is not None:
|
if protection_decision is not None:
|
||||||
return protection_decision
|
return protection_decision
|
||||||
|
|
||||||
|
# Flip diagnostics before READY / supervisor.
|
||||||
|
# Это не меняет торговую логику: только фиксирует, что противоположный
|
||||||
|
# сигнал появился при уже открытой позиции.
|
||||||
|
flip_requested = self._should_flip_position(state)
|
||||||
|
|
||||||
|
if flip_requested:
|
||||||
|
self._log_flip_diagnostic(
|
||||||
|
state=state,
|
||||||
|
stage="FLIP_REQUESTED",
|
||||||
|
reason="opposite signal while position is open",
|
||||||
|
)
|
||||||
|
|
||||||
# Signal readiness validation
|
# Signal readiness validation
|
||||||
if state.decision_status != EXECUTION_DECISION_READY or not state.is_signal_ready:
|
if state.decision_status != EXECUTION_DECISION_READY or not state.is_signal_ready:
|
||||||
reason = (
|
reason = (
|
||||||
@@ -115,6 +128,13 @@ class ExecutionEngine(
|
|||||||
f"ready={state.is_signal_ready})."
|
f"ready={state.is_signal_ready})."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if flip_requested:
|
||||||
|
self._log_flip_diagnostic(
|
||||||
|
state=state,
|
||||||
|
stage="FLIP_BLOCKED_BY_READY",
|
||||||
|
reason=reason,
|
||||||
|
)
|
||||||
|
|
||||||
return self._skip_execution(
|
return self._skip_execution(
|
||||||
state,
|
state,
|
||||||
reason,
|
reason,
|
||||||
@@ -123,6 +143,13 @@ class ExecutionEngine(
|
|||||||
# Execution supervisor
|
# Execution supervisor
|
||||||
supervisor_decision = self._process_execution_supervisor(state)
|
supervisor_decision = self._process_execution_supervisor(state)
|
||||||
if supervisor_decision is not None:
|
if supervisor_decision is not None:
|
||||||
|
if flip_requested:
|
||||||
|
self._log_flip_diagnostic(
|
||||||
|
state=state,
|
||||||
|
stage="FLIP_BLOCKED_BY_SUPERVISOR",
|
||||||
|
reason=supervisor_decision.reason,
|
||||||
|
)
|
||||||
|
|
||||||
return supervisor_decision
|
return supervisor_decision
|
||||||
|
|
||||||
# Existing position validation
|
# Existing position validation
|
||||||
@@ -143,7 +170,7 @@ class ExecutionEngine(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Position flip
|
# Position flip
|
||||||
if self._should_flip_position(state):
|
if flip_requested:
|
||||||
flip_block_reason = self._flip_block_reason(state)
|
flip_block_reason = self._flip_block_reason(state)
|
||||||
|
|
||||||
if flip_block_reason is not None:
|
if flip_block_reason is not None:
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class _ExecutionFlipProtocol(Protocol):
|
|||||||
_flip_cooldown_seconds: int
|
_flip_cooldown_seconds: int
|
||||||
_loss_flip_confidence: float
|
_loss_flip_confidence: float
|
||||||
_last_flip_block_key: str | None
|
_last_flip_block_key: str | None
|
||||||
|
_last_flip_diagnostic_key: str | None
|
||||||
|
|
||||||
def _create_trade_id(self, state: AutoTradeState, side: str) -> str:
|
def _create_trade_id(self, state: AutoTradeState, side: str) -> str:
|
||||||
...
|
...
|
||||||
@@ -111,6 +112,67 @@ class _ExecutionFlipProtocol(Protocol):
|
|||||||
|
|
||||||
|
|
||||||
class ExecutionFlipMixin(_ExecutionFlipProtocol):
|
class ExecutionFlipMixin(_ExecutionFlipProtocol):
|
||||||
|
# ---------- Diagnostics ----------
|
||||||
|
# записать диагностическое событие по пути flip без изменения торговой логики
|
||||||
|
def _log_flip_diagnostic(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
state: AutoTradeState,
|
||||||
|
stage: str,
|
||||||
|
reason: str,
|
||||||
|
) -> None:
|
||||||
|
position = type(self)._position
|
||||||
|
confidence = safe_float(state.last_signal_confidence) or 0.0
|
||||||
|
repeat_count = int(safe_float(state.last_signal_repeat_count) or 0)
|
||||||
|
|
||||||
|
key = (
|
||||||
|
f"{stage}:"
|
||||||
|
f"{state.symbol}:"
|
||||||
|
f"{position.side}:"
|
||||||
|
f"{state.last_signal}:"
|
||||||
|
f"{repeat_count}:"
|
||||||
|
f"{confidence:.2f}:"
|
||||||
|
f"{reason}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if key == type(self)._last_flip_diagnostic_key:
|
||||||
|
return
|
||||||
|
|
||||||
|
type(self)._last_flip_diagnostic_key = key
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"execution_type": "FLIP_DIAGNOSTIC",
|
||||||
|
"stage": stage,
|
||||||
|
"reason": reason,
|
||||||
|
"symbol": state.symbol,
|
||||||
|
"position_side": position.side,
|
||||||
|
"signal": state.last_signal,
|
||||||
|
"signal_confidence": confidence,
|
||||||
|
"signal_repeat_count": repeat_count,
|
||||||
|
"decision_status": state.decision_status,
|
||||||
|
"is_signal_ready": state.is_signal_ready,
|
||||||
|
"is_signal_confirmed": state.is_signal_confirmed,
|
||||||
|
"execution_confidence_score": state.execution_confidence_score,
|
||||||
|
"execution_confidence_required_score": (
|
||||||
|
state.execution_confidence_required_score
|
||||||
|
),
|
||||||
|
"execution_block_reason": state.execution_block_reason,
|
||||||
|
"entry_block_reason": state.entry_block_reason,
|
||||||
|
"entry_block_message": state.entry_block_message,
|
||||||
|
"unrealized_pnl_usd": state.unrealized_pnl_usd,
|
||||||
|
**build_market_context_payload(state),
|
||||||
|
}
|
||||||
|
|
||||||
|
JournalService().log_ui_info(
|
||||||
|
event_type="position_flip_diagnostic",
|
||||||
|
message=f"Flip diagnostic: {stage} · {reason}",
|
||||||
|
screen="auto",
|
||||||
|
action="paper_execution",
|
||||||
|
payload=payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
EventBus.emit("paper_flip_diagnostic", payload)
|
||||||
|
|
||||||
# ---------- Payload builders ----------
|
# ---------- Payload builders ----------
|
||||||
# собрать payload отказа flip без изменения состояния
|
# собрать payload отказа flip без изменения состояния
|
||||||
def _build_flip_rejected_payload(
|
def _build_flip_rejected_payload(
|
||||||
|
|||||||
Reference in New Issue
Block a user