07.4.4.1.4 Entry Decision Diagnostics Layer
This commit is contained in:
@@ -31,6 +31,9 @@ EVENT_TITLES = {
|
||||
"market_stream_disconnected": "Рынок",
|
||||
"market_symbol_changed": "Рынок",
|
||||
|
||||
# Мониторинг позиций
|
||||
"entry_blocked": "Вход в позицию",
|
||||
|
||||
# Журнал
|
||||
"journal_exported": "Журнал",
|
||||
"journal_export_error": "Журнал",
|
||||
|
||||
@@ -155,16 +155,23 @@ def _build_waiting_text(state) -> str:
|
||||
entry_price_override=price,
|
||||
)
|
||||
|
||||
signal_lines = [
|
||||
_signal_line(state),
|
||||
_market_state_line(state),
|
||||
_entry_block_line(state),
|
||||
*_signal_confidence_lines(state),
|
||||
*_execution_block_lines(state),
|
||||
]
|
||||
|
||||
signal_lines = [line for line in signal_lines if line]
|
||||
|
||||
parts = [
|
||||
f"🤖 Автоторговля {_status_text(state.status)}",
|
||||
_account_mode_line(),
|
||||
"",
|
||||
f"<b>Доступно</b> · $ {_format_money_compact(available)}",
|
||||
"",
|
||||
_signal_line(state),
|
||||
_market_state_line(state),
|
||||
*_signal_confidence_lines(state),
|
||||
*_execution_block_lines(state),
|
||||
*signal_lines,
|
||||
"",
|
||||
"🧾 <b>Подготовка ордера</b>",
|
||||
"",
|
||||
@@ -253,16 +260,28 @@ def _market_state_line(state) -> str:
|
||||
return labels.get(market_state, "⏳ Рынок · Идёт анализ")
|
||||
|
||||
|
||||
def _entry_block_line(state) -> str:
|
||||
message = getattr(state, "entry_block_message", None)
|
||||
|
||||
if not message:
|
||||
return ""
|
||||
|
||||
return f"Вход в позицию · {message}"
|
||||
|
||||
|
||||
def _execution_block_lines(state) -> list[str]:
|
||||
lines: list[str] = []
|
||||
|
||||
reason = getattr(state, "execution_block_reason", None)
|
||||
if reason:
|
||||
lines.append(f"Blocked · {reason}")
|
||||
lines.append(f"Исполнение · {reason}")
|
||||
|
||||
adjustment = getattr(state, "execution_size_adjustment_reason", None)
|
||||
|
||||
if adjustment == "MARGIN_LIMIT":
|
||||
lines.append("Size adjusted by Max Reserved")
|
||||
lines.append(
|
||||
"Позиция ограничена настройкой Max Reserved."
|
||||
)
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class AutoTradeService:
|
||||
_last_logged_market_state: str | None = None
|
||||
_last_logged_market_trend: str | None = None
|
||||
_last_logged_market_volatility: str | None = None
|
||||
_last_logged_entry_block_reason: str | None = None
|
||||
_same_signal_count = 0
|
||||
|
||||
# debug: принудительно выставить сигнал и decision
|
||||
@@ -663,6 +664,8 @@ class AutoTradeService:
|
||||
state.market_volatility = payload.get("market_volatility")
|
||||
state.market_analysis_interval = payload.get("market_analysis_interval")
|
||||
state.market_analysis_reason = payload.get("market_analysis_reason")
|
||||
state.entry_block_reason = payload.get("entry_block_reason")
|
||||
state.entry_block_message = payload.get("entry_block_message")
|
||||
|
||||
self._log_market_state_if_changed(
|
||||
state=state,
|
||||
@@ -672,6 +675,48 @@ class AutoTradeService:
|
||||
previous_market_volatility=previous_market_volatility,
|
||||
)
|
||||
|
||||
self._log_entry_block_if_changed(
|
||||
state=state,
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
def _log_entry_block_if_changed(
|
||||
self,
|
||||
*,
|
||||
state: AutoTradeState,
|
||||
payload: dict,
|
||||
) -> None:
|
||||
reason = state.entry_block_reason
|
||||
message = state.entry_block_message
|
||||
|
||||
if not reason or not message:
|
||||
return
|
||||
|
||||
key = f"{state.status}:{state.symbol}:{state.strategy}:{reason}:{message}"
|
||||
|
||||
if key == type(self)._last_logged_entry_block_reason:
|
||||
return
|
||||
|
||||
type(self)._last_logged_entry_block_reason = key
|
||||
|
||||
try:
|
||||
JournalService().log_ui_info(
|
||||
event_type="entry_blocked",
|
||||
message=f"Вход в позицию не выполнен: {message}.",
|
||||
screen="auto",
|
||||
action="entry_diagnostics",
|
||||
payload={
|
||||
**payload,
|
||||
"entry_block_reason": reason,
|
||||
"entry_block_message": message,
|
||||
"symbol": state.symbol,
|
||||
"strategy": state.strategy,
|
||||
"status": state.status,
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _log_market_state_if_changed(
|
||||
self,
|
||||
*,
|
||||
|
||||
@@ -119,4 +119,10 @@ class AutoTradeState:
|
||||
market_analysis_interval: str | None = None
|
||||
|
||||
# объяснение последнего анализа рынка
|
||||
market_analysis_reason: str | None = None
|
||||
market_analysis_reason: str | None = None
|
||||
|
||||
# код причины, почему вход в позицию сейчас не выполнен
|
||||
entry_block_reason: str | None = None
|
||||
|
||||
# человекочитаемое объяснение причины не входа
|
||||
entry_block_message: str | None = None
|
||||
@@ -92,6 +92,8 @@ class TrendStrategy:
|
||||
payload={
|
||||
**base_payload,
|
||||
"market_filter_blocked": True,
|
||||
"entry_block_reason": "MARKET_FILTER_BLOCKED",
|
||||
"entry_block_message": "рынок сейчас не подходит для входа",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user