Spaces:
Paused
Paused
Update trade_manager.py
Browse files- trade_manager.py +13 -31
trade_manager.py
CHANGED
|
@@ -1,10 +1,5 @@
|
|
| 1 |
# ============================================================
|
| 2 |
-
# 🛡️ trade_manager.py (V35.
|
| 3 |
-
# ============================================================
|
| 4 |
-
# التغييرات الجوهرية:
|
| 5 |
-
# 1. تم دمج SmartPortfolio كمدير حصري للمخاطر ورأس المال.
|
| 6 |
-
# 2. إزالة القيود اليدوية لعدد الصفقات والاعتماد على منطق المحفظة.
|
| 7 |
-
# 3. استخدام الثقة المركبة (Consensus) والمزاج العام في اتخاذ القرار.
|
| 8 |
# ============================================================
|
| 9 |
|
| 10 |
import asyncio
|
|
@@ -50,7 +45,7 @@ class TradeManager:
|
|
| 50 |
}
|
| 51 |
|
| 52 |
self.execution_lock = asyncio.Lock()
|
| 53 |
-
print(f"🛡️ [TradeManager V35.
|
| 54 |
|
| 55 |
async def initialize_sentry_exchanges(self):
|
| 56 |
print("🛡️ [TradeManager] Syncing state & Initializing Portfolio...")
|
|
@@ -65,9 +60,7 @@ class TradeManager:
|
|
| 65 |
print(f" -> [Sync] Recovered {len(self.open_positions)} active trades.")
|
| 66 |
|
| 67 |
# 🔄 مزامنة رأس المال المحجوز مع المحفظة بناءً على الصفقات المستعادة
|
| 68 |
-
# (خطوة احترازية لضمان دقة allocated_capital في حالة إعادة التشغيل)
|
| 69 |
total_allocated = sum(float(t.get('entry_capital', 0.0)) for t in self.open_positions.values())
|
| 70 |
-
# نحدث الحالة المحلية للمحفظة فقط (دون حفظ لتجنب الكتابة فوق القيمة الدقيقة إذا كانت موجودة)
|
| 71 |
self.smart_portfolio.state["allocated_capital_usd"] = total_allocated
|
| 72 |
|
| 73 |
except Exception as e:
|
|
@@ -108,9 +101,6 @@ class TradeManager:
|
|
| 108 |
# 🎯 L4 Sniper Execution Logic
|
| 109 |
# ==============================================================================
|
| 110 |
async def select_and_execute_best_signal(self, oracle_approved_signals: List[Dict[str, Any]]):
|
| 111 |
-
# ❌ حذفنا القيد اليدوي (if len > 0)
|
| 112 |
-
# ✅ الاعتماد الآن كلياً على smart_portfolio.request_entry_approval
|
| 113 |
-
|
| 114 |
if not self.processor.initialized:
|
| 115 |
await self.processor.initialize()
|
| 116 |
|
|
@@ -168,7 +158,6 @@ class TradeManager:
|
|
| 168 |
best_signal = sniper_candidates[0]
|
| 169 |
|
| 170 |
async with self.execution_lock:
|
| 171 |
-
# لم نعد نتحقق من عدد الصفقات هنا، نترك الأمر للمحفظة عند التنفيذ
|
| 172 |
print(f"🚀 [EXECUTING] Attempting entry for best candidate: {best_signal['symbol']}")
|
| 173 |
await self._execute_entry_from_signal(best_signal['symbol'], best_signal)
|
| 174 |
|
|
@@ -178,7 +167,6 @@ class TradeManager:
|
|
| 178 |
async def _execute_entry_from_signal(self, symbol, signal_data):
|
| 179 |
try:
|
| 180 |
# 1. 💼 طلب الموافقة من المحفظة الذكية
|
| 181 |
-
# نمرر عدد الصفقات المفتوحة حالياً لتطبيق قيود الـ Slots
|
| 182 |
is_approved, plan = await self.smart_portfolio.request_entry_approval(
|
| 183 |
signal_data,
|
| 184 |
open_positions_count=len(self.open_positions)
|
|
@@ -220,7 +208,7 @@ class TradeManager:
|
|
| 220 |
'direction': 'LONG',
|
| 221 |
'entry_time': datetime.now().isoformat(),
|
| 222 |
'status': 'OPEN',
|
| 223 |
-
'tp_price': approved_tp,
|
| 224 |
'sl_price': sl_price,
|
| 225 |
'last_update': datetime.now().isoformat(),
|
| 226 |
'last_oracle_check': datetime.now().isoformat(),
|
|
@@ -230,15 +218,15 @@ class TradeManager:
|
|
| 230 |
'initial_oracle_class': oracle_class,
|
| 231 |
'oracle_tp_map': signal_data.get('tp_map', {}),
|
| 232 |
|
| 233 |
-
'entry_capital': approved_size_usd,
|
| 234 |
'entry_fee_usd': entry_fee_usd,
|
| 235 |
'l1_score': float(signal_data.get('enhanced_final_score', 0.0)),
|
| 236 |
'target_class_int': target_class_int,
|
| 237 |
'decision_data': {
|
| 238 |
'components': signal_data.get('components', {}),
|
| 239 |
'oracle_conf': signal_data.get('confidence', 0),
|
| 240 |
-
'system_confidence': system_conf,
|
| 241 |
-
'market_mood': market_mood
|
| 242 |
},
|
| 243 |
'highest_price': current_price
|
| 244 |
}
|
|
@@ -249,7 +237,6 @@ class TradeManager:
|
|
| 249 |
# 3. 📝 تسجيل الصفقة ف�� المحفظة (حجز رأس المال)
|
| 250 |
await self.smart_portfolio.register_new_position(approved_size_usd)
|
| 251 |
|
| 252 |
-
# تحديث timestamp لأول صفقة إذا لزم الأمر
|
| 253 |
portfolio_state = await self.r2.get_portfolio_state_async()
|
| 254 |
if portfolio_state.get('first_trade_timestamp') is None:
|
| 255 |
portfolio_state['first_trade_timestamp'] = new_trade['entry_time']
|
|
@@ -311,12 +298,14 @@ class TradeManager:
|
|
| 311 |
t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 300)
|
| 312 |
t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 200)
|
| 313 |
|
|
|
|
|
|
|
|
|
|
| 314 |
try:
|
| 315 |
-
d1, d5, d15 = await asyncio.gather(t1, t5, t15)
|
| 316 |
except:
|
| 317 |
continue
|
| 318 |
|
| 319 |
-
# التحقق من كفاية البيانات
|
| 320 |
if d1 and d5 and d15 and len(d1) >= 200:
|
| 321 |
context_data = {
|
| 322 |
'entry_price': trade['entry_price'],
|
|
@@ -324,15 +313,15 @@ class TradeManager:
|
|
| 324 |
'sl_price': trade['sl_price'],
|
| 325 |
'entry_time': trade['entry_time'],
|
| 326 |
'oracle_conf': trade.get('decision_data', {}).get('oracle_conf', 0.8),
|
| 327 |
-
'system_conf': trade.get('decision_data', {}).get('system_confidence', 0.8),
|
| 328 |
'l2_score': trade.get('l1_score', 0.7),
|
| 329 |
'target_class_int': trade.get('target_class_int', 3),
|
| 330 |
'highest_price': float(trade['highest_price']),
|
| 331 |
'time_in_trade_mins': (datetime.now() - datetime.fromisoformat(trade['entry_time'])).total_seconds() / 60
|
| 332 |
}
|
| 333 |
|
| 334 |
-
#
|
| 335 |
-
decision = self.processor.consult_dual_guardians(symbol, d1, d5, d15, context_data)
|
| 336 |
|
| 337 |
action = decision.get('action', 'HOLD')
|
| 338 |
reason = decision.get('reason', '')
|
|
@@ -386,7 +375,6 @@ class TradeManager:
|
|
| 386 |
await asyncio.sleep(5)
|
| 387 |
|
| 388 |
async def _consult_oracle_strategy_update(self, symbol, trade):
|
| 389 |
-
"""Oracle V4 Strategy Update"""
|
| 390 |
try:
|
| 391 |
tasks = [self.data_manager.get_latest_ohlcv(symbol, tf, limit=100) for tf in ["15m", "1h", "4h"]]
|
| 392 |
results = await asyncio.gather(*tasks)
|
|
@@ -489,7 +477,6 @@ class TradeManager:
|
|
| 489 |
total_fees = entry_fee_usd + exit_fee_usd
|
| 490 |
|
| 491 |
# 1. 💼 إبلاغ المحفظة الذكية لتحرير رأس المال وتحديث الرصيد
|
| 492 |
-
# (تمرير PnL الصافي والرسوم للمحفظة لتدقيق الحسابات)
|
| 493 |
await self.smart_portfolio.register_closed_position(entry_capital, net_pnl_usd, total_fees)
|
| 494 |
|
| 495 |
trade.update({
|
|
@@ -502,11 +489,7 @@ class TradeManager:
|
|
| 502 |
})
|
| 503 |
|
| 504 |
# 2. 📊 تحديث الإحصائيات (Counters Only) في TradeManager
|
| 505 |
-
# ملاحظة: نحدث العدادات فقط، ونترك للمحفظة إدارة current_capital_usd في R2
|
| 506 |
-
# لتجنب تضارب الكتابة، نقرأ أحدث حالة (التي حدثتها المحفظة للتو)
|
| 507 |
portfolio = await self.r2.get_portfolio_state_async()
|
| 508 |
-
|
| 509 |
-
# نحدث العدادات فقط
|
| 510 |
portfolio['total_trades'] = portfolio.get('total_trades', 0) + 1
|
| 511 |
if net_pnl_usd >= 0:
|
| 512 |
portfolio['winning_trades'] = portfolio.get('winning_trades', 0) + 1
|
|
@@ -517,7 +500,6 @@ class TradeManager:
|
|
| 517 |
portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0) + abs(net_pnl_usd)
|
| 518 |
trade['result'] = 'LOSS'
|
| 519 |
|
| 520 |
-
# نحفظ الحالة المحدثة (التي تحتوي على الرصيد الجديد من المحفظة + العدادات الجديدة منا)
|
| 521 |
await self.r2.save_portfolio_state_async(portfolio)
|
| 522 |
await self.r2.save_open_trades_async(list(self.open_positions.values()))
|
| 523 |
await self.r2.append_to_closed_trades_history(trade)
|
|
|
|
| 1 |
# ============================================================
|
| 2 |
+
# 🛡️ trade_manager.py (V35.1 - GEM-Architect: Portfolio-Driven & Muzzled Guardians)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
# ============================================================
|
| 4 |
|
| 5 |
import asyncio
|
|
|
|
| 45 |
}
|
| 46 |
|
| 47 |
self.execution_lock = asyncio.Lock()
|
| 48 |
+
print(f"🛡️ [TradeManager V35.1] Portfolio-Driven Engine Online.")
|
| 49 |
|
| 50 |
async def initialize_sentry_exchanges(self):
|
| 51 |
print("🛡️ [TradeManager] Syncing state & Initializing Portfolio...")
|
|
|
|
| 60 |
print(f" -> [Sync] Recovered {len(self.open_positions)} active trades.")
|
| 61 |
|
| 62 |
# 🔄 مزامنة رأس المال المحجوز مع المحفظة بناءً على الصفقات المستعادة
|
|
|
|
| 63 |
total_allocated = sum(float(t.get('entry_capital', 0.0)) for t in self.open_positions.values())
|
|
|
|
| 64 |
self.smart_portfolio.state["allocated_capital_usd"] = total_allocated
|
| 65 |
|
| 66 |
except Exception as e:
|
|
|
|
| 101 |
# 🎯 L4 Sniper Execution Logic
|
| 102 |
# ==============================================================================
|
| 103 |
async def select_and_execute_best_signal(self, oracle_approved_signals: List[Dict[str, Any]]):
|
|
|
|
|
|
|
|
|
|
| 104 |
if not self.processor.initialized:
|
| 105 |
await self.processor.initialize()
|
| 106 |
|
|
|
|
| 158 |
best_signal = sniper_candidates[0]
|
| 159 |
|
| 160 |
async with self.execution_lock:
|
|
|
|
| 161 |
print(f"🚀 [EXECUTING] Attempting entry for best candidate: {best_signal['symbol']}")
|
| 162 |
await self._execute_entry_from_signal(best_signal['symbol'], best_signal)
|
| 163 |
|
|
|
|
| 167 |
async def _execute_entry_from_signal(self, symbol, signal_data):
|
| 168 |
try:
|
| 169 |
# 1. 💼 طلب الموافقة من المحفظة الذكية
|
|
|
|
| 170 |
is_approved, plan = await self.smart_portfolio.request_entry_approval(
|
| 171 |
signal_data,
|
| 172 |
open_positions_count=len(self.open_positions)
|
|
|
|
| 208 |
'direction': 'LONG',
|
| 209 |
'entry_time': datetime.now().isoformat(),
|
| 210 |
'status': 'OPEN',
|
| 211 |
+
'tp_price': approved_tp,
|
| 212 |
'sl_price': sl_price,
|
| 213 |
'last_update': datetime.now().isoformat(),
|
| 214 |
'last_oracle_check': datetime.now().isoformat(),
|
|
|
|
| 218 |
'initial_oracle_class': oracle_class,
|
| 219 |
'oracle_tp_map': signal_data.get('tp_map', {}),
|
| 220 |
|
| 221 |
+
'entry_capital': approved_size_usd,
|
| 222 |
'entry_fee_usd': entry_fee_usd,
|
| 223 |
'l1_score': float(signal_data.get('enhanced_final_score', 0.0)),
|
| 224 |
'target_class_int': target_class_int,
|
| 225 |
'decision_data': {
|
| 226 |
'components': signal_data.get('components', {}),
|
| 227 |
'oracle_conf': signal_data.get('confidence', 0),
|
| 228 |
+
'system_confidence': system_conf,
|
| 229 |
+
'market_mood': market_mood
|
| 230 |
},
|
| 231 |
'highest_price': current_price
|
| 232 |
}
|
|
|
|
| 237 |
# 3. 📝 تسجيل الصفقة ف�� المحفظة (حجز رأس المال)
|
| 238 |
await self.smart_portfolio.register_new_position(approved_size_usd)
|
| 239 |
|
|
|
|
| 240 |
portfolio_state = await self.r2.get_portfolio_state_async()
|
| 241 |
if portfolio_state.get('first_trade_timestamp') is None:
|
| 242 |
portfolio_state['first_trade_timestamp'] = new_trade['entry_time']
|
|
|
|
| 298 |
t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 300)
|
| 299 |
t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 200)
|
| 300 |
|
| 301 |
+
# ✅ GEM-Architect Update: Fetch Order Book for Validation
|
| 302 |
+
tob = self.data_manager.get_order_book_snapshot(symbol)
|
| 303 |
+
|
| 304 |
try:
|
| 305 |
+
d1, d5, d15, d_ob = await asyncio.gather(t1, t5, t15, tob)
|
| 306 |
except:
|
| 307 |
continue
|
| 308 |
|
|
|
|
| 309 |
if d1 and d5 and d15 and len(d1) >= 200:
|
| 310 |
context_data = {
|
| 311 |
'entry_price': trade['entry_price'],
|
|
|
|
| 313 |
'sl_price': trade['sl_price'],
|
| 314 |
'entry_time': trade['entry_time'],
|
| 315 |
'oracle_conf': trade.get('decision_data', {}).get('oracle_conf', 0.8),
|
| 316 |
+
'system_conf': trade.get('decision_data', {}).get('system_confidence', 0.8),
|
| 317 |
'l2_score': trade.get('l1_score', 0.7),
|
| 318 |
'target_class_int': trade.get('target_class_int', 3),
|
| 319 |
'highest_price': float(trade['highest_price']),
|
| 320 |
'time_in_trade_mins': (datetime.now() - datetime.fromisoformat(trade['entry_time'])).total_seconds() / 60
|
| 321 |
}
|
| 322 |
|
| 323 |
+
# ✅ Pass order book (d_ob) to Processor
|
| 324 |
+
decision = self.processor.consult_dual_guardians(symbol, d1, d5, d15, context_data, order_book_snapshot=d_ob)
|
| 325 |
|
| 326 |
action = decision.get('action', 'HOLD')
|
| 327 |
reason = decision.get('reason', '')
|
|
|
|
| 375 |
await asyncio.sleep(5)
|
| 376 |
|
| 377 |
async def _consult_oracle_strategy_update(self, symbol, trade):
|
|
|
|
| 378 |
try:
|
| 379 |
tasks = [self.data_manager.get_latest_ohlcv(symbol, tf, limit=100) for tf in ["15m", "1h", "4h"]]
|
| 380 |
results = await asyncio.gather(*tasks)
|
|
|
|
| 477 |
total_fees = entry_fee_usd + exit_fee_usd
|
| 478 |
|
| 479 |
# 1. 💼 إبلاغ المحفظة الذكية لتحرير رأس المال وتحديث الرصيد
|
|
|
|
| 480 |
await self.smart_portfolio.register_closed_position(entry_capital, net_pnl_usd, total_fees)
|
| 481 |
|
| 482 |
trade.update({
|
|
|
|
| 489 |
})
|
| 490 |
|
| 491 |
# 2. 📊 تحديث الإحصائيات (Counters Only) في TradeManager
|
|
|
|
|
|
|
| 492 |
portfolio = await self.r2.get_portfolio_state_async()
|
|
|
|
|
|
|
| 493 |
portfolio['total_trades'] = portfolio.get('total_trades', 0) + 1
|
| 494 |
if net_pnl_usd >= 0:
|
| 495 |
portfolio['winning_trades'] = portfolio.get('winning_trades', 0) + 1
|
|
|
|
| 500 |
portfolio['total_loss_usd'] = portfolio.get('total_loss_usd', 0) + abs(net_pnl_usd)
|
| 501 |
trade['result'] = 'LOSS'
|
| 502 |
|
|
|
|
| 503 |
await self.r2.save_portfolio_state_async(portfolio)
|
| 504 |
await self.r2.save_open_trades_async(list(self.open_positions.values()))
|
| 505 |
await self.r2.append_to_closed_trades_history(trade)
|