Riy777 commited on
Commit
e2a2b99
·
1 Parent(s): 6f27eda

Update LLM.py

Browse files
Files changed (1) hide show
  1. LLM.py +182 -127
LLM.py CHANGED
@@ -1,4 +1,4 @@
1
- # LLM.py (V13.8 - Production Fix - Robust Null Safety)
2
  import os
3
  import traceback
4
  import json
@@ -6,7 +6,7 @@ import re
6
  import time
7
  from datetime import datetime
8
  from typing import Dict, Any, Optional
9
- from openai import AsyncOpenAI, RateLimitError, APIError
10
 
11
  # ==============================================================================
12
  # 🔌 إعدادات الاتصال بالنموذج (Model Connection Settings)
@@ -15,12 +15,12 @@ LLM_API_URL = os.getenv("LLM_API_URL", "https://integrate.api.nvidia.com/v1")
15
  LLM_API_KEY = os.getenv("LLM_API_KEY")
16
  LLM_MODEL = os.getenv("LLM_MODEL", "nvidia/llama-3.1-nemotron-ultra-253b-v1")
17
 
18
- # بارامترات التوليد (مضبوطة لصرامة التحليل)
19
- LLM_TEMPERATURE = 0.2
20
  LLM_TOP_P = 0.7
21
- LLM_MAX_TOKENS = 16384
22
- LLM_FREQUENCY_PENALTY = 0.8
23
- LLM_PRESENCE_PENALTY = 0.5
24
  CLIENT_TIMEOUT = 300.0
25
 
26
  class LLMService:
@@ -36,10 +36,12 @@ class LLMService:
36
  self.r2_service = None
37
  self.learning_hub = None
38
 
39
- print(f"🧠 [LLM V13.8] Omniscient Brain Online. Model: {LLM_MODEL}")
40
 
41
  async def _call_llm(self, prompt: str) -> Optional[str]:
42
- """تنفيذ استدعاء API للنموذج مع حماية ضد الاستجابات الفارغة."""
 
 
43
  system_prompt_trigger = "detailed thinking on"
44
 
45
  try:
@@ -58,13 +60,13 @@ class LLMService:
58
  response_format={"type": "json_object"}
59
  )
60
 
61
- # [ 🚀 ] حماية ضد NoneType في الاستجابة
62
  if response and response.choices and len(response.choices) > 0:
63
  message = response.choices[0].message
64
  if message and message.content:
65
  return message.content
66
 
67
- print("⚠️ [LLM Warning] Received empty or malformed response from model.")
68
  return None
69
 
70
  except Exception as e:
@@ -72,9 +74,10 @@ class LLMService:
72
  return None
73
 
74
  def _parse_json_secure(self, text: str) -> Optional[Dict]:
75
- """محلل JSON قوي."""
76
  try:
77
  if not text: return None
 
78
  json_match = re.search(r'\{.*\}', text, re.DOTALL)
79
  if json_match:
80
  return json.loads(json_match.group(0))
@@ -89,24 +92,29 @@ class LLMService:
89
  return None
90
 
91
  # ==================================================================
92
- # 🧠 الوظيفة الرئيسية 1: قرار الدخول الاستراتيجي (Layer 3)
93
  # ==================================================================
94
  async def get_trading_decision(self, candidate_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
95
  symbol = candidate_data.get('symbol', 'UNKNOWN_ASSET')
96
  try:
 
97
  learning_context = "Playbook: No specific prior learning records found."
98
  if self.learning_hub:
99
  learning_context = await self.learning_hub.get_active_context_for_llm("general", f"{symbol} entry analysis")
100
 
101
- prompt = self._create_heavyweight_entry_prompt(candidate_data, learning_context)
 
102
 
 
103
  response_text = await self._call_llm(prompt)
104
  if not response_text: return None
105
 
 
106
  decision = self._parse_json_secure(response_text)
107
 
 
108
  if self.r2_service and decision:
109
- await self.r2_service.save_llm_prompts_async(symbol, "entry_decision_full", prompt, response_text)
110
 
111
  return decision
112
 
@@ -121,20 +129,24 @@ class LLMService:
121
  async def re_analyze_trade_async(self, trade_data: Dict[str, Any], current_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
122
  symbol = trade_data.get('symbol', 'UNKNOWN_ASSET')
123
  try:
124
- strategy_name = trade_data.get('entry_reason', 'GENERIC_STRATEGY')
125
- learning_context = "Playbook: Stick to original trading plan."
126
  if self.learning_hub:
127
- learning_context = await self.learning_hub.get_active_context_for_llm("strategy", f"{symbol} re-eval {strategy_name}")
128
 
129
- prompt = self._create_heavyweight_reanalysis_prompt(trade_data, current_data, learning_context)
 
130
 
 
131
  response_text = await self._call_llm(prompt)
132
  if not response_text: return None
133
 
 
134
  decision = self._parse_json_secure(response_text)
135
 
 
136
  if self.r2_service and decision:
137
- await self.r2_service.save_llm_prompts_async(symbol, "re_analysis_full", prompt, response_text)
138
 
139
  return decision
140
 
@@ -143,101 +155,131 @@ class LLMService:
143
  return None
144
 
145
  # ==================================================================
146
- # 📝 قسم هندسة البرومبتات الكاملة (Full Prompt Engineering)
147
  # ==================================================================
148
- def _create_heavyweight_entry_prompt(self, data: Dict[str, Any], learning_context: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  symbol = data.get('symbol', 'UNKNOWN')
150
- current_price = data.get('current_price', 0.0)
151
 
152
- l1_total_score = data.get('enhanced_final_score', 0.0)
153
- l2_total_score = data.get('layer2_score', 0.0)
 
154
 
155
- titan_raw_score = data.get('titan_details', {}).get('score', 0.0)
156
- titan_trend_label = "STRONG_UPTREND" if titan_raw_score > 0.7 else "UPTREND" if titan_raw_score > 0.5 else "NEUTRAL/WEAK"
157
 
158
- pat_data = data.get('pattern_details', {})
159
- pattern_name = pat_data.get('pattern_detected', 'None detected')
160
- pattern_confidence = pat_data.get('pattern_confidence', 0.0)
161
 
162
- mc_prob = data.get('components', {}).get('mc_score', 0.0)
163
-
164
- # [ 🚀 ] حماية استخراج بيانات الحيتان
165
- whale = data.get('whale_data') or {} # (استخدم قاموس فارغ إذا كان None)
166
- whale_1h = whale.get('exchange_flows') or {}
167
- whale_24h = whale.get('accumulation_analysis_24h') or {}
 
 
 
 
168
 
169
- whale_evidence_block = f"""
170
- - [1H Window] Net Flow to Exchanges: ${whale_1h.get('net_flow_usd', 0):,.2f}
171
- - [1H Window] Deposit Tx Count: {whale_1h.get('deposit_count', 0)} | Withdrawal Tx Count: {whale_1h.get('withdrawal_count', 0)}
172
- - [24H Window] Net Accumulation Flow: ${whale_24h.get('net_flow_usd', 0):,.2f}
173
- - [24H Window] Total Large Transactions: {whale_24h.get('whale_transfers_count', 0)}
174
- - [24H Window] Relative Flow Impact: {whale_24h.get('relative_net_flow_percent', 0):.4f}% of daily volume
175
- """
176
-
177
- raw_news_text = data.get('news_text', 'No specific news articles found.')
178
-
179
- # [ 🚀 ] حماية حلقة الشموع (OHLCV)
180
- ohlcv_data = data.get('ohlcv_sample') or {}
181
- price_snapshot_block = ""
182
- for tf, candle in ohlcv_data.items():
183
- # التأكد من أن candle ليس None وأنه قائمة تحتوي على بيانات
184
- if candle and isinstance(candle, (list, tuple)) and len(candle) >= 6:
185
- price_snapshot_block += f" - {tf.upper()} Frame: Open={candle[1]}, High={candle[2]}, Low={candle[3]}, Close={candle[4]}, Vol={candle[5]}\n"
186
 
187
  return f"""
188
- YOU ARE THE OMNISCIENT BRAIN. A skeptical, master-level crypto trading AI with absolute veto power.
189
- Your goal is to validate the preliminary findings of your sub-systems and make the FINAL GO/NO-GO decision for asset: {symbol}.
190
- Current Market Price: {current_price}
191
-
192
- ========== 🧠 PART 1: SUB-SYSTEM REPORTS (PRELIMINARY OPINIONS) ==========
193
- Your subordinate analytical layers have flagged this asset with the following scores:
194
- * Layer 1 Technical Score: {l1_total_score:.4f} / 1.0
195
- - Titan ML Trend Model: {titan_raw_score:.4f} ({titan_trend_label})
196
- - Chart Pattern Recognition: {pattern_name} (Confidence: {pattern_confidence:.2f})
197
- - Monte Carlo Win Probability (1H): {mc_prob:.4f}
198
- * Layer 2 Enhanced Score: {l2_total_score:.4f} / 1.0 (Adjusted for initial whale/news sentiment)
199
-
200
- ========== 🔍 PART 2: RAW EVIDENCE FOR VERIFICATION (THE TRUTH) ==========
201
- Do NOT trust the scores above blindly. Verify them against this raw data yourself:
202
-
203
- [A] RAW PRICE ACTION SNAPSHOT (Latest Closed Candles OHLCV):
204
- {price_snapshot_block}
205
- -> VERIFICATION TASK: Does this raw price action actually confirm the 'Titan Trend' reported above? Look for contradictions.
206
-
207
- [B] RAW WHALE ON-CHAIN ACTIVITY (Flows & Accumulation):
208
- {whale_evidence_block}
209
- -> VERIFICATION TASK: Is there hidden distribution (selling) occurring despite the technical uptrend? High inflows to exchanges are a red flag.
210
-
211
- [C] RAW NEWSWIRE FEED (Latest Headlines & Summaries):
212
- \"\"\"
213
- {raw_news_text}
214
- \"\"\"
215
- -> VERIFICATION TASK: Read the text above. Are there any immediate red flags, FUD (Fear, Uncertainty, Doubt), or regulatory risks that the sentiment score might have missed?
216
-
217
- ========== 📖 PART 3: INSTITUTIONAL MEMORY (LEARNING PLAYBOOK) ==========
218
- The following rules have been learned from previous trading outcomes:
219
- {learning_context}
220
-
221
- ========== 🛑 FINAL DECISION TASK ==========
222
- Perform a deep, step-by-step internal analysis (triggered by your 'detailed thinking' mode).
223
- Compare PART 1 (Opinions) vs PART 2 (Facts).
224
- If FACTS strongly contradict OPINIONS, you MUST reject the trade regardless of the high scores.
225
-
226
- REQUIRED OUTPUT (Strict JSON format ONLY, no other text):
 
 
227
  {{
228
  "action": "WATCH" or "IGNORE",
229
- "confidence_level": 0.00 to 1.00 (Two decimal places),
230
- "reasoning": "A rigorous, professional justification citing specific raw evidence points that swayed your decision.",
231
- "strategy_directive": "MOMENTUM_BREAKOUT" or "DIP_ACCUMULATION" or "SCALP_REVERSAL",
232
- "key_risk_factor": "Identify the single biggest risk factor based on the raw evidence provided."
233
  }}
234
  """
235
 
236
- def _create_heavyweight_reanalysis_prompt(self, trade: Dict, current: Dict, learning_context: str) -> str:
 
 
 
237
  symbol = trade.get('symbol', 'UNKNOWN')
238
  entry_price = trade.get('entry_price', 0.0)
239
  current_price = current.get('current_price', 0.0)
240
 
 
241
  try:
242
  entry_time = datetime.fromisoformat(trade.get('entry_time').replace('Z', '+00:00'))
243
  duration_minutes = (datetime.now(entry_time.tzinfo) - entry_time).total_seconds() / 60
@@ -245,53 +287,66 @@ REQUIRED OUTPUT (Strict JSON format ONLY, no other text):
245
  duration_minutes = 0.0
246
 
247
  pnl_percentage = ((current_price - entry_price) / entry_price) * 100
 
 
248
  titan_score_now = current.get('titan_score', 0.0)
249
 
250
- # [ 🚀 ] حماية بيانات الحيتان في إعادة التحليل
251
- whale_now = current.get('whale_data') or {}
252
- whale_1h_net = whale_now.get('exchange_flows', {}).get('net_flow_usd', 0.0)
253
- whale_24h_net = whale_now.get('accumulation_analysis_24h', {}).get('net_flow_usd', 0.0)
254
 
 
 
 
 
 
255
  news_text_now = current.get('news_text', 'No new significant news.')
256
 
 
 
 
 
 
257
  return f"""
258
- ROLE: Omniscient Brain (Trade Guardian Mode).
259
- EVENT: Mandatory periodic strategic re-evaluation of an OPEN POSITION.
260
- ASSET: {symbol}
261
- TIME IN TRADE: {duration_minutes:.1f} minutes
262
 
263
- ========== 📉 CURRENT POSITION STATUS ==========
 
 
 
 
264
  * Entry Price: {entry_price}
265
- * Current Market Price: {current_price}
266
- * Unrealized PnL: {pnl_percentage:+.2f}%
267
- * Original Entry Reason: "{trade.get('entry_reason', 'N/A')}"
268
  * Current Targets -> TP: {trade.get('tp_price', 'N/A')} | SL: {trade.get('sl_price', 'N/A')}
269
 
270
- ========== 🆕 CHANGED MARKET CONDITIONS (RAW DATA UPDATE) ==========
271
- 1. ML Trend Model Update (Titan): Currently at {titan_score_now:.4f}
272
- (Is the trend weakening compared to entry?)
273
-
274
- 2. Fresh Whale Activity (Last 1H Window): Net Flow ${whale_1h_net:,.0f}
275
- (Positive value = potential selling pressure flowing to exchanges. Negative = accumulation.)
276
 
277
- 3. 24H Cumulative Whale Flow: Net ${whale_24h_net:,.0f}
 
 
278
 
279
- 4. Latest News Update Raw Text:
280
- \"\"\"{news_text_now[:1500]}\"\"\"
281
 
282
- ========== 📖 PLAYBOOK & STRATEGY GUIDELINES ==========
283
- {learning_context}
 
 
284
 
285
- ========== 🛡️ GUARDIAN DECISION TASK ==========
286
- Analyze if the original investment thesis is still valid based on the NEW raw data above.
287
- Do NOT recommend closing just because of small fluctuations. Look for FUNDAMENTAL thesis INVALIDATION.
288
 
289
- REQUIRED OUTPUT (Strict JSON format ONLY):
290
  {{
291
  "action": "HOLD" or "EMERGENCY_EXIT" or "UPDATE_TARGETS",
292
- "new_tp": null or a specific new float value (if action is UPDATE_TARGETS),
293
- "new_sl": null or a specific new float value (if action is UPDATE_TARGETS),
294
- "reasoning": "Concise professional assessment of current risk vs original thesis based on new data."
295
  }}
296
  """
297
  print("✅ LLM Service V13.8 (Robust Null Safety) Loaded")
 
1
+ # LLM.py (V14.3 - Independent Analyst & Raw Data Verifier)
2
  import os
3
  import traceback
4
  import json
 
6
  import time
7
  from datetime import datetime
8
  from typing import Dict, Any, Optional
9
+ from openai import AsyncOpenAI
10
 
11
  # ==============================================================================
12
  # 🔌 إعدادات الاتصال بالنموذج (Model Connection Settings)
 
15
  LLM_API_KEY = os.getenv("LLM_API_KEY")
16
  LLM_MODEL = os.getenv("LLM_MODEL", "nvidia/llama-3.1-nemotron-ultra-253b-v1")
17
 
18
+ # بارامترات التوليد (مضبوطة للتفكير العميق والتحليل الدقيق)
19
+ LLM_TEMPERATURE = 0.2 # حرارة منخفضة للدقة والمنطق
20
  LLM_TOP_P = 0.7
21
+ LLM_MAX_TOKENS = 16384 # الحد الأقصى للسماح بتحليل كميات ضخمة من البيانات
22
+ LLM_FREQUENCY_PENALTY = 0.0
23
+ LLM_PRESENCE_PENALTY = 0.0
24
  CLIENT_TIMEOUT = 300.0
25
 
26
  class LLMService:
 
36
  self.r2_service = None
37
  self.learning_hub = None
38
 
39
+ print(f"🧠 [LLM V14.3] Independent Analyst (Reasoning Mode) Online. Model: {LLM_MODEL}")
40
 
41
  async def _call_llm(self, prompt: str) -> Optional[str]:
42
+ """تنفيذ استدعاء API للنموذج مع تفعيل وضع التفكير."""
43
+
44
+ # [ 🔥 مفتاح التفعيل ]: هذه العبارة تجبر النموذج على الدخول في وضع التفكير المتسلسل
45
  system_prompt_trigger = "detailed thinking on"
46
 
47
  try:
 
60
  response_format={"type": "json_object"}
61
  )
62
 
63
+ # حماية ضد الاستجابات الفارغة
64
  if response and response.choices and len(response.choices) > 0:
65
  message = response.choices[0].message
66
  if message and message.content:
67
  return message.content
68
 
69
+ print("⚠️ [LLM Warning] Received empty response from model.")
70
  return None
71
 
72
  except Exception as e:
 
74
  return None
75
 
76
  def _parse_json_secure(self, text: str) -> Optional[Dict]:
77
+ """محلل JSON قوي يستخرج الكائن من بين نصوص التفكير."""
78
  try:
79
  if not text: return None
80
+ # البحث عن أول كائن JSON صالح يبدأ بـ { وينتهي بـ }
81
  json_match = re.search(r'\{.*\}', text, re.DOTALL)
82
  if json_match:
83
  return json.loads(json_match.group(0))
 
92
  return None
93
 
94
  # ==================================================================
95
+ # 🧠 الوظيفة الرئيسية 1: قرار الدخول الاستراتيجي (Layer 3 Analysis)
96
  # ==================================================================
97
  async def get_trading_decision(self, candidate_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
98
  symbol = candidate_data.get('symbol', 'UNKNOWN_ASSET')
99
  try:
100
+ # جلب سياق التعلم السابق
101
  learning_context = "Playbook: No specific prior learning records found."
102
  if self.learning_hub:
103
  learning_context = await self.learning_hub.get_active_context_for_llm("general", f"{symbol} entry analysis")
104
 
105
+ # إنشاء البرومبت التحليلي (البيانات الخام)
106
+ prompt = self._create_raw_data_analyst_prompt(candidate_data, learning_context)
107
 
108
+ # استدعاء النموذج
109
  response_text = await self._call_llm(prompt)
110
  if not response_text: return None
111
 
112
+ # تحليل النتيجة
113
  decision = self._parse_json_secure(response_text)
114
 
115
+ # أرشفة القرار
116
  if self.r2_service and decision:
117
+ await self.r2_service.save_llm_prompts_async(symbol, "raw_analyst_decision", prompt, response_text)
118
 
119
  return decision
120
 
 
129
  async def re_analyze_trade_async(self, trade_data: Dict[str, Any], current_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
130
  symbol = trade_data.get('symbol', 'UNKNOWN_ASSET')
131
  try:
132
+ # جلب سياق الاستراتيجية
133
+ learning_context = "Playbook: Stick to original trading plan unless invalidated."
134
  if self.learning_hub:
135
+ learning_context = await self.learning_hub.get_active_context_for_llm("strategy", f"{symbol} re-eval")
136
 
137
+ # إنشاء برومبت إعادة التحليل (البيانات الخام)
138
+ prompt = self._create_raw_reanalysis_prompt(trade_data, current_data, learning_context)
139
 
140
+ # استدعاء النموذج
141
  response_text = await self._call_llm(prompt)
142
  if not response_text: return None
143
 
144
+ # تحليل النتيجة
145
  decision = self._parse_json_secure(response_text)
146
 
147
+ # أرشفة القرار
148
  if self.r2_service and decision:
149
+ await self.r2_service.save_llm_prompts_async(symbol, "raw_reanalysis_decision", prompt, response_text)
150
 
151
  return decision
152
 
 
155
  return None
156
 
157
  # ==================================================================
158
+ # 📝 أدوات المساعدة في التنسيق (Helpers)
159
  # ==================================================================
160
+ def _format_candles_to_csv(self, ohlcv_dict: Dict[str, list], limit: int = 200) -> str:
161
+ """
162
+ تحويل بيانات الشموع من صيغة JSON/List إلى صيغة CSV مضغوطة ليفهمها النموذج بسهولة.
163
+ """
164
+ csv_output = ""
165
+ # تحديد الأولويات للفريمات الزمنية (الأهم فالأهم)
166
+ priority_tfs = ['1h', '4h', '1d']
167
+
168
+ for tf in priority_tfs:
169
+ if tf not in ohlcv_dict: continue
170
+ candles = ohlcv_dict[tf]
171
+ if not candles: continue
172
+
173
+ # اقتطاع آخر 'limit' شمعة فقط
174
+ sliced_candles = candles[-limit:]
175
+
176
+ csv_output += f"\n=== TIMEFRAME {tf.upper()} (Last {len(sliced_candles)} Candles) ===\n"
177
+ csv_output += "Index,Open,High,Low,Close,Volume\n"
178
+
179
+ for idx, c in enumerate(sliced_candles):
180
+ # التنسيق: Index, Open, High, Low, Close, Volume
181
+ # (نتأكد من وجود العناصر لتجنب الأخطاء)
182
+ if len(c) >= 6:
183
+ csv_output += f"{idx},{c[1]},{c[2]},{c[3]},{c[4]},{c[5]}\n"
184
+
185
+ if not csv_output:
186
+ return "NO CANDLE DATA AVAILABLE."
187
+
188
+ return csv_output
189
+
190
+ # ==================================================================
191
+ # 🧠 هندسة البرومبت: الدخول (Raw Data Analyst Prompt)
192
+ # ==================================================================
193
+ def _create_raw_data_analyst_prompt(self, data: Dict[str, Any], learning_context: str) -> str:
194
  symbol = data.get('symbol', 'UNKNOWN')
 
195
 
196
+ # 1. استخراج بيانات الطبقة الأولى (HEARSAY)
197
+ l1_components = data.get('components', {})
198
+ titan_score = l1_components.get('titan_score', 0.0)
199
 
200
+ pat_details = data.get('pattern_details', {})
201
+ pat_conf = pat_details.get('pattern_confidence', 0.0)
202
 
203
+ simple_mc_score = l1_components.get('mc_score', 0.0)
 
 
204
 
205
+ # 2. استخراج بيانات الطبقة الثانية (CONTEXT)
206
+ # الحيتان
207
+ whale_raw = data.get('l2_raw_values', {}).get('whale', 'No Data')
208
+ if whale_raw == "No Data" or whale_raw == "$0" or whale_raw == "No Data ($0)":
209
+ whale_section = "WHALE DATA: ⚠️ NO DATA FOUND (Assume Neutral/Unknown)"
210
+ else:
211
+ whale_section = f"WHALE DATA (Net Flow): {whale_raw} (Positive=Outflow/Sell, Negative=Inflow/Buy? Check Context)"
212
+
213
+ # الأخبار
214
+ news_raw = data.get('news_text', 'No specific news found.')
215
 
216
+ # مونت كارلو المتقدمة
217
+ adv_mc_raw = data.get('l2_raw_values', {}).get('adv_mc', 'No Data')
218
+
219
+ # 3. استخراج البيانات الخام (THE TRUTH)
220
+ ohlcv_raw = data.get('ohlcv', {})
221
+ candles_csv_block = self._format_candles_to_csv(ohlcv_raw, limit=200)
 
 
 
 
 
 
 
 
 
 
 
222
 
223
  return f"""
224
+ ROLE: You are the 'Omniscient Brain', the Lead Quantitative Analyst.
225
+ OBJECTIVE: Analyze the RAW DATA for {symbol} and decide to TRADE or IGNORE.
226
+
227
+ ⚠️ CRITICAL INSTRUCTION:
228
+ The "Local Model Reports" below are generated by smaller, potentially flawed algorithms.
229
+ They are strictly HEARSAY. You must NOT trust them blindly.
230
+ Your job is to verify them against the RAW CANDLE DATA provided below.
231
+ If your visual/statistical analysis of the candles contradicts the Local Models, YOUR ANALYSIS PREVAILS.
232
+
233
+ ========== 1. LOCAL MODEL REPORTS (HEARSAY - TREAT WITH SKEPTICISM) ==========
234
+ [A] Technical Indicator Analysis (formerly Titan): Score {titan_score:.2f}/1.00
235
+ - Note: Based on standard RSI/MACD/Bollinger calculations.
236
+ [B] Pattern Recognition Model: Confidence {pat_conf:.2f}/1.00
237
+ - Note: This model DOES NOT identify pattern names. It only outputs a confidence score.
238
+ [C] Simple Monte Carlo: Score {simple_mc_score:.2f}
239
+ - Method: Basic Random Walk based on recent volatility.
240
+ [D] Advanced Monte Carlo: Probability {adv_mc_raw}
241
+ - Method: GARCH(1,1) model with Fat-Tailed distribution simulation.
242
+
243
+ ========== 2. EXTERNAL FACTORS (CONTEXT) ==========
244
+ [A] {whale_section}
245
+ [B] RAW NEWS FEED:
246
+ "{news_raw}"
247
+
248
+ ========== 3. THE TRUTH (RAW CANDLE DATA) ==========
249
+ Below are the last 200 candles for key timeframes (1H, 4H, 1D).
250
+ Index 199 is the MOST RECENT candle.
251
+ Format: Index,Open,High,Low,Close,Volume
252
+
253
+ {candles_csv_block}
254
+
255
+ ========== 4. YOUR ANALYTICAL TASKS ==========
256
+ 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.)?
257
+ 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?
258
+ 3. **Volume Analysis:** Look at the Volume column. Is there a volume spike supporting the price move?
259
+ 4. **Conflict Resolution:** If Local Models say "UP" but Raw Candles show "Lower Highs" (Downtrend), you must REJECT.
260
+
261
+ ========== 5. FINAL DECISION OUTPUT ==========
262
+ Based ONLY on your analysis of the raw data:
263
+
264
+ REQUIRED JSON FORMAT:
265
  {{
266
  "action": "WATCH" or "IGNORE",
267
+ "confidence": 0.00 to 1.00,
268
+ "identified_patterns": ["List specific patterns YOU found in the raw data (e.g., 'Bull Flag on 1H')"],
269
+ "analysis_summary": "Your reasoning. Explicitly mention if you agreed or disagreed with the Local Models.",
270
+ "key_raw_evidence": "Cite specific candle indexes or volume figures that support your decision."
271
  }}
272
  """
273
 
274
+ # ==================================================================
275
+ # 🧠 هندسة البرومبت: إعادة التحليل (Raw Re-Analysis Prompt)
276
+ # ==================================================================
277
+ def _create_raw_reanalysis_prompt(self, trade: Dict, current: Dict, learning_context: str) -> str:
278
  symbol = trade.get('symbol', 'UNKNOWN')
279
  entry_price = trade.get('entry_price', 0.0)
280
  current_price = current.get('current_price', 0.0)
281
 
282
+ # حساب مدة الصفقة والربح
283
  try:
284
  entry_time = datetime.fromisoformat(trade.get('entry_time').replace('Z', '+00:00'))
285
  duration_minutes = (datetime.now(entry_time.tzinfo) - entry_time).total_seconds() / 60
 
287
  duration_minutes = 0.0
288
 
289
  pnl_percentage = ((current_price - entry_price) / entry_price) * 100
290
+
291
+ # 1. البيانات الجديدة (Local Models)
292
  titan_score_now = current.get('titan_score', 0.0)
293
 
294
+ # 2. البيانات الخارجية (الحيتان والأخبار)
295
+ whale_now = current.get('whale_data', {})
296
+ # محاولة استخراج القيمة الخام بشكل آمن
297
+ whale_net_flow = whale_now.get('accumulation_analysis_24h', {}).get('net_flow_usd', 'No Data')
298
 
299
+ if whale_net_flow == 'No Data':
300
+ whale_section = "WHALE DATA: ⚠️ NO DATA FOUND"
301
+ else:
302
+ whale_section = f"WHALE DATA (24H Net Flow): ${whale_net_flow}"
303
+
304
  news_text_now = current.get('news_text', 'No new significant news.')
305
 
306
+ # 3. البيانات الخام (الشموع الجديدة)
307
+ ohlcv_raw = current.get('ohlcv', {})
308
+ # نكتفي بـ 100 شمعة للمتابعة لتقليل الحمل
309
+ candles_csv_block = self._format_candles_to_csv(ohlcv_raw, limit=100)
310
+
311
  return f"""
312
+ ROLE: You are the 'Omniscient Brain', the Lead Quantitative Analyst (Guardian Mode).
313
+ EVENT: RE-EVALUATION of an OPEN POSITION.
314
+ ASSET: {symbol} | DURATION: {duration_minutes:.1f} min | PnL: {pnl_percentage:+.2f}%
 
315
 
316
+ ⚠️ CRITICAL INSTRUCTION:
317
+ You must analyze the RAW DATA to see if the original trade thesis is INVALIDATED.
318
+ Do NOT panic over small fluctuations. Look for structural breaks or trend reversals in the raw candles.
319
+
320
+ ========== 1. POSITION STATUS ==========
321
  * Entry Price: {entry_price}
322
+ * Current Price: {current_price}
 
 
323
  * Current Targets -> TP: {trade.get('tp_price', 'N/A')} | SL: {trade.get('sl_price', 'N/A')}
324
 
325
+ ========== 2. NEW CONTEXT (HEARSAY) ==========
326
+ [A] Tech Indicator Score Now: {titan_score_now:.2f}/1.00
327
+ [B] {whale_section}
328
+ [C] LATEST NEWS: "{news_text_now[:500]}..."
 
 
329
 
330
+ ========== 3. THE TRUTH (LATEST RAW CANDLES) ==========
331
+ Index 99 is the MOST RECENT candle.
332
+ Format: Index,Open,High,Low,Close,Volume
333
 
334
+ {candles_csv_block}
 
335
 
336
+ ========== 4. YOUR ANALYTICAL TASKS ==========
337
+ 1. **Momentum Check:** Look at the last 10-20 candles. Is the momentum stalling or reversing against you?
338
+ 2. **Volume check:** Is there heavy volume selling (red candles with high volume)?
339
+ 3. **Thesis Validation:** Does the raw price action still support the original direction (UP)?
340
 
341
+ ========== 5. FINAL DECISION OUTPUT ==========
342
+ Based ONLY on raw data analysis:
 
343
 
344
+ REQUIRED JSON FORMAT:
345
  {{
346
  "action": "HOLD" or "EMERGENCY_EXIT" or "UPDATE_TARGETS",
347
+ "new_tp": null or float (only if action is UPDATE_TARGETS),
348
+ "new_sl": null or float (only if action is UPDATE_TARGETS),
349
+ "reasoning": "Concise analysis of why the trade is still valid or broken based on raw candles."
350
  }}
351
  """
352
  print("✅ LLM Service V13.8 (Robust Null Safety) Loaded")