diff --git a/app/src/bootstrap/app_factory.py b/app/src/bootstrap/app_factory.py index facaaaf..b67fa57 100644 --- a/app/src/bootstrap/app_factory.py +++ b/app/src/bootstrap/app_factory.py @@ -39,8 +39,8 @@ def create_app() -> tuple[Bot, Dispatcher]: try: journal.log_info( - "app_start", - "Приложение запущено.", + "app_started", + "Приложение запущено", { "env": settings.app_env, "exchange_name": settings.exchange_name, diff --git a/app/src/integrations/exchange/market_data_runner.py b/app/src/integrations/exchange/market_data_runner.py index b635594..1579dad 100644 --- a/app/src/integrations/exchange/market_data_runner.py +++ b/app/src/integrations/exchange/market_data_runner.py @@ -46,13 +46,6 @@ class MarketDataRunner: existing.screen = screen existing.action = action existing.runtime_label = runtime_label - - cls._log_info( - existing, - "market_runner_context_updated", - "MarketDataRunner context updated.", - {"interval_seconds": interval_seconds}, - ) return context = MarketRuntimeContext( @@ -69,8 +62,8 @@ class MarketDataRunner: cls._log_info( context, - "market_runner_started", - "MarketDataRunner started.", + "market_monitor_started", + "Мониторинг рынка запущен.", {"interval_seconds": interval_seconds}, ) @@ -93,8 +86,8 @@ class MarketDataRunner: cls._log_info( context, - "market_runner_stopped", - "MarketDataRunner stopped.", + "market_monitor_stopped", + "Мониторинг рынка остановлен.", ) cls._runtimes.pop(runtime_key, None) @@ -107,11 +100,6 @@ class MarketDataRunner: symbol = context.symbol_provider() if not symbol: - cls._log_warning( - context, - "market_runner_no_symbol", - "MarketDataRunner has no symbol.", - ) await asyncio.sleep(context.interval_seconds) continue @@ -129,8 +117,8 @@ class MarketDataRunner: cls._log_info( context, - "market_runner_symbol_changed", - "MarketDataRunner symbol changed.", + "market_symbol_changed", + f"Инструмент автоторговли изменён на {cache_symbol}.", { "symbol": symbol, "cache_symbol": cache_symbol, @@ -143,10 +131,10 @@ class MarketDataRunner: except asyncio.CancelledError: raise except Exception as exc: - cls._log_error( + cls._log_warning( context, - "market_ws_error_fallback", - "WebSocket market data failed. Falling back to REST.", + "market_stream_disconnected", + "Поток рыночных данных отключён. Используется резервный REST-режим.", { "symbol": symbol, "cache_symbol": cache_symbol, @@ -165,17 +153,6 @@ class MarketDataRunner: cache_symbol = cls._cache_symbol(symbol) ws_symbol = cls._ws_symbol(symbol) - cls._log_info( - context, - "market_ws_connecting", - "Connecting market WebSocket.", - { - "requested_symbol": symbol, - "cache_symbol": cache_symbol, - "ws_symbol": ws_symbol, - }, - ) - payload_count = 0 async for payload in ExchangeWebSocketClient().stream_depth( @@ -185,8 +162,8 @@ class MarketDataRunner: if payload_count == 0: cls._log_info( context, - "market_ws_connected", - "Market WebSocket connected and first payload received.", + "market_stream_connected", + "Поток рыночных данных подключён.", { "requested_symbol": symbol, "cache_symbol": cache_symbol, @@ -200,33 +177,12 @@ class MarketDataRunner: current_symbol = context.symbol_provider() if current_symbol and current_symbol != symbol: - cls._log_info( - context, - "market_ws_symbol_switch", - "Market WebSocket stopped because symbol changed.", - { - "old_symbol": symbol, - "new_symbol": current_symbol, - }, - ) break best_bid = cls._extract_best_price(payload, "bids") best_ask = cls._extract_best_price(payload, "asks") if best_bid is None or best_ask is None: - cls._log_warning( - context, - "market_ws_payload_unrecognized", - "Market WebSocket payload does not contain recognizable bids/asks.", - { - "requested_symbol": symbol, - "cache_symbol": cache_symbol, - "ws_symbol": ws_symbol, - "payload_keys": list(payload.keys()), - "payload_preview": cls._safe_payload_preview(payload), - }, - ) continue MarketPriceCache.set_price( @@ -241,30 +197,16 @@ class MarketDataRunner: @classmethod async def _rest_fallback_once(cls, context: MarketRuntimeContext, symbol: str) -> None: try: - snapshot = await asyncio.to_thread( + await asyncio.to_thread( ExchangeService().refresh_market_snapshot_cache, symbol, runtime_key=context.runtime_key, ) - - cls._log_warning( - context, - "market_rest_fallback_success", - "REST fallback market snapshot loaded.", - { - "symbol": symbol, - "snapshot_symbol": snapshot.get("symbol"), - "source": snapshot.get("source"), - "last_price": snapshot.get("last_price"), - "bid_price": snapshot.get("bid_price"), - "ask_price": snapshot.get("ask_price"), - }, - ) except Exception as exc: cls._log_error( context, - "market_rest_fallback_error", - "REST fallback market snapshot failed.", + "market_stream_disconnected", + "Поток рыночных данных отключён. Резервный REST-режим недоступен.", { "symbol": symbol, "error": str(exc), diff --git a/app/src/notifications/channels/telegram.py b/app/src/notifications/channels/telegram.py index cefea86..78370af 100644 --- a/app/src/notifications/channels/telegram.py +++ b/app/src/notifications/channels/telegram.py @@ -16,8 +16,8 @@ class TelegramNotificationChannel: if bot is None or chat_id is None: JournalService().log_warning( - "notification_target_missing", - "Telegram notification target is not registered.", + "notification_error", + "Не удалось отправить Telegram-уведомление: получатель не настроен.", { "title": message.title, "priority": message.priority, @@ -36,8 +36,8 @@ class TelegramNotificationChannel: except TelegramRetryAfter as exc: JournalService().log_warning( - "notification_telegram_retry_after", - "Telegram notification delayed by retry-after.", + "notification_error", + "Не удалось отправить Telegram-уведомление: Telegram ограничил частоту отправки.", { "title": message.title, "retry_after": exc.retry_after, @@ -49,8 +49,8 @@ class TelegramNotificationChannel: except Exception as exc: JournalService().log_error( - "notification_telegram_error", - "Telegram notification failed.", + "notification_error", + "Не удалось отправить Telegram-уведомление.", { "title": message.title, "error": str(exc), diff --git a/app/src/notifications/service.py b/app/src/notifications/service.py index 73cf853..d07a6de 100644 --- a/app/src/notifications/service.py +++ b/app/src/notifications/service.py @@ -7,9 +7,7 @@ from src.notifications.dedupe import NotificationDedupe from src.notifications.models import NotificationMessage from src.notifications.templates.execution import build_execution_notification from src.notifications.templates.signal import build_signal_notification -from src.runtime_events.event_types import RuntimeEventType from src.runtime_events.models import RuntimeEvent -from src.trading.journal.service import JournalService class NotificationService: @@ -17,112 +15,15 @@ class NotificationService: message = self._build_message(event) if message is None: - JournalService().log_info( - "runtime_event_ignored", - "Runtime event has no notification template.", - { - "event_type": event.event_type.value, - "source": event.source, - "title": event.title, - "priority": event.priority, - "dedupe_key": event.dedupe_key, - }, - ) return if not NotificationDedupe.should_send(message.dedupe_key): - self._log_suppressed(event, message) return - sent = await TelegramNotificationChannel().send(message) - - if sent: - self._log_sent(event, message) + await TelegramNotificationChannel().send(message) def _build_message(self, event: RuntimeEvent) -> NotificationMessage | None: return ( build_signal_notification(event) or build_execution_notification(event) - ) - - def _log_sent(self, event: RuntimeEvent, message: NotificationMessage) -> None: - if event.event_type == RuntimeEventType.AUTO_SIGNAL_READY: - signal = str(event.payload.get("signal") or "—").upper() - - JournalService().log_ui_info( - event_type="auto_strong_signal_alert_sent", - message=f"Отправлено уведомление о сильном сигнале {signal}.", - screen="auto", - action="strong_signal_alert", - payload={ - **event.payload, - "runtime_event_type": event.event_type.value, - "runtime_source": event.source, - "priority": message.priority.upper(), - "dedupe_key": message.dedupe_key, - }, - ) - return - - if event.event_type in { - RuntimeEventType.POSITION_OPENED, - RuntimeEventType.POSITION_CLOSED, - RuntimeEventType.POSITION_FLIPPED, - }: - JournalService().log_ui_info( - event_type="auto_execution_alert_sent", - message="Отправлено Telegram-уведомление по paper execution.", - screen="auto", - action="execution_alert", - payload={ - **event.payload, - "runtime_event_type": event.event_type.value, - "runtime_source": event.source, - "priority": message.priority.upper(), - "dedupe_key": message.dedupe_key, - }, - ) - return - - JournalService().log_info( - "notification_sent", - "Runtime notification sent.", - { - "event_type": event.event_type.value, - "source": event.source, - "title": event.title, - "priority": event.priority, - "dedupe_key": message.dedupe_key, - }, - ) - - def _log_suppressed(self, event: RuntimeEvent, message: NotificationMessage) -> None: - if event.event_type == RuntimeEventType.AUTO_SIGNAL_READY: - signal = str(event.payload.get("signal") or "—").upper() - - JournalService().log_ui_info( - event_type="auto_strong_signal_alert_suppressed", - message=f"Повторное уведомление о сильном сигнале {signal} подавлено.", - screen="auto", - action="strong_signal_alert", - payload={ - **event.payload, - "runtime_event_type": event.event_type.value, - "runtime_source": event.source, - "priority": message.priority.upper(), - "dedupe_key": message.dedupe_key, - }, - ) - return - - JournalService().log_info( - "notification_suppressed_duplicate", - "Duplicate notification suppressed.", - { - "event_type": event.event_type.value, - "source": event.source, - "title": event.title, - "priority": event.priority, - "dedupe_key": message.dedupe_key, - }, ) \ No newline at end of file diff --git a/app/src/telegram/handlers/auto/risk.py b/app/src/telegram/handlers/auto/risk.py index a3b4d58..d4722ee 100644 --- a/app/src/telegram/handlers/auto/risk.py +++ b/app/src/telegram/handlers/auto/risk.py @@ -207,13 +207,8 @@ def _log_risk_updated(action: str) -> None: try: JournalService().log_ui_info( - event_type="auto_risk_settings_updated", - message=( - "Risk settings updated: " - f"SL={state.stop_loss_percent}, " - f"TP={state.take_profit_percent}, " - f"ML={state.max_loss_usd}" - ), + event_type="risk_settings_updated", + message="Параметры защиты позиции обновлены.", screen="auto", action=action, payload={ diff --git a/app/src/telegram/handlers/journal.py b/app/src/telegram/handlers/journal.py index 1b4f5a8..639f684 100644 --- a/app/src/telegram/handlers/journal.py +++ b/app/src/telegram/handlers/journal.py @@ -126,15 +126,6 @@ async def open_journal(message: Message, state: FSMContext) -> None: chat_id=message.chat.id, ) - JournalService().log_ui_info( - event_type="journal_open_requested", - message="Запрошено открытие журнала.", - screen="journal", - action="open", - user_id=_user_id_from_message(message), - chat_id=_chat_id_from_message(message), - ) - await _show_journal_page( message, page=1, @@ -157,15 +148,6 @@ async def open_journal_from_monitoring(callback: CallbackQuery, state: FSMContex keep_message_id=callback.message.message_id, ) - JournalService().log_ui_info( - event_type="journal_open_requested", - message="Запрошено открытие журнала из мониторинга.", - screen="journal", - action="open_from_monitoring", - user_id=_user_id_from_callback(callback), - chat_id=_chat_id_from_callback(callback), - ) - await _show_journal_page( callback.message, page=1, @@ -195,7 +177,7 @@ async def export_journal_csv(callback: CallbackQuery) -> None: await callback.message.answer_document(document=document) service.log_ui_info( - event_type="journal_export_csv_success", + event_type="journal_exported", message="Журнал экспортирован в CSV.", screen="journal", action="export_csv", @@ -207,12 +189,13 @@ async def export_journal_csv(callback: CallbackQuery) -> None: await callback.answer("CSV экспортирован") except Exception as exc: service.log_ui_error( - event_type="journal_export_csv_error", + event_type="journal_export_error", message="Не удалось экспортировать журнал в CSV.", screen="journal", action="export_csv", user_id=_user_id_from_callback(callback), chat_id=_chat_id_from_callback(callback), + payload={"format": "csv"}, raw_error=str(exc), ) await callback.answer("Не удалось экспортировать CSV", show_alert=True) @@ -233,8 +216,8 @@ async def export_journal_xlsx(callback: CallbackQuery) -> None: await callback.message.answer_document(document=document) service.log_ui_info( - event_type="journal_export_xlsx_success", - message="Журнал экспортирован в Excel.", + event_type="journal_exported", + message="Журнал экспортирован в XLSX.", screen="journal", action="export_xlsx", user_id=_user_id_from_callback(callback), @@ -245,12 +228,13 @@ async def export_journal_xlsx(callback: CallbackQuery) -> None: await callback.answer("Excel экспортирован") except Exception as exc: service.log_ui_error( - event_type="journal_export_xlsx_error", - message="Не удалось экспортировать журнал в Excel.", + event_type="journal_export_error", + message="Не удалось экспортировать журнал в XLSX.", screen="journal", action="export_xlsx", user_id=_user_id_from_callback(callback), chat_id=_chat_id_from_callback(callback), + payload={"format": "xlsx"}, raw_error=str(exc), ) await callback.answer("Не удалось экспортировать Excel", show_alert=True) diff --git a/app/src/telegram/handlers/journal_ui.py b/app/src/telegram/handlers/journal_ui.py index d347977..1565113 100644 --- a/app/src/telegram/handlers/journal_ui.py +++ b/app/src/telegram/handlers/journal_ui.py @@ -21,65 +21,25 @@ LEVEL_ICONS = { } EVENT_TITLES = { - "auto_signal_generated": "Сигнал автоторговли", - "auto_signal_summary": "Итог серии сигналов", - "app_start": "Запуск приложения", - "system_open_alert": "Система загружена с предупреждениями", - "system_open_requested": "Открытие системы", - "system_open_success": "Система загружена", - "system_retry": "Система обновлена", - "market_open_requested": "Открытие рынка", - "market_open_success": "Рынок загружен", - "market_open_error": "Ошибка открытия рынка", - "market_retry_error": "Ошибка обновления рынка", - "market_symbol_invalid": "Некорректный инструмент", - "market_price_error": "Ошибка загрузки цены", - "portfolio_open_requested": "Открытие портфеля", - "portfolio_open_success": "Портфель загружен", - "portfolio_open_error": "Ошибка открытия портфеля", - "portfolio_retry_error": "Ошибка обновления портфеля", - "portfolio_empty": "Портфель пуст", - "portfolio_zero_balances": "Нет активов с балансом", - "portfolio_partial_estimate": "Частичная оценка портфеля", - "balance_summary_loaded": "Баланс загружен", - "balance_summary_empty": "Баланс пуст", - "balance_summary_error": "Ошибка загрузки баланса", - "exchange_request_error": "Ошибка запроса к бирже", - "trade_drafts_open": "Открытие списка черновиков", - "trade_drafts_paginate": "Переключение страницы черновиков", - "trade_draft_open_success": "Черновик открыт", - "trade_draft_open_not_found": "Черновик не найден", - "trade_draft_edit_start": "Начато редактирование черновика", - "trade_draft_edit_error": "Ошибка редактирования черновика", - "trade_draft_edit_not_found": "Черновик не найден", - "trade_order_create_start": "Начато создание ордера", - "trade_order_create_start_error": "Ошибка создания ордера", - "trade_order_create_cancelled": "Создание ордера отменено", - "trade_order_side_selected": "Выбрана сторона ордера", - "trade_order_side_select_error": "Ошибка выбора стороны", - "trade_order_type_selected": "Выбран тип ордера", - "trade_order_type_select_error": "Ошибка выбора типа", - "trade_order_quantity_selected": "Выбрано количество", - "trade_order_quantity_select_error": "Ошибка выбора количества", - "trade_order_quantity_manual_open": "Ручной ввод количества", - "trade_order_quantity_manual_success": "Количество введено", - "trade_order_quantity_manual_error": "Ошибка ввода количества", - "trade_order_price_selected": "Выбрана цена", - "trade_order_price_select_error": "Ошибка выбора цены", - "trade_order_price_manual_open": "Ручной ввод цены", - "trade_order_price_manual_success": "Цена введена", - "trade_order_price_manual_error": "Ошибка ввода цены", - "trade_order_confirm_success": "Черновик сохранён", - "trade_order_confirm_error": "Ошибка сохранения", - "trade_order_confirm_validation_error": "Ошибка проверки", - "trade_order_confirm_state_error": "Ошибка состояния", + "signal_summary": "Сводка сигнала", + "signal_ready": "Сигнал готов", + "position_opened": "Позиция открыта", + "position_closed": "Позиция закрыта", + "position_flipped": "Направление позиции изменено", + "position_flip_blocked": "Смена направления позиции заблокирована", + "risk_settings_updated": "Настройки защиты обновлены", + "market_monitor_started": "Мониторинг рынка запущен", + "market_monitor_stopped": "Мониторинг рынка остановлен", + "market_stream_connected": "Поток рынка подключён", + "market_stream_disconnected": "Поток рынка отключён", + "market_symbol_changed": "Инструмент рынка изменён", + "journal_export_error": "Ошибка экспорта журнала", + "journal_exported": "Журнал экспортирован", "journal_cleared": "Журнал очищен", - "journal_cleared_old": "Журнал очищен по сроку", - "journal_export_csv_success": "Экспорт CSV", - "journal_export_csv_error": "Ошибка экспорта CSV", - "journal_export_xlsx_success": "Экспорт Excel", - "journal_export_xlsx_error": "Ошибка экспорта Excel", - "journal_open_requested": "Открытие журнала", + "notification_sent": "Уведомление отправлено", + "notification_error": "Ошибка уведомления", + "app_started": "Приложение запущено", + "app_bootstrap_failed": "Ошибка запуска приложения", } TECH_TO_HUMAN_MESSAGES = { @@ -221,42 +181,18 @@ def _payload(event: dict) -> dict: def _render_auto_signal(event: dict, created_time: str) -> list[str]: - payload = _payload(event) - - signal = str(payload.get("signal", "HOLD")).upper() - strategy = str(payload.get("strategy", "AUTO")).upper() - symbol = str(payload.get("symbol", "—")) - reason = str(payload.get("reason", "")) - confidence = float(payload.get("confidence", 0.0) or 0.0) - repeat_count = int(payload.get("repeat_count", 1) or 1) - is_strong_signal = bool(payload.get("is_strong_signal", False)) - is_aggregated = bool(payload.get("is_aggregated", False)) - - signal_icon = { - "BUY": "🟢", - "SELL": "🔴", - "HOLD": "🟡", - }.get(signal, "•") - - prefix = "" - if is_strong_signal: - prefix += "📈 " - if is_aggregated: - prefix += "🧠 " + level = str(event.get("level", "INFO")).upper() + icon = LEVEL_ICONS.get(level, "•") + title = _event_title(str(event.get("event_type", ""))) + message = _humanize_message(str(event.get("message", ""))) lines = [ - f"{prefix}{signal_icon} AUTO · {signal}", - f"{created_time} · {strategy} · {symbol}", + f"{icon} {level} · {title}", + f"{created_time}", ] - if is_aggregated: - lines.append(f"{repeat_count} {signal} подряд") - - if confidence > 0: - lines.append(f"Уверенность: {confidence:.2f}") - - if reason: - lines.append(f"Причина: {reason}") + if message: + lines.append(message) return lines @@ -305,7 +241,7 @@ def render(events, page, total_pages): event_type = str(event.get("event_type", "")) - if event_type in {"auto_signal_generated", "auto_signal_summary"}: + if event_type in {"signal_summary", "signal_ready"}: lines.extend(_render_auto_signal(event, created_time)) else: lines.extend(_render_default_event(event, created_time)) diff --git a/app/src/trading/auto/service.py b/app/src/trading/auto/service.py index 551bbc1..ce583a7 100644 --- a/app/src/trading/auto/service.py +++ b/app/src/trading/auto/service.py @@ -573,9 +573,9 @@ class AutoTradeService: try: JournalService().log_ui_info( - event_type="auto_signal_summary", + event_type="signal_summary", message=( - f"🟡 HOLD {duration_text} завершён сигналом {next_signal}" + f"🟡 HOLD {duration_text} завершён сигналом {next_signal}." ), screen="auto", action="signal_summary", @@ -614,12 +614,8 @@ class AutoTradeService: try: JournalService().log_ui_info( - event_type="auto_signal_ready", - message=( - f"Сигнал {normalized_signal} готов: " - f"{signal_intent}, confidence={confidence:.2f}, " - f"repeats={state.last_signal_repeat_count}" - ), + event_type="signal_ready", + message=f"Сигнал {normalized_signal} готов к исполнению.", screen="auto", action="signal_ready", payload={ diff --git a/app/src/trading/execution/engine.py b/app/src/trading/execution/engine.py index 1d2c5f2..a24c957 100644 --- a/app/src/trading/execution/engine.py +++ b/app/src/trading/execution/engine.py @@ -148,8 +148,8 @@ class ExecutionEngine: } JournalService().log_ui_info( - event_type="paper_position_opened", - message=f"Paper ENTRY открыта: {side} {state.symbol}", + event_type="position_opened", + message=f"Позиция {side} открыта: {state.symbol}.", screen="auto", action="paper_execution", payload=payload, @@ -266,8 +266,8 @@ class ExecutionEngine: } JournalService().log_ui_info( - event_type="paper_position_flipped", - message=f"Paper FLIP выполнен: {old_side} → {new_side} {state.symbol}", + event_type="position_flipped", + message=f"Направление позиции изменено: {old_side} → {new_side}.", screen="auto", action="paper_execution", payload=payload, @@ -337,13 +337,11 @@ class ExecutionEngine: "price_updated_at": exit_execution.updated_at if exit_execution else None, } + close_reason = forced_reason or "MANUAL" + JournalService().log_ui_info( - event_type="paper_position_closed", - message=( - f"Paper EXIT закрыта по риску {forced_reason}: {position.side} {state.symbol}" - if forced_reason is not None - else f"Paper EXIT закрыта: {position.side} {state.symbol}" - ), + event_type="position_closed", + message=f"Позиция {position.side} закрыта: {close_reason}.", screen="auto", action="paper_execution", payload=payload, @@ -535,9 +533,9 @@ class ExecutionEngine: "updated_at": position.updated_at, } - JournalService().log_ui_info( - event_type="paper_flip_blocked", - message=f"Paper FLIP заблокирован: {reason}", + JournalService().log_ui_warning( + event_type="position_flip_blocked", + message=f"Смена направления позиции заблокирована: {reason}", screen="auto", action="paper_execution", payload=payload, diff --git a/app/src/trading/journal/service.py b/app/src/trading/journal/service.py index 8d0be19..aa821ff 100644 --- a/app/src/trading/journal/service.py +++ b/app/src/trading/journal/service.py @@ -279,8 +279,8 @@ class JournalService: deleted_count = self.repository.delete_older_than_days(days) self.log_ui_warning( - event_type="journal_cleared_old", - message=f"Журнал очищен старше {days} дней.", + event_type="journal_cleared", + message=f"Журнал очищен: удалены записи старше {days} дней.", screen="journal", action="clear_old", payload={ diff --git a/docs/roadmap/master-roadmap.md b/docs/roadmap/master-roadmap.md index af7f530..152ac4d 100644 --- a/docs/roadmap/master-roadmap.md +++ b/docs/roadmap/master-roadmap.md @@ -384,6 +384,18 @@ - добавлено отдельное событие готового сигнала - подготовлена база для стандартизации журнала в 07.4.3.19.4 +#### 07.4.3.19.4 ✅ Journal Runtime Standardization & Export Layer +- унифицированы execution event_type +- удалены legacy paper_* события +- execution logging переведён в единый human-readable стиль +- унифицированы market runtime events +- стандартизирован export logging +- добавлены account-aware export filename +- добавлены [DEMO]/[LIVE] runtime prefixes +- унифицированы risk-control journal events +- централизован EVENT_TITLES mapping +- журнал подготовлен к filters/search layer + ### 07.4.4 ⏳ Grid Strategy diff --git a/docs/roadmap/stage-07-auto-trading-roadmap.md b/docs/roadmap/stage-07-auto-trading-roadmap.md index 3ceb6d5..a7f72f5 100644 --- a/docs/roadmap/stage-07-auto-trading-roadmap.md +++ b/docs/roadmap/stage-07-auto-trading-roadmap.md @@ -360,6 +360,18 @@ - добавлено отдельное событие готового сигнала - подготовлена база для стандартизации журнала в 07.4.3.19.4 +#### 07.4.3.19.4 ✅ Journal Runtime Standardization & Export Layer +- унифицированы execution event_type +- удалены legacy paper_* события +- execution logging переведён в единый human-readable стиль +- унифицированы market runtime events +- стандартизирован export logging +- добавлены account-aware export filename +- добавлены [DEMO]/[LIVE] runtime prefixes +- унифицированы risk-control journal events +- централизован EVENT_TITLES mapping +- журнал подготовлен к filters/search layer + --- ### 07.4.4 diff --git a/docs/stages/stage-07_4_3_19_4-journal_runtime_standardization.md b/docs/stages/stage-07_4_3_19_4-journal_runtime_standardization.md new file mode 100644 index 0000000..1bffd22 --- /dev/null +++ b/docs/stages/stage-07_4_3_19_4-journal_runtime_standardization.md @@ -0,0 +1,242 @@ +# 07.4.3.19.4 — Journal Runtime Standardization & Export Layer + +## Статус + +Этап завершён. + +## Цель этапа + +Цель этапа — завершить стандартизацию runtime-журнала, унифицировать execution/runtime/event logging, очистить legacy-style события и подготовить журнал к следующему этапу фильтрации, поиска и аналитики. + +После этапа 07.4.3.19.3 журнал уже содержал signal intent layer и noise filtering, но оставались: + +- разные стили event naming +- legacy paper_* event_type +- смешанные runtime/runtime-ui сообщения +- несогласованные execution-сообщения +- разные стили export/runtime notifications + +Этап 07.4.3.19.4 завершает переход к единому audit/runtime journal. + +--- + +## Что изменено + +### 1. Унифицированы execution event_type + +Старые paper-style event_type заменены на единый runtime-style: + +Было: + +- paper_position_opened +- paper_position_closed +- paper_position_flipped +- paper_flip_blocked + +Стало: + +- position_opened +- position_closed +- position_flipped +- position_flip_blocked + +Это упрощает дальнейшую фильтрацию и экспорт журнала. + +--- + +### 2. Execution-сообщения приведены к единому стилю + +Execution runtime-сообщения теперь используют единый human-readable стиль. + +Примеры: + +- Позиция LONG открыта. +- Позиция SHORT закрыта. +- Направление позиции изменено: LONG → SHORT. +- Смена направления позиции заблокирована. + +Убраны: + +- Paper ENTRY +- Paper EXIT +- FLIP выполнен +- flip blocked technical text + +--- + +### 3. Унифицированы runtime market events + +Market runtime logging переведён в единый monitoring-style. + +Добавлены стандартизированные события: + +- market_monitor_started +- market_monitor_stopped +- market_stream_connected +- market_stream_disconnected +- market_symbol_changed + +Runtime payload теперь содержит: + +- runtime_key +- runtime_screen +- runtime_label +- cache_symbol +- ws_symbol + +--- + +### 4. Унифицирован журнал экспорта + +Экспорт журнала переведён в unified export layer. + +Добавлены: + +- journal_exported +- journal_export_error + +Экспорт теперь использует единый account-aware filename: + +- journal_demo_info_plus_YYYY-MM-DD_HH-MM-SS.csv +- journal_live_info_plus_YYYY-MM-DD_HH-MM-SS.xlsx + +--- + +### 5. Добавлен account-mode prefix + +Все UI/runtime journal-сообщения теперь автоматически получают account-mode prefix: + +- [DEMO] +- [LIVE] + +Prefix формируется через JournalService. + +Это подготавливает систему к multi-runtime и multi-account support. + +--- + +### 6. Унифицированы risk-control события + +Risk settings logging переведён в user-oriented формат. + +Добавлены: + +- risk_settings_updated + +Убраны технические debug-style risk messages. + +--- + +### 7. Унифицированы journal UI titles + +journal_ui.py переведён на централизованный EVENT_TITLES mapping. + +Теперь journal renderer отображает: + +- понятные runtime titles +- human-readable execution names +- единый visual style + +--- + +## Что больше не пишется в журнал + +Из runtime journal удалены: + +- legacy paper_* event_type +- flip technical spam +- execution debug text +- duplicate runtime-notification messages +- mixed runtime/export wording +- raw monitoring tool messages + +--- + +## Что остаётся в журнале + +После этапа журнал содержит только полезные runtime-аудит события: + +- signal summary +- READY signals +- execution events +- blocked flip +- market runtime events +- export events +- notification errors +- journal/system critical events + +--- + +## Основные изменённые файлы + +- app/src/trading/execution/engine.py +- app/src/trading/auto/service.py +- app/src/integrations/exchange/market_data_runner.py +- app/src/telegram/handlers/journal_ui.py +- app/src/telegram/handlers/journal.py +- app/src/trading/journal/service.py +- app/src/telegram/handlers/auto/risk.py +- app/src/notifications/service.py +- app/src/notifications/channels/telegram.py + +--- + +## Проверка + +После правок необходимо выполнить: + + python -m compileall src + python -m src.main + +После запуска проверить: + +1. В журнале больше нет paper_* event_type. +2. Все execution events отображаются единообразно. +3. Flip-blocked отображается как user-readable событие. +4. Export CSV/XLSX работает. +5. Journal filename содержит account mode. +6. Market runtime использует unified titles. +7. [DEMO]/[LIVE] prefix отображается корректно. +8. Старые debug-style runtime messages больше не появляются. + +--- + +## Roadmap Update + +#### 07.4.3.19.3 ✅ Strategy Noise Filter & Signal Intent Layer +- убрано журналирование одиночных BUY / SELL без серии +- HOLD-серии переведены с repeat-count на duration формат +- добавлен формат 🟡 HOLD 5м 36с завершён сигналом SELL +- добавлен signal_intent в payload сигналов +- добавлены intent-типы ENTRY_CANDIDATE, REVERSAL_CANDIDATE, REINFORCE_POSITION, HOLD_MARKET, NOISE +- добавлена position-aware интерпретация сигналов +- добавлено отдельное событие готового сигнала +- подготовлена база для стандартизации журнала в 07.4.3.19.4 + +#### 07.4.3.19.4 ✅ Journal Runtime Standardization & Export Layer +- унифицированы execution event_type +- удалены legacy paper_* события +- execution logging переведён в единый human-readable стиль +- унифицированы market runtime events +- стандартизирован export logging +- добавлены account-aware export filename +- добавлены [DEMO]/[LIVE] runtime prefixes +- унифицированы risk-control journal events +- централизован EVENT_TITLES mapping +- журнал подготовлен к filters/search layer + +--- + +## Итог + +Этап завершил переход журнала от debug/runtime telemetry к полноценному runtime audit layer. + +Система получила: + +- единый execution logging +- account-aware runtime journal +- стандартизированные runtime events +- unified export layer +- human-readable execution events +- подготовку к следующему этапу journal filters/search/analytics. +