Riy777 commited on
Commit
cb097e2
·
1 Parent(s): 3b02785

Update learning_hub/reflector.py

Browse files
Files changed (1) hide show
  1. learning_hub/reflector.py +51 -123
learning_hub/reflector.py CHANGED
@@ -1,12 +1,11 @@
1
  # learning_hub/reflector.py
2
- # (محدث بالكامل - V2 - تمرير الأخبار للتعلم السريع)
3
  import json
4
  import traceback
5
  from typing import Dict, Any, TYPE_CHECKING
6
  from .schemas import TraceLog, ReflectorOutput
7
  from .memory_store import MemoryStore
8
 
9
- # (استخدام TYPE_CHECKING لتجنب الاستيراد الدائري الفعلي)
10
  if TYPE_CHECKING:
11
  from LLM import LLMService
12
 
@@ -14,170 +13,99 @@ class Reflector:
14
  def __init__(self, llm_service: 'LLMService', memory_store: MemoryStore):
15
  self.llm_service = llm_service
16
  self.memory_store = memory_store
17
- print("✅ Learning Hub Module: Reflector (Fast-Learner) loaded")
18
 
19
  async def analyze_trade_outcome(self, trade_object: Dict[str, Any], close_reason: str):
20
- """
21
- Analyzes the trade outcome using LLM to generate a learning 'Delta' (rule).
22
- (Implements Point 2 & 4 of the 16-point plan)
23
- """
24
  try:
25
- # 1. Create the TraceLog
26
- # (Note: We assume TradeManager now saves 'market_context_at_decision',
27
- # 'indicators_at_decision', 'news_text', and 'news_score'
28
- # inside 'decision_data' when opening the trade)
29
-
30
  decision_data = trade_object.get('decision_data', {})
31
 
32
  trace_log = TraceLog(
33
  decision_context=decision_data,
34
  market_context_at_decision=decision_data.get('market_context_at_decision', {}),
35
  indicators_at_decision=decision_data.get('indicators_at_decision', {}),
 
 
36
  closed_trade_object=trade_object,
37
  actual_outcome_reason=close_reason
38
  )
39
 
40
- # 2. Create the Reflector Prompt (Now in English)
41
- # 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
42
- # (تمرير بيانات الأخبار (التي يفترض أنها في decision_data) إلى الـ prompt)
43
- news_text_at_decision = decision_data.get('news_text', 'No news data available at decision time.')
44
- news_score_at_decision = decision_data.get('news_score', 0.0) # (VADER raw score)
45
-
46
- prompt = self._create_reflector_prompt(
47
- trace_log,
48
- news_text_at_decision,
49
- news_score_at_decision
50
- )
51
- # 🔴 --- END OF CHANGE --- 🔴
52
-
53
- # 3. Call the LLM
54
  response_text = await self.llm_service._call_llm(prompt)
55
 
56
- if not response_text:
57
- raise ValueError("Reflector LLM call returned no response.")
58
 
59
- # 4. Parse the response
60
- # (We use the enhanced parser from LLM.py which handles JSON)
61
  reflector_json = self.llm_service._parse_llm_response_enhanced(
62
- response_text,
63
- fallback_strategy="reflection",
64
- symbol=trade_object.get('symbol', 'N/A')
65
  )
66
 
67
- if not reflector_json:
68
- raise ValueError(f"Failed to parse Reflector LLM response: {response_text}")
69
 
70
- # (Validate against the strict schema from schemas.py)
71
  reflector_output = ReflectorOutput(**reflector_json)
 
72
 
73
- # 5. Determine the 'Domain' for the Delta
74
- strategy = trade_object.get('strategy', 'general')
75
- domain = self._determine_domain(strategy, reflector_output.error_mode)
76
-
77
- # 6. Save the suggested 'Delta' to the Memory Store
78
- # (MemoryStore will use PolicyEngine to decide on auto-approval)
79
- await self.memory_store.save_new_delta(
80
- reflector_output=reflector_output,
81
- trade_object=trade_object,
82
- domain=domain
83
- )
84
-
85
- print(f"✅ [Reflector] Successfully analyzed {trade_object.get('symbol')}. New Delta created.")
86
 
87
  except Exception as e:
88
- print(f"❌ [Reflector] Failed to analyze trade outcome for {trade_object.get('symbol')}: {e}")
89
  traceback.print_exc()
90
 
91
  def _determine_domain(self, strategy: str, error_mode: str) -> str:
92
- """Determines the domain the suggested Delta belongs to."""
93
- error_mode = error_mode.lower()
94
- if "pattern" in error_mode or "triangle" in error_mode or "flag" in error_mode:
95
- return "pattern"
96
- if "indicator" in error_mode or "rsi" in error_mode or "macd" in error_mode:
97
- return "indicator"
98
- if "monte_carlo" in error_mode or "garch" in error_mode or "simulation" in error_mode:
99
- return "monte_carlo"
100
-
101
- # 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
102
- if "news" in error_mode or "sentiment" in error_mode or "sec" in error_mode:
103
- return "general" # (أو يمكننا إنشاء مجال "news" جديد)
104
- # 🔴 --- END OF CHANGE --- 🔴
105
-
106
- if "strategy" in error_mode or "exit" in error_mode or "entry" in error_mode:
107
- return "strategy"
108
-
109
- # Default to the strategy's domain
110
- if strategy in ["trend_following", "mean_reversion", "breakout_momentum"]:
111
- return "strategy"
112
-
113
- return "general"
114
 
115
- # 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
116
- def _create_reflector_prompt(
117
- self,
118
- trace_log: TraceLog,
119
- news_text: str,
120
- news_score: float
121
- ) -> str:
122
- # 🔴 --- END OF CHANGE --- 🔴
123
- """
124
- Creates the (English-only) prompt for the LLM to act as a Reflector.
125
- (Implements Point 4 - Reflector prompt)
126
- """
127
-
128
  trade = trace_log.closed_trade_object
129
- pnl_percent = trade.get('pnl_percent', 0)
130
-
131
- # Determine initial success
132
- is_success = pnl_percent > 0.1 # (Consider any small profit a success)
133
 
134
- # 🔴 --- START OF CHANGE (V2 - News Learning) --- 🔴
135
- # (إضافة قسم الأخبار إلى الـ prompt)
136
- news_context_section = f"""
137
- 4. **News Context (at entry):**
138
- * VADER Score (Raw): {news_score:.4f}
139
- * News Text: {news_text}
 
 
 
 
 
 
140
  """
141
- # 🔴 --- END OF CHANGE --- 🔴
142
-
143
- prompt = f"""
144
- 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.
145
 
146
- --- TRACE LOG START ---
 
 
147
 
148
- 1. **Original Decision Context (What we decided):**
149
- * Strategy Used: {trade.get('strategy', 'N/A')}
150
- * Exit Profile: {trade.get('decision_data', {}).get('exit_profile', 'N/A')}
151
- * Reasoning (at entry): {trade.get('decision_data', {}).get('reasoning', 'N/A')[:200]}...
152
- * Entry Price: {trade.get('entry_price')}
153
- * Initial Stop Loss: {trade.get('stop_loss')}
154
- * Initial Take Profit: {trade.get('take_profit')}
155
 
156
- 2. **Environment Context (When we decided):**
157
- * Market Context: {json.dumps(trace_log.market_context_at_decision)}
158
- * Key Indicators: {json.dumps(trace_log.indicators_at_decision)}
159
 
160
- 3. **Actual Outcome (What happened):**
161
- * Close Price: {trade.get('close_price')}
162
- * Final PnL: {pnl_percent:+.2f}%
163
  * Close Reason: {trace_log.actual_outcome_reason}
164
- * Trade Duration: {trade.get('hold_duration_minutes', 'N/A')} minutes
165
 
166
- {news_context_section}
167
  --- TRACE LOG END ---
168
 
169
- TASK: Analyze the Trace above.
170
- 1. Compare the "Actual Outcome" with the "Original Decision Context".
171
- 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?
172
- 3. Identify the primary "Error Mode" (e.g., 'ignored_negative_news', 'premature_exit') or "Success Factor" (e.g., 'correct_pattern_identification').
173
- 4. Suggest ONE concise "Rule" (Delta) (max 25 words) to improve performance. If the news was the cause, the rule MUST mention news.
174
 
175
- OUTPUT FORMAT (JSON Only - Adhere strictly to this schema):
176
  {{
177
  "success": {str(is_success).lower()},
178
  "score": 0.0,
179
- "error_mode": "Short description of the error mode (e.g., 'ignored_negative_news_SEC_investigation').",
180
- "suggested_rule": "The concise 25-word rule (e.g., 'If news contains 'SEC' or 'investigation', do not BUY regardless of technicals.').",
181
  "confidence": 0.0
182
  }}
183
  """
 
1
  # learning_hub/reflector.py
2
+ # (V12.3 Full - Hybrid Aware)
3
  import json
4
  import traceback
5
  from typing import Dict, Any, TYPE_CHECKING
6
  from .schemas import TraceLog, ReflectorOutput
7
  from .memory_store import MemoryStore
8
 
 
9
  if TYPE_CHECKING:
10
  from LLM import LLMService
11
 
 
13
  def __init__(self, llm_service: 'LLMService', memory_store: MemoryStore):
14
  self.llm_service = llm_service
15
  self.memory_store = memory_store
16
+ print("✅ Learning Hub: Reflector (Hybrid Aware) loaded")
17
 
18
  async def analyze_trade_outcome(self, trade_object: Dict[str, Any], close_reason: str):
 
 
 
 
19
  try:
 
 
 
 
 
20
  decision_data = trade_object.get('decision_data', {})
21
 
22
  trace_log = TraceLog(
23
  decision_context=decision_data,
24
  market_context_at_decision=decision_data.get('market_context_at_decision', {}),
25
  indicators_at_decision=decision_data.get('indicators_at_decision', {}),
26
+ # تمرير الأوزان التي استخدمت فعلياً
27
+ hybrid_weights_used=decision_data.get('hybrid_weights_at_entry', {}),
28
  closed_trade_object=trade_object,
29
  actual_outcome_reason=close_reason
30
  )
31
 
32
+ prompt = self._create_reflector_prompt(trace_log, decision_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  response_text = await self.llm_service._call_llm(prompt)
34
 
35
+ if not response_text: raise ValueError("LLM returned no response.")
 
36
 
 
 
37
  reflector_json = self.llm_service._parse_llm_response_enhanced(
38
+ response_text, fallback_strategy="reflection", symbol=trade_object.get('symbol', 'N/A')
 
 
39
  )
40
 
41
+ if not reflector_json: raise ValueError("Failed to parse LLM response")
 
42
 
 
43
  reflector_output = ReflectorOutput(**reflector_json)
44
+ domain = self._determine_domain(trade_object.get('strategy', 'general'), reflector_output.error_mode)
45
 
46
+ await self.memory_store.save_new_delta(reflector_output, trade_object, domain)
47
+ print(f"✅ [Reflector] Analyzed {trade_object.get('symbol')}. New Delta created.")
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  except Exception as e:
50
+ print(f"❌ [Reflector] Analysis failed: {e}")
51
  traceback.print_exc()
52
 
53
  def _determine_domain(self, strategy: str, error_mode: str) -> str:
54
+ em = error_mode.lower()
55
+ if "pattern" in em: return "pattern"
56
+ if "indicator" in em or "rsi" in em: return "indicator"
57
+ if "monte_carlo" in em: return "monte_carlo"
58
+ if "titan" in em or "model" in em: return "strategy" # تصنيف أخطاء تيتان ضمن الاستراتيجية
59
+ if "news" in em: return "general"
60
+ return "strategy"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ def _create_reflector_prompt(self, trace_log: TraceLog, decision_data: Dict) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
63
  trade = trace_log.closed_trade_object
64
+ pnl = trade.get('pnl_percent', 0)
65
+ is_success = pnl > 0.1
 
 
66
 
67
+ # 🔴 إدراج تفاصيل المكونات الهجينة في الموجه
68
+ comps = decision_data.get('components', {})
69
+ hybrid_context = f"""
70
+ * Hybrid Score Breakdown (Total: {trade.get('score', 'N/A'):.4f}):
71
+ - Titan Model Score: {comps.get('titan_score', 'N/A'):.2f}
72
+ - Chart Patterns Score: {comps.get('patterns_score', 'N/A'):.2f}
73
+ - Monte Carlo Score: {comps.get('mc_score', 'N/A'):.2f}
74
+ * Weights Used at Entry: {json.dumps(trace_log.hybrid_weights_used)}
75
+ """
76
+ news_context = f"""
77
+ * News VADER Score: {decision_data.get('news_score', 0.0):.4f}
78
+ * News Text: {decision_data.get('news_text', 'N/A')}
79
  """
 
 
 
 
80
 
81
+ return f"""
82
+ SYSTEM: You are an expert trading analyst Reflector. Analyze this "Trace" of a hybrid AI trading decision.
83
+ Did the primary model (Titan) mislead us, or did the secondary models (Patterns/MC) cause a false positive?
84
 
85
+ --- TRACE LOG START ---
86
+ 1. DECISION CONTEXT:
87
+ * Strategy: {trade.get('strategy', 'N/A')}
88
+ {hybrid_context}
89
+ * Entry Reasoning: {decision_data.get('reasoning', 'N/A')[:300]}...
 
 
90
 
91
+ 2. NEWS CONTEXT:
92
+ {news_context}
 
93
 
94
+ 3. OUTCOME:
95
+ * Final PnL: {pnl:+.2f}%
 
96
  * Close Reason: {trace_log.actual_outcome_reason}
 
97
 
 
98
  --- TRACE LOG END ---
99
 
100
+ TASK: Identify the root cause of success/failure. Focus on which hybrid component was most accurate/inaccurate.
101
+ SUGGEST RULE (Delta): Max 25 words. E.g., "If Titan > 0.95 but Patterns < 0.60, reduce position size."
 
 
 
102
 
103
+ OUTPUT JSON:
104
  {{
105
  "success": {str(is_success).lower()},
106
  "score": 0.0,
107
+ "error_mode": "Short description (e.g., 'titan_false_positive_ignored_patterns')",
108
+ "suggested_rule": "Concise 25-word rule.",
109
  "confidence": 0.0
110
  }}
111
  """