Riy777 commited on
Commit
6ce53be
·
verified ·
1 Parent(s): 7ca1715

Update r2.py

Browse files
Files changed (1) hide show
  1. r2.py +141 -284
r2.py CHANGED
@@ -1,5 +1,5 @@
1
  # ==============================================================================
2
- # ☁️ r2.py (V39.0 - GEM-Architect: Split PnL Support)
3
  # ==============================================================================
4
 
5
  import os
@@ -21,41 +21,49 @@ R2_ACCESS_KEY_ID = os.getenv("R2_ACCESS_KEY_ID")
21
  R2_SECRET_ACCESS_KEY = os.getenv("R2_SECRET_ACCESS_KEY")
22
  BUCKET_NAME = "trading"
23
 
24
- # 🔴 الرصيد الافتراضي
25
- INITIAL_CAPITAL = 10.0
26
-
27
- # 📁 مفاتيح الملفات الأساسية في R2
28
- WHALE_LEARNING_PENDING_KEY = "learning_whale_pending_records.json"
29
- WHALE_LEARNING_COMPLETED_KEY = "learning_whale_completed_records.json"
30
- WHALE_LEARNING_CONFIG_KEY = "learning_whale_optimal_config.json"
31
-
32
- PORTFOLIO_STATE_KEY = "portfolio_state.json"
33
- SMART_PORTFOLIO_STATE_KEY = "smart_portfolio_state.json"
34
-
35
- OPEN_TRADES_KEY = "open_trades.json"
36
- CLOSED_TRADES_KEY = "closed_trades_history.json"
37
-
38
- CANDIDATES_KEY = "Candidates.json"
39
- CONTRACTS_DB_KEY = "contracts.json"
40
- SYSTEM_LOGS_KEY = "system_logs.json"
41
- LEARNING_DATA_KEY = "learning_data.json"
42
- ANALYSIS_AUDIT_KEY = "analysis_audit_log.json"
43
- DEEP_STEWARD_AUDIT_KEY = "DeepSteward_Audit_Log.json"
44
-
45
- # ✅ مفتاح جديد خاص ببيانات تدريب الحوكمة (156 مؤشر + النتيجة)
46
- GOVERNANCE_TRAINING_KEY = "datasets/governance_training_data.json"
47
 
48
- # 📊 مفاتيح التحليلات والتشخيص (جديد)
49
- DIAGNOSTIC_STATS_KEY = "analytics/model_diagnostic_matrix.json"
50
- GUARDIAN_STATS_KEY = "analytics/guardian_performance_stats.json"
51
-
52
- # 🧬 مفاتيح النظام السيبراني
53
- STRATEGIC_DNA_KEY = "learning/strategic_dna_v2.json"
54
-
55
- # 🆕 مفاتيح التدريب (Legacy Support)
56
- TRAINING_PENDING_BATCH_KEY = "datasets/pending_training_batch.json"
57
- MODEL_TITAN_KEY = "ml_models/layer2/Titan_XGB_V1.json"
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  class R2Service:
61
  def __init__(self):
@@ -69,32 +77,27 @@ class R2Service:
69
  )
70
  self.lock_acquired = False
71
  self.BUCKET_NAME = BUCKET_NAME
72
- print(f"✅ [R2 V39.0] Service Loaded (Split PnL Ready).")
73
 
74
  except Exception as e:
75
  raise RuntimeError(f"Failed to initialize S3 client: {e}")
76
 
77
  # ==============================================================================
78
- # 🔴 دالة التصفير الشامل (Updated & Non-Blocking)
79
  # ==============================================================================
80
  async def reset_all_stats_async(self):
81
- """تصفير المحفظة والسجلات التاريخية والعودة لنقطة الصفر"""
82
  try:
83
- print("🔄 [R2 Reset] بدء عملية التصفير...")
84
 
85
  initial_state = {
86
  "current_capital_usd": INITIAL_CAPITAL,
87
  "invested_capital_usd": 0.0,
88
  "allocated_capital_usd": 0.0,
89
  "initial_capital_usd": INITIAL_CAPITAL,
90
- "total_trades": 0,
91
- "winning_trades": 0,
92
- "losing_trades": 0,
93
- "total_profit_usd": 0.0,
94
- "total_loss_usd": 0.0,
95
- "win_rate": 0.0,
96
- "daily_net_pnl": 0.0,
97
- "is_trading_halted": False,
98
  "last_update": datetime.now().isoformat()
99
  }
100
 
@@ -114,29 +117,31 @@ class R2Service:
114
  empty_list_json = json.dumps([], indent=2).encode('utf-8')
115
  empty_logs_json = json.dumps({"logs": []}, indent=2).encode('utf-8')
116
 
 
117
  def _reset_sync():
118
- self.s3_client.put_object(
119
- Bucket=BUCKET_NAME, Key=CLOSED_TRADES_KEY, Body=empty_list_json, ContentType="application/json"
120
- )
121
- self.s3_client.put_object(
122
- Bucket=BUCKET_NAME, Key=OPEN_TRADES_KEY, Body=empty_list_json, ContentType="application/json"
123
- )
124
- self.s3_client.put_object(
125
- Bucket=BUCKET_NAME, Key=SYSTEM_LOGS_KEY, Body=empty_logs_json, ContentType="application/json"
126
- )
127
-
 
 
 
 
128
  await asyncio.to_thread(_reset_sync)
129
-
130
- # تصفير المصفوفات أيضاً عند الطلب الشامل
131
  await self.reset_diagnostic_stats_async()
132
  await self.reset_guardian_stats_async()
133
 
134
- print("✅ [R2 Reset] تم تصفير جميع البيانات والمحفظة بنجاح.")
135
  return True
136
 
137
  except Exception as e:
138
  print(f"❌ [R2 Reset Error] فشل التصفير: {e}")
139
- traceback.print_exc()
140
  return False
141
 
142
  # ==============================================================================
@@ -145,19 +150,12 @@ class R2Service:
145
  async def upload_json_async(self, data: Any, key: str):
146
  try:
147
  json_bytes = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
148
- await asyncio.to_thread(
149
- self.s3_client.put_object,
150
- Bucket=self.BUCKET_NAME,
151
- Key=key,
152
- Body=json_bytes,
153
- ContentType="application/json"
154
- )
155
  except Exception as e:
156
  print(f"❌ [R2] Upload JSON failed for {key}: {e}")
157
  raise
158
 
159
  async def get_file_json_async(self, key: str) -> Optional[Any]:
160
- """مساعد لقراءة JSON مباشرة"""
161
  try:
162
  data = await self.get_file_async(key)
163
  return json.loads(data) if data else None
@@ -165,279 +163,138 @@ class R2Service:
165
 
166
  async def get_file_async(self, key: str) -> Optional[bytes]:
167
  try:
168
- response = await asyncio.to_thread(
169
- self.s3_client.get_object,
170
- Bucket=self.BUCKET_NAME,
171
- Key=key
172
- )
173
  return response['Body'].read()
174
  except ClientError as e:
175
- if e.response['Error']['Code'] == 'NoSuchKey':
176
- return None
177
  raise
178
 
179
  async def upload_file_async(self, file_obj, key: str):
180
  try:
181
- await asyncio.to_thread(
182
- self.s3_client.upload_fileobj,
183
- file_obj,
184
- self.BUCKET_NAME,
185
- key
186
- )
187
  except Exception as e:
188
  print(f"❌ [R2] File upload failed for {key}: {e}")
189
  raise
190
 
191
  # ==============================================================================
192
- # 📊 Diagnostic & Guardian Stats (Split PnL Logic)
 
 
 
 
 
 
 
 
 
 
 
193
  # ==============================================================================
 
 
194
  async def get_diagnostic_stats_async(self) -> Dict[str, Any]:
195
- """استرجاع مصفوفة التشخيص"""
196
- try:
197
- data = await self.get_file_async(DIAGNOSTIC_STATS_KEY)
198
- defaults = {
199
- "Titan": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
200
- "Patterns": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
201
- "Oracle": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
202
- "Sniper": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
203
- "MonteCarlo_L": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
204
- "MonteCarlo_A": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
205
- "News": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0},
206
- "Governance": {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0}
207
- }
208
- loaded = json.loads(data) if data else {}
209
- # دمج القيم الافتراضية
210
- for k, v in defaults.items():
211
- if k not in loaded: loaded[k] = v
212
- else:
213
- # التأكد من وجود المفاتيح الجديدة في البيانات القديمة
214
- if "profit_accum" not in loaded[k]: loaded[k]["profit_accum"] = 0.0
215
- if "loss_accum" not in loaded[k]: loaded[k]["loss_accum"] = 0.0
216
- return loaded
217
- except: return {}
218
 
219
  async def update_diagnostic_stats_async(self, updates: Dict[str, Dict[str, float]]):
220
- """تحديث إحصائيات النماذج بشكل آمن مع دعم Split PnL"""
221
  try:
222
  current = await self.get_diagnostic_stats_async()
223
  for model, metrics in updates.items():
224
- if model not in current:
225
- current[model] = {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0}
226
-
227
  current[model]['wins'] += metrics.get('wins', 0)
228
  current[model]['losses'] += metrics.get('losses', 0)
229
  current[model]['pnl'] += metrics.get('pnl', 0.0)
230
-
231
- # ✅ FIX: Explicitly sum split PnL
232
  current[model]['profit_accum'] += metrics.get('profit_accum', 0.0)
233
  current[model]['loss_accum'] += metrics.get('loss_accum', 0.0)
234
-
235
  await self.upload_json_async(current, DIAGNOSTIC_STATS_KEY)
236
- except Exception as e:
237
- print(f"❌ [R2] Failed to update diagnostics: {e}")
238
-
239
- async def save_diagnostic_stats_async(self, stats: Dict[str, Any]):
240
- """حفظ المصفوفة كاملة (للاستخدام المباشر)"""
241
- await self.upload_json_async(stats, DIAGNOSTIC_STATS_KEY)
242
 
243
  async def reset_diagnostic_stats_async(self):
244
- """تصفير مصفوفة التشخيص"""
245
  empty = {k: {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0} for k in
246
  ["Titan", "Patterns", "Oracle", "Sniper", "MonteCarlo_L", "MonteCarlo_A", "News", "Governance"]}
247
  await self.upload_json_async(empty, DIAGNOSTIC_STATS_KEY)
248
- print("📊 [R2] Diagnostic Matrix Reset.")
249
 
250
- # --- Guardian Stats ---
251
  async def get_guardian_stats_async(self) -> Dict[str, Any]:
252
- data = await self.get_file_async(GUARDIAN_STATS_KEY)
253
- defaults = {
254
- "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
255
- "crash": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
256
- "giveback": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
257
- "stagnation": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0}
258
- }
259
- loaded = json.loads(data) if data else {}
260
  for k, v in defaults.items():
261
- if k not in loaded: loaded[k] = v
262
- return loaded
263
 
264
- async def save_guardian_stats_async(self, stats: Dict[str, Any]):
265
- await self.upload_json_async(stats, GUARDIAN_STATS_KEY)
266
-
267
  async def reset_guardian_stats_async(self):
268
- """تصفير إحصائيات الحراس"""
269
- defaults = {
270
- "hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
271
- "crash": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
272
- "giveback": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
273
- "stagnation": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0}
274
- }
275
  await self.upload_json_async(defaults, GUARDIAN_STATS_KEY)
276
- print("🛡️ [R2] Guardian Stats Reset.")
277
 
278
- # ==============================================================================
279
- # 🏛️ بيانات تدريب الحوكمة (Governance Training Data)
280
- # ==============================================================================
281
- async def append_governance_training_data(self, record: Dict[str, Any]):
282
  try:
283
- data = await self.get_file_async(GOVERNANCE_TRAINING_KEY)
284
- dataset = json.loads(data) if data else []
285
  dataset.append(record)
286
  if len(dataset) > 5000: dataset = dataset[-5000:]
287
  await self.upload_json_async(dataset, GOVERNANCE_TRAINING_KEY)
288
- print(f"🏛️ [R2 Governance] تم حفظ بيانات التدريب لـ {record.get('symbol', 'UNKNOWN')}")
289
- except Exception as e:
290
- print(f"❌ [R2 Error] فشل حفظ بيانات تدريب الحوكمة: {e}")
291
-
292
- # ==============================================================================
293
- # 🧬 إدارة الاستراتيجية (Strategic DNA)
294
- # ==============================================================================
295
- async def load_strategy_dna_async(self) -> Dict[str, Any]:
296
- try:
297
- data = await self.get_file_async(STRATEGIC_DNA_KEY)
298
- return json.loads(data) if data else {}
299
- except: return {}
300
-
301
- async def save_strategy_dna_async(self, dna_data: Dict[str, Any]):
302
- await self.upload_json_async(dna_data, STRATEGIC_DNA_KEY)
303
-
304
- # ==============================================================================
305
- # 📊 إدارة المرشحين
306
- # ==============================================================================
307
- async def save_candidates_async(self, candidates):
308
- try:
309
- data = {"timestamp": datetime.now().isoformat(), "total_candidates": len(candidates), "candidates": candidates}
310
- await self.upload_json_async(data, CANDIDATES_KEY)
311
- except Exception: pass
312
-
313
- async def load_candidates_async(self):
314
- try:
315
- data_bytes = await self.get_file_async(CANDIDATES_KEY)
316
- if data_bytes: return json.loads(data_bytes).get('candidates', [])
317
- return []
318
- except Exception: return []
319
-
320
- # ==============================================================================
321
- # 📝 السجلات والتعلم
322
- # ==============================================================================
323
- async def save_system_logs_async(self, log_data):
324
- pass
325
-
326
- async def append_deep_steward_audit(self, audit_record: Dict[str, Any]):
327
- try:
328
- data = await self.get_file_async(DEEP_STEWARD_AUDIT_KEY)
329
- history = json.loads(data) if data else []
330
- history.append(audit_record)
331
- if len(history) > 2000: history = history[-2000:]
332
- await self.upload_json_async(history, DEEP_STEWARD_AUDIT_KEY)
333
- except Exception as e:
334
- print(f"❌ [R2 Error] فشل حفظ التدقيق: {e}")
335
 
336
- # ==============================================================================
337
- # 💰 إدارة المحفظة
338
- # ==============================================================================
339
  async def get_portfolio_state_async(self):
340
- try:
341
- data_bytes = await self.get_file_async(PORTFOLIO_STATE_KEY)
342
- if data_bytes:
343
- state = json.loads(data_bytes)
344
- if 'losing_trades' not in state: state['losing_trades'] = 0
345
- if 'initial_capital_usd' not in state: state['initial_capital_usd'] = INITIAL_CAPITAL
346
- if 'allocated_capital_usd' not in state: state['allocated_capital_usd'] = 0.0
347
- if 'daily_net_pnl' not in state: state['daily_net_pnl'] = 0.0
348
- return state
349
-
350
- initial_state = {
351
- "current_capital_usd": INITIAL_CAPITAL,
352
- "allocated_capital_usd": 0.0,
353
- "invested_capital_usd": 0.0,
354
- "initial_capital_usd": INITIAL_CAPITAL,
355
- "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
356
- "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_rate": 0.0,
357
- "daily_net_pnl": 0.0,
358
- "is_trading_halted": False,
359
- "last_update": datetime.now().isoformat()
360
- }
361
- await self.save_portfolio_state_async(initial_state)
362
- return initial_state
363
- except Exception: raise
364
 
365
  async def save_portfolio_state_async(self, state):
366
  state['last_update'] = datetime.now().isoformat()
367
  await self.upload_json_async(state, PORTFOLIO_STATE_KEY)
368
 
369
- # ==============================================================================
370
- # 📈 إدارة الصفقات
371
- # ==============================================================================
372
  async def get_open_trades_async(self):
373
- try:
374
- data = await self.get_file_async(OPEN_TRADES_KEY)
375
- return json.loads(data) if data else []
376
- except: return []
377
 
378
- async def save_open_trades_async(self, trades):
379
- await self.upload_json_async(trades, OPEN_TRADES_KEY)
380
 
381
- async def append_to_closed_trades_history(self, trade_data: Dict[str, Any]):
382
  try:
383
- data = await self.get_file_async(CLOSED_TRADES_KEY)
384
- history = json.loads(data) if data else []
385
  history.append(trade_data)
386
  if len(history) > 1000: history = history[-1000:]
387
  await self.upload_json_async(history, CLOSED_TRADES_KEY)
388
- except Exception: pass
389
-
390
- # ==============================================================================
391
- # 📜 قاعدة العقود
392
- # ==============================================================================
393
- async def load_contracts_db_async(self):
394
- try:
395
- data = await self.get_file_async(CONTRACTS_DB_KEY)
396
- return json.loads(data) if data else {}
397
- except: return {}
398
-
399
- async def save_contracts_db_async(self, data):
400
- await self.upload_json_async(data, CONTRACTS_DB_KEY)
401
-
402
- # ==============================================================================
403
- # 🐋 تعلم الحيتان (Legacy Support)
404
- # ==============================================================================
405
- async def save_whale_learning_record_async(self, record: Dict[str, Any]):
406
- try:
407
- data = await self.get_file_async(WHALE_LEARNING_PENDING_KEY)
408
- pending = json.loads(data) if data else []
409
- pending.append(record)
410
- await self.upload_json_async(pending, WHALE_LEARNING_PENDING_KEY)
411
  except: pass
412
 
413
- async def get_pending_whale_learning_records_async(self) -> List[Dict[str, Any]]:
414
- data = await self.get_file_async(WHALE_LEARNING_PENDING_KEY)
415
- return json.loads(data) if data else []
 
416
 
417
- async def update_completed_whale_learning_record_async(self, completed_record: Dict[str, Any]):
418
  try:
419
- rec_id = completed_record.get("record_id")
420
- if not rec_id: return
421
-
422
- comp_data = await self.get_file_async(WHALE_LEARNING_COMPLETED_KEY)
423
- completed = json.loads(comp_data) if comp_data else []
424
- completed.append(completed_record)
425
- if len(completed) > 5000: completed = completed[-5000:]
426
- await self.upload_json_async(completed, WHALE_LEARNING_COMPLETED_KEY)
427
-
428
- pend_data = await self.get_file_async(WHALE_LEARNING_PENDING_KEY)
429
- pending = json.loads(pend_data) if pend_data else []
430
- updated_pending = [r for r in pending if r.get("record_id") != rec_id]
431
- await self.upload_json_async(updated_pending, WHALE_LEARNING_PENDING_KEY)
432
  except: pass
433
 
434
- async def get_all_completed_whale_records_async(self) -> List[Dict[str, Any]]:
435
- data = await self.get_file_async(WHALE_LEARNING_COMPLETED_KEY)
436
- return json.loads(data) if data else []
437
-
438
- async def save_whale_learning_config_async(self, config: Dict[str, Any]):
439
- await self.upload_json_async(config, WHALE_LEARNING_CONFIG_KEY)
440
-
441
- async def load_whale_learning_config_async(self) -> Dict[str, Any]:
442
- data = await self.get_file_async(WHALE_LEARNING_CONFIG_KEY)
443
- return json.loads(data) if data else {}
 
1
  # ==============================================================================
2
+ # ☁️ r2.py (V40.0 - GEM-Architect: Dev Environment Isolation)
3
  # ==============================================================================
4
 
5
  import os
 
21
  R2_SECRET_ACCESS_KEY = os.getenv("R2_SECRET_ACCESS_KEY")
22
  BUCKET_NAME = "trading"
23
 
24
+ # 🔴 بادئة التطوير (لعزل الملفات عن النظام الحي)
25
+ # سيتم إضافة هذه البادئة أمام كل اسم ملف يتم التعامل معه
26
+ FILE_PREFIX = "dev_"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # الرصيد الافتراضي
29
+ INITIAL_CAPITAL = 10.0
 
 
 
 
 
 
 
 
30
 
31
+ # ==============================================================================
32
+ # 📁 مفاتيح الملفات (مع البادئة الديناميكية)
33
+ # ==============================================================================
34
+ def get_key(filename):
35
+ """تضيف بادئة التطوير للمسارات لضمان عدم تداخل الملفات"""
36
+ # إذا كان المسار يحتوي على مجلدات (مثل datasets/) نضيف البادئة لاسم الملف نفسه
37
+ if "/" in filename:
38
+ folder, name = filename.rsplit("/", 1)
39
+ return f"{folder}/{FILE_PREFIX}{name}"
40
+ return f"{FILE_PREFIX}{filename}"
41
+
42
+ # تعريف المفاتيح باستخدام الدالة المساعدة
43
+ WHALE_LEARNING_PENDING_KEY = get_key("learning_whale_pending_records.json")
44
+ WHALE_LEARNING_COMPLETED_KEY = get_key("learning_whale_completed_records.json")
45
+ WHALE_LEARNING_CONFIG_KEY = get_key("learning_whale_optimal_config.json")
46
+
47
+ PORTFOLIO_STATE_KEY = get_key("portfolio_state.json")
48
+ SMART_PORTFOLIO_STATE_KEY = get_key("smart_portfolio_state.json")
49
+
50
+ OPEN_TRADES_KEY = get_key("open_trades.json")
51
+ CLOSED_TRADES_KEY = get_key("closed_trades_history.json")
52
+
53
+ CANDIDATES_KEY = get_key("Candidates.json")
54
+ CONTRACTS_DB_KEY = get_key("contracts.json") # قد نحتاج لنسخ هذا الملف يدوياً أو تركه مشتركاً إذا كان للقراءة فقط
55
+ SYSTEM_LOGS_KEY = get_key("system_logs.json")
56
+ LEARNING_DATA_KEY = get_key("learning_data.json")
57
+ ANALYSIS_AUDIT_KEY = get_key("analysis_audit_log.json")
58
+ DEEP_STEWARD_AUDIT_KEY = get_key("DeepSteward_Audit_Log.json")
59
+
60
+ GOVERNANCE_TRAINING_KEY = get_key("datasets/governance_training_data.json")
61
+ DIAGNOSTIC_STATS_KEY = get_key("analytics/model_diagnostic_matrix.json")
62
+ GUARDIAN_STATS_KEY = get_key("analytics/guardian_performance_stats.json")
63
+ STRATEGIC_DNA_KEY = get_key("learning/strategic_dna_v2.json")
64
+
65
+ # ملف القضية الجديد (The Case File)
66
+ AGENCY_CASE_FILE_KEY = get_key("agency_case_file.json")
67
 
68
  class R2Service:
69
  def __init__(self):
 
77
  )
78
  self.lock_acquired = False
79
  self.BUCKET_NAME = BUCKET_NAME
80
+ print(f"✅ [R2 V40.0] Service Loaded (DEV MODE: '{FILE_PREFIX}' prefix active).")
81
 
82
  except Exception as e:
83
  raise RuntimeError(f"Failed to initialize S3 client: {e}")
84
 
85
  # ==============================================================================
86
+ # 🔴 دالة التصفير الشامل (DEV MODE)
87
  # ==============================================================================
88
  async def reset_all_stats_async(self):
89
+ """تصفير بيئة التطوير فقط"""
90
  try:
91
+ print(f"🔄 [R2 DEV Reset] مسح بيانات البيئة '{FILE_PREFIX}'...")
92
 
93
  initial_state = {
94
  "current_capital_usd": INITIAL_CAPITAL,
95
  "invested_capital_usd": 0.0,
96
  "allocated_capital_usd": 0.0,
97
  "initial_capital_usd": INITIAL_CAPITAL,
98
+ "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
99
+ "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_rate": 0.0,
100
+ "daily_net_pnl": 0.0, "is_trading_halted": False,
 
 
 
 
 
101
  "last_update": datetime.now().isoformat()
102
  }
103
 
 
117
  empty_list_json = json.dumps([], indent=2).encode('utf-8')
118
  empty_logs_json = json.dumps({"logs": []}, indent=2).encode('utf-8')
119
 
120
+ # مسح الملفات الأساسية
121
  def _reset_sync():
122
+ self.s3_client.put_object(Bucket=BUCKET_NAME, Key=CLOSED_TRADES_KEY, Body=empty_list_json, ContentType="application/json")
123
+ self.s3_client.put_object(Bucket=BUCKET_NAME, Key=OPEN_TRADES_KEY, Body=empty_list_json, ContentType="application/json")
124
+ self.s3_client.put_object(Bucket=BUCKET_NAME, Key=SYSTEM_LOGS_KEY, Body=empty_logs_json, ContentType="application/json")
125
+
126
+ # إنشاء ملف القضية فارغ
127
+ case_file_init = {
128
+ "pulse": "ALIVE",
129
+ "mode": "DEV_OBSERVATION",
130
+ "active_issues": [],
131
+ "roadmap": "Monitor_and_Report",
132
+ "logs_buffer": []
133
+ }
134
+ self.s3_client.put_object(Bucket=BUCKET_NAME, Key=AGENCY_CASE_FILE_KEY, Body=json.dumps(case_file_init).encode('utf-8'), ContentType="application/json")
135
+
136
  await asyncio.to_thread(_reset_sync)
 
 
137
  await self.reset_diagnostic_stats_async()
138
  await self.reset_guardian_stats_async()
139
 
140
+ print(f"✅ [R2 DEV Reset] تم تصفير بيئة التطوير '{FILE_PREFIX}'.")
141
  return True
142
 
143
  except Exception as e:
144
  print(f"❌ [R2 Reset Error] فشل التصفير: {e}")
 
145
  return False
146
 
147
  # ==============================================================================
 
150
  async def upload_json_async(self, data: Any, key: str):
151
  try:
152
  json_bytes = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
153
+ await asyncio.to_thread(self.s3_client.put_object, Bucket=self.BUCKET_NAME, Key=key, Body=json_bytes, ContentType="application/json")
 
 
 
 
 
 
154
  except Exception as e:
155
  print(f"❌ [R2] Upload JSON failed for {key}: {e}")
156
  raise
157
 
158
  async def get_file_json_async(self, key: str) -> Optional[Any]:
 
159
  try:
160
  data = await self.get_file_async(key)
161
  return json.loads(data) if data else None
 
163
 
164
  async def get_file_async(self, key: str) -> Optional[bytes]:
165
  try:
166
+ response = await asyncio.to_thread(self.s3_client.get_object, Bucket=self.BUCKET_NAME, Key=key)
 
 
 
 
167
  return response['Body'].read()
168
  except ClientError as e:
169
+ if e.response['Error']['Code'] == 'NoSuchKey': return None
 
170
  raise
171
 
172
  async def upload_file_async(self, file_obj, key: str):
173
  try:
174
+ await asyncio.to_thread(self.s3_client.upload_fileobj, file_obj, self.BUCKET_NAME, key)
 
 
 
 
 
175
  except Exception as e:
176
  print(f"❌ [R2] File upload failed for {key}: {e}")
177
  raise
178
 
179
  # ==============================================================================
180
+ # 🕵️ ملف القضية (The Agency Link)
181
+ # ==============================================================================
182
+ async def get_agency_case_file_async(self):
183
+ """قراءة ملف القضية الذي يستخدمه الوكلاء"""
184
+ return await self.get_file_json_async(AGENCY_CASE_FILE_KEY)
185
+
186
+ async def update_agency_case_file_async(self, data):
187
+ """تحديث ملف القضية"""
188
+ await self.upload_json_async(data, AGENCY_CASE_FILE_KEY)
189
+
190
+ # ==============================================================================
191
+ # بقية الدوال (معتمدة على المفاتيح المعرفة بالأعلى)
192
  # ==============================================================================
193
+
194
+ # 📊 Diagnostic Stats
195
  async def get_diagnostic_stats_async(self) -> Dict[str, Any]:
196
+ data = await self.get_file_json_async(DIAGNOSTIC_STATS_KEY)
197
+ defaults = {k: {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0}
198
+ for k in ["Titan", "Patterns", "Oracle", "Sniper", "MonteCarlo_L", "MonteCarlo_A", "News", "Governance"]}
199
+ if not data: return defaults
200
+ for k, v in defaults.items():
201
+ if k not in data: data[k] = v
202
+ return data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
  async def update_diagnostic_stats_async(self, updates: Dict[str, Dict[str, float]]):
 
205
  try:
206
  current = await self.get_diagnostic_stats_async()
207
  for model, metrics in updates.items():
208
+ if model not in current: current[model] = {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0}
 
 
209
  current[model]['wins'] += metrics.get('wins', 0)
210
  current[model]['losses'] += metrics.get('losses', 0)
211
  current[model]['pnl'] += metrics.get('pnl', 0.0)
 
 
212
  current[model]['profit_accum'] += metrics.get('profit_accum', 0.0)
213
  current[model]['loss_accum'] += metrics.get('loss_accum', 0.0)
 
214
  await self.upload_json_async(current, DIAGNOSTIC_STATS_KEY)
215
+ except Exception as e: print(f"❌ [R2] Failed to update diagnostics: {e}")
 
 
 
 
 
216
 
217
  async def reset_diagnostic_stats_async(self):
 
218
  empty = {k: {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0} for k in
219
  ["Titan", "Patterns", "Oracle", "Sniper", "MonteCarlo_L", "MonteCarlo_A", "News", "Governance"]}
220
  await self.upload_json_async(empty, DIAGNOSTIC_STATS_KEY)
 
221
 
222
+ # 🛡️ Guardian Stats
223
  async def get_guardian_stats_async(self) -> Dict[str, Any]:
224
+ data = await self.get_file_json_async(GUARDIAN_STATS_KEY)
225
+ defaults = {k: {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0} for k in ["hybrid", "crash", "giveback", "stagnation"]}
226
+ if not data: return defaults
 
 
 
 
 
227
  for k, v in defaults.items():
228
+ if k not in data: data[k] = v
229
+ return data
230
 
231
+ async def save_guardian_stats_async(self, stats): await self.upload_json_async(stats, GUARDIAN_STATS_KEY)
 
 
232
  async def reset_guardian_stats_async(self):
233
+ defaults = {k: {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0} for k in ["hybrid", "crash", "giveback", "stagnation"]}
 
 
 
 
 
 
234
  await self.upload_json_async(defaults, GUARDIAN_STATS_KEY)
 
235
 
236
+ # 🏛️ Governance Data
237
+ async def append_governance_training_data(self, record):
 
 
238
  try:
239
+ data = await self.get_file_json_async(GOVERNANCE_TRAINING_KEY)
240
+ dataset = data if data else []
241
  dataset.append(record)
242
  if len(dataset) > 5000: dataset = dataset[-5000:]
243
  await self.upload_json_async(dataset, GOVERNANCE_TRAINING_KEY)
244
+ except: pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
+ # 💰 Portfolio
 
 
247
  async def get_portfolio_state_async(self):
248
+ data = await self.get_file_json_async(PORTFOLIO_STATE_KEY)
249
+ if data: return data
250
+ initial = {
251
+ "current_capital_usd": INITIAL_CAPITAL, "allocated_capital_usd": 0.0,
252
+ "invested_capital_usd": 0.0, "initial_capital_usd": INITIAL_CAPITAL,
253
+ "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
254
+ "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_rate": 0.0,
255
+ "daily_net_pnl": 0.0, "is_trading_halted": False,
256
+ "last_update": datetime.now().isoformat()
257
+ }
258
+ await self.save_portfolio_state_async(initial)
259
+ return initial
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
  async def save_portfolio_state_async(self, state):
262
  state['last_update'] = datetime.now().isoformat()
263
  await self.upload_json_async(state, PORTFOLIO_STATE_KEY)
264
 
265
+ # 📈 Trades
 
 
266
  async def get_open_trades_async(self):
267
+ data = await self.get_file_json_async(OPEN_TRADES_KEY)
268
+ return data if data else []
 
 
269
 
270
+ async def save_open_trades_async(self, trades): await self.upload_json_async(trades, OPEN_TRADES_KEY)
 
271
 
272
+ async def append_to_closed_trades_history(self, trade_data):
273
  try:
274
+ data = await self.get_file_json_async(CLOSED_TRADES_KEY)
275
+ history = data if data else []
276
  history.append(trade_data)
277
  if len(history) > 1000: history = history[-1000:]
278
  await self.upload_json_async(history, CLOSED_TRADES_KEY)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  except: pass
280
 
281
+ # 📜 Contracts & Audit
282
+ async def load_contracts_db_async(self):
283
+ data = await self.get_file_json_async(CONTRACTS_DB_KEY)
284
+ return data if data else {}
285
 
286
+ async def append_deep_steward_audit(self, record):
287
  try:
288
+ data = await self.get_file_json_async(DEEP_STEWARD_AUDIT_KEY)
289
+ history = data if data else []
290
+ history.append(record)
291
+ if len(history) > 2000: history = history[-2000:]
292
+ await self.upload_json_async(history, DEEP_STEWARD_AUDIT_KEY)
 
 
 
 
 
 
 
 
293
  except: pass
294
 
295
+ # 🧬 Strategy DNA
296
+ async def load_strategy_dna_async(self):
297
+ data = await self.get_file_json_async(STRATEGIC_DNA_KEY)
298
+ return data if data else {}
299
+
300
+ async def save_strategy_dna_async(self, dna): await self.upload_json_async(dna, STRATEGIC_DNA_KEY)