Spaces:
Paused
Paused
Update LLM.py
Browse files
LLM.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# LLM.py (
|
| 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
|
| 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.
|
| 23 |
-
LLM_PRESENCE_PENALTY = 0.
|
| 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
|
| 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 |
-
#
|
| 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
|
| 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 |
-
|
|
|
|
| 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, "
|
| 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 |
-
|
| 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
|
| 128 |
|
| 129 |
-
|
|
|
|
| 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, "
|
| 138 |
|
| 139 |
return decision
|
| 140 |
|
|
@@ -143,101 +155,131 @@ class LLMService:
|
|
| 143 |
return None
|
| 144 |
|
| 145 |
# ==================================================================
|
| 146 |
-
# 📝
|
| 147 |
# ==================================================================
|
| 148 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
symbol = data.get('symbol', 'UNKNOWN')
|
| 150 |
-
current_price = data.get('current_price', 0.0)
|
| 151 |
|
| 152 |
-
|
| 153 |
-
|
|
|
|
| 154 |
|
| 155 |
-
|
| 156 |
-
|
| 157 |
|
| 158 |
-
|
| 159 |
-
pattern_name = pat_data.get('pattern_detected', 'None detected')
|
| 160 |
-
pattern_confidence = pat_data.get('pattern_confidence', 0.0)
|
| 161 |
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 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 |
-
|
| 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 |
"action": "WATCH" or "IGNORE",
|
| 229 |
-
"
|
| 230 |
-
"
|
| 231 |
-
"
|
| 232 |
-
"
|
| 233 |
}}
|
| 234 |
"""
|
| 235 |
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
| 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'
|
| 252 |
-
|
| 253 |
-
|
| 254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
news_text_now = current.get('news_text', 'No new significant news.')
|
| 256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
return f"""
|
| 258 |
-
ROLE: Omniscient Brain (
|
| 259 |
-
EVENT:
|
| 260 |
-
ASSET: {symbol}
|
| 261 |
-
TIME IN TRADE: {duration_minutes:.1f} minutes
|
| 262 |
|
| 263 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
* Entry Price: {entry_price}
|
| 265 |
-
* Current
|
| 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 |
-
==========
|
| 271 |
-
|
| 272 |
-
|
| 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.
|
|
|
|
|
|
|
| 278 |
|
| 279 |
-
|
| 280 |
-
\"\"\"{news_text_now[:1500]}\"\"\"
|
| 281 |
|
| 282 |
-
==========
|
| 283 |
-
|
|
|
|
|
|
|
| 284 |
|
| 285 |
-
==========
|
| 286 |
-
|
| 287 |
-
Do NOT recommend closing just because of small fluctuations. Look for FUNDAMENTAL thesis INVALIDATION.
|
| 288 |
|
| 289 |
-
REQUIRED
|
| 290 |
{{
|
| 291 |
"action": "HOLD" or "EMERGENCY_EXIT" or "UPDATE_TARGETS",
|
| 292 |
-
"new_tp": null or
|
| 293 |
-
"new_sl": null or
|
| 294 |
-
"reasoning": "Concise
|
| 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")
|