Skip to main content
Glama
COMPREHENSIVE_BUG_FIX_PROMPT.md35.7 kB
# 🔴 КОМПЛЕКСНОЕ ИСПРАВЛЕНИЕ КРИТИЧЕСКИХ ПРОБЛЕМ - ПОЛНАЯ ИНСТРУКЦИЯ **Дата создания:** 2025-11-22 **Версия:** 1.0 **Статус:** ГОТОВО К ИСПОЛНЕНИЮ --- ## 📋 EXECUTIVE SUMMARY Этот документ содержит **ПОЛНЫЕ РЕШЕНИЯ** для всех критических проблем, выявленных в логах системы `autonomous_agent.main`. Каждое решение включает: - Детальный анализ проблемы - Точное местоположение в коде - Готовый код исправления - Пояснения и best practices **Общая стратегия исправлений:** 1. **Defensive Programming** - проверка данных перед использованием 2. **Graceful Degradation** - система работает даже при частичных ошибках 3. **Unified Naming** - единообразие в названиях полей 4. **Better Logging** - информативные логи для отладки 5. **Performance** - оптимизация без потери качества --- ## 🎯 ПРОБЛЕМА #1: RuntimeWarning: Mean of empty slice ### 📊 Анализ проблемы **Частота:** Десятки раз в каждом анализе **Критичность:** 🔴 ВЫСОКАЯ **Влияние:** - Засоряет логи предупреждениями - Приводит к `NaN` значениям в индикаторах - Потенциально некорректные расчеты **Локации проблемы:** ``` mcp_server/technical_analysis.py:136 - df['volume'].tail(20).mean() mcp_server/technical_analysis.py:233 - df['volume'].rolling(20).mean().iloc[-1] mcp_server/technical_analysis.py:235 - df['volume'].iloc[-1] / df['volume'].rolling(20).mean().iloc[-1] mcp_server/technical_analysis.py:697 - np.mean([abs(c['close'] - c['open']) for c in candles[i-5:i]]) mcp_server/technical_analysis.py:836 - np.mean([c['volume'] for c in prev_candles]) ``` **Причина:** - Недостаточно данных для расчета (менее 20 свечей для rolling mean) - Пустые срезы при расчете среднего для паттернов - Отсутствие проверки на пустые массивы перед `np.mean()` ### ✅ РЕШЕНИЕ **Файл:** `mcp_server/technical_analysis.py` #### 1. Добавить глобальную утилиту в начало файла: ```python # После импортов, перед классом TechnicalAnalysis def safe_mean(data, default=0.0): """ Безопасный расчет среднего с проверкой на пустые данные Args: data: Series, list или np.ndarray для расчета среднего default: Значение по умолчанию если данные пустые или невалидные Returns: float: Среднее значение или default """ if data is None: return default # Для pandas Series if hasattr(data, '__len__') and len(data) == 0: return default # Для numpy arrays или lists try: if isinstance(data, (list, np.ndarray)): if len(data) == 0: return default result = np.mean(data) else: # Для pandas Series result = float(data.mean()) # Проверка на NaN if np.isnan(result): return default return float(result) except Exception: return default def safe_rolling_mean(series, window, default=0.0): """ Безопасный расчет rolling mean с проверкой данных Args: series: pandas Series для расчета window: Размер окна default: Значение по умолчанию Returns: float: Rolling mean или default """ if series is None or len(series) == 0: return default if len(series) < window: # Если данных меньше окна, используем все доступные return safe_mean(series, default) try: result = float(series.rolling(window).mean().iloc[-1]) if np.isnan(result): return default return result except Exception: return default ``` #### 2. Исправить строку 136 (_analyze_timeframe): ```python # БЫЛО: "volume_avg": float(df['volume'].tail(20).mean()) # ДОЛЖНО БЫТЬ: "volume_avg": safe_mean(df['volume'].tail(20)) ``` #### 3. Исправить строки 233-236 (_calculate_all_indicators): ```python # БЫЛО: indicators['volume'] = { 'obv': float(ta.volume.on_balance_volume(df['close'], df['volume']).iloc[-1]), 'volume_sma': float(df['volume'].rolling(20).mean().iloc[-1]), 'current_volume': float(df['volume'].iloc[-1]), 'volume_ratio': float(df['volume'].iloc[-1] / df['volume'].rolling(20).mean().iloc[-1]) } # ДОЛЖНО БЫТЬ: volume_sma = safe_rolling_mean(df['volume'], 20, default=float(df['volume'].mean()) if len(df) > 0 else 0) current_volume = float(df['volume'].iloc[-1]) if len(df) > 0 else 0 volume_ratio = (current_volume / volume_sma) if volume_sma > 0 else 1.0 indicators['volume'] = { 'obv': float(ta.volume.on_balance_volume(df['close'], df['volume']).iloc[-1]) if len(df) > 0 else 0, 'volume_sma': volume_sma, 'current_volume': current_volume, 'volume_ratio': volume_ratio } ``` #### 4. Исправить строку 697 (find_order_blocks): ```python # БЫЛО: avg_body = np.mean([abs(c['close'] - c['open']) for c in candles[i-5:i]]) # ДОЛЖНО БЫТЬ: candle_slice = candles[max(0, i-5):i] if len(candle_slice) > 0: avg_body = safe_mean([abs(c['close'] - c['open']) for c in candle_slice]) else: avg_body = 0.0 ``` #### 5. Исправить строку 836 (detect_liquidity_grabs): ```python # БЫЛО: avg_vol = np.mean([c['volume'] for c in prev_candles]) # ДОЛЖНО БЫТЬ: avg_vol = safe_mean([c['volume'] for c in prev_candles]) if prev_candles else 1.0 ``` #### 6. Добавить валидацию данных в начале _analyze_timeframe: ```python async def _analyze_timeframe( self, symbol: str, timeframe: str, include_patterns: bool ) -> Dict[str, Any]: """Анализ на одном таймфрейме""" # Получаем OHLCV данные ohlcv = await self.client.get_ohlcv(symbol, timeframe, limit=200) # НОВАЯ ВАЛИДАЦИЯ if not ohlcv or len(ohlcv) < 20: logger.warning( f"Insufficient data for {symbol} on {timeframe}: " f"{len(ohlcv) if ohlcv else 0} candles (need min 20)" ) return { "timeframe": timeframe, "error": "insufficient_data", "data_points": len(ohlcv) if ohlcv else 0, "message": f"Only {len(ohlcv) if ohlcv else 0} candles available, need min 20" } # Конвертируем в DataFrame # ... остальной код без изменений ``` --- ## 🎯 ПРОБЛЕМА #2: Best LONG/SHORT Score = 0.00 (Несоответствие полей) ### 📊 Анализ проблемы **Критичность:** 🔴 КРИТИЧНО **Влияние:** - Пользователь видит 0.00, хотя есть сигналы с score > 0 - Неправильная оценка качества сигналов - Некорректная оценка риска **Причина:** В разных частях кода используются разные названия для score: - `confluence_score` - в валидации - `final_score` - после deep analysis - `score` - в market scanner - `publication` использует смесь этих полей **Локации:** ``` autonomous_agent/detailed_formatter.py:69-70 - использует confluence_score publish_market_analysis.py:110-111 - использует score autonomous_agent/autonomous_analyzer.py - создает final_score ``` ### ✅ РЕШЕНИЕ **Стратегия:** Унифицировать на `final_score` как основное поле, добавить fallback для обратной совместимости #### 1. Исправить detailed_formatter.py (строки 69-70): ```python # БЫЛО: best_long_score = max([opp.get("confluence_score", 0) for opp in all_longs], default=0) best_short_score = max([opp.get("confluence_score", 0) for opp in all_shorts], default=0) # ДОЛЖНО БЫТЬ: def get_score(opp): """Извлечение score с fallback на разные названия полей""" return ( opp.get("final_score", 0) or opp.get("confluence_score", 0) or opp.get("score", 0) or 0 ) best_long_score = max([get_score(opp) for opp in all_longs], default=0) best_short_score = max([get_score(opp) for opp in all_shorts], default=0) ``` #### 2. Исправить publish_market_analysis.py (строки 110-111): ```python # БЫЛО: best_long_score = max([o['score'] for o in longs], default=0) best_short_score = max([o['score'] for o in shorts], default=0) # ДОЛЖНО БЫТЬ: def get_score(opp): """Извлечение score с fallback на разные названия полей""" return ( opp.get("final_score", 0) or opp.get("confluence_score", 0) or opp.get("score", 0) or 0 ) best_long_score = max([get_score(o) for o in longs], default=0) best_short_score = max([get_score(o) for o in shorts], default=0) ``` #### 3. Унифицировать в autonomous_analyzer.py (_format_opportunity): ```python def _format_opportunity(self, opp: Dict[str, Any]) -> Dict[str, Any]: """Форматирование возможности для публикации""" entry_plan = opp.get("entry_plan", {}) analysis = opp.get("full_analysis", {}) composite = analysis.get("composite_signal", {}) if analysis else {} # УНИФИЦИРОВАННЫЙ SCORE final_score = ( opp.get("final_score", 0) or opp.get("confluence_score", 0) or opp.get("score", 0) or 0 ) return { "symbol": opp.get("symbol", ""), "current_price": opp.get("current_price", 0), "side": opp.get("side", "long"), "entry_price": entry_plan.get("entry_price", opp.get("current_price", 0)), "stop_loss": entry_plan.get("stop_loss", 0), "take_profit": entry_plan.get("take_profit", 0), "risk_reward": entry_plan.get("risk_reward", 0), # ВСЕ ТРИ ВАРИАНТА для обратной совместимости "final_score": round(final_score, 1), "confluence_score": round(final_score, 1), "score": round(final_score, 1), "probability": opp.get("probability", 0), "reasoning": opp.get("why", ""), "timeframes_alignment": list(analysis.get("timeframes", {}).keys()) if analysis else [], "key_factors": self._extract_key_factors(opp, analysis), "validation": opp.get("validation", {}) } ``` #### 4. Также исправить в detailed_formatter.py строку 199: ```python # БЫЛО: score = opp.get("confluence_score", opp.get("final_score", 0)) # ДОЛЖНО БЫТЬ: score = ( opp.get("final_score", 0) or opp.get("confluence_score", 0) or opp.get("score", 0) or 0 ) ``` --- ## 🎯 ПРОБЛЕМА #3: OpenRouter API Error 401 "User not found" ### 📊 Анализ проблемы **Критичность:** 🔴 КРИТИЧНО **Влияние:** - Qwen AI анализ полностью отключен - Система работает без AI-улучшений - Потеря важного компонента анализа **Локация:** `autonomous_agent/qwen_client.py:132` **Причина:** - Неверный или истекший API ключ OpenRouter - Проблема с аутентификацией - Возможно, ключ не установлен в переменных окружения ### ✅ РЕШЕНИЕ **Файл:** `autonomous_agent/qwen_client.py` #### 1. Улучшить обработку ошибок в методе generate (строка 98-198): ```python async def generate( self, prompt: str, system_prompt: Optional[str] = None, temperature: float = 0.7, max_tokens: int = 2000, top_p: float = 0.8 ) -> Dict[str, Any]: """ Генерация ответа от Qwen """ headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", "HTTP-Referer": "https://github.com/your-repo", "X-Title": "Trader Agent" } # Формируем сообщения в формате OpenAI messages = [] if system_prompt: messages.append({ "role": "system", "content": system_prompt }) messages.append({ "role": "user", "content": prompt }) payload = { "model": self.model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, "top_p": top_p } try: async with aiohttp.ClientSession() as session: async with session.post( self.base_url, headers=headers, json=payload, timeout=aiohttp.ClientTimeout(total=60) ) as response: response_text = await response.text() # НОВАЯ ОБРАБОТКА 401 ОШИБКИ if response.status == 401: logger.error( f"OpenRouter API authentication failed (401). " f"Check your OPENROUTER_API_KEY. Response: {response_text}" ) return { "success": False, "error": "Authentication failed - check API key", "content": "", "graceful_fallback": True, "message": "Qwen AI analysis skipped due to API authentication error" } if response.status == 200: data = await response.json() if "choices" in data and len(data["choices"]) > 0: choice = data["choices"][0] content = choice.get("message", {}).get("content", "") return { "success": True, "content": content, "usage": data.get("usage", {}), "model": data.get("model", self.model), "id": data.get("id", "") } logger.warning(f"Unexpected response format: {data}") return { "success": True, "content": json.dumps(data, ensure_ascii=False), "usage": data.get("usage", {}), "model": data.get("model", self.model) } else: logger.error(f"OpenRouter API error {response.status}: {response_text}") # НОВЫЙ GRACEFUL FALLBACK return { "success": False, "error": f"API error {response.status}", "content": "", "graceful_fallback": True, "message": f"Qwen AI analysis skipped (API error {response.status})" } except asyncio.TimeoutError: logger.error("Qwen API request timeout") return { "success": False, "error": "Request timeout", "content": "", "graceful_fallback": True, "message": "Qwen AI analysis skipped (timeout)" } except Exception as e: logger.error(f"Qwen API error: {e}", exc_info=True) return { "success": False, "error": str(e), "content": "", "graceful_fallback": True, "message": f"Qwen AI analysis skipped ({str(e)})" } ``` #### 2. Обновить autonomous_analyzer.py (строки 286-298) для graceful fallback: ```python # ШАГ 5: Анализ через Qwen (с graceful fallback) logger.info("Step 5: Qwen AI analysis...") market_data = { "market_overview": market_overview, "btc_analysis": btc_analysis, "scanned_opportunities": top_candidates, "timestamp": datetime.now().isoformat() } qwen_analysis = await self.qwen.analyze_market_opportunities( market_data=market_data, system_instructions=self.system_instructions ) # НОВЫЙ GRACEFUL FALLBACK if not qwen_analysis.get("success"): if qwen_analysis.get("graceful_fallback"): logger.warning( f"Qwen AI analysis skipped: {qwen_analysis.get('message', 'Unknown reason')}. " "Continuing with technical analysis only." ) # Продолжаем без Qwen анализа qwen_analysis = { "success": False, "graceful_fallback": True, "message": qwen_analysis.get("message", "AI analysis unavailable") } else: logger.error(f"Qwen analysis failed: {qwen_analysis.get('error', 'Unknown error')}") ``` #### 3. Добавить проверку API ключа в __init__: ```python def __init__(self, api_key: str, model: str = "qwen/qwen-turbo"): """ Инициализация клиента Qwen через OpenRouter """ if not api_key: raise ValueError("API key is required for Qwen client") # НОВАЯ ПРОВЕРКА формата ключа if not api_key.startswith("sk-or-"): logger.warning( f"OpenRouter API key should start with 'sk-or-'. " f"Your key starts with: {api_key[:10]}... " f"Please verify your OPENROUTER_API_KEY" ) self.api_key = api_key self.model = model self.base_url = self.BASE_URL self.available_models = [ "qwen/qwen-turbo", "qwen/qwen-plus", "qwen/qwen-max" ] logger.info(f"Qwen client initialized with OpenRouter, model: {model}") ``` --- ## 🎯 ПРОБЛЕМА #4: Validation Failed - Hard Stops (Volume = 0.00) ### 📊 Анализ проблемы **Критичность:** ⚠️ СРЕДНЯЯ **Влияние:** - Много сигналов отфильтровывается - Volume = 0.00 (проблема с данными или расчетом) - Только 1 LONG из 4 прошел валидацию **Локация:** `autonomous_agent/autonomous_analyzer.py:_validate_opportunities` **Связана с:** Проблема #1 (mean of empty slice) ### ✅ РЕШЕНИЕ **Файл:** `mcp_server/technical_analysis.py` #### 1. Улучшить _check_hard_stops_for_validation (строки 871-968): ```python def _check_hard_stops_for_validation(self, analysis: Dict, is_long: bool, entry_timeframe: str = "5m") -> Dict: """ Обязательные проверки которые БЛОКИРУЮТ вход (для validate_entry) """ stops = [] blocked = False details = {} composite = analysis.get('composite_signal', {}) # STOP #1: Composite Signal = HOLD с низкой confidence signal = composite.get('signal', 'HOLD') confidence = composite.get('confidence', 0.5) if signal == 'HOLD' and confidence < 0.5: stops.append(f"Composite signal HOLD with low confidence ({confidence:.2f} < 0.5)") blocked = True details['composite_signal'] = {"signal": signal, "confidence": confidence} # STOP #2: Confidence слишком низкая if confidence < 0.4: stops.append(f"Composite confidence too low ({confidence:.2f} < 0.4)") blocked = True details['confidence'] = confidence # STOP #3: MACD bearish на 3+ коротких TF для LONG (было 2+) if is_long: bearish_count = 0 macd_details = {} for tf in ['1m', '5m', '15m']: tf_data = analysis.get('timeframes', {}).get(tf, {}) if 'error' in tf_data: # НОВАЯ ПРОВЕРКА continue macd = tf_data.get('indicators', {}).get('macd', {}) crossover = macd.get('crossover', 'neutral') macd_details[tf] = crossover if crossover == 'bearish': bearish_count += 1 # ИЗМЕНЕНО: 3+ вместо 2+ для менее строгой фильтрации if bearish_count >= 3: stops.append(f"MACD bearish on {bearish_count} short timeframes") blocked = True details['macd'] = macd_details # STOP #4: MACD bullish на 3+ коротких TF для SHORT (было 2+) if not is_long: bullish_count = 0 macd_details = {} for tf in ['1m', '5m', '15m']: tf_data = analysis.get('timeframes', {}).get(tf, {}) if 'error' in tf_data: # НОВАЯ ПРОВЕРКА continue macd = tf_data.get('indicators', {}).get('macd', {}) crossover = macd.get('crossover', 'neutral') macd_details[tf] = crossover if crossover == 'bullish': bullish_count += 1 # ИЗМЕНЕНО: 3+ вместо 2+ if bullish_count >= 3: stops.append(f"MACD bullish on {bullish_count} short timeframes") blocked = True details['macd'] = macd_details # STOP #5: Volume слишком низкий для скальпинга (УЛУЧШЕННАЯ ПРОВЕРКА) volume_checks = {} valid_volume_found = False for tf in ['1m', '5m', '15m']: tf_data = analysis.get('timeframes', {}).get(tf, {}) if 'error' in tf_data: # Пропускаем таймфреймы с ошибками continue vol_data = tf_data.get('indicators', {}).get('volume', {}) vol_ratio = vol_data.get('volume_ratio', 0) volume_checks[tf] = vol_ratio # Если хотя бы на одном TF volume адекватный - не блокируем if vol_ratio > 0.5: valid_volume_found = True # ИЗМЕНЕНО: Блокируем только если ВСЕ volume_ratio = 0 или очень низкие entry_vol = volume_checks.get(entry_timeframe, 0) if entry_timeframe in ['1m', '5m'] and not valid_volume_found and entry_vol < 0.3: avg_vol_ratio = safe_mean(list(volume_checks.values())) if volume_checks else 0 stops.append( f"Volume too low for scalping on {entry_timeframe}: {entry_vol:.2f} " f"(avg across TFs: {avg_vol_ratio:.2f})" ) blocked = True details['volume'] = volume_checks # STOP #6: BB Squeeze без volume confirmation (СМЯГЧЕНО) squeeze_count = 0 for tf in ['1m', '5m', '15m']: tf_data = analysis.get('timeframes', {}).get(tf, {}) if 'error' in tf_data: continue bb = tf_data.get('indicators', {}).get('bollinger_bands', {}) vol_data = tf_data.get('indicators', {}).get('volume', {}) if bb.get('squeeze', False) and vol_data.get('volume_ratio', 1.0) < 0.5: squeeze_count += 1 # ИЗМЕНЕНО: Блокируем только если squeeze на 2+ таймфреймах (было 1+) if squeeze_count >= 2: stops.append( f"BB Squeeze on {squeeze_count} timeframes without volume confirmation" ) blocked = True details['bb_squeeze_count'] = squeeze_count return { "blocked": blocked, "stops": stops, "can_proceed": not blocked, "details": details } ``` --- ## 🎯 ПРОБЛЕМА #5: Cache Disabled (Несоответствие) ### 📊 Анализ проблемы **Критичность:** ⚠️ СРЕДНЯЯ **Влияние:** - Больше API запросов к Bybit - Медленнее выполнение анализа - Два разных механизма кэширования **Причина:** - CacheManager отключен по умолчанию (`enabled=False`) - bybit_client использует свой внутренний кэш - Несогласованность в логах ### ✅ РЕШЕНИЕ **Файл:** `.env` (или создать `.env.local`) #### Добавить/изменить переменную: ```bash # Кэширование данных ENABLE_CACHE=true CACHE_TTL=300 # 5 минут по умолчанию ``` **Файл:** `mcp_server/cache_manager.py` (если нужна более детальная конфигурация) ```python # Убедиться что initialization использует переменную окружения: def __init__(self, enabled: Optional[bool] = None): """ Initialize cache manager Args: enabled: Enable caching (None = use env variable) """ if enabled is None: # ИСПОЛЬЗУЕМ ПЕРЕМЕННУЮ ОКРУЖЕНИЯ enabled = os.getenv("ENABLE_CACHE", "false").lower() in ["true", "1", "yes"] self.enabled = enabled self.cache: Dict[str, Dict[str, Any]] = {} self.lock = asyncio.Lock() logger.info( f"CacheManager initialized with caching " f"{'ENABLED' if enabled else 'DISABLED'}" ) ``` --- ## 🎯 ПРОБЛЕМА #6: Low Experience Scores ### 📊 Анализ проблемы **Критичность:** ℹ️ НИЗКАЯ **Влияние:** - Накопление опыта с низкими score - `pattern=unknown` - паттерны не распознаются **Причина:** - Низкие confluence scores (это нормально, фильтрация работает) - Паттерны не всегда детектируются ### ✅ РЕШЕНИЕ Это не баг, а **ожидаемое поведение**. Система правильно отфильтровывает слабые сигналы. **Рекомендация:** Добавить лучшее логирование для понимания: ```python # В autonomous_analyzer.py, метод _calculate_final_score, строка ~855 # УЛУЧШЕННОЕ ЛОГИРОВАНИЕ if self.ml_predictor: try: # ... существующий код ... opp["experience_data"] = experience_data logger.info( # ИЗМЕНЕНО: info вместо debug f"Experience logged for {opp.get('symbol')}: " f"pattern={pattern_type}, score={score:.1f}, " f"rsi={rsi:.1f}, volume_ratio={volume_ratio:.2f}" ) except Exception as e: logger.warning(f"Experience logging failed: {e}") ``` --- ## 📝 ДОПОЛНИТЕЛЬНЫЕ УЛУЧШЕНИЯ ### 1. Добавить глобальную обработку ошибок в autonomous_analyzer.py ```python # В методе analyze_market, обернуть каждый шаг в try-except async def analyze_market(self) -> Dict[str, Any]: """Полный анализ рынка с поиском топовых точек входа""" logger.info("Starting comprehensive market analysis...") results = { "success": True, "timestamp": datetime.now().isoformat(), "errors": [], "warnings": [] } try: # ШАГ 1 try: logger.info("Step 1: Getting market overview...") market_overview = await self.bybit_client.get_market_overview("both") results["market_overview"] = market_overview except Exception as e: logger.error(f"Step 1 failed: {e}", exc_info=True) results["errors"].append(f"Market overview failed: {str(e)}") results["market_overview"] = {"error": str(e)} # ШАГ 2 try: logger.info("Step 2: Analyzing BTC...") btc_analysis = await self._analyze_btc() results["btc_analysis"] = btc_analysis except Exception as e: logger.error(f"Step 2 failed: {e}", exc_info=True) results["errors"].append(f"BTC analysis failed: {str(e)}") results["btc_analysis"] = {"error": str(e)} # ... и так далее для каждого шага except Exception as e: logger.error(f"Fatal error during market analysis: {e}", exc_info=True) results["success"] = False results["error"] = str(e) return results ``` ### 2. Добавить мониторинг метрик ```python # Создать новый файл: mcp_server/metrics_logger.py import time from loguru import logger from typing import Dict, Any class MetricsLogger: """Логирование метрик производительности и качества""" def __init__(self): self.metrics = { "analysis_count": 0, "total_time": 0, "avg_time": 0, "opportunities_found": 0, "validation_passed": 0, "validation_failed": 0 } def log_analysis(self, duration: float, opportunities: int, validated: int): """Логировать результаты анализа""" self.metrics["analysis_count"] += 1 self.metrics["total_time"] += duration self.metrics["avg_time"] = self.metrics["total_time"] / self.metrics["analysis_count"] self.metrics["opportunities_found"] += opportunities self.metrics["validation_passed"] += validated self.metrics["validation_failed"] += (opportunities - validated) logger.info( f"Analysis metrics: " f"duration={duration:.2f}s, " f"opportunities={opportunities}, " f"validated={validated}, " f"avg_time={self.metrics['avg_time']:.2f}s" ) def get_summary(self) -> Dict[str, Any]: """Получить сводку метрик""" validation_rate = ( self.metrics["validation_passed"] / self.metrics["opportunities_found"] if self.metrics["opportunities_found"] > 0 else 0 ) return { **self.metrics, "validation_rate": round(validation_rate, 2) } ``` --- ## ✅ ЧЕКЛИСТ ИСПОЛНЕНИЯ Для Code Mode - выполнить в следующем порядке: ### Фаза 1: Критические исправления (ОБЯЗАТЕЛЬНО) - [ ] **1.1** Добавить `safe_mean` и `safe_rolling_mean` в `technical_analysis.py` - [ ] **1.2** Исправить все 5 локаций RuntimeWarning в `technical_analysis.py` - [ ] **1.3** Добавить валидацию данных в `_analyze_timeframe` - [ ] **1.4** Унифицировать score fields в `detailed_formatter.py` - [ ] **1.5** Унифицировать score fields в `publish_market_analysis.py` - [ ] **1.6** Унифицировать score fields в `autonomous_analyzer.py` - [ ] **1.7** Добавить graceful fallback в `qwen_client.py` - [ ] **1.8** Обновить обработку Qwen в `autonomous_analyzer.py` ### Фаза 2: Оптимизации (РЕКОМЕНДУЕТСЯ) - [ ] **2.1** Смягчить hard stops в `_check_hard_stops_for_validation` - [ ] **2.2** Включить кэширование в `.env` - [ ] **2.3** Улучшить логирование experience - [ ] **2.4** Добавить глобальную обработку ошибок ### Фаза 3: Мониторинг (ОПЦИОНАЛЬНО) - [ ] **3.1** Создать `metrics_logger.py` - [ ] **3.2** Интегрировать MetricsLogger в `autonomous_analyzer.py` --- ## 🧪 ТЕСТИРОВАНИЕ После внесения изменений протестировать: ```bash # 1. Запустить анализ рынка python autonomous_agent/main.py # 2. Проверить логи на отсутствие RuntimeWarning grep "RuntimeWarning" logs/*.log # 3. Проверить что score отображается корректно grep "Best LONG score" logs/*.log # 4. Проверить graceful fallback Qwen grep "Qwen AI analysis skipped" logs/*.log # 5. Проверить метрики валидации grep "validation_rate" logs/*.log ``` --- ## 📚 BEST PRACTICES ПРИМЕНЁННЫЕ 1. **Defensive Programming** - Проверка данных перед использованием - Safe defaults для всех операций - Обработка edge cases 2. **Graceful Degradation** - Система работает даже при частичных сбоях - Автоматический fallback для внешних API - Информативные сообщения об ошибках 3. **Data Validation** - Проверка типов и размеров данных - Валидация перед математическими операциями - Early return при недостаточных данных 4. **Unified Interface** - Единообразные названия полей - Fallback для обратной совместимости - Консистентная структура данных 5. **Comprehensive Logging** - Информативные логи для отладки - Разные уровни логирования - Метрики производительности 6. **Error Handling** - Try-except для всех критических операций - Специфичные исключения - Graceful recovery --- ## 🎯 ОЖИДАЕМЫЕ РЕЗУЛЬТАТЫ После внесения всех исправлений: ✅ **Логи будут чистыми** - нет RuntimeWarning ✅ **Score отображается корректно** - Best LONG/SHORT > 0 ✅ **Qwen работает или gracefully degrades** - система работает в обоих случаях ✅ **Валидация эффективнее** - меньше false negatives ✅ **Кэширование активно** - быстрее анализ ✅ **Метрики собираются** - лучший мониторинг --- **КОНЕЦ ДОКУМЕНТА** *Этот документ является самодостаточным и содержит все необходимое для исправления критических проблем системы.*

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheMacroeconomicDao/bybit-ai-trader'

If you have feedback or need assistance with the MCP directory API, please join our Discord server