Riy777 commited on
Commit
42bad9f
·
verified ·
1 Parent(s): 9c38b0c

Update r2.py

Browse files
Files changed (1) hide show
  1. r2.py +2 -341
r2.py CHANGED
@@ -1,14 +1,9 @@
1
- # r2 (23).py - الإصلاحات
2
- import os
3
- import traceback
4
- import json
5
  from datetime import datetime, timedelta
6
  import asyncio
7
- import time
8
  import boto3
9
  from botocore.exceptions import NoCredentialsError, ClientError
10
 
11
- # --- R2 Service Configuration ---
12
  R2_ACCOUNT_ID = os.getenv("R2_ACCOUNT_ID")
13
  R2_ACCESS_KEY_ID = os.getenv("R2_ACCESS_KEY_ID")
14
  R2_SECRET_ACCESS_KEY = os.getenv("R2_SECRET_ACCESS_KEY")
@@ -36,9 +31,7 @@ class R2Service:
36
  raise RuntimeError(f"Failed to initialize S3 client: {e}")
37
 
38
  def acquire_lock(self, max_retries=3):
39
- """Acquires a lock file in R2 with retry logic."""
40
  lock_path = "lock.txt"
41
-
42
  for attempt in range(max_retries):
43
  try:
44
  try:
@@ -56,12 +49,10 @@ class R2Service:
56
  except Exception as e:
57
  print(f"❌ Failed to acquire lock: {e}")
58
  time.sleep(1)
59
-
60
  print(f"❌ Failed to acquire lock after {max_retries} attempts.")
61
  return False
62
 
63
  def release_lock(self):
64
- """Releases the lock file from R2."""
65
  lock_path = "lock.txt"
66
  if self.lock_acquired:
67
  try:
@@ -71,98 +62,7 @@ class R2Service:
71
  except Exception as e:
72
  print(f"❌ Failed to release lock: {e}")
73
 
74
- async def close_trade_async(self, trade_to_close, close_price):
75
- """Closes a trade, archives it, updates summary, and updates portfolio capital."""
76
- try:
77
- trade_to_close['status'] = 'CLOSED'
78
- trade_to_close['close_price'] = close_price
79
- trade_to_close['close_timestamp'] = datetime.now().isoformat()
80
- trade_to_close['is_monitored'] = False
81
-
82
- entry_price = trade_to_close['entry_price']
83
- position_size = trade_to_close['position_size_usd']
84
- trade_type = trade_to_close.get('trade_type', 'LONG')
85
- strategy = trade_to_close.get('strategy', 'unknown')
86
-
87
- # ✅ الإصلاح: حساب PnL بشكل صحيح
88
- pnl = 0.0
89
- pnl_percent = 0.0
90
-
91
- if entry_price and entry_price > 0 and close_price and close_price > 0:
92
- try:
93
- if trade_type == 'LONG':
94
- pnl_percent = ((close_price - entry_price) / entry_price) * 100
95
- pnl = position_size * (pnl_percent / 100)
96
- elif trade_type == 'SHORT':
97
- pnl_percent = ((entry_price - close_price) / entry_price) * 100
98
- pnl = position_size * (pnl_percent / 100)
99
-
100
- print(f"💰 PnL Calculation: Entry=${entry_price:.6f}, Close=${close_price:.6f}, "
101
- f"Position=${position_size:.2f}, Type={trade_type}, "
102
- f"PnL=${pnl:.4f} ({pnl_percent:+.4f}%)")
103
-
104
- except (TypeError, ZeroDivisionError) as calc_error:
105
- print(f"⚠️ PnL calculation error: {calc_error}")
106
- pnl = 0.0
107
- pnl_percent = 0.0
108
- else:
109
- print(f"⚠️ Invalid prices for PnL calculation: Entry={entry_price}, Close={close_price}")
110
-
111
- trade_to_close['pnl_usd'] = pnl
112
- trade_to_close['pnl_percent'] = pnl_percent
113
-
114
- await self._archive_closed_trade_async(trade_to_close)
115
- await self._update_trade_summary_async(trade_to_close)
116
-
117
- # ✅ الإصلاح: تحديث رأس المال بشكل صحيح
118
- portfolio_state = await self.get_portfolio_state_async()
119
- current_capital = portfolio_state.get("current_capital_usd", 0)
120
- invested_capital = portfolio_state.get("invested_capital_usd", 0)
121
-
122
- # حساب رأس المال الجديد
123
- new_capital = current_capital + position_size + pnl
124
-
125
- portfolio_state["current_capital_usd"] = new_capital
126
- portfolio_state["invested_capital_usd"] = 0.0
127
-
128
- if pnl > 0:
129
- portfolio_state["winning_trades"] = portfolio_state.get("winning_trades", 0) + 1
130
- portfolio_state["total_profit_usd"] = portfolio_state.get("total_profit_usd", 0.0) + pnl
131
- elif pnl < 0:
132
- portfolio_state["total_loss_usd"] = portfolio_state.get("total_loss_usd", 0.0) + abs(pnl)
133
-
134
- await self.save_portfolio_state_async(portfolio_state)
135
-
136
- print(f"📈 Trade PnL: ${pnl:.4f} ({pnl_percent:+.4f}%). "
137
- f"New available capital: ${new_capital:.4f}. Strategy: {strategy}")
138
-
139
- open_trades = await self.get_open_trades_async()
140
- trades_to_keep = [t for t in open_trades if t.get('id') != trade_to_close.get('id')]
141
- await self.save_open_trades_async(trades_to_keep)
142
-
143
- print(f"✅ Trade for {trade_to_close.get('symbol')} closed and archived successfully. Strategy: {strategy}")
144
-
145
- await self.save_system_logs_async({
146
- "trade_closed": True,
147
- "symbol": trade_to_close.get('symbol'),
148
- "entry_price": entry_price,
149
- "close_price": close_price,
150
- "pnl_usd": pnl,
151
- "pnl_percent": pnl_percent,
152
- "new_capital": new_capital,
153
- "strategy": strategy,
154
- "position_size": position_size,
155
- "trade_type": trade_type
156
- })
157
-
158
- except Exception as e:
159
- print(f"❌ Failed to close trade: {e}")
160
- traceback.print_exc()
161
- raise
162
-
163
- # باقي الدوال تبقى كما هي بدون تغيير
164
  async def save_candidates_data_async(self, candidates_data, reanalysis_data):
165
- """حفظ بيانات المرشحين العشرة وبيانات إعادة التحليل"""
166
  try:
167
  key = "candidates_data.json"
168
  data = {
@@ -170,7 +70,6 @@ class R2Service:
170
  "top_candidates": candidates_data,
171
  "reanalysis_data": reanalysis_data
172
  }
173
-
174
  data_json = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
175
  self.s3_client.put_object(
176
  Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
@@ -180,7 +79,6 @@ class R2Service:
180
  print(f"❌ Failed to save candidates data: {e}")
181
 
182
  async def save_llm_responses_async(self, symbol, prompt, full_response, parsed_decision):
183
- """حفظ إجابات النموذج الكاملة"""
184
  try:
185
  key = "llm_responses.json"
186
  try:
@@ -201,7 +99,6 @@ class R2Service:
201
  }
202
 
203
  existing_data["responses"].append(new_response)
204
-
205
  if len(existing_data["responses"]) > 1000:
206
  existing_data["responses"] = existing_data["responses"][-1000:]
207
 
@@ -214,7 +111,6 @@ class R2Service:
214
  print(f"❌ Failed to save LLM response: {e}")
215
 
216
  async def save_system_logs_async(self, log_data):
217
- """حفظ سجلات النظام"""
218
  try:
219
  key = "system_logs.json"
220
  try:
@@ -232,7 +128,6 @@ class R2Service:
232
  }
233
 
234
  existing_logs["logs"].append(log_entry)
235
-
236
  if len(existing_logs["logs"]) > 2000:
237
  existing_logs["logs"] = existing_logs["logs"][-2000:]
238
 
@@ -245,14 +140,12 @@ class R2Service:
245
  print(f"❌ Failed to save system logs: {e}")
246
 
247
  async def save_learning_data_async(self, learning_data):
248
- """حفظ بيانات التعلم"""
249
  try:
250
  key = "learning_data.json"
251
  data = {
252
  "timestamp": datetime.now().isoformat(),
253
  "learning_data": learning_data
254
  }
255
-
256
  data_json = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
257
  self.s3_client.put_object(
258
  Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
@@ -262,7 +155,6 @@ class R2Service:
262
  print(f"❌ Failed to save learning data: {e}")
263
 
264
  async def load_learning_data_async(self):
265
- """تحميل بيانات التعلم"""
266
  try:
267
  key = "learning_data.json"
268
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
@@ -277,15 +169,12 @@ class R2Service:
277
  raise
278
 
279
  async def get_portfolio_state_async(self):
280
- """Fetches the current portfolio state from R2, or initializes it."""
281
  key = "portfolio_state.json"
282
  try:
283
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
284
  state = json.loads(response['Body'].read())
285
-
286
  if hasattr(self, '_portfolio_warning_printed'):
287
  delattr(self, '_portfolio_warning_printed')
288
-
289
  print(f"💰 Portfolio state loaded: Current Capital ${state.get('current_capital_usd', 0):.2f}")
290
  return state
291
  except ClientError as e:
@@ -293,7 +182,6 @@ class R2Service:
293
  if not hasattr(self, '_portfolio_warning_printed'):
294
  print(f"⚠️ No portfolio state file found. Initializing with ${INITIAL_CAPITAL:.2f}")
295
  self._portfolio_warning_printed = True
296
-
297
  initial_state = {
298
  "current_capital_usd": INITIAL_CAPITAL,
299
  "invested_capital_usd": 0.0,
@@ -309,7 +197,6 @@ class R2Service:
309
  raise
310
 
311
  async def save_portfolio_state_async(self, state):
312
- """Saves the portfolio state to R2."""
313
  key = "portfolio_state.json"
314
  try:
315
  data_json = json.dumps(state, indent=2).encode('utf-8')
@@ -322,14 +209,11 @@ class R2Service:
322
  raise
323
 
324
  async def get_open_trades_async(self):
325
- """Fetches all open trades from R2 with reduced noise."""
326
  try:
327
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key="open_trades.json")
328
  trades = json.loads(response['Body'].read())
329
-
330
  if hasattr(self, '_open_trades_warning_printed'):
331
  delattr(self, '_open_trades_warning_printed')
332
-
333
  return trades
334
  except ClientError as e:
335
  if e.response['Error']['Code'] == 'NoSuchKey':
@@ -342,7 +226,6 @@ class R2Service:
342
  raise
343
 
344
  async def save_open_trades_async(self, trades):
345
- """Saves the list of open trades to R2."""
346
  try:
347
  data_json = json.dumps(trades, indent=2).encode('utf-8')
348
  self.s3_client.put_object(
@@ -353,227 +236,13 @@ class R2Service:
353
  print(f"❌ Failed to save open trades: {e}")
354
  raise
355
 
356
- async def save_new_trade_async(self, symbol, decision, current_price):
357
- """Creates a new trade using the full available capital and saves it."""
358
- try:
359
- portfolio_state = await self.get_portfolio_state_async()
360
- available_capital = portfolio_state.get("current_capital_usd", 0)
361
-
362
- if available_capital < 1:
363
- print(f"❌ Insufficient capital (${available_capital:.2f}) to open a new trade.")
364
- return
365
-
366
- expected_target_minutes = decision.get('expected_target_minutes', 15)
367
- expected_target_minutes = max(5, min(expected_target_minutes, 45))
368
- expected_target_time = (datetime.now() + timedelta(minutes=expected_target_minutes)).isoformat()
369
-
370
- # ✅ الإصلاح النهائي: التأكد من وجود استراتيجية صالحة
371
- strategy = decision.get('strategy')
372
- if not strategy or strategy == 'unknown':
373
- strategy = 'GENERIC'
374
- print(f"⚠️ Strategy was missing or unknown. Setting to GENERIC for {symbol}")
375
-
376
- trades = await self.get_open_trades_async()
377
- new_trade = {
378
- "id": str(int(time.time())),
379
- "symbol": symbol,
380
- "entry_price": current_price,
381
- "entry_timestamp": datetime.now().isoformat(),
382
- "decision_data": decision,
383
- "status": "OPEN",
384
- "stop_loss": decision.get("stop_loss"),
385
- "take_profit": decision.get("take_profit"),
386
- "trade_type": decision.get("trade_type"),
387
- "position_size_usd": available_capital,
388
- "expected_target_minutes": expected_target_minutes,
389
- "expected_target_time": expected_target_time,
390
- "is_monitored": True,
391
- "strategy": strategy # ✅ استخدام الاستراتيجية المؤكدة
392
- }
393
- trades.append(new_trade)
394
- await self.save_open_trades_async(trades)
395
-
396
- original_expected = decision.get('expected_target_minutes', 15)
397
- if original_expected > 45:
398
- print(f"⚠️ LLM wanted {original_expected} minutes, CAPPED to 45 minutes for strategy consistency")
399
-
400
- print(f"✅ New trade for {symbol} saved with position size ${available_capital:.2f}. Strategy: {strategy}. Expected results in {expected_target_minutes} minutes.")
401
-
402
- portfolio_state["invested_capital_usd"] = available_capital
403
- portfolio_state["current_capital_usd"] = 0.0
404
- portfolio_state["total_trades"] = portfolio_state.get("total_trades", 0) + 1
405
- await self.save_portfolio_state_async(portfolio_state)
406
-
407
- await self.save_system_logs_async({
408
- "new_trade_opened": True,
409
- "symbol": symbol,
410
- "position_size": available_capital,
411
- "expected_minutes": expected_target_minutes,
412
- "trade_type": decision.get("trade_type", "LONG"),
413
- "strategy": strategy # ✅ استخدام الاستراتيجية المؤكدة
414
- })
415
-
416
- except Exception as e:
417
- print(f"❌ Failed to save new trade: {e}")
418
- raise
419
-
420
- async def update_trade_async(self, trade_to_update, re_analysis_decision):
421
- """Updates an existing trade with new parameters from re-analysis."""
422
- try:
423
- if re_analysis_decision.get('new_stop_loss'):
424
- trade_to_update['stop_loss'] = re_analysis_decision['new_stop_loss']
425
- if re_analysis_decision.get('new_take_profit'):
426
- trade_to_update['take_profit'] = re_analysis_decision['new_take_profit']
427
-
428
- new_expected_minutes = re_analysis_decision.get('new_expected_minutes')
429
- if new_expected_minutes:
430
- new_expected_minutes = max(5, min(new_expected_minutes, 45))
431
- trade_to_update['expected_target_minutes'] = new_expected_minutes
432
- trade_to_update['expected_target_time'] = (datetime.now() + timedelta(minutes=new_expected_minutes)).isoformat()
433
- print(f"⏰ Trade time expectation updated to {new_expected_minutes} minutes.")
434
-
435
- # ✅ الإصلاح: الحفاظ على الاستراتيجية الأصلية
436
- original_strategy = trade_to_update.get('strategy')
437
- if not original_strategy or original_strategy == 'unknown':
438
- original_strategy = re_analysis_decision.get('strategy', 'GENERIC')
439
-
440
- trade_to_update['strategy'] = original_strategy
441
- trade_to_update['decision_data'] = re_analysis_decision
442
- trade_to_update['is_monitored'] = True
443
-
444
- open_trades = await self.get_open_trades_async()
445
- for i, trade in enumerate(open_trades):
446
- if trade.get('id') == trade_to_update.get('id'):
447
- open_trades[i] = trade_to_update
448
- break
449
-
450
- await self.save_open_trades_async(open_trades)
451
- print(f"✅ Trade for {trade_to_update.get('symbol')} updated successfully. Strategy: {original_strategy}")
452
-
453
- await self.save_system_logs_async({
454
- "trade_updated": True,
455
- "symbol": trade_to_update.get('symbol'),
456
- "new_expected_minutes": new_expected_minutes,
457
- "action": "UPDATE_TRADE",
458
- "strategy": original_strategy
459
- })
460
-
461
- except Exception as e:
462
- print(f"❌ Failed to update trade: {e}")
463
- raise
464
-
465
- async def immediate_close_trade_async(self, symbol, close_price, reason="Real-time monitoring"):
466
- """Closes a trade immediately without full re-analysis."""
467
- try:
468
- open_trades = await self.get_open_trades_async()
469
- trade_to_close = None
470
-
471
- for trade in open_trades:
472
- if trade['symbol'] == symbol and trade['status'] == 'OPEN':
473
- trade_to_close = trade
474
- break
475
-
476
- if not trade_to_close:
477
- print(f"❌ No open trade found for {symbol}")
478
- return False
479
-
480
- await self.close_trade_async(trade_to_close, close_price)
481
- print(f"🚨 IMMEDIATE CLOSE: {symbol} at {close_price} - {reason}")
482
-
483
- await self.save_system_logs_async({
484
- "immediate_close": True,
485
- "symbol": symbol,
486
- "close_price": close_price,
487
- "reason": reason,
488
- "strategy": trade_to_close.get('strategy', 'unknown')
489
- })
490
-
491
- return True
492
-
493
- except Exception as e:
494
- print(f"❌ Failed to immediately close trade {symbol}: {e}")
495
- return False
496
-
497
- async def _archive_closed_trade_async(self, closed_trade):
498
- """Appends a closed trade to the history file."""
499
- key = "closed_trades_history.json"
500
- try:
501
- response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
502
- history = json.loads(response['Body'].read())
503
- except ClientError as e:
504
- if e.response['Error']['Code'] == 'NoSuchKey':
505
- history = []
506
- else:
507
- raise
508
-
509
- history.append(closed_trade)
510
-
511
- data_json = json.dumps(history, indent=2).encode('utf-8')
512
- self.s3_client.put_object(
513
- Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
514
- )
515
- print(f"📚 Trade archived. Total archived trades: {len(history)}")
516
-
517
- async def _update_trade_summary_async(self, closed_trade):
518
- """Updates the trade summary statistics file."""
519
- key = "trade_summary.json"
520
- try:
521
- try:
522
- response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
523
- summary = json.loads(response['Body'].read())
524
- except ClientError as e:
525
- if e.response['Error']['Code'] == 'NoSuchKey':
526
- summary = {
527
- "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
528
- "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_percentage": 0.0,
529
- "avg_profit_per_trade": 0.0, "avg_loss_per_trade": 0.0,
530
- "largest_win": 0.0, "largest_loss": 0.0
531
- }
532
- else:
533
- raise
534
-
535
- pnl = closed_trade.get('pnl_usd', 0.0)
536
-
537
- summary['total_trades'] += 1
538
- if pnl >= 0:
539
- summary['winning_trades'] += 1
540
- summary['total_profit_usd'] += pnl
541
- if pnl > summary.get('largest_win', 0):
542
- summary['largest_win'] = pnl
543
- else:
544
- summary['losing_trades'] += 1
545
- summary['total_loss_usd'] += abs(pnl)
546
- if abs(pnl) > summary.get('largest_loss', 0):
547
- summary['largest_loss'] = abs(pnl)
548
-
549
- if summary['total_trades'] > 0:
550
- summary['win_percentage'] = (summary['winning_trades'] / summary['total_trades']) * 100
551
-
552
- if summary['winning_trades'] > 0:
553
- summary['avg_profit_per_trade'] = summary['total_profit_usd'] / summary['winning_trades']
554
- if summary['losing_trades'] > 0:
555
- summary['avg_loss_per_trade'] = summary['total_loss_usd'] / summary['losing_trades']
556
-
557
- data_json = json.dumps(summary, indent=2).encode('utf-8')
558
- self.s3_client.put_object(
559
- Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
560
- )
561
- print(f"📊 Trade summary updated. Win Rate: {summary['win_percentage']:.2f}%")
562
-
563
- except Exception as e:
564
- print(f"❌ Failed to update trade summary: {e}")
565
- raise
566
-
567
  async def load_contracts_db_async(self):
568
- """Loads the contracts database from R2 with reduced noise."""
569
  key = "contracts.json"
570
  try:
571
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
572
  contracts_db = json.loads(response['Body'].read())
573
-
574
  if hasattr(self, '_contracts_warning_printed'):
575
  delattr(self, '_contracts_warning_printed')
576
-
577
  print(f"💾 Contracts database loaded from R2. Total entries: {len(contracts_db)}")
578
  return contracts_db
579
  except ClientError as e:
@@ -586,7 +255,6 @@ class R2Service:
586
  raise
587
 
588
  async def save_contracts_db_async(self, data):
589
- """Saves the contracts database to R2."""
590
  key = "contracts.json"
591
  try:
592
  data_json = json.dumps(data, indent=2).encode('utf-8')
@@ -599,7 +267,6 @@ class R2Service:
599
  raise
600
 
601
  async def get_trade_by_symbol_async(self, symbol):
602
- """Fetches a specific trade by symbol."""
603
  try:
604
  open_trades = await self.get_open_trades_async()
605
  for trade in open_trades:
@@ -611,32 +278,26 @@ class R2Service:
611
  return None
612
 
613
  async def update_trade_monitoring_status_async(self, symbol, is_monitored):
614
- """Updates the monitoring status of a trade."""
615
  try:
616
  open_trades = await self.get_open_trades_async()
617
  updated = False
618
-
619
  for trade in open_trades:
620
  if trade['symbol'] == symbol:
621
  trade['is_monitored'] = is_monitored
622
  updated = True
623
  break
624
-
625
  if updated:
626
  await self.save_open_trades_async(open_trades)
627
  status = "ENABLED" if is_monitored else "DISABLED"
628
  print(f"✅ Real-time monitoring {status} for {symbol}")
629
  else:
630
  print(f"⚠️ Trade {symbol} not found for monitoring status update")
631
-
632
  return updated
633
-
634
  except Exception as e:
635
  print(f"❌ Failed to update monitoring status for {symbol}: {e}")
636
  return False
637
 
638
  async def get_monitored_trades_async(self):
639
- """Fetches all trades that are currently being monitored."""
640
  try:
641
  open_trades = await self.get_open_trades_async()
642
  monitored_trades = [trade for trade in open_trades if trade.get('is_monitored', False)]
@@ -645,4 +306,4 @@ class R2Service:
645
  print(f"❌ Failed to get monitored trades: {e}")
646
  return []
647
 
648
- print("✅ Enhanced R2 Service Loaded - Comprehensive Logging System with Learning Support")
 
1
+ import os, traceback, json, time
 
 
 
2
  from datetime import datetime, timedelta
3
  import asyncio
 
4
  import boto3
5
  from botocore.exceptions import NoCredentialsError, ClientError
6
 
 
7
  R2_ACCOUNT_ID = os.getenv("R2_ACCOUNT_ID")
8
  R2_ACCESS_KEY_ID = os.getenv("R2_ACCESS_KEY_ID")
9
  R2_SECRET_ACCESS_KEY = os.getenv("R2_SECRET_ACCESS_KEY")
 
31
  raise RuntimeError(f"Failed to initialize S3 client: {e}")
32
 
33
  def acquire_lock(self, max_retries=3):
 
34
  lock_path = "lock.txt"
 
35
  for attempt in range(max_retries):
36
  try:
37
  try:
 
49
  except Exception as e:
50
  print(f"❌ Failed to acquire lock: {e}")
51
  time.sleep(1)
 
52
  print(f"❌ Failed to acquire lock after {max_retries} attempts.")
53
  return False
54
 
55
  def release_lock(self):
 
56
  lock_path = "lock.txt"
57
  if self.lock_acquired:
58
  try:
 
62
  except Exception as e:
63
  print(f"❌ Failed to release lock: {e}")
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  async def save_candidates_data_async(self, candidates_data, reanalysis_data):
 
66
  try:
67
  key = "candidates_data.json"
68
  data = {
 
70
  "top_candidates": candidates_data,
71
  "reanalysis_data": reanalysis_data
72
  }
 
73
  data_json = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
74
  self.s3_client.put_object(
75
  Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
 
79
  print(f"❌ Failed to save candidates data: {e}")
80
 
81
  async def save_llm_responses_async(self, symbol, prompt, full_response, parsed_decision):
 
82
  try:
83
  key = "llm_responses.json"
84
  try:
 
99
  }
100
 
101
  existing_data["responses"].append(new_response)
 
102
  if len(existing_data["responses"]) > 1000:
103
  existing_data["responses"] = existing_data["responses"][-1000:]
104
 
 
111
  print(f"❌ Failed to save LLM response: {e}")
112
 
113
  async def save_system_logs_async(self, log_data):
 
114
  try:
115
  key = "system_logs.json"
116
  try:
 
128
  }
129
 
130
  existing_logs["logs"].append(log_entry)
 
131
  if len(existing_logs["logs"]) > 2000:
132
  existing_logs["logs"] = existing_logs["logs"][-2000:]
133
 
 
140
  print(f"❌ Failed to save system logs: {e}")
141
 
142
  async def save_learning_data_async(self, learning_data):
 
143
  try:
144
  key = "learning_data.json"
145
  data = {
146
  "timestamp": datetime.now().isoformat(),
147
  "learning_data": learning_data
148
  }
 
149
  data_json = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
150
  self.s3_client.put_object(
151
  Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
 
155
  print(f"❌ Failed to save learning data: {e}")
156
 
157
  async def load_learning_data_async(self):
 
158
  try:
159
  key = "learning_data.json"
160
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
 
169
  raise
170
 
171
  async def get_portfolio_state_async(self):
 
172
  key = "portfolio_state.json"
173
  try:
174
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
175
  state = json.loads(response['Body'].read())
 
176
  if hasattr(self, '_portfolio_warning_printed'):
177
  delattr(self, '_portfolio_warning_printed')
 
178
  print(f"💰 Portfolio state loaded: Current Capital ${state.get('current_capital_usd', 0):.2f}")
179
  return state
180
  except ClientError as e:
 
182
  if not hasattr(self, '_portfolio_warning_printed'):
183
  print(f"⚠️ No portfolio state file found. Initializing with ${INITIAL_CAPITAL:.2f}")
184
  self._portfolio_warning_printed = True
 
185
  initial_state = {
186
  "current_capital_usd": INITIAL_CAPITAL,
187
  "invested_capital_usd": 0.0,
 
197
  raise
198
 
199
  async def save_portfolio_state_async(self, state):
 
200
  key = "portfolio_state.json"
201
  try:
202
  data_json = json.dumps(state, indent=2).encode('utf-8')
 
209
  raise
210
 
211
  async def get_open_trades_async(self):
 
212
  try:
213
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key="open_trades.json")
214
  trades = json.loads(response['Body'].read())
 
215
  if hasattr(self, '_open_trades_warning_printed'):
216
  delattr(self, '_open_trades_warning_printed')
 
217
  return trades
218
  except ClientError as e:
219
  if e.response['Error']['Code'] == 'NoSuchKey':
 
226
  raise
227
 
228
  async def save_open_trades_async(self, trades):
 
229
  try:
230
  data_json = json.dumps(trades, indent=2).encode('utf-8')
231
  self.s3_client.put_object(
 
236
  print(f"❌ Failed to save open trades: {e}")
237
  raise
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  async def load_contracts_db_async(self):
 
240
  key = "contracts.json"
241
  try:
242
  response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
243
  contracts_db = json.loads(response['Body'].read())
 
244
  if hasattr(self, '_contracts_warning_printed'):
245
  delattr(self, '_contracts_warning_printed')
 
246
  print(f"💾 Contracts database loaded from R2. Total entries: {len(contracts_db)}")
247
  return contracts_db
248
  except ClientError as e:
 
255
  raise
256
 
257
  async def save_contracts_db_async(self, data):
 
258
  key = "contracts.json"
259
  try:
260
  data_json = json.dumps(data, indent=2).encode('utf-8')
 
267
  raise
268
 
269
  async def get_trade_by_symbol_async(self, symbol):
 
270
  try:
271
  open_trades = await self.get_open_trades_async()
272
  for trade in open_trades:
 
278
  return None
279
 
280
  async def update_trade_monitoring_status_async(self, symbol, is_monitored):
 
281
  try:
282
  open_trades = await self.get_open_trades_async()
283
  updated = False
 
284
  for trade in open_trades:
285
  if trade['symbol'] == symbol:
286
  trade['is_monitored'] = is_monitored
287
  updated = True
288
  break
 
289
  if updated:
290
  await self.save_open_trades_async(open_trades)
291
  status = "ENABLED" if is_monitored else "DISABLED"
292
  print(f"✅ Real-time monitoring {status} for {symbol}")
293
  else:
294
  print(f"⚠️ Trade {symbol} not found for monitoring status update")
 
295
  return updated
 
296
  except Exception as e:
297
  print(f"❌ Failed to update monitoring status for {symbol}: {e}")
298
  return False
299
 
300
  async def get_monitored_trades_async(self):
 
301
  try:
302
  open_trades = await self.get_open_trades_async()
303
  monitored_trades = [trade for trade in open_trades if trade.get('is_monitored', False)]
 
306
  print(f"❌ Failed to get monitored trades: {e}")
307
  return []
308
 
309
+ print("✅ Enhanced R2 Service Loaded - Comprehensive Logging System")