File size: 10,539 Bytes
b14782e
2b74733
629cd1d
cc5540e
2b74733
 
370e8b2
629cd1d
b14782e
2b74733
 
b14782e
 
 
 
 
 
2b74733
370e8b2
 
 
 
2b74733
 
370e8b2
 
b14782e
2b74733
 
 
 
b14782e
2b74733
b14782e
 
2b74733
b14782e
 
2b74733
b14782e
2b74733
b14782e
2b74733
b14782e
 
 
2b74733
b14782e
 
 
 
 
 
 
2b74733
 
370e8b2
2b74733
 
cc5540e
2b74733
cc5540e
 
2b74733
 
 
 
 
b14782e
2b74733
 
370e8b2
2b74733
 
 
 
b14782e
2b74733
3f14a6c
b14782e
2b74733
 
3f14a6c
2b74733
3f14a6c
2b74733
 
 
 
 
3f14a6c
2b74733
3f14a6c
b14782e
3f14a6c
2b74733
 
 
629cd1d
 
2b74733
 
 
 
cc5540e
2b74733
 
 
629cd1d
 
2b74733
629cd1d
2b74733
b14782e
2b74733
b14782e
2b74733
b14782e
 
629cd1d
 
 
2b74733
 
 
 
b14782e
2b74733
 
 
b14782e
2b74733
629cd1d
b14782e
2b74733
 
 
b14782e
2b74733
b14782e
 
 
6c86b44
2b74733
cc5540e
2b74733
370e8b2
2b74733
370e8b2
2b74733
 
 
370e8b2
 
629cd1d
 
370e8b2
 
 
 
629cd1d
370e8b2
629cd1d
 
2b74733
 
 
629cd1d
2b74733
 
 
 
370e8b2
2b74733
629cd1d
2b74733
629cd1d
370e8b2
 
2b74733
370e8b2
 
 
2b74733
370e8b2
 
 
 
 
2b74733
629cd1d
2b74733
629cd1d
 
370e8b2
 
629cd1d
370e8b2
629cd1d
2b74733
629cd1d
370e8b2
 
 
 
2b74733
 
629cd1d
2b74733
629cd1d
 
 
 
2b74733
629cd1d
2b74733
 
 
370e8b2
2b74733
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
# learning_hub/hub_manager.py
# (محدث بالكامل - V4 - Adaptive Hybrid Weights Support)

import asyncio
import traceback
from typing import Any, Dict, List
from datetime import datetime, timezone
from collections import defaultdict

# (استيراد جميع المكونات الداخلية للمركز)
from .schemas import *
from .policy_engine import PolicyEngine
from .memory_store import MemoryStore
from .statistical_analyzer import StatisticalAnalyzer
from .reflector import Reflector
from .curator import Curator

# استيراد لتحليل الارتباط (Whale Learning)
try:
    import numpy as np
    from scipy.stats import pearsonr
    NUMPY_AVAILABLE = True
except ImportError:
    print("❌ [HubManager] مكتبة numpy أو scipy غير مثبتة! لن يعمل تعلم الحيتان.")
    NUMPY_AVAILABLE = False

class LearningHubManager:
    def __init__(self, r2_service: Any, llm_service: Any, data_manager: Any):
        print("🚀 Initializing Learning Hub Manager (V4 - Adaptive)...")
        
        # 1. الخدمات الأساسية
        self.r2_service = r2_service
        self.llm_service = llm_service
        self.data_manager = data_manager

        # 2. تهيئة المكونات
        self.policy_engine = PolicyEngine()
        self.memory_store = MemoryStore(
            r2_service=self.r2_service, 
            policy_engine=self.policy_engine,
            llm_service=self.llm_service 
        )
        self.reflector = Reflector(
            llm_service=self.llm_service,
            memory_store=self.memory_store
        )
        self.curator = Curator(
            llm_service=self.llm_service,
            memory_store=self.memory_store
        )
        self.statistical_analyzer = StatisticalAnalyzer(
            r2_service=self.r2_service,
            data_manager=self.data_manager
        )
        
        # متغيرات حالة لتعلم الحيتان
        self.whale_learning_lock = asyncio.Lock()
        self.optimal_whale_config = {}
        
        self.initialized = False
        print("✅ Learning Hub Manager constructed. Ready for initialization.")

    async def initialize(self):
        """تهيئة جميع الأنظمة الفرعية"""
        if self.initialized: return
        print("🔄 [HubManager] Initializing all sub-modules...")
        
        # تهيئة المحلل الإحصائي (المسؤول عن الأوزان المتكيفة)
        await self.statistical_analyzer.initialize()
        
        # تحميل إعدادات تعلم الحيتان
        if hasattr(self.r2_service, 'load_whale_learning_config_async'):
            self.optimal_whale_config = await self.r2_service.load_whale_learning_config_async()
            if self.optimal_whale_config:
                print(f"✅ [HubManager] Loaded optimal whale config: {self.optimal_whale_config.get('best_metric', 'N/A')}")
        
        self.initialized = True
        print("✅ [HubManager] All sub-modules initialized. Learning Hub is LIVE.")

    async def analyze_trade_and_learn(self, trade_object: Dict[str, Any], close_reason: str):
        """الدالة الرئيسية للتعلم من الصفقات المغلقة"""
        if not self.initialized: return

        print(f"🧠 [HubManager] Learning from trade {trade_object.get('symbol')}...")

        # 1. التعلم السريع (Reflector)
        try:
            await self.reflector.analyze_trade_outcome(trade_object, close_reason)
        except Exception as e:
            print(f"❌ [HubManager] Reflector failed: {e}")

        # 2. التعلم البطيء وتكييف الأوزان (StatisticalAnalyzer)
        try:
            await self.statistical_analyzer.update_statistics(trade_object, close_reason)
        except Exception as e:
            print(f"❌ [HubManager] StatisticalAnalyzer failed: {e}")
            
        print(f"✅ [HubManager] Learning complete for {trade_object.get('symbol')}.")

    async def get_optimized_weights(self, market_condition: str = None) -> Dict[str, Any]:
        """
        جلب الأوزان المحسنة (بما في ذلك الأوزان الهجينة المتكيفة).
        يستخدمها MLProcessor لتحديث معادلته الهجينة.
        """
        if not self.initialized:
             return await self.statistical_analyzer.get_default_strategy_weights()

        # إرجاع قاموس الأوزان الكامل من المحلل الإحصائي
        return self.statistical_analyzer.weights

    # --- دوال مساعدة أخرى (للـ LLM وغيرها) ---
    async def get_active_context_for_llm(self, domain: str, query: str) -> str:
        if not self.initialized: return "Learning Hub not initialized."
        return await self.memory_store.get_active_context(domain, query)
        
    async def get_statistical_feedback_for_llm(self, entry_strategy: str) -> str:
        if not self.initialized: return "Learning Hub not initialized."
        best_profile = await self.statistical_analyzer.get_best_exit_profile(entry_strategy)
        if best_profile != "unknown":
            return f"Statistical Feedback: For '{entry_strategy}', '{best_profile}' exit profile performed best."
        return "No statistical feedback available yet."

    async def get_statistical_news_score(self, raw_vader_score: float) -> float:
        if not self.initialized: return 0.0
        return await self.statistical_analyzer.get_statistical_vader_pnl(raw_vader_score)

    async def run_distillation_check(self):
        """تشغيل دوري للتقطير (Curator)"""
        if not self.initialized: return
        # print("ℹ️ [HubManager] Running distillation check...")
        for domain in self.memory_store.domain_files.keys():
            await self.curator.check_and_distill_domain(domain)

    async def shutdown(self):
        """حفظ كل البيانات عند الإغلاق"""
        if not self.initialized: return
        print("🔄 [HubManager] Shutting down... Saving learning data.")
        try:
            # المحلل الإحصائي يحفظ الأوزان المتكيفة وسجل الأداء
            await self.statistical_analyzer.save_weights_to_r2()
            await self.statistical_analyzer.save_performance_history()
            await self.statistical_analyzer.save_exit_profile_effectiveness()
            await self.statistical_analyzer.save_vader_effectiveness()
            print("✅ [HubManager] Data saved successfully.")
        except Exception as e:
            print(f"❌ [HubManager] Save failed: {e}")

    # --- Whale Learning Loop (كما هي من V3) ---
    async def run_whale_learning_check(self):
        if not self.initialized: await asyncio.sleep(60)
        print(f"🧠 [Whale-Logger] Starting background learning loop...")
        await asyncio.sleep(600) 
        while True:
            try:
                pending = await self.r2_service.get_pending_whale_learning_records_async()
                if not pending:
                    await asyncio.sleep(600)
                    continue

                now_utc = datetime.now(timezone.utc)
                for record in pending:
                    try:
                        target_time = datetime.fromisoformat(record['target_time_utc'])
                        if now_utc >= target_time:
                            symbol = record['symbol']
                            target_price = await self.data_manager.get_latest_price_async(symbol)
                            if target_price and target_price > 0 and record['start_price_usd'] > 0:
                                pct_change = ((target_price - record['start_price_usd']) / record['start_price_usd']) * 100
                                record.update({'target_price_usd': target_price, 'price_change_percentage': pct_change, 'status': "COMPLETED"})
                                await self.r2_service.update_completed_whale_learning_record_async(record)
                    except Exception: pass
                
                await self.update_optimal_whale_window()
                await asyncio.sleep(300) 
            except Exception as e:
                print(f"❌ [Whale-Logger] Error: {e}")
                await asyncio.sleep(600)

    async def update_optimal_whale_window(self):
        if not NUMPY_AVAILABLE: return
        async with self.whale_learning_lock:
            try:
                all_completed = await self.r2_service.get_all_completed_whale_records_async()
                if len(all_completed) < 20: return

                price_changes = []
                metrics_data = defaultdict(lambda: defaultdict(list))
                windows = ['30m', '1h', '2h', '4h', '24h']
                metric_keys = ['relative_net_flow_percent', 'transaction_density', 'net_flow_usd']
                
                for r in all_completed:
                    if r.get('price_change_percentage') is None: continue
                    price_changes.append(r['price_change_percentage'])
                    analysis = r.get('window_analysis', {})
                    for w in windows:
                        for k in metric_keys:
                            metrics_data[w][k].append(analysis.get(w, {}).get(k, 0.0))

                price_np = np.array(price_changes)
                best_corr = 0
                best_key = None

                for w in windows:
                    for k in metric_keys:
                        metric_np = np.array(metrics_data[w][k])
                        if len(metric_np) == len(price_np):
                            corr, p_val = pearsonr(metric_np, price_np)
                            if not np.isnan(corr) and p_val < 0.1 and abs(corr) > best_corr:
                                best_corr = abs(corr)
                                best_key = f"{w}_{k}"

                if best_key:
                    w, m = best_key.split('_', 1)
                    new_config = {"best_window": w, "best_metric": m, "correlation_score": best_corr, "total_samples": len(price_np), "last_updated_utc": datetime.now(timezone.utc).isoformat()}
                    self.optimal_whale_config = new_config
                    await self.r2_service.save_whale_learning_config_async(new_config)
                    print(f"🏆 [Whale-Teacher] New best signal: {m} on {w} (Corr: {best_corr:.2f})")

            except Exception as e:
                print(f"❌ [Whale-Teacher] Error: {e}")