Spaces:
Paused
Paused
File size: 16,631 Bytes
e2a2b99 9fa0d0b bacfebc e2a2b99 3e1d7b3 bacfebc 9fa0d0b bacfebc 3e1d7b3 bacfebc 3e1d7b3 e2a2b99 3e1d7b3 e2a2b99 6c23b98 53cf6c0 99c948e 9fa0d0b 99c948e bacfebc 9fa0d0b e2a2b99 99c948e 3e1d7b3 e2a2b99 9fa0d0b 53cf6c0 bacfebc 9fa0d0b bacfebc 6c23b98 bacfebc 9fa0d0b e2a2b99 6c23b98 e2a2b99 6c23b98 9fa0d0b 99c948e 9fa0d0b bacfebc 3b07030 bacfebc e2a2b99 99c948e 6c23b98 e2a2b99 9fa0d0b 99c948e bacfebc e2a2b99 bacfebc 99c948e 9fa0d0b 99c948e e2a2b99 6c23b98 99c948e bacfebc 3b07030 e2a2b99 9fa0d0b e2a2b99 3e1d7b3 9fa0d0b e2a2b99 bacfebc 3b07030 e2a2b99 9fa0d0b e2a2b99 3b07030 bacfebc 9fa0d0b 53cf6c0 9fa0d0b 99c948e bacfebc 99c948e 9fa0d0b 99c948e 9fa0d0b 53cf6c0 e2a2b99 99c948e e2a2b99 99c948e e2a2b99 9fa0d0b e2a2b99 3e1d7b3 9fa0d0b e2a2b99 bacfebc 53cf6c0 e2a2b99 9fa0d0b e2a2b99 53cf6c0 bacfebc 9fa0d0b 60efff2 9fa0d0b bacfebc 99c948e bacfebc e2a2b99 bacfebc e2a2b99 9fa0d0b e2a2b99 99c948e e2a2b99 7f9b720 e2a2b99 9fa0d0b e2a2b99 9fa0d0b e2a2b99 9fa0d0b bacfebc e2a2b99 3e1d7b3 bacfebc e2a2b99 3e1d7b3 99c948e 3b07030 e2a2b99 9fa0d0b e2a2b99 9fa0d0b e2a2b99 9fa0d0b e2a2b99 9fa0d0b e2a2b99 9fa0d0b bacfebc e2a2b99 bacfebc e2a2b99 bacfebc e2a2b99 bacfebc e2a2b99 9fa0d0b e2a2b99 bacfebc e2a2b99 bacfebc e2a2b99 9fa0d0b e2a2b99 1af938b e2a2b99 9fa0d0b e2a2b99 3e1d7b3 bacfebc e2a2b99 3e1d7b3 99c948e 6c23b98 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# LLM.py (V14.3 - Independent Analyst & Raw Data Verifier)
import os
import traceback
import json
import re
import time
from datetime import datetime
from typing import Dict, Any, Optional
from openai import AsyncOpenAI
# ==============================================================================
# 🔌 إعدادات الاتصال بالنموذج (Model Connection Settings)
# ==============================================================================
LLM_API_URL = os.getenv("LLM_API_URL", "https://integrate.api.nvidia.com/v1")
LLM_API_KEY = os.getenv("LLM_API_KEY")
LLM_MODEL = os.getenv("LLM_MODEL", "nvidia/llama-3.1-nemotron-ultra-253b-v1")
# بارامترات التوليد (مضبوطة للتفكير العميق والتحليل الدقيق)
LLM_TEMPERATURE = 0.2 # حرارة منخفضة للدقة والمنطق
LLM_TOP_P = 0.7
LLM_MAX_TOKENS = 16384 # الحد الأقصى للسماح بتحليل كميات ضخمة من البيانات
LLM_FREQUENCY_PENALTY = 0.0
LLM_PRESENCE_PENALTY = 0.0
CLIENT_TIMEOUT = 300.0
class LLMService:
def __init__(self):
if not LLM_API_KEY:
raise ValueError("❌ [LLM FATAL] LLM_API_KEY environment variable is missing!")
self.client = AsyncOpenAI(
base_url=LLM_API_URL,
api_key=LLM_API_KEY,
timeout=CLIENT_TIMEOUT
)
self.r2_service = None
self.learning_hub = None
print(f"🧠 [LLM V14.3] Independent Analyst (Reasoning Mode) Online. Model: {LLM_MODEL}")
async def _call_llm(self, prompt: str) -> Optional[str]:
"""تنفيذ استدعاء API للنموذج مع تفعيل وضع التفكير."""
# [ 🔥 مفتاح التفعيل ]: هذه العبارة تجبر النموذج على الدخول في وضع التفكير المتسلسل
system_prompt_trigger = "detailed thinking on"
try:
response = await self.client.chat.completions.create(
model=LLM_MODEL,
messages=[
{"role": "system", "content": system_prompt_trigger},
{"role": "user", "content": prompt}
],
temperature=LLM_TEMPERATURE,
top_p=LLM_TOP_P,
max_tokens=LLM_MAX_TOKENS,
frequency_penalty=LLM_FREQUENCY_PENALTY,
presence_penalty=LLM_PRESENCE_PENALTY,
stream=False,
response_format={"type": "json_object"}
)
# حماية ضد الاستجابات الفارغة
if response and response.choices and len(response.choices) > 0:
message = response.choices[0].message
if message and message.content:
return message.content
print("⚠️ [LLM Warning] Received empty response from model.")
return None
except Exception as e:
print(f"❌ [LLM Call Error] API request failed: {e}")
return None
def _parse_json_secure(self, text: str) -> Optional[Dict]:
"""محلل JSON قوي يستخرج الكائن من بين نصوص التفكير."""
try:
if not text: return None
# البحث عن أول كائن JSON صالح يبدأ بـ { وينتهي بـ }
json_match = re.search(r'\{.*\}', text, re.DOTALL)
if json_match:
return json.loads(json_match.group(0))
else:
print("⚠️ [LLM Parser] No JSON object found in response text.")
return None
except json.JSONDecodeError as e:
print(f"⚠️ [LLM Parser] JSON decode failed: {e}")
return None
except Exception as e:
print(f"❌ [LLM Parser] Unexpected error: {e}")
return None
# ==================================================================
# 🧠 الوظيفة الرئيسية 1: قرار الدخول الاستراتيجي (Layer 3 Analysis)
# ==================================================================
async def get_trading_decision(self, candidate_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
symbol = candidate_data.get('symbol', 'UNKNOWN_ASSET')
try:
# جلب سياق التعلم السابق
learning_context = "Playbook: No specific prior learning records found."
if self.learning_hub:
learning_context = await self.learning_hub.get_active_context_for_llm("general", f"{symbol} entry analysis")
# إنشاء البرومبت التحليلي (البيانات الخام)
prompt = self._create_raw_data_analyst_prompt(candidate_data, learning_context)
# استدعاء النموذج
response_text = await self._call_llm(prompt)
if not response_text: return None
# تحليل النتيجة
decision = self._parse_json_secure(response_text)
# أرشفة القرار
if self.r2_service and decision:
await self.r2_service.save_llm_prompts_async(symbol, "raw_analyst_decision", prompt, response_text)
return decision
except Exception as e:
print(f"❌ [LLM Entry Error] Critical failure for {symbol}: {e}")
traceback.print_exc()
return None
# ==================================================================
# 🔄 الوظيفة الرئيسية 2: إعادة التحليل الدوري (Strategic Re-eval)
# ==================================================================
async def re_analyze_trade_async(self, trade_data: Dict[str, Any], current_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
symbol = trade_data.get('symbol', 'UNKNOWN_ASSET')
try:
# جلب سياق الاستراتيجية
learning_context = "Playbook: Stick to original trading plan unless invalidated."
if self.learning_hub:
learning_context = await self.learning_hub.get_active_context_for_llm("strategy", f"{symbol} re-eval")
# إنشاء برومبت إعادة التحليل (البيانات الخام)
prompt = self._create_raw_reanalysis_prompt(trade_data, current_data, learning_context)
# استدعاء النموذج
response_text = await self._call_llm(prompt)
if not response_text: return None
# تحليل النتيجة
decision = self._parse_json_secure(response_text)
# أرشفة القرار
if self.r2_service and decision:
await self.r2_service.save_llm_prompts_async(symbol, "raw_reanalysis_decision", prompt, response_text)
return decision
except Exception as e:
print(f"❌ [LLM Re-Eval Error] Critical failure for {symbol}: {e}")
return None
# ==================================================================
# 📝 أدوات المساعدة في التنسيق (Helpers)
# ==================================================================
def _format_candles_to_csv(self, ohlcv_dict: Dict[str, list], limit: int = 200) -> str:
"""
تحويل بيانات الشموع من صيغة JSON/List إلى صيغة CSV مضغوطة ليفهمها النموذج بسهولة.
"""
csv_output = ""
# تحديد الأولويات للفريمات الزمنية (الأهم فالأهم)
priority_tfs = ['1h', '4h', '1d']
for tf in priority_tfs:
if tf not in ohlcv_dict: continue
candles = ohlcv_dict[tf]
if not candles: continue
# اقتطاع آخر 'limit' شمعة فقط
sliced_candles = candles[-limit:]
csv_output += f"\n=== TIMEFRAME {tf.upper()} (Last {len(sliced_candles)} Candles) ===\n"
csv_output += "Index,Open,High,Low,Close,Volume\n"
for idx, c in enumerate(sliced_candles):
# التنسيق: Index, Open, High, Low, Close, Volume
# (نتأكد من وجود العناصر لتجنب الأخطاء)
if len(c) >= 6:
csv_output += f"{idx},{c[1]},{c[2]},{c[3]},{c[4]},{c[5]}\n"
if not csv_output:
return "NO CANDLE DATA AVAILABLE."
return csv_output
# ==================================================================
# 🧠 هندسة البرومبت: الدخول (Raw Data Analyst Prompt)
# ==================================================================
def _create_raw_data_analyst_prompt(self, data: Dict[str, Any], learning_context: str) -> str:
symbol = data.get('symbol', 'UNKNOWN')
# 1. استخراج بيانات الطبقة الأولى (HEARSAY)
l1_components = data.get('components', {})
titan_score = l1_components.get('titan_score', 0.0)
pat_details = data.get('pattern_details', {})
pat_conf = pat_details.get('pattern_confidence', 0.0)
simple_mc_score = l1_components.get('mc_score', 0.0)
# 2. استخراج بيانات الطبقة الثانية (CONTEXT)
# الحيتان
whale_raw = data.get('l2_raw_values', {}).get('whale', 'No Data')
if whale_raw == "No Data" or whale_raw == "$0" or whale_raw == "No Data ($0)":
whale_section = "WHALE DATA: ⚠️ NO DATA FOUND (Assume Neutral/Unknown)"
else:
whale_section = f"WHALE DATA (Net Flow): {whale_raw} (Positive=Outflow/Sell, Negative=Inflow/Buy? Check Context)"
# الأخبار
news_raw = data.get('news_text', 'No specific news found.')
# مونت كارلو المتقدمة
adv_mc_raw = data.get('l2_raw_values', {}).get('adv_mc', 'No Data')
# 3. استخراج البيانات الخام (THE TRUTH)
ohlcv_raw = data.get('ohlcv', {})
candles_csv_block = self._format_candles_to_csv(ohlcv_raw, limit=200)
return f"""
ROLE: You are the 'Omniscient Brain', the Lead Quantitative Analyst.
OBJECTIVE: Analyze the RAW DATA for {symbol} and decide to TRADE or IGNORE.
⚠️ CRITICAL INSTRUCTION:
The "Local Model Reports" below are generated by smaller, potentially flawed algorithms.
They are strictly HEARSAY. You must NOT trust them blindly.
Your job is to verify them against the RAW CANDLE DATA provided below.
If your visual/statistical analysis of the candles contradicts the Local Models, YOUR ANALYSIS PREVAILS.
========== 1. LOCAL MODEL REPORTS (HEARSAY - TREAT WITH SKEPTICISM) ==========
[A] Technical Indicator Analysis (formerly Titan): Score {titan_score:.2f}/1.00
- Note: Based on standard RSI/MACD/Bollinger calculations.
[B] Pattern Recognition Model: Confidence {pat_conf:.2f}/1.00
- Note: This model DOES NOT identify pattern names. It only outputs a confidence score.
[C] Simple Monte Carlo: Score {simple_mc_score:.2f}
- Method: Basic Random Walk based on recent volatility.
[D] Advanced Monte Carlo: Probability {adv_mc_raw}
- Method: GARCH(1,1) model with Fat-Tailed distribution simulation.
========== 2. EXTERNAL FACTORS (CONTEXT) ==========
[A] {whale_section}
[B] RAW NEWS FEED:
"{news_raw}"
========== 3. THE TRUTH (RAW CANDLE DATA) ==========
Below are the last 200 candles for key timeframes (1H, 4H, 1D).
Index 199 is the MOST RECENT candle.
Format: Index,Open,High,Low,Close,Volume
{candles_csv_block}
========== 4. YOUR ANALYTICAL TASKS ==========
1. **Pattern Hunting:** Scan the CSV data above (especially 1H and 4H). Do you see any classic patterns (Bull Flag, Head & Shoulders, Double Bottom, etc.)?
2. **Trend Verification:** Compare the 'Technical Indicator Analysis' score with the slope of the raw Close prices. Is the score hallucinating a trend where there is actually a range or downtrend?
3. **Volume Analysis:** Look at the Volume column. Is there a volume spike supporting the price move?
4. **Conflict Resolution:** If Local Models say "UP" but Raw Candles show "Lower Highs" (Downtrend), you must REJECT.
========== 5. FINAL DECISION OUTPUT ==========
Based ONLY on your analysis of the raw data:
REQUIRED JSON FORMAT:
{{
"action": "WATCH" or "IGNORE",
"confidence": 0.00 to 1.00,
"identified_patterns": ["List specific patterns YOU found in the raw data (e.g., 'Bull Flag on 1H')"],
"analysis_summary": "Your reasoning. Explicitly mention if you agreed or disagreed with the Local Models.",
"key_raw_evidence": "Cite specific candle indexes or volume figures that support your decision."
}}
"""
# ==================================================================
# 🧠 هندسة البرومبت: إعادة التحليل (Raw Re-Analysis Prompt)
# ==================================================================
def _create_raw_reanalysis_prompt(self, trade: Dict, current: Dict, learning_context: str) -> str:
symbol = trade.get('symbol', 'UNKNOWN')
entry_price = trade.get('entry_price', 0.0)
current_price = current.get('current_price', 0.0)
# حساب مدة الصفقة والربح
try:
entry_time = datetime.fromisoformat(trade.get('entry_time').replace('Z', '+00:00'))
duration_minutes = (datetime.now(entry_time.tzinfo) - entry_time).total_seconds() / 60
except:
duration_minutes = 0.0
pnl_percentage = ((current_price - entry_price) / entry_price) * 100
# 1. البيانات الجديدة (Local Models)
titan_score_now = current.get('titan_score', 0.0)
# 2. البيانات الخارجية (الحيتان والأخبار)
whale_now = current.get('whale_data', {})
# محاولة استخراج القيمة الخام بشكل آمن
whale_net_flow = whale_now.get('accumulation_analysis_24h', {}).get('net_flow_usd', 'No Data')
if whale_net_flow == 'No Data':
whale_section = "WHALE DATA: ⚠️ NO DATA FOUND"
else:
whale_section = f"WHALE DATA (24H Net Flow): ${whale_net_flow}"
news_text_now = current.get('news_text', 'No new significant news.')
# 3. البيانات الخام (الشموع الجديدة)
ohlcv_raw = current.get('ohlcv', {})
# نكتفي بـ 100 شمعة للمتابعة لتقليل الحمل
candles_csv_block = self._format_candles_to_csv(ohlcv_raw, limit=100)
return f"""
ROLE: You are the 'Omniscient Brain', the Lead Quantitative Analyst (Guardian Mode).
EVENT: RE-EVALUATION of an OPEN POSITION.
ASSET: {symbol} | DURATION: {duration_minutes:.1f} min | PnL: {pnl_percentage:+.2f}%
⚠️ CRITICAL INSTRUCTION:
You must analyze the RAW DATA to see if the original trade thesis is INVALIDATED.
Do NOT panic over small fluctuations. Look for structural breaks or trend reversals in the raw candles.
========== 1. POSITION STATUS ==========
* Entry Price: {entry_price}
* Current Price: {current_price}
* Current Targets -> TP: {trade.get('tp_price', 'N/A')} | SL: {trade.get('sl_price', 'N/A')}
========== 2. NEW CONTEXT (HEARSAY) ==========
[A] Tech Indicator Score Now: {titan_score_now:.2f}/1.00
[B] {whale_section}
[C] LATEST NEWS: "{news_text_now[:500]}..."
========== 3. THE TRUTH (LATEST RAW CANDLES) ==========
Index 99 is the MOST RECENT candle.
Format: Index,Open,High,Low,Close,Volume
{candles_csv_block}
========== 4. YOUR ANALYTICAL TASKS ==========
1. **Momentum Check:** Look at the last 10-20 candles. Is the momentum stalling or reversing against you?
2. **Volume check:** Is there heavy volume selling (red candles with high volume)?
3. **Thesis Validation:** Does the raw price action still support the original direction (UP)?
========== 5. FINAL DECISION OUTPUT ==========
Based ONLY on raw data analysis:
REQUIRED JSON FORMAT:
{{
"action": "HOLD" or "EMERGENCY_EXIT" or "UPDATE_TARGETS",
"new_tp": null or float (only if action is UPDATE_TARGETS),
"new_sl": null or float (only if action is UPDATE_TARGETS),
"reasoning": "Concise analysis of why the trade is still valid or broken based on raw candles."
}}
"""
print("✅ LLM Service V13.8 (Robust Null Safety) Loaded") |