07.4.4.1.3 — Journal Runtime Cleanup & Event Titles Layer
This commit is contained in:
@@ -582,7 +582,7 @@ class AutoTradeService:
|
||||
JournalService().log_ui_info(
|
||||
event_type="signal_summary",
|
||||
message=(
|
||||
f"🟡 HOLD {duration_text} завершён сигналом {next_signal}."
|
||||
f"HOLD длился {duration_text} и завершился сигналом {next_signal}."
|
||||
),
|
||||
screen="auto",
|
||||
action="signal_summary",
|
||||
@@ -622,7 +622,9 @@ class AutoTradeService:
|
||||
try:
|
||||
JournalService().log_ui_info(
|
||||
event_type="signal_ready",
|
||||
message=f"Сигнал {normalized_signal} готов к исполнению.",
|
||||
message=(
|
||||
f"Сигнал {normalized_signal} подтверждён и готов к исполнению."
|
||||
),
|
||||
screen="auto",
|
||||
action="signal_ready",
|
||||
payload={
|
||||
@@ -691,28 +693,15 @@ class AutoTradeService:
|
||||
and market_state != type(self)._last_logged_market_state
|
||||
)
|
||||
|
||||
trend_changed = (
|
||||
market_trend is not None
|
||||
and market_trend != previous_market_trend
|
||||
and market_trend != type(self)._last_logged_market_trend
|
||||
)
|
||||
|
||||
volatility_changed = (
|
||||
market_volatility is not None
|
||||
and market_volatility != previous_market_volatility
|
||||
and market_volatility != type(self)._last_logged_market_volatility
|
||||
)
|
||||
|
||||
if not state_changed and not trend_changed and not volatility_changed:
|
||||
if not state_changed and not volatility_changed:
|
||||
return
|
||||
|
||||
type(self)._last_logged_market_state = market_state
|
||||
type(self)._last_logged_market_trend = market_trend
|
||||
type(self)._last_logged_market_volatility = market_volatility
|
||||
|
||||
level = self._market_journal_level(market_state)
|
||||
message = self._market_state_message(market_state)
|
||||
|
||||
journal_payload = {
|
||||
**payload,
|
||||
"previous_market_state": previous_market_state,
|
||||
@@ -724,25 +713,64 @@ class AutoTradeService:
|
||||
}
|
||||
|
||||
try:
|
||||
if level == "WARNING":
|
||||
JournalService().log_ui_warning(
|
||||
if state_changed:
|
||||
self._write_market_journal_event(
|
||||
event_type="market_state_changed",
|
||||
message=message,
|
||||
screen="auto",
|
||||
action="market_analysis",
|
||||
market_state=market_state,
|
||||
message=self._market_state_message(market_state),
|
||||
payload=journal_payload,
|
||||
)
|
||||
return
|
||||
|
||||
JournalService().log_ui_info(
|
||||
event_type="market_state_changed",
|
||||
if volatility_changed:
|
||||
self._write_market_journal_event(
|
||||
event_type="market_volatility_changed",
|
||||
market_state=market_state,
|
||||
message=self._market_volatility_message(market_volatility),
|
||||
payload=journal_payload,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
type(self)._last_logged_market_state = market_state
|
||||
type(self)._last_logged_market_trend = market_trend
|
||||
type(self)._last_logged_market_volatility = market_volatility
|
||||
|
||||
def _write_market_journal_event(
|
||||
self,
|
||||
*,
|
||||
event_type: str,
|
||||
market_state: str,
|
||||
message: str,
|
||||
payload: dict,
|
||||
) -> None:
|
||||
level = self._market_journal_level(market_state)
|
||||
|
||||
if level == "WARNING":
|
||||
JournalService().log_ui_warning(
|
||||
event_type=event_type,
|
||||
message=message,
|
||||
screen="auto",
|
||||
action="market_analysis",
|
||||
payload=journal_payload,
|
||||
payload=payload,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
JournalService().log_ui_info(
|
||||
event_type=event_type,
|
||||
message=message,
|
||||
screen="auto",
|
||||
action="market_analysis",
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
def _market_volatility_message(self, market_volatility: str | None) -> str:
|
||||
messages = {
|
||||
"LOW": "Волатильность изменена: низкая.",
|
||||
"NORMAL": "Волатильность изменена: нормальная.",
|
||||
"HIGH": "Волатильность изменена: высокая.",
|
||||
}
|
||||
|
||||
return messages.get(str(market_volatility or ""), "Волатильность не определена.")
|
||||
|
||||
def _market_journal_level(self, market_state: str) -> str:
|
||||
if market_state == "HIGH_VOLATILITY":
|
||||
@@ -752,14 +780,14 @@ class AutoTradeService:
|
||||
|
||||
def _market_state_message(self, market_state: str) -> str:
|
||||
messages = {
|
||||
"TREND_UP": "📈 Рынок перешёл в рост.",
|
||||
"TREND_DOWN": "📉 Рынок перешёл в снижение.",
|
||||
"RANGE": "🟰 На рынке нет выраженного направления.",
|
||||
"HIGH_VOLATILITY": "⚠️ Рынок стал слишком волатильным.",
|
||||
"LOW_VOLATILITY": "💤 Рынок почти не движется.",
|
||||
"TREND_UP": "Состояние рынка изменено: рост.",
|
||||
"TREND_DOWN": "Состояние рынка изменено: снижение.",
|
||||
"RANGE": "Состояние рынка изменено: нет выраженного направления.",
|
||||
"HIGH_VOLATILITY": "Состояние рынка изменено: высокая волатильность.",
|
||||
"LOW_VOLATILITY": "Состояние рынка изменено: низкая активность.",
|
||||
}
|
||||
|
||||
return messages.get(market_state, "⏳ Состояние рынка анализируется.")
|
||||
return messages.get(market_state, "Состояние рынка анализируется.")
|
||||
|
||||
def run_cycle(self) -> AutoTradeState:
|
||||
state = self.get_state()
|
||||
|
||||
@@ -125,7 +125,7 @@ class ExecutionEngine:
|
||||
state.execution_block_reason = None
|
||||
state.last_flip_block_reason = None
|
||||
state.last_execution_action = action
|
||||
state.last_execution_reason = f"Paper ENTRY {side} открыта."
|
||||
state.last_execution_reason = f"Позиция {side} открыта."
|
||||
|
||||
payload = {
|
||||
"execution_type": "ENTRY",
|
||||
@@ -157,7 +157,7 @@ class ExecutionEngine:
|
||||
|
||||
EventBus.emit("paper_position_opened", payload)
|
||||
|
||||
return ExecutionDecision(action, True, f"Paper ENTRY {side} открыта.")
|
||||
return ExecutionDecision(action, True, f"Позиция {side} открыта.")
|
||||
|
||||
def _flip_position(self, state: AutoTradeState) -> ExecutionDecision:
|
||||
position = type(self)._position
|
||||
@@ -227,7 +227,7 @@ class ExecutionEngine:
|
||||
state.execution_block_reason = None
|
||||
state.last_flip_block_reason = None
|
||||
state.last_execution_action = f"FLIP_{old_side}_TO_{new_side}"
|
||||
state.last_execution_reason = "Paper FLIP выполнен."
|
||||
state.last_execution_reason = "Направление позиции изменено."
|
||||
state.last_flip_at = now
|
||||
type(self)._last_flip_block_key = None
|
||||
|
||||
@@ -278,7 +278,7 @@ class ExecutionEngine:
|
||||
return ExecutionDecision(
|
||||
f"FLIP_{old_side}_TO_{new_side}",
|
||||
True,
|
||||
f"Paper FLIP выполнен: {old_side} → {new_side}.",
|
||||
f"Направление позиции изменено: {old_side} → {new_side}.",
|
||||
)
|
||||
|
||||
def _close_position(
|
||||
@@ -359,9 +359,9 @@ class ExecutionEngine:
|
||||
else "CLOSE"
|
||||
)
|
||||
state.last_execution_reason = (
|
||||
f"Paper EXIT выполнена по риску: {forced_reason}."
|
||||
f"Позиция закрыта по правилу защиты: {forced_reason}."
|
||||
if forced_reason is not None
|
||||
else "Paper EXIT выполнена."
|
||||
else "Позиция закрыта."
|
||||
)
|
||||
type(self)._last_flip_block_key = None
|
||||
|
||||
@@ -369,10 +369,10 @@ class ExecutionEngine:
|
||||
return ExecutionDecision(
|
||||
f"FORCE_CLOSE_{forced_reason}",
|
||||
True,
|
||||
f"Paper EXIT выполнена по риску: {forced_reason}.",
|
||||
f"Позиция закрыта по правилу защиты: {forced_reason}.",
|
||||
)
|
||||
|
||||
return ExecutionDecision("CLOSE", True, "Paper EXIT выполнена.")
|
||||
return ExecutionDecision("CLOSE", True, "Позиция закрыта.")
|
||||
|
||||
def _risk_close_decision(self, state: AutoTradeState) -> ExecutionDecision | None:
|
||||
position = type(self)._position
|
||||
@@ -472,26 +472,26 @@ class ExecutionEngine:
|
||||
|
||||
if confidence < self._min_flip_confidence:
|
||||
return (
|
||||
"Flip blocked: signal confidence "
|
||||
f"{confidence:.2f} < {self._min_flip_confidence:.2f}."
|
||||
"уверенность сигнала ниже порога "
|
||||
f"({confidence:.2f} < {self._min_flip_confidence:.2f})"
|
||||
)
|
||||
|
||||
if repeat_count < self._min_flip_repeat_count:
|
||||
return (
|
||||
"Flip blocked: repeat count "
|
||||
f"{repeat_count} < {self._min_flip_repeat_count}."
|
||||
"сигнал ещё не подтверждён нужным количеством повторов "
|
||||
f"({repeat_count} < {self._min_flip_repeat_count})"
|
||||
)
|
||||
|
||||
if hold_seconds is not None and hold_seconds < self._min_flip_hold_seconds:
|
||||
return (
|
||||
"Flip blocked: position hold time "
|
||||
f"{hold_seconds}s < {self._min_flip_hold_seconds}s."
|
||||
"позиция открыта слишком недавно "
|
||||
f"({hold_seconds}с < {self._min_flip_hold_seconds}с)"
|
||||
)
|
||||
|
||||
if unrealized_pnl < 0 and confidence < self._loss_flip_confidence:
|
||||
return (
|
||||
"Flip blocked: position is negative and signal is not strong enough "
|
||||
f"({confidence:.2f} < {self._loss_flip_confidence:.2f})."
|
||||
"позиция сейчас в минусе, а сигнал недостаточно сильный "
|
||||
f"({confidence:.2f} < {self._loss_flip_confidence:.2f})"
|
||||
)
|
||||
|
||||
return None
|
||||
@@ -535,7 +535,7 @@ class ExecutionEngine:
|
||||
|
||||
JournalService().log_ui_warning(
|
||||
event_type="position_flip_blocked",
|
||||
message=f"Смена направления позиции заблокирована: {reason}",
|
||||
message=f"Смена направления позиции заблокирована: {reason}.",
|
||||
screen="auto",
|
||||
action="paper_execution",
|
||||
payload=payload,
|
||||
|
||||
@@ -13,30 +13,7 @@ from openpyxl import Workbook
|
||||
from openpyxl.styles import Font
|
||||
|
||||
from src.core.config import load_settings
|
||||
|
||||
|
||||
EVENT_TITLES = {
|
||||
"app_start": "Запуск приложения",
|
||||
"journal_open_requested": "Открытие журнала",
|
||||
"journal_export_csv_success": "Экспорт CSV",
|
||||
"journal_export_csv_error": "Ошибка экспорта CSV",
|
||||
"journal_export_xlsx_success": "Экспорт Excel",
|
||||
"journal_export_xlsx_error": "Ошибка экспорта Excel",
|
||||
"journal_cleared": "Журнал очищен",
|
||||
"journal_cleared_old": "Очистка старых записей",
|
||||
"system_open_alert": "Система загружена с предупреждениями",
|
||||
"system_open_success": "Система загружена",
|
||||
"market_open_requested": "Открытие рынка",
|
||||
"market_open_success": "Рынок загружен",
|
||||
"market_open_error": "Ошибка открытия рынка",
|
||||
"portfolio_open_requested": "Открытие портфеля",
|
||||
"portfolio_open_success": "Портфель загружен",
|
||||
"portfolio_open_error": "Ошибка открытия портфеля",
|
||||
"portfolio_partial_estimate": "Частичная оценка портфеля",
|
||||
"exchange_request_error": "Ошибка запроса к бирже",
|
||||
"balance_summary_loaded": "Баланс загружен",
|
||||
"balance_summary_error": "Ошибка загрузки баланса",
|
||||
}
|
||||
from src.core.event_titles import event_title
|
||||
|
||||
|
||||
_EMOJI_RE = re.compile(
|
||||
@@ -81,8 +58,7 @@ def _format_datetime(value: object) -> str:
|
||||
|
||||
|
||||
def _event_title(event_type: object) -> str:
|
||||
value = str(event_type or "").strip()
|
||||
return EVENT_TITLES.get(value, value.replace("_", " ").strip().capitalize())
|
||||
return event_title(event_type)
|
||||
|
||||
|
||||
def _payload(row: dict) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user