Update LLM.py
Browse files
LLM.py
CHANGED
|
@@ -1,19 +1,21 @@
|
|
| 1 |
# LLM.py (Updated to integrate LearningHub and English-only prompts)
|
| 2 |
import os, traceback, asyncio, json, time
|
| 3 |
-
import re
|
| 4 |
from datetime import datetime
|
| 5 |
from functools import wraps
|
| 6 |
from backoff import on_exception, expo
|
| 7 |
from openai import OpenAI, RateLimitError, APITimeoutError
|
| 8 |
import numpy as np
|
| 9 |
from sentiment_news import NewsFetcher
|
|
|
|
| 10 |
from helpers import validate_required_fields, format_technical_indicators, format_strategy_scores, format_candle_data_for_pattern_analysis, format_whale_analysis_for_llm, parse_json_from_response
|
| 11 |
-
from ml_engine.processor import safe_json_parse
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
# (Note: PatternAnalysisEngine code remains unchanged, so it is omitted here for brevity)
|
| 14 |
-
# ... (PatternAnalysisEngine class code as before) ...
|
| 15 |
class PatternAnalysisEngine:
|
| 16 |
-
# --- (
|
| 17 |
def __init__(self, llm_service):
|
| 18 |
self.llm = llm_service
|
| 19 |
|
|
@@ -163,9 +165,11 @@ class PatternAnalysisEngine:
|
|
| 163 |
}
|
| 164 |
|
| 165 |
async def analyze_chart_patterns(self, symbol, ohlcv_data):
|
|
|
|
| 166 |
pass
|
| 167 |
|
| 168 |
def _parse_pattern_response(self, response_text):
|
|
|
|
| 169 |
pass
|
| 170 |
|
| 171 |
|
|
@@ -331,8 +335,16 @@ class LLMService:
|
|
| 331 |
return None
|
| 332 |
|
| 333 |
async def _get_pattern_analysis(self, data_payload):
|
| 334 |
-
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
# 🔴 --- START OF PROMPT CHANGE --- 🔴
|
| 338 |
def _create_comprehensive_trading_prompt(
|
|
@@ -458,16 +470,104 @@ OUTPUT FORMAT (JSON - SPOT ONLY - INCLUDE EXIT PROFILE AND SELF-CRITIQUE):
|
|
| 458 |
|
| 459 |
|
| 460 |
def _format_candle_data_comprehensive(self, ohlcv_data):
|
| 461 |
-
|
| 462 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 463 |
|
| 464 |
def _analyze_timeframe_candles(self, candles, timeframe):
|
| 465 |
-
|
| 466 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 467 |
|
| 468 |
def _format_market_context(self, sentiment_data):
|
| 469 |
-
|
| 470 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 471 |
|
| 472 |
|
| 473 |
async def re_analyze_trade_async(self, trade_data: dict, processed_data: dict):
|
|
|
|
| 1 |
# LLM.py (Updated to integrate LearningHub and English-only prompts)
|
| 2 |
import os, traceback, asyncio, json, time
|
| 3 |
+
import re # ✅ استيراد مكتبة re
|
| 4 |
from datetime import datetime
|
| 5 |
from functools import wraps
|
| 6 |
from backoff import on_exception, expo
|
| 7 |
from openai import OpenAI, RateLimitError, APITimeoutError
|
| 8 |
import numpy as np
|
| 9 |
from sentiment_news import NewsFetcher
|
| 10 |
+
# ✅ تعديل الاستيراد: parse_json_from_response لم يعد مستخدماً هنا بشكل مباشر لتحليل استجابة النموذج الرئيسية
|
| 11 |
from helpers import validate_required_fields, format_technical_indicators, format_strategy_scores, format_candle_data_for_pattern_analysis, format_whale_analysis_for_llm, parse_json_from_response
|
| 12 |
+
from ml_engine.processor import safe_json_parse # ✅ الإصلاح: استيراد المحلل الآمن من الموجه الجديد
|
| 13 |
+
|
| 14 |
+
NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
|
| 15 |
+
PRIMARY_MODEL = "nvidia/llama-3.1-nemotron-ultra-253b-v1"
|
| 16 |
|
|
|
|
|
|
|
| 17 |
class PatternAnalysisEngine:
|
| 18 |
+
# --- (هذا الكلاس جزء من LLM.py ومطلوب لتحليل الشموع) ---
|
| 19 |
def __init__(self, llm_service):
|
| 20 |
self.llm = llm_service
|
| 21 |
|
|
|
|
| 165 |
}
|
| 166 |
|
| 167 |
async def analyze_chart_patterns(self, symbol, ohlcv_data):
|
| 168 |
+
"""(تم تركها فارغة عمداً لأن النموذج الضخم يقوم بها الآن)"""
|
| 169 |
pass
|
| 170 |
|
| 171 |
def _parse_pattern_response(self, response_text):
|
| 172 |
+
"""(تم تركها فارغة عمداً)"""
|
| 173 |
pass
|
| 174 |
|
| 175 |
|
|
|
|
| 335 |
return None
|
| 336 |
|
| 337 |
async def _get_pattern_analysis(self, data_payload):
|
| 338 |
+
try:
|
| 339 |
+
symbol = data_payload['symbol']
|
| 340 |
+
ohlcv_data = data_payload.get('raw_ohlcv') or data_payload.get('ohlcv')
|
| 341 |
+
if ohlcv_data:
|
| 342 |
+
# (This is a placeholder, as PatternAnalysisEngine.analyze_chart_patterns is not implemented)
|
| 343 |
+
return None
|
| 344 |
+
return None
|
| 345 |
+
except Exception as e:
|
| 346 |
+
print(f"❌ Pattern analysis failed for {data_payload.get('symbol')}: {e}")
|
| 347 |
+
return None
|
| 348 |
|
| 349 |
# 🔴 --- START OF PROMPT CHANGE --- 🔴
|
| 350 |
def _create_comprehensive_trading_prompt(
|
|
|
|
| 470 |
|
| 471 |
|
| 472 |
def _format_candle_data_comprehensive(self, ohlcv_data):
|
| 473 |
+
"""تنسيق شامل لبيانات الشموع الخام"""
|
| 474 |
+
if not ohlcv_data:
|
| 475 |
+
return "No raw candle data available for analysis"
|
| 476 |
+
|
| 477 |
+
try:
|
| 478 |
+
timeframes_available = []
|
| 479 |
+
total_candles = 0
|
| 480 |
+
|
| 481 |
+
for timeframe, candles in ohlcv_data.items():
|
| 482 |
+
if candles and len(candles) >= 5:
|
| 483 |
+
timeframes_available.append(f"{timeframe.upper()} ({len(candles)} candles)")
|
| 484 |
+
total_candles += len(candles)
|
| 485 |
+
|
| 486 |
+
if not timeframes_available:
|
| 487 |
+
return "Insufficient candle data across all timeframes"
|
| 488 |
+
|
| 489 |
+
summary = f"📊 Available Timeframes: {', '.join(timeframes_available)}\n"
|
| 490 |
+
summary += f"📈 Total Candles Available: {total_candles}\n\n"
|
| 491 |
+
|
| 492 |
+
raw_candle_analysis_text = self.pattern_engine._format_chart_data_for_llm(ohlcv_data)
|
| 493 |
+
|
| 494 |
+
summary += raw_candle_analysis_text
|
| 495 |
+
|
| 496 |
+
return summary
|
| 497 |
+
except Exception as e:
|
| 498 |
+
return f"Error formatting raw candle data: {str(e)}"
|
| 499 |
|
| 500 |
def _analyze_timeframe_candles(self, candles, timeframe):
|
| 501 |
+
"""تحليل الشموع لإطار زمني محدد - (تستخدم داخلياً بواسطة _format_raw_candle_data)"""
|
| 502 |
+
try:
|
| 503 |
+
if len(candles) < 10:
|
| 504 |
+
return f"Insufficient data ({len(candles)} candles)"
|
| 505 |
+
|
| 506 |
+
recent_candles = candles[-15:]
|
| 507 |
+
|
| 508 |
+
closes = [c[4] for c in recent_candles]
|
| 509 |
+
opens = [c[1] for c in recent_candles]
|
| 510 |
+
highs = [c[2] for c in recent_candles]
|
| 511 |
+
lows = [c[3] for c in recent_candles]
|
| 512 |
+
volumes = [c[5] for c in recent_candles]
|
| 513 |
+
|
| 514 |
+
current_price = closes[-1]
|
| 515 |
+
first_price = closes[0]
|
| 516 |
+
price_change = ((current_price - first_price) / first_price) * 100 if first_price > 0 else 0
|
| 517 |
+
|
| 518 |
+
if price_change > 2: trend = "🟢 UPTREND"
|
| 519 |
+
elif price_change < -2: trend = "🔴 DOWNTREND"
|
| 520 |
+
else: trend = "⚪ SIDEWAYS"
|
| 521 |
+
|
| 522 |
+
high_max = max(highs)
|
| 523 |
+
low_min = min(lows)
|
| 524 |
+
volatility = ((high_max - low_min) / low_min) * 100 if low_min > 0 else 0
|
| 525 |
+
|
| 526 |
+
avg_volume = sum(volumes) / len(volumes) if volumes else 1
|
| 527 |
+
current_volume = volumes[-1] if volumes else 0
|
| 528 |
+
volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1
|
| 529 |
+
|
| 530 |
+
green_candles = sum(1 for i in range(len(closes)) if closes[i] > opens[i])
|
| 531 |
+
red_candles = len(closes) - green_candles
|
| 532 |
+
candle_ratio = green_candles / len(closes) if closes else 0
|
| 533 |
+
|
| 534 |
+
analysis = [
|
| 535 |
+
f"📈 Trend: {trend} ({price_change:+.2f}%)",
|
| 536 |
+
f"🌊 Volatility: {volatility:.2f}%",
|
| 537 |
+
f"📦 Volume: {volume_ratio:.2f}x average",
|
| 538 |
+
f"🕯️ Candles: {green_candles}🟢/{red_candles}🔴 ({candle_ratio:.1%} green)",
|
| 539 |
+
f"💰 Range: {low_min:.6f} - {high_max:.6f}",
|
| 540 |
+
f"🎯 Current: {current_price:.6f}"
|
| 541 |
+
]
|
| 542 |
+
|
| 543 |
+
return "\n".join(analysis)
|
| 544 |
+
except Exception as e:
|
| 545 |
+
return f"Analysis error: {str(e)}"
|
| 546 |
|
| 547 |
def _format_market_context(self, sentiment_data):
|
| 548 |
+
"""تنسيق سياق السوق"""
|
| 549 |
+
if not sentiment_data or sentiment_data.get('data_quality', 'LOW') == 'LOW':
|
| 550 |
+
return "Market context data not available or incomplete."
|
| 551 |
+
|
| 552 |
+
btc_sentiment = sentiment_data.get('btc_sentiment', 'N/A')
|
| 553 |
+
fear_greed = sentiment_data.get('fear_and_greed_index', 'N/A')
|
| 554 |
+
market_trend = sentiment_data.get('market_trend', 'N/A')
|
| 555 |
+
|
| 556 |
+
lines = [
|
| 557 |
+
f"• Bitcoin Sentiment: {btc_sentiment}",
|
| 558 |
+
f"• Fear & Greed Index: {fear_greed} ({sentiment_data.get('sentiment_class', 'Neutral')})",
|
| 559 |
+
f"• Overall Market Trend: {market_trend.replace('_', ' ').title() if isinstance(market_trend, str) else 'N/A'}"
|
| 560 |
+
]
|
| 561 |
+
|
| 562 |
+
general_whale = sentiment_data.get('general_whale_activity', {})
|
| 563 |
+
if general_whale and general_whale.get('sentiment') != 'NEUTRAL':
|
| 564 |
+
whale_sentiment = general_whale.get('sentiment', 'N/A')
|
| 565 |
+
critical_alert = general_whale.get('critical_alert', False)
|
| 566 |
+
lines.append(f"• General Whale Sentiment: {whale_sentiment.replace('_', ' ').title() if isinstance(whale_sentiment, str) else 'N/A'}")
|
| 567 |
+
if critical_alert:
|
| 568 |
+
lines.append(" ⚠️ CRITICAL WHALE ALERT ACTIVE")
|
| 569 |
+
|
| 570 |
+
return "\n".join(lines)
|
| 571 |
|
| 572 |
|
| 573 |
async def re_analyze_trade_async(self, trade_data: dict, processed_data: dict):
|