File size: 8,359 Bytes
ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b 706e07a ed6a20b |
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 |
# learning_hub/reflector.py
# (محدث بالكامل - V2 - تمرير الأخبار للتعلم السريع)
import json
import traceback
from typing import Dict, Any, TYPE_CHECKING
from .schemas import TraceLog, ReflectorOutput
from .memory_store import MemoryStore
# (استخدام TYPE_CHECKING لتجنب الاستيراد الدائري الفعلي)
if TYPE_CHECKING:
from LLM import LLMService
class Reflector:
def __init__(self, llm_service: 'LLMService', memory_store: MemoryStore):
self.llm_service = llm_service
self.memory_store = memory_store
print("✅ Learning Hub Module: Reflector (Fast-Learner) loaded")
async def analyze_trade_outcome(self, trade_object: Dict[str, Any], close_reason: str):
"""
Analyzes the trade outcome using LLM to generate a learning 'Delta' (rule).
(Implements Point 2 & 4 of the 16-point plan)
"""
try:
# 1. Create the TraceLog
# (Note: We assume TradeManager now saves 'market_context_at_decision',
# 'indicators_at_decision', 'news_text', and 'news_score'
# inside 'decision_data' when opening the trade)
decision_data = trade_object.get('decision_data', {})
trace_log = TraceLog(
decision_context=decision_data,
market_context_at_decision=decision_data.get('market_context_at_decision', {}),
indicators_at_decision=decision_data.get('indicators_at_decision', {}),
closed_trade_object=trade_object,
actual_outcome_reason=close_reason
)
# 2. Create the Reflector Prompt (Now in English)
# 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
# (تمرير بيانات الأخبار (التي يفترض أنها في decision_data) إلى الـ prompt)
news_text_at_decision = decision_data.get('news_text', 'No news data available at decision time.')
news_score_at_decision = decision_data.get('news_score', 0.0) # (VADER raw score)
prompt = self._create_reflector_prompt(
trace_log,
news_text_at_decision,
news_score_at_decision
)
# 🔴 --- END OF CHANGE --- 🔴
# 3. Call the LLM
response_text = await self.llm_service._call_llm(prompt)
if not response_text:
raise ValueError("Reflector LLM call returned no response.")
# 4. Parse the response
# (We use the enhanced parser from LLM.py which handles JSON)
reflector_json = self.llm_service._parse_llm_response_enhanced(
response_text,
fallback_strategy="reflection",
symbol=trade_object.get('symbol', 'N/A')
)
if not reflector_json:
raise ValueError(f"Failed to parse Reflector LLM response: {response_text}")
# (Validate against the strict schema from schemas.py)
reflector_output = ReflectorOutput(**reflector_json)
# 5. Determine the 'Domain' for the Delta
strategy = trade_object.get('strategy', 'general')
domain = self._determine_domain(strategy, reflector_output.error_mode)
# 6. Save the suggested 'Delta' to the Memory Store
# (MemoryStore will use PolicyEngine to decide on auto-approval)
await self.memory_store.save_new_delta(
reflector_output=reflector_output,
trade_object=trade_object,
domain=domain
)
print(f"✅ [Reflector] Successfully analyzed {trade_object.get('symbol')}. New Delta created.")
except Exception as e:
print(f"❌ [Reflector] Failed to analyze trade outcome for {trade_object.get('symbol')}: {e}")
traceback.print_exc()
def _determine_domain(self, strategy: str, error_mode: str) -> str:
"""Determines the domain the suggested Delta belongs to."""
error_mode = error_mode.lower()
if "pattern" in error_mode or "triangle" in error_mode or "flag" in error_mode:
return "pattern"
if "indicator" in error_mode or "rsi" in error_mode or "macd" in error_mode:
return "indicator"
if "monte_carlo" in error_mode or "garch" in error_mode or "simulation" in error_mode:
return "monte_carlo"
# 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
if "news" in error_mode or "sentiment" in error_mode or "sec" in error_mode:
return "general" # (أو يمكننا إنشاء مجال "news" جديد)
# 🔴 --- END OF CHANGE --- 🔴
if "strategy" in error_mode or "exit" in error_mode or "entry" in error_mode:
return "strategy"
# Default to the strategy's domain
if strategy in ["trend_following", "mean_reversion", "breakout_momentum"]:
return "strategy"
return "general"
# 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
def _create_reflector_prompt(
self,
trace_log: TraceLog,
news_text: str,
news_score: float
) -> str:
# 🔴 --- END OF CHANGE --- 🔴
"""
Creates the (English-only) prompt for the LLM to act as a Reflector.
(Implements Point 4 - Reflector prompt)
"""
trade = trace_log.closed_trade_object
pnl_percent = trade.get('pnl_percent', 0)
# Determine initial success
is_success = pnl_percent > 0.1 # (Consider any small profit a success)
# 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
# (إضافة قسم الأخبار إلى الـ prompt)
news_context_section = f"""
4. **News Context (at entry):**
* VADER Score (Raw): {news_score:.4f}
* News Text: {news_text}
"""
# 🔴 --- END OF CHANGE --- 🔴
prompt = f"""
SYSTEM: You are an expert trading analyst Reflector. Your task is to analyze a completed trade "Trace" and determine the cause of success or failure. You must suggest a concise "Rule" (Delta) (max 25 words) to improve future performance.
--- TRACE LOG START ---
1. **Original Decision Context (What we decided):**
* Strategy Used: {trade.get('strategy', 'N/A')}
* Exit Profile: {trade.get('decision_data', {}).get('exit_profile', 'N/A')}
* Reasoning (at entry): {trade.get('decision_data', {}).get('reasoning', 'N/A')[:200]}...
* Entry Price: {trade.get('entry_price')}
* Initial Stop Loss: {trade.get('stop_loss')}
* Initial Take Profit: {trade.get('take_profit')}
2. **Environment Context (When we decided):**
* Market Context: {json.dumps(trace_log.market_context_at_decision)}
* Key Indicators: {json.dumps(trace_log.indicators_at_decision)}
3. **Actual Outcome (What happened):**
* Close Price: {trade.get('close_price')}
* Final PnL: {pnl_percent:+.2f}%
* Close Reason: {trace_log.actual_outcome_reason}
* Trade Duration: {trade.get('hold_duration_minutes', 'N/A')} minutes
{news_context_section}
--- TRACE LOG END ---
TASK: Analyze the Trace above.
1. Compare the "Actual Outcome" with the "Original Decision Context".
2. **Crucially, review the "News Context".** Did the market react as the VADER score predicted? Did the news text contain critical information that was missed?
3. Identify the primary "Error Mode" (e.g., 'ignored_negative_news', 'premature_exit') or "Success Factor" (e.g., 'correct_pattern_identification').
4. Suggest ONE concise "Rule" (Delta) (max 25 words) to improve performance. If the news was the cause, the rule MUST mention news.
OUTPUT FORMAT (JSON Only - Adhere strictly to this schema):
{{
"success": {str(is_success).lower()},
"score": 0.0,
"error_mode": "Short description of the error mode (e.g., 'ignored_negative_news_SEC_investigation').",
"suggested_rule": "The concise 25-word rule (e.g., 'If news contains 'SEC' or 'investigation', do not BUY regardless of technicals.').",
"confidence": 0.0
}}
"""
return prompt |