Spaces:
Paused
Paused
Update trade_manager.py
Browse files- trade_manager.py +13 -33
trade_manager.py
CHANGED
|
@@ -1,10 +1,6 @@
|
|
| 1 |
# trade_manager.py
|
| 2 |
-
# (
|
| 3 |
-
#
|
| 4 |
-
# 1. Full wording for Hydra Stats (Crash, Giveback, Stagnation).
|
| 5 |
-
# 2. Handles "Cold Start" zeros by showing "Gathering Data" status.
|
| 6 |
-
# 3. Improved formatting to percentages for better readability.
|
| 7 |
-
# 4. [GEM-FIX] Added 'highest_price' tracking for accurate Giveback detection.
|
| 8 |
|
| 9 |
import asyncio
|
| 10 |
import uuid
|
|
@@ -18,7 +14,7 @@ class TradeManager:
|
|
| 18 |
self.r2 = r2_service
|
| 19 |
self.data_manager = data_manager
|
| 20 |
self.processor = processor
|
| 21 |
-
self.learning_hub = None
|
| 22 |
|
| 23 |
self.open_positions = {}
|
| 24 |
self.watchlist = {}
|
|
@@ -34,17 +30,16 @@ class TradeManager:
|
|
| 34 |
# إعدادات المراجعة الاستراتيجية (Oracle Re-check)
|
| 35 |
self.ORACLE_CHECK_INTERVAL = 900 # كل 15 دقيقة
|
| 36 |
|
| 37 |
-
# إحصائيات AI
|
| 38 |
self.ai_stats = {
|
| 39 |
"hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
|
| 40 |
-
# رؤوس الهيدرا الثلاثة
|
| 41 |
"crash": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
|
| 42 |
"giveback": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
|
| 43 |
"stagnation": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0}
|
| 44 |
}
|
| 45 |
|
| 46 |
self.execution_lock = asyncio.Lock()
|
| 47 |
-
print(f"🛡️ [TradeManager
|
| 48 |
|
| 49 |
async def initialize_sentry_exchanges(self):
|
| 50 |
print("🛡️ [TradeManager] Syncing state with R2...")
|
|
@@ -178,7 +173,6 @@ class TradeManager:
|
|
| 178 |
oracle_strength = float(signal_data.get('strength', 0.5))
|
| 179 |
oracle_class = signal_data.get('target_class', 'TP2')
|
| 180 |
|
| 181 |
-
# Map target class to int for Hydra if needed (TP1=1, TP2=2...)
|
| 182 |
target_class_int = 3
|
| 183 |
if isinstance(oracle_class, str) and oracle_class.startswith('TP'):
|
| 184 |
try: target_class_int = int(oracle_class[-1])
|
|
@@ -204,13 +198,11 @@ class TradeManager:
|
|
| 204 |
'entry_capital': current_capital,
|
| 205 |
'entry_fee_usd': entry_fee_usd,
|
| 206 |
'l1_score': float(signal_data.get('enhanced_final_score', 0.0)),
|
| 207 |
-
# نحفظ target_class كـ int لسهولة استخدامه في Hydra لاحقاً
|
| 208 |
'target_class_int': target_class_int,
|
| 209 |
'decision_data': {
|
| 210 |
'components': signal_data.get('components', {}),
|
| 211 |
'oracle_conf': signal_data.get('confidence', 0)
|
| 212 |
},
|
| 213 |
-
# [GEM-FIX] Initialize highest_price at entry
|
| 214 |
'highest_price': current_price
|
| 215 |
}
|
| 216 |
|
|
@@ -249,18 +241,16 @@ class TradeManager:
|
|
| 249 |
trade = self.open_positions.get(symbol)
|
| 250 |
if not trade:
|
| 251 |
break
|
| 252 |
-
|
| 253 |
current_ticker_price = await self.data_manager.get_latest_price_async(symbol)
|
| 254 |
|
| 255 |
-
# [GEM-FIX] Track Highest Price for Giveback Logic (Dynamic Update)
|
| 256 |
if 'highest_price' not in trade:
|
| 257 |
trade['highest_price'] = float(trade['entry_price'])
|
| 258 |
|
| 259 |
if current_ticker_price > float(trade['highest_price']):
|
| 260 |
trade['highest_price'] = current_ticker_price
|
| 261 |
-
# Note: We update memory instantly. Persistence happens on next save event.
|
| 262 |
|
| 263 |
-
#
|
| 264 |
if current_ticker_price >= trade['tp_price']:
|
| 265 |
print(f"🎯 [TP HIT] Price {current_ticker_price} hit Target {trade['tp_price']}")
|
| 266 |
async with self.execution_lock:
|
|
@@ -273,9 +263,9 @@ class TradeManager:
|
|
| 273 |
await self._execute_exit(symbol, trade['sl_price'], "SL_HIT")
|
| 274 |
break
|
| 275 |
|
| 276 |
-
#
|
| 277 |
if time.time() - last_ai_check_time > 60:
|
| 278 |
-
t1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 200)
|
| 279 |
t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 100)
|
| 280 |
t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 100)
|
| 281 |
|
|
@@ -284,9 +274,7 @@ class TradeManager:
|
|
| 284 |
except:
|
| 285 |
continue
|
| 286 |
|
| 287 |
-
# [MODIFIED] Clear Check: Only run if data is sufficient
|
| 288 |
if d1 and d5 and d15 and len(d1) >= 100:
|
| 289 |
-
# ✅ تجهيز سياق الصفقة لـ Hydra
|
| 290 |
context_data = {
|
| 291 |
'entry_price': trade['entry_price'],
|
| 292 |
'tp_price': trade['tp_price'],
|
|
@@ -295,11 +283,10 @@ class TradeManager:
|
|
| 295 |
'oracle_conf': trade.get('decision_data', {}).get('oracle_conf', 0.8),
|
| 296 |
'l2_score': trade.get('l1_score', 0.7),
|
| 297 |
'target_class': trade.get('target_class_int', 3),
|
| 298 |
-
# [GEM-FIX] Pass the highest price reached so far
|
| 299 |
'highest_price': float(trade['highest_price'])
|
| 300 |
}
|
| 301 |
|
| 302 |
-
#
|
| 303 |
decision = self.processor.consult_guardian(symbol, d1, d5, d15, context_data)
|
| 304 |
|
| 305 |
action = decision.get('action', 'HOLD')
|
|
@@ -308,7 +295,6 @@ class TradeManager:
|
|
| 308 |
p_giveback = probs.get('giveback', 0.0)
|
| 309 |
p_stag = probs.get('stagnation', 0.0)
|
| 310 |
|
| 311 |
-
# [UPDATED LOG] Full words + Percentage display
|
| 312 |
log_msg = (f"🛡️ {action} | "
|
| 313 |
f"Crash: {p_crash:.0%} | "
|
| 314 |
f"Giveback: {p_giveback:.0%} | "
|
|
@@ -316,7 +302,6 @@ class TradeManager:
|
|
| 316 |
|
| 317 |
self.latest_guardian_log = log_msg
|
| 318 |
|
| 319 |
-
# تنفيذ القرار
|
| 320 |
if action in ['EXIT_HARD', 'EXIT_SOFT']:
|
| 321 |
print(f"🐲 [Hydra] {action}: {decision.get('reason')}")
|
| 322 |
async with self.execution_lock:
|
|
@@ -344,13 +329,12 @@ class TradeManager:
|
|
| 344 |
self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
|
| 345 |
await self.r2.save_open_trades_async(list(self.open_positions.values()))
|
| 346 |
else:
|
| 347 |
-
# [ADDED] Inform user why zeros/waiting exists
|
| 348 |
self.latest_guardian_log = f"📡 Hydra: Gathering Data (Need 100 candles)..."
|
| 349 |
|
| 350 |
last_ai_check_time = time.time()
|
| 351 |
self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
|
| 352 |
|
| 353 |
-
# 4.
|
| 354 |
last_oracle_check = datetime.fromisoformat(trade.get('last_oracle_check', datetime.now().isoformat()))
|
| 355 |
if (datetime.now() - last_oracle_check).total_seconds() > self.ORACLE_CHECK_INTERVAL:
|
| 356 |
self.open_positions[symbol]['last_oracle_check'] = datetime.now().isoformat()
|
|
@@ -425,23 +409,21 @@ class TradeManager:
|
|
| 425 |
|
| 426 |
self._update_specific_stat("hybrid", is_good_exit, usd_impact)
|
| 427 |
|
| 428 |
-
# تحديث إحصائيات Hydra
|
| 429 |
if ai_scores:
|
| 430 |
-
# إذا كان الاحتمال عالياً وتصرفنا بناءً عليه، هل كان التصرف صحيحاً؟
|
| 431 |
if ai_scores.get('crash', 0) >= 0.60:
|
| 432 |
self._update_specific_stat("crash", is_good_exit, usd_impact)
|
| 433 |
if ai_scores.get('giveback', 0) >= 0.70:
|
| 434 |
self._update_specific_stat("giveback", is_good_exit, usd_impact)
|
| 435 |
if ai_scores.get('stagnation', 0) >= 0.50:
|
| 436 |
-
# للركود، الخروج الجيد يعني السعر لم يصعد بقوة بعدنا
|
| 437 |
self._update_specific_stat("stagnation", is_good_exit, usd_impact)
|
| 438 |
|
| 439 |
record = {"symbol": symbol, "exit_price": exit_price, "price_15m": curr, "usd_impact": usd_impact, "verdict": "SUCCESS" if is_good_exit else "MISS"}
|
| 440 |
await self.r2.append_deep_steward_audit(record)
|
| 441 |
|
|
|
|
| 442 |
if self.learning_hub and trade_obj:
|
| 443 |
trade_obj['pnl_percent'] = trade_obj.get('profit_pct', 0.0)
|
| 444 |
-
await self.learning_hub.
|
| 445 |
|
| 446 |
except Exception as e:
|
| 447 |
print(f"⚠️ [Ghost/Learning Error] {e}")
|
|
@@ -458,7 +440,6 @@ class TradeManager:
|
|
| 458 |
entry_capital = float(trade.get('entry_capital', 100.0))
|
| 459 |
entry_fee_usd = float(trade.get('entry_fee_usd', 0.0))
|
| 460 |
|
| 461 |
-
# Spot PnL
|
| 462 |
exit_value_gross = (exit_price / entry_price) * entry_capital
|
| 463 |
exit_fee_usd = exit_value_gross * self.FEE_RATE
|
| 464 |
net_exit_value = exit_value_gross - exit_fee_usd
|
|
@@ -498,7 +479,6 @@ class TradeManager:
|
|
| 498 |
|
| 499 |
self._launch_post_exit_analysis(symbol, exit_price, trade.get('exit_time'), entry_capital, ai_scores, trade)
|
| 500 |
|
| 501 |
-
# Update UI Log
|
| 502 |
self.latest_guardian_log = f"✅ Closed {symbol} ({reason})"
|
| 503 |
|
| 504 |
if symbol in self.sentry_tasks:
|
|
|
|
| 1 |
# trade_manager.py
|
| 2 |
+
# (V33.0 - GEM-Architect: Adaptive Feedback Loop Edition)
|
| 3 |
+
# Linked with AdaptiveHub via register_trade_outcome
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
import asyncio
|
| 6 |
import uuid
|
|
|
|
| 14 |
self.r2 = r2_service
|
| 15 |
self.data_manager = data_manager
|
| 16 |
self.processor = processor
|
| 17 |
+
self.learning_hub = None # سيتم حقن AdaptiveHub هنا من app.py
|
| 18 |
|
| 19 |
self.open_positions = {}
|
| 20 |
self.watchlist = {}
|
|
|
|
| 30 |
# إعدادات المراجعة الاستراتيجية (Oracle Re-check)
|
| 31 |
self.ORACLE_CHECK_INTERVAL = 900 # كل 15 دقيقة
|
| 32 |
|
| 33 |
+
# إحصائيات AI
|
| 34 |
self.ai_stats = {
|
| 35 |
"hybrid": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
|
|
|
|
| 36 |
"crash": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
|
| 37 |
"giveback": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0},
|
| 38 |
"stagnation": {"total": 0, "good": 0, "saved": 0.0, "missed": 0.0}
|
| 39 |
}
|
| 40 |
|
| 41 |
self.execution_lock = asyncio.Lock()
|
| 42 |
+
print(f"🛡️ [TradeManager V33.0] Adaptive Feedback Engine Online.")
|
| 43 |
|
| 44 |
async def initialize_sentry_exchanges(self):
|
| 45 |
print("🛡️ [TradeManager] Syncing state with R2...")
|
|
|
|
| 173 |
oracle_strength = float(signal_data.get('strength', 0.5))
|
| 174 |
oracle_class = signal_data.get('target_class', 'TP2')
|
| 175 |
|
|
|
|
| 176 |
target_class_int = 3
|
| 177 |
if isinstance(oracle_class, str) and oracle_class.startswith('TP'):
|
| 178 |
try: target_class_int = int(oracle_class[-1])
|
|
|
|
| 198 |
'entry_capital': current_capital,
|
| 199 |
'entry_fee_usd': entry_fee_usd,
|
| 200 |
'l1_score': float(signal_data.get('enhanced_final_score', 0.0)),
|
|
|
|
| 201 |
'target_class_int': target_class_int,
|
| 202 |
'decision_data': {
|
| 203 |
'components': signal_data.get('components', {}),
|
| 204 |
'oracle_conf': signal_data.get('confidence', 0)
|
| 205 |
},
|
|
|
|
| 206 |
'highest_price': current_price
|
| 207 |
}
|
| 208 |
|
|
|
|
| 241 |
trade = self.open_positions.get(symbol)
|
| 242 |
if not trade:
|
| 243 |
break
|
| 244 |
+
|
| 245 |
current_ticker_price = await self.data_manager.get_latest_price_async(symbol)
|
| 246 |
|
|
|
|
| 247 |
if 'highest_price' not in trade:
|
| 248 |
trade['highest_price'] = float(trade['entry_price'])
|
| 249 |
|
| 250 |
if current_ticker_price > float(trade['highest_price']):
|
| 251 |
trade['highest_price'] = current_ticker_price
|
|
|
|
| 252 |
|
| 253 |
+
# Hard Limits
|
| 254 |
if current_ticker_price >= trade['tp_price']:
|
| 255 |
print(f"🎯 [TP HIT] Price {current_ticker_price} hit Target {trade['tp_price']}")
|
| 256 |
async with self.execution_lock:
|
|
|
|
| 263 |
await self._execute_exit(symbol, trade['sl_price'], "SL_HIT")
|
| 264 |
break
|
| 265 |
|
| 266 |
+
# Hydra AI Check (Processor determines Policy)
|
| 267 |
if time.time() - last_ai_check_time > 60:
|
| 268 |
+
t1 = self.data_manager.get_latest_ohlcv(symbol, '1m', 200)
|
| 269 |
t5 = self.data_manager.get_latest_ohlcv(symbol, '5m', 100)
|
| 270 |
t15 = self.data_manager.get_latest_ohlcv(symbol, '15m', 100)
|
| 271 |
|
|
|
|
| 274 |
except:
|
| 275 |
continue
|
| 276 |
|
|
|
|
| 277 |
if d1 and d5 and d15 and len(d1) >= 100:
|
|
|
|
| 278 |
context_data = {
|
| 279 |
'entry_price': trade['entry_price'],
|
| 280 |
'tp_price': trade['tp_price'],
|
|
|
|
| 283 |
'oracle_conf': trade.get('decision_data', {}).get('oracle_conf', 0.8),
|
| 284 |
'l2_score': trade.get('l1_score', 0.7),
|
| 285 |
'target_class': trade.get('target_class_int', 3),
|
|
|
|
| 286 |
'highest_price': float(trade['highest_price'])
|
| 287 |
}
|
| 288 |
|
| 289 |
+
# Processor consults Guardian AND applies SystemLimits overrides
|
| 290 |
decision = self.processor.consult_guardian(symbol, d1, d5, d15, context_data)
|
| 291 |
|
| 292 |
action = decision.get('action', 'HOLD')
|
|
|
|
| 295 |
p_giveback = probs.get('giveback', 0.0)
|
| 296 |
p_stag = probs.get('stagnation', 0.0)
|
| 297 |
|
|
|
|
| 298 |
log_msg = (f"🛡️ {action} | "
|
| 299 |
f"Crash: {p_crash:.0%} | "
|
| 300 |
f"Giveback: {p_giveback:.0%} | "
|
|
|
|
| 302 |
|
| 303 |
self.latest_guardian_log = log_msg
|
| 304 |
|
|
|
|
| 305 |
if action in ['EXIT_HARD', 'EXIT_SOFT']:
|
| 306 |
print(f"🐲 [Hydra] {action}: {decision.get('reason')}")
|
| 307 |
async with self.execution_lock:
|
|
|
|
| 329 |
self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
|
| 330 |
await self.r2.save_open_trades_async(list(self.open_positions.values()))
|
| 331 |
else:
|
|
|
|
| 332 |
self.latest_guardian_log = f"📡 Hydra: Gathering Data (Need 100 candles)..."
|
| 333 |
|
| 334 |
last_ai_check_time = time.time()
|
| 335 |
self.open_positions[symbol]['last_update'] = datetime.now().isoformat()
|
| 336 |
|
| 337 |
+
# 4. Oracle Re-Check
|
| 338 |
last_oracle_check = datetime.fromisoformat(trade.get('last_oracle_check', datetime.now().isoformat()))
|
| 339 |
if (datetime.now() - last_oracle_check).total_seconds() > self.ORACLE_CHECK_INTERVAL:
|
| 340 |
self.open_positions[symbol]['last_oracle_check'] = datetime.now().isoformat()
|
|
|
|
| 409 |
|
| 410 |
self._update_specific_stat("hybrid", is_good_exit, usd_impact)
|
| 411 |
|
|
|
|
| 412 |
if ai_scores:
|
|
|
|
| 413 |
if ai_scores.get('crash', 0) >= 0.60:
|
| 414 |
self._update_specific_stat("crash", is_good_exit, usd_impact)
|
| 415 |
if ai_scores.get('giveback', 0) >= 0.70:
|
| 416 |
self._update_specific_stat("giveback", is_good_exit, usd_impact)
|
| 417 |
if ai_scores.get('stagnation', 0) >= 0.50:
|
|
|
|
| 418 |
self._update_specific_stat("stagnation", is_good_exit, usd_impact)
|
| 419 |
|
| 420 |
record = {"symbol": symbol, "exit_price": exit_price, "price_15m": curr, "usd_impact": usd_impact, "verdict": "SUCCESS" if is_good_exit else "MISS"}
|
| 421 |
await self.r2.append_deep_steward_audit(record)
|
| 422 |
|
| 423 |
+
# ✅ [Adaptive Loop] Feed results back to AdaptiveHub
|
| 424 |
if self.learning_hub and trade_obj:
|
| 425 |
trade_obj['pnl_percent'] = trade_obj.get('profit_pct', 0.0)
|
| 426 |
+
await self.learning_hub.register_trade_outcome(trade_obj)
|
| 427 |
|
| 428 |
except Exception as e:
|
| 429 |
print(f"⚠️ [Ghost/Learning Error] {e}")
|
|
|
|
| 440 |
entry_capital = float(trade.get('entry_capital', 100.0))
|
| 441 |
entry_fee_usd = float(trade.get('entry_fee_usd', 0.0))
|
| 442 |
|
|
|
|
| 443 |
exit_value_gross = (exit_price / entry_price) * entry_capital
|
| 444 |
exit_fee_usd = exit_value_gross * self.FEE_RATE
|
| 445 |
net_exit_value = exit_value_gross - exit_fee_usd
|
|
|
|
| 479 |
|
| 480 |
self._launch_post_exit_analysis(symbol, exit_price, trade.get('exit_time'), entry_capital, ai_scores, trade)
|
| 481 |
|
|
|
|
| 482 |
self.latest_guardian_log = f"✅ Closed {symbol} ({reason})"
|
| 483 |
|
| 484 |
if symbol in self.sentry_tasks:
|