Riy777 commited on
Commit
629cd1d
·
verified ·
1 Parent(s): 343fecd

Update learning_hub/hub_manager.py

Browse files
Files changed (1) hide show
  1. learning_hub/hub_manager.py +86 -216
learning_hub/hub_manager.py CHANGED
@@ -1,9 +1,11 @@
1
  # learning_hub/hub_manager.py
2
- # (محدث بالكامل - V3 - Whale Learning Loop)
 
3
  import asyncio
4
  import traceback
5
  from typing import Any, Dict, List
6
  from datetime import datetime, timezone
 
7
 
8
  # (استيراد جميع المكونات الداخلية للمركز)
9
  from .schemas import *
@@ -13,7 +15,7 @@ from .statistical_analyzer import StatisticalAnalyzer
13
  from .reflector import Reflector
14
  from .curator import Curator
15
 
16
- # 🔴 --- (جديد V3) استيراد لتحليل الارتباط --- 🔴
17
  try:
18
  import numpy as np
19
  from scipy.stats import pearsonr
@@ -22,17 +24,16 @@ except ImportError:
22
  print("❌ [HubManager] مكتبة numpy أو scipy غير مثبتة! لن يعمل تعلم الحيتان.")
23
  NUMPY_AVAILABLE = False
24
 
25
-
26
  class LearningHubManager:
27
  def __init__(self, r2_service: Any, llm_service: Any, data_manager: Any):
28
- print("🚀 Initializing Learning Hub Manager (V3)...")
29
 
30
- # 1. الخدمات الأساسية (يتم تمريرها من app.py)
31
  self.r2_service = r2_service
32
  self.llm_service = llm_service
33
  self.data_manager = data_manager
34
 
35
- # 2. تهيئة المكونات (بناء النظام)
36
  self.policy_engine = PolicyEngine()
37
  self.memory_store = MemoryStore(
38
  r2_service=self.r2_service,
@@ -52,299 +53,168 @@ class LearningHubManager:
52
  data_manager=self.data_manager
53
  )
54
 
55
- # 🔴 --- (جديد V3) متغيرات حالة لتعلم الحيتان --- 🔴
56
  self.whale_learning_lock = asyncio.Lock()
57
- self.optimal_whale_config = {} # (الأوزان المتعلمة)
58
 
59
  self.initialized = False
60
  print("✅ Learning Hub Manager constructed. Ready for initialization.")
61
 
62
  async def initialize(self):
63
- """
64
- تهيئة جميع الأنظمة الفرعية، وخاصة تحميل الإحصائيات والأوزان.
65
- """
66
- if self.initialized:
67
- return
68
-
69
  print("🔄 [HubManager] Initializing all sub-modules...")
 
 
70
  await self.statistical_analyzer.initialize()
71
 
72
- # 🔴 --- (جديد V3) تحميل إعدادات الحيتان المتعلمة --- 🔴
73
  if hasattr(self.r2_service, 'load_whale_learning_config_async'):
74
  self.optimal_whale_config = await self.r2_service.load_whale_learning_config_async()
75
  if self.optimal_whale_config:
76
- print(f"✅ [HubManager] تم تحميل إعدادات تعلم الحيتان المثلى: {self.optimal_whale_config}")
77
- else:
78
- print("ℹ️ [HubManager] لم يتم العثور على إعدادات تعلم حيتان سابقة.")
79
 
80
  self.initialized = True
81
  print("✅ [HubManager] All sub-modules initialized. Learning Hub is LIVE.")
82
 
83
  async def analyze_trade_and_learn(self, trade_object: Dict[str, Any], close_reason: str):
84
- """
85
- هذه هي الدالة الرئيسية التي يستدعيها TradeManager.
86
- إنها تشغل كلاً من نظام التعلم السريع (Reflector) والبطيء (StatsAnalyzer).
87
- """
88
- if not self.initialized:
89
- print("⚠️ [HubManager] Learning Hub not initialized. Skipping learning.")
90
- return
91
 
92
  print(f"🧠 [HubManager] Learning from trade {trade_object.get('symbol')}...")
93
 
 
94
  try:
95
- # 1. التعلم السريع (Reflector):
96
  await self.reflector.analyze_trade_outcome(trade_object, close_reason)
97
  except Exception as e:
98
- print(f"❌ [HubManager] Reflector (Fast-Learner) failed: {e}")
99
 
 
100
  try:
101
- # 2. التعلم البطيء (StatisticalAnalyzer):
102
  await self.statistical_analyzer.update_statistics(trade_object, close_reason)
103
  except Exception as e:
104
- print(f"❌ [HubManager] StatisticalAnalyzer (Slow-Learner) failed: {e}")
105
 
106
  print(f"✅ [HubManager] Learning complete for {trade_object.get('symbol')}.")
107
-
108
- async def get_active_context_for_llm(self, domain: str, query: str) -> str:
109
  """
110
- يُستخدم بواسطة LLMService لجلب "الدفتر" (Playbook) / القواعد (Deltas).
 
111
  """
112
  if not self.initialized:
113
- return "Learning Hub not initialized."
114
-
 
 
 
 
 
 
115
  return await self.memory_store.get_active_context(domain, query)
116
 
117
  async def get_statistical_feedback_for_llm(self, entry_strategy: str) -> str:
118
- """
119
- يُستخدم بواسطة LLMService لجلب أفضل ملف خروج (إحصائياً).
120
- """
121
- if not self.initialized:
122
- return "Learning Hub not initialized."
123
-
124
  best_profile = await self.statistical_analyzer.get_best_exit_profile(entry_strategy)
125
-
126
  if best_profile != "unknown":
127
- # (Prompt in English as requested)
128
- feedback = f"Statistical Feedback: For the '{entry_strategy}' strategy, the '{best_profile}' exit profile has historically performed best."
129
- return feedback
130
- else:
131
- return "No statistical feedback available for this strategy yet."
132
-
133
- # 🔴 --- START OF CHANGE (V2 - VADER Learning) --- 🔴
134
  async def get_statistical_news_score(self, raw_vader_score: float) -> float:
135
- """
136
- يحول درجة VADER الخام إلى متوسط الربح/الخسارة التاريخي المتوقع.
137
- (يُستخدم بواسطة app.py / MLProcessor للترتيب الداخلي)
138
- """
139
- if not self.initialized:
140
- return 0.0 # محايد
141
-
142
- # (جلب متوسط الربح/الخسارة الفعلي من المحلل الإحصائي)
143
- historical_pnl = await self.statistical_analyzer.get_statistical_vader_pnl(raw_vader_score)
144
-
145
- # (إرجاع النسبة المئوية للربح/الخسارة مباشرة، مثلاً: 1.1 أو -0.5)
146
- return historical_pnl
147
- # 🔴 --- END OF CHANGE --- 🔴
148
 
149
- # 🔴 --- START OF CHANGE --- 🔴
150
- async def get_optimized_weights(self, market_condition: str) -> Dict[str, float]:
151
- """
152
- يُستخدم بواسطة MLProcessor/StrategyEngine/Sentry لجلب الأوزان المعدلة إحصائياً.
153
- """
154
- if not self.initialized:
155
- # (الحصول على كل الأوزان الافتراضية)
156
- return await self.statistical_analyzer.get_default_strategy_weights()
157
-
158
- # (الحصول على كل الأوزان المحسنة)
159
- return await self.statistical_analyzer.get_optimized_weights(market_condition)
160
- # 🔴 --- END OF CHANGE --- 🔴
161
-
162
  async def run_distillation_check(self):
163
- """
164
- (يتم استدعاؤها دورياً من app.py)
165
- للتحقق من جميع المجالات وتشغيل التقطير إذا لزم الأمر.
166
- """
167
- if not self.initialized:
168
- return
169
-
170
- print("ℹ️ [HubManager] Running periodic distillation check...")
171
  for domain in self.memory_store.domain_files.keys():
172
  await self.curator.check_and_distill_domain(domain)
173
- print("✅ [HubManager] Distillation check complete.")
174
-
175
- # (No change to shutdown function)
176
  async def shutdown(self):
177
- """
178
- Saves all persistent data from the statistical analyzer.
179
- """
180
- if not self.initialized:
181
- return
182
-
183
- print("🔄 [HubManager] Shutting down... Saving all learning data.")
184
  try:
 
185
  await self.statistical_analyzer.save_weights_to_r2()
186
  await self.statistical_analyzer.save_performance_history()
187
  await self.statistical_analyzer.save_exit_profile_effectiveness()
188
- # 🔴 --- START OF CHANGE (V2 - VADER Learning) --- 🔴
189
  await self.statistical_analyzer.save_vader_effectiveness()
190
- # 🔴 --- END OF CHANGE --- 🔴
191
- print("✅ [HubManager] All statistical (slow-learner) data saved.")
192
  except Exception as e:
193
- print(f"❌ [HubManager] Failed to save learning data on shutdown: {e}")
194
-
195
- # 🔴 --- START OF CHANGE (V3 - Whale Learning Loop) --- 🔴
196
 
 
197
  async def run_whale_learning_check(self):
198
- """
199
- (جديد V3 - "المُسجّل" Logger)
200
- يعمل في الخلفية لإكمال سجلات تعلم الحيتان المعلقة.
201
- """
202
- if not self.initialized:
203
- await asyncio.sleep(60) # انتظر حتى تتم التهيئة
204
-
205
- print(f"🧠 [Whale-Logger] بدء تشغيل حلقة تعلم الحيتان (المُسجّل)...")
206
-
207
- # (الانتظار 10 دقائق عند بدء التشغيل للسماح بجمع بعض البيانات)
208
  await asyncio.sleep(600)
209
-
210
  while True:
211
  try:
212
- # (1. جلب السجلات المعلقة)
213
- pending_records = await self.r2_service.get_pending_whale_learning_records_async()
214
- if not pending_records:
215
- # (لا توجد سجلات، انتظر 10 دقائق)
216
  await asyncio.sleep(600)
217
  continue
218
 
219
- print(f"🧠 [Whale-Logger] تم العثور على {len(pending_records)} سجل تعلم معلق. بدء المعالجة...")
220
  now_utc = datetime.now(timezone.utc)
221
-
222
- for record in pending_records:
223
  try:
224
- target_time_utc = datetime.fromisoformat(record['target_time_utc'])
225
-
226
- # (2. التحقق من الوقت)
227
- if now_utc >= target_time_utc:
228
- print(f" -> [Whale-Logger] معالجة سجل {record['symbol']} (ID: {record['record_id']})...")
229
- # (حان وقت جلب السعر المستقبلي)
230
  symbol = record['symbol']
231
  target_price = await self.data_manager.get_latest_price_async(symbol)
232
-
233
  if target_price and target_price > 0 and record['start_price_usd'] > 0:
234
- # (3. حساب النتيجة)
235
- price_change_pct = ((target_price - record['start_price_usd']) / record['start_price_usd']) * 100
236
-
237
- record['target_price_usd'] = target_price
238
- record['price_change_percentage'] = price_change_pct
239
- record['status'] = "COMPLETED"
240
-
241
- # (4. تحديث السجل في R2)
242
  await self.r2_service.update_completed_whale_learning_record_async(record)
243
- else:
244
- print(f" ⚠️ [Whale-Logger] فشل جلب السعر المستقبلي لـ {symbol}. سيعاد المحاولة لاحقاً.")
245
- else:
246
- # (لم يحن الوقت بعد)
247
- pass
248
-
249
- except Exception as e_inner:
250
- print(f"❌ [Whale-Logger] فشل معالجة سجل فردي: {e_inner}")
251
 
252
- # (تشغيل "المعلّم" بعد كل دورة تسجيل)
253
  await self.update_optimal_whale_window()
254
-
255
- # (الانتظار 5 دقائق قبل التحقق مرة أخرى)
256
  await asyncio.sleep(300)
257
-
258
- except Exception as e_outer:
259
- print(f"❌ [Whale-Logger] خطأ فادح في حلقة تعلم الحيتان: {e_outer}")
260
- traceback.print_exc()
261
- await asyncio.sleep(600) # (انتظار 10 دقائق عند الفشل الفادح)
262
 
263
  async def update_optimal_whale_window(self):
264
- """
265
- (جديد V3 - "المعلّم" Teacher)
266
- يحلل جميع السجلات المكتملة ويجد أفضل "مقياس + نافذة" للارتباط.
267
- """
268
- if not NUMPY_AVAILABLE:
269
- print("⚠️ [Whale-Teacher] لا يمكن تشغيل تحليل الارتباط (numpy/scipy مفقودة).")
270
- return
271
-
272
  async with self.whale_learning_lock:
273
- print("👨‍🏫 [Whale-Teacher] بدء تحليل الارتباط الإحصائي...")
274
  try:
275
- # (1. جلب جميع السجلات المكتملة)
276
  all_completed = await self.r2_service.get_all_completed_whale_records_async()
277
- if len(all_completed) < 20: # (نحتاج 20 عينة على الأقل لبدء التعلم)
278
- print(f"👨‍🏫 [Whale-Teacher] نحتاج 20 سجل مكتمل على الأقل (الحالي: {len(all_completed)}). تخطي التحليل.")
279
- return
280
 
281
- # (2. استخراج البيانات في مصفوفات Numpy)
282
  price_changes = []
283
  metrics_data = defaultdict(lambda: defaultdict(list))
284
-
285
- # (قائمة بجميع المقاييس التي نريد اختبارها)
286
  windows = ['30m', '1h', '2h', '4h', '24h']
287
  metric_keys = ['relative_net_flow_percent', 'transaction_density', 'net_flow_usd']
288
 
289
- for record in all_completed:
290
- if record.get('price_change_percentage') is None: continue
291
-
292
- price_changes.append(record['price_change_percentage'])
293
- analysis = record.get('window_analysis', {})
294
-
295
  for w in windows:
296
- window_data = analysis.get(w, {})
297
  for k in metric_keys:
298
- metrics_data[w][k].append(window_data.get(k, 0.0))
299
 
300
- price_changes_np = np.array(price_changes)
301
- if len(price_changes_np) < 20:
302
- print("👨‍🏫 [Whale-Teacher] لا توجد بيانات كافية (NP) للارتباط.")
303
- return
304
 
305
- # (3. حساب الارتباط)
306
- correlation_results = {}
307
  for w in windows:
308
  for k in metric_keys:
309
  metric_np = np.array(metrics_data[w][k])
310
- if len(metric_np) != len(price_changes_np): continue
311
-
312
- # (حساب ارتباط بيرسون)
313
- corr, p_value = pearsonr(metric_np, price_changes_np)
314
-
315
- if not np.isnan(corr) and p_value < 0.1: # (نهتم فقط بالارتباطات ذات الدلالة الإحصائية)
316
- correlation_results[f"{w}_{k}"] = abs(corr) # (نهتم بقوة الارتباط، بغض النظر عن الاتجاه)
317
-
318
- if not correlation_results:
319
- print("👨‍🏫 [Whale-Teacher] لم يتم العثور على ارتباطات إحصائية ذات دلالة.")
320
- return
321
-
322
- # (4. العثور على الفائز وحفظه)
323
- best_metric_key = max(correlation_results, key=correlation_results.get)
324
- best_correlation = correlation_results[best_metric_key]
325
-
326
- # (تقسيم المفتاح: '1h_relative_net_flow_percent')
327
- best_window, best_metric = best_metric_key.split('_', 1)
328
-
329
- new_config = {
330
- "best_window": best_window,
331
- "best_metric": best_metric,
332
- "correlation_score": best_correlation,
333
- "total_samples": len(price_changes_np),
334
- "last_updated_utc": datetime.now(timezone.utc).isoformat()
335
- }
336
-
337
- # (حفظ الإعدادات الجديدة ومشاركتها مع النظام)
338
- self.optimal_whale_config = new_config
339
- await self.r2_service.save_whale_learning_config_async(new_config)
340
-
341
- print(f"🏆 [Whale-Teacher] تم العثور على أفضل إشارة جديدة!")
342
- print(f" -> المقياس: {best_metric}")
343
- print(f" -> النافذة: {best_window}")
344
- print(f" -> الارتباط: {best_correlation:.4f} (على {len(price_changes_np)} عينة)")
345
 
346
  except Exception as e:
347
- print(f"❌ [Whale-Teacher] فشل تحليل الارتباط: {e}")
348
- traceback.print_exc()
349
-
350
- # 🔴 --- END OF CHANGE --- 🔴
 
1
  # learning_hub/hub_manager.py
2
+ # (محدث بالكامل - V4 - Adaptive Hybrid Weights Support)
3
+
4
  import asyncio
5
  import traceback
6
  from typing import Any, Dict, List
7
  from datetime import datetime, timezone
8
+ from collections import defaultdict
9
 
10
  # (استيراد جميع المكونات الداخلية للمركز)
11
  from .schemas import *
 
15
  from .reflector import Reflector
16
  from .curator import Curator
17
 
18
+ # استيراد لتحليل الارتباط (Whale Learning)
19
  try:
20
  import numpy as np
21
  from scipy.stats import pearsonr
 
24
  print("❌ [HubManager] مكتبة numpy أو scipy غير مثبتة! لن يعمل تعلم الحيتان.")
25
  NUMPY_AVAILABLE = False
26
 
 
27
  class LearningHubManager:
28
  def __init__(self, r2_service: Any, llm_service: Any, data_manager: Any):
29
+ print("🚀 Initializing Learning Hub Manager (V4 - Adaptive)...")
30
 
31
+ # 1. الخدمات الأساسية
32
  self.r2_service = r2_service
33
  self.llm_service = llm_service
34
  self.data_manager = data_manager
35
 
36
+ # 2. تهيئة المكونات
37
  self.policy_engine = PolicyEngine()
38
  self.memory_store = MemoryStore(
39
  r2_service=self.r2_service,
 
53
  data_manager=self.data_manager
54
  )
55
 
56
+ # متغيرات حالة لتعلم الحيتان
57
  self.whale_learning_lock = asyncio.Lock()
58
+ self.optimal_whale_config = {}
59
 
60
  self.initialized = False
61
  print("✅ Learning Hub Manager constructed. Ready for initialization.")
62
 
63
  async def initialize(self):
64
+ """تهيئة جميع الأنظمة الفرعية"""
65
+ if self.initialized: return
 
 
 
 
66
  print("🔄 [HubManager] Initializing all sub-modules...")
67
+
68
+ # تهيئة المحلل الإحصائي (المسؤول عن الأوزان المتكيفة)
69
  await self.statistical_analyzer.initialize()
70
 
71
+ # تحميل إعدادات تعلم الحيتان
72
  if hasattr(self.r2_service, 'load_whale_learning_config_async'):
73
  self.optimal_whale_config = await self.r2_service.load_whale_learning_config_async()
74
  if self.optimal_whale_config:
75
+ print(f"✅ [HubManager] Loaded optimal whale config: {self.optimal_whale_config.get('best_metric', 'N/A')}")
 
 
76
 
77
  self.initialized = True
78
  print("✅ [HubManager] All sub-modules initialized. Learning Hub is LIVE.")
79
 
80
  async def analyze_trade_and_learn(self, trade_object: Dict[str, Any], close_reason: str):
81
+ """الدالة الرئيسية للتعلم من الصفقات المغلقة"""
82
+ if not self.initialized: return
 
 
 
 
 
83
 
84
  print(f"🧠 [HubManager] Learning from trade {trade_object.get('symbol')}...")
85
 
86
+ # 1. التعلم السريع (Reflector)
87
  try:
 
88
  await self.reflector.analyze_trade_outcome(trade_object, close_reason)
89
  except Exception as e:
90
+ print(f"❌ [HubManager] Reflector failed: {e}")
91
 
92
+ # 2. التعلم البطيء وتكييف الأوزان (StatisticalAnalyzer)
93
  try:
 
94
  await self.statistical_analyzer.update_statistics(trade_object, close_reason)
95
  except Exception as e:
96
+ print(f"❌ [HubManager] StatisticalAnalyzer failed: {e}")
97
 
98
  print(f"✅ [HubManager] Learning complete for {trade_object.get('symbol')}.")
99
+
100
+ async def get_optimized_weights(self, market_condition: str = None) -> Dict[str, Any]:
101
  """
102
+ جلب الأوزان المحسنة (بما في ذلك الأوزان الهجينة المتكيفة).
103
+ يستخدمها MLProcessor لتحديث معادلته الهجينة.
104
  """
105
  if not self.initialized:
106
+ return await self.statistical_analyzer.get_default_strategy_weights()
107
+
108
+ # إرجاع قاموس الأوزان الكامل من المحلل الإحصائي
109
+ return self.statistical_analyzer.weights
110
+
111
+ # --- دوال مساعدة أخرى (للـ LLM وغيرها) ---
112
+ async def get_active_context_for_llm(self, domain: str, query: str) -> str:
113
+ if not self.initialized: return "Learning Hub not initialized."
114
  return await self.memory_store.get_active_context(domain, query)
115
 
116
  async def get_statistical_feedback_for_llm(self, entry_strategy: str) -> str:
117
+ if not self.initialized: return "Learning Hub not initialized."
 
 
 
 
 
118
  best_profile = await self.statistical_analyzer.get_best_exit_profile(entry_strategy)
 
119
  if best_profile != "unknown":
120
+ return f"Statistical Feedback: For '{entry_strategy}', '{best_profile}' exit profile performed best."
121
+ return "No statistical feedback available yet."
122
+
 
 
 
 
123
  async def get_statistical_news_score(self, raw_vader_score: float) -> float:
124
+ if not self.initialized: return 0.0
125
+ return await self.statistical_analyzer.get_statistical_vader_pnl(raw_vader_score)
 
 
 
 
 
 
 
 
 
 
 
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  async def run_distillation_check(self):
128
+ """تشغيل دوري للتقطير (Curator)"""
129
+ if not self.initialized: return
130
+ # print("ℹ️ [HubManager] Running distillation check...")
 
 
 
 
 
131
  for domain in self.memory_store.domain_files.keys():
132
  await self.curator.check_and_distill_domain(domain)
133
+
 
 
134
  async def shutdown(self):
135
+ """حفظ كل البيانات عند الإغلاق"""
136
+ if not self.initialized: return
137
+ print("🔄 [HubManager] Shutting down... Saving learning data.")
 
 
 
 
138
  try:
139
+ # المحلل الإحصائي يحفظ الأوزان المتكيفة وسجل الأداء
140
  await self.statistical_analyzer.save_weights_to_r2()
141
  await self.statistical_analyzer.save_performance_history()
142
  await self.statistical_analyzer.save_exit_profile_effectiveness()
 
143
  await self.statistical_analyzer.save_vader_effectiveness()
144
+ print("✅ [HubManager] Data saved successfully.")
 
145
  except Exception as e:
146
+ print(f"❌ [HubManager] Save failed: {e}")
 
 
147
 
148
+ # --- Whale Learning Loop (كما هي من V3) ---
149
  async def run_whale_learning_check(self):
150
+ if not self.initialized: await asyncio.sleep(60)
151
+ print(f"🧠 [Whale-Logger] Starting background learning loop...")
 
 
 
 
 
 
 
 
152
  await asyncio.sleep(600)
 
153
  while True:
154
  try:
155
+ pending = await self.r2_service.get_pending_whale_learning_records_async()
156
+ if not pending:
 
 
157
  await asyncio.sleep(600)
158
  continue
159
 
 
160
  now_utc = datetime.now(timezone.utc)
161
+ for record in pending:
 
162
  try:
163
+ target_time = datetime.fromisoformat(record['target_time_utc'])
164
+ if now_utc >= target_time:
 
 
 
 
165
  symbol = record['symbol']
166
  target_price = await self.data_manager.get_latest_price_async(symbol)
 
167
  if target_price and target_price > 0 and record['start_price_usd'] > 0:
168
+ pct_change = ((target_price - record['start_price_usd']) / record['start_price_usd']) * 100
169
+ record.update({'target_price_usd': target_price, 'price_change_percentage': pct_change, 'status': "COMPLETED"})
 
 
 
 
 
 
170
  await self.r2_service.update_completed_whale_learning_record_async(record)
171
+ except Exception: pass
 
 
 
 
 
 
 
172
 
 
173
  await self.update_optimal_whale_window()
 
 
174
  await asyncio.sleep(300)
175
+ except Exception as e:
176
+ print(f"❌ [Whale-Logger] Error: {e}")
177
+ await asyncio.sleep(600)
 
 
178
 
179
  async def update_optimal_whale_window(self):
180
+ if not NUMPY_AVAILABLE: return
 
 
 
 
 
 
 
181
  async with self.whale_learning_lock:
 
182
  try:
 
183
  all_completed = await self.r2_service.get_all_completed_whale_records_async()
184
+ if len(all_completed) < 20: return
 
 
185
 
 
186
  price_changes = []
187
  metrics_data = defaultdict(lambda: defaultdict(list))
 
 
188
  windows = ['30m', '1h', '2h', '4h', '24h']
189
  metric_keys = ['relative_net_flow_percent', 'transaction_density', 'net_flow_usd']
190
 
191
+ for r in all_completed:
192
+ if r.get('price_change_percentage') is None: continue
193
+ price_changes.append(r['price_change_percentage'])
194
+ analysis = r.get('window_analysis', {})
 
 
195
  for w in windows:
 
196
  for k in metric_keys:
197
+ metrics_data[w][k].append(analysis.get(w, {}).get(k, 0.0))
198
 
199
+ price_np = np.array(price_changes)
200
+ best_corr = 0
201
+ best_key = None
 
202
 
 
 
203
  for w in windows:
204
  for k in metric_keys:
205
  metric_np = np.array(metrics_data[w][k])
206
+ if len(metric_np) == len(price_np):
207
+ corr, p_val = pearsonr(metric_np, price_np)
208
+ if not np.isnan(corr) and p_val < 0.1 and abs(corr) > best_corr:
209
+ best_corr = abs(corr)
210
+ best_key = f"{w}_{k}"
211
+
212
+ if best_key:
213
+ w, m = best_key.split('_', 1)
214
+ 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()}
215
+ self.optimal_whale_config = new_config
216
+ await self.r2_service.save_whale_learning_config_async(new_config)
217
+ print(f"🏆 [Whale-Teacher] New best signal: {m} on {w} (Corr: {best_corr:.2f})")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
  except Exception as e:
220
+ print(f"❌ [Whale-Teacher] Error: {e}")