Riy777 commited on
Commit
3f42b72
·
1 Parent(s): 19ecbac

Update trade_manager.py

Browse files
Files changed (1) hide show
  1. trade_manager.py +288 -59
trade_manager.py CHANGED
@@ -1,116 +1,345 @@
1
- # trade_manager.py (V12.3 - Hybrid Executor with Correct Naming)
2
 
3
  import asyncio
4
  import time
5
  import json
6
- from datetime import datetime
7
- from typing import Dict, List, Deque
 
8
  from collections import deque
9
  import numpy as np
10
  import ccxt.async_support as ccxtasync
11
 
 
 
 
 
 
 
 
 
 
12
  class TacticalData:
 
13
  def __init__(self, symbol):
14
  self.symbol = symbol
15
  self.order_book = None
16
- self.trades = deque(maxlen=1000)
17
- self.ohlcv = {tf: deque(maxlen=500) for tf in ['5m', '15m', '1h', '4h', '1d']}
 
18
  self.last_update = time.time()
19
 
20
  def update_ohlcv(self, tf, candles):
21
  if not candles: return
22
- current = self.ohlcv[tf]
23
- last_ts = current[-1][0] if len(current) > 0 else 0
24
- new_candles = [c for c in candles if c[0] > last_ts]
25
- current.extend(new_candles)
 
 
 
 
 
 
 
 
 
26
 
27
- def get_full_packet(self):
28
- if len(self.ohlcv['5m']) < 200 or len(self.ohlcv['1h']) < 200:
29
- return None
30
- return {tf: list(data) for tf, data in self.ohlcv.items()}
31
 
32
  class TradeManager:
33
  def __init__(self, r2_service, data_manager, titan_engine=None, processor=None):
34
  self.r2 = r2_service
35
  self.dm = data_manager
36
- self.titan = titan_engine
37
- self.processor = processor # (اختياري للاستخدام المستقبلي للهجين الكامل في الحارس)
 
38
 
39
  self.is_running = False
40
- self.watchlist = {}
 
41
  self.tasks = {}
42
  self.data_cache = {}
43
  self.lock = asyncio.Lock()
44
  self.exchange = None
45
 
46
- # استيراد العتبة بالاسم الهجين الجديد
47
- self.ENTRY_THRESH = self.dm.HYBRID_ENTRY_THRESHOLD if self.dm else 0.90
48
- print(f"⚔️ [TradeManager] Hybrid Executor Ready. Entry Threshold: {self.ENTRY_THRESH}")
 
 
49
 
50
  async def initialize_sentry_exchanges(self):
51
- self.exchange = ccxtasync.kucoin({'enableRateLimit': True, 'timeout': 20000})
52
- await self.exchange.load_markets()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  async def start_sentry_loops(self):
 
55
  self.is_running = True
 
56
  while self.is_running:
57
- await asyncio.sleep(5)
58
- async with self.lock:
59
- targets = list(self.watchlist.keys())
60
- for sym in targets:
61
- if sym not in self.tasks:
62
- print(f"🛡️ [Sentry] Starting monitor for {sym}")
63
- self.data_cache[sym] = TacticalData(sym)
64
- self.tasks[sym] = asyncio.create_task(self._monitor_symbol(sym))
 
 
 
 
 
 
 
 
 
 
65
 
66
  async def stop_sentry_loops(self):
67
  self.is_running = False
68
  for t in self.tasks.values(): t.cancel()
69
  if self.exchange: await self.exchange.close()
 
70
 
71
  async def update_sentry_watchlist(self, candidates):
 
72
  async with self.lock:
73
- self.watchlist = {c['symbol']: c for c in candidates}
74
- print(f"📋 [Sentry] Watchlist updated: {len(candidates)} targets.")
 
 
 
 
75
 
76
- async def _monitor_symbol(self, symbol):
 
 
 
 
 
 
 
77
  while self.is_running and symbol in self.watchlist:
78
  try:
79
- tfs = ['5m', '15m', '1h', '4h', '1d']
80
- tasks = [self.exchange.fetch_ohlcv(symbol, tf, limit=100) for tf in tfs]
81
- tasks.append(self.exchange.fetch_order_book(symbol, limit=20))
 
82
  results = await asyncio.gather(*tasks, return_exceptions=True)
83
 
84
  data = self.data_cache[symbol]
85
- for i, tf in enumerate(tfs):
86
- if isinstance(results[i], list):
87
- data.update_ohlcv(tf, results[i])
88
- if not isinstance(results[-1], Exception):
 
 
 
 
89
  data.order_book = results[-1]
90
 
91
- # حالياً الحارس يستخدم Titan فقط للسرعة في المراقبة اللحظية
92
- # (يمكن ترقيته لاستخدام الهجين الكامل processor.process... لاحقاً إذا لزم الأمر)
93
- packet = data.get_full_packet()
94
- if packet and self.titan:
95
- prediction = self.titan.predict(packet)
96
- score = prediction.get('score', 0.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- if score >= self.ENTRY_THRESH:
99
- print(f"🚀 [EXECUTOR] Hybrid Signal Re-confirmed by Titan! {symbol} Score: {score:.4f}")
100
- await self._execute_entry(symbol, score)
101
- async with self.lock: self.watchlist.pop(symbol, None)
102
- break
 
103
 
104
- await asyncio.sleep(60)
 
 
 
105
  except Exception as e:
106
  print(f"⚠️ [Monitor Error] {symbol}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  await asyncio.sleep(10)
108
 
109
- async def _execute_entry(self, symbol, score):
110
- price = (await self.exchange.fetch_ticker(symbol))['last']
111
- trade = {
112
- "symbol": symbol, "entry_price": price, "score": score,
113
- "timestamp": datetime.now().isoformat(), "status": "OPEN",
114
- "tp": price * 1.015, "sl": price * 0.9925
115
- }
116
- print(f"✅ [TRADE OPENED] {json.dumps(trade, indent=2)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # trade_manager.py (V13.1 - Tactical Sniper with Full Reporting)
2
 
3
  import asyncio
4
  import time
5
  import json
6
+ import traceback
7
+ from datetime import datetime, timezone
8
+ from typing import Dict, List, Deque, Optional
9
  from collections import deque
10
  import numpy as np
11
  import ccxt.async_support as ccxtasync
12
 
13
+ # استيراد التبعيات الضرورية للتحليل اللحظي
14
+ try:
15
+ from ml_engine.titan_engine import TitanEngine
16
+ from ml_engine.patterns import ChartPatternAnalyzer
17
+ except ImportError:
18
+ TitanEngine = None
19
+ ChartPatternAnalyzer = None
20
+ print("⚠️ [TradeManager] Warning: ML engines not found. Sniper mode will be limited.")
21
+
22
  class TacticalData:
23
+ """حاوية بيانات تكتيكية سريعة للمراقبة اللحظية"""
24
  def __init__(self, symbol):
25
  self.symbol = symbol
26
  self.order_book = None
27
+ self.trades = deque(maxlen=1000) # آخر 1000 صفقة لتتبع الزخم
28
+ # نحتاج فريم 5 دقائق بشكل أساسي للزناد، و 1 ساعة للسياق
29
+ self.ohlcv = {tf: deque(maxlen=300) for tf in ['5m', '1h']}
30
  self.last_update = time.time()
31
 
32
  def update_ohlcv(self, tf, candles):
33
  if not candles: return
34
+ current = self.ohlcv.get(tf)
35
+ if current is None: return
36
+
37
+ # دمج الشموع الجديدة بكفاءة
38
+ if len(current) == 0:
39
+ current.extend(candles)
40
+ else:
41
+ last_ts = current[-1][0]
42
+ new_candles = [c for c in candles if c[0] > last_ts]
43
+ current.extend(new_candles)
44
+ # تحديث الشمعة الأخيرة إذا كانت لا تزال مفتوحة
45
+ if candles[-1][0] == last_ts:
46
+ current[-1] = candles[-1]
47
 
48
+ def is_ready_for_analysis(self):
49
+ # نحتاج على الأقل 100 شمعة 5 دقائق لتحليل فني معقول
50
+ return len(self.ohlcv['5m']) >= 100
 
51
 
52
  class TradeManager:
53
  def __init__(self, r2_service, data_manager, titan_engine=None, processor=None):
54
  self.r2 = r2_service
55
  self.dm = data_manager
56
+ # استخدام محركات خاصة للحارس لضمان عدم تضارب الذاكرة مع المستكشف
57
+ self.titan = TitanEngine() if TitanEngine else None
58
+ self.pattern_analyzer = ChartPatternAnalyzer() if ChartPatternAnalyzer else None
59
 
60
  self.is_running = False
61
+ self.watchlist = {} # العملات قيد المراقبة للدخول
62
+ self.open_positions = {} # العملات التي تم الدخول فيها
63
  self.tasks = {}
64
  self.data_cache = {}
65
  self.lock = asyncio.Lock()
66
  self.exchange = None
67
 
68
+ # إعدادات القناص
69
+ self.SNIPER_ENTRY_THRESHOLD = 0.75 # عتبة دخول لحظية مرتفعة
70
+ self.MOMENTUM_CONFIRMATION = True # تطلب تأكيد الزخم من دفتر الطلبات
71
+
72
+ print(f"⚔️ [Sniper] Tactical Trade Manager Initialized. Entry Threshold: {self.SNIPER_ENTRY_THRESHOLD}")
73
 
74
  async def initialize_sentry_exchanges(self):
75
+ """تهيئة اتصال المنصة الخاص بالحارس"""
76
+ try:
77
+ if not self.exchange:
78
+ self.exchange = ccxtasync.kucoin({
79
+ 'enableRateLimit': True,
80
+ 'timeout': 15000, # تايم آوت أقصر للاستجابة السريعة
81
+ 'options': {'defaultType': 'spot'}
82
+ })
83
+ await self.exchange.load_markets()
84
+
85
+ # تهيئة المحركات اللحظية
86
+ if self.titan and not self.titan.initialized: await self.titan.initialize()
87
+ if self.pattern_analyzer and not self.pattern_analyzer.initialized: await self.pattern_analyzer.initialize()
88
+
89
+ except Exception as e:
90
+ print(f"❌ [Sniper Init Error] {e}")
91
 
92
  async def start_sentry_loops(self):
93
+ """بدء حلقة المراقبة الرئيسية"""
94
  self.is_running = True
95
+ print("🔭 [Sniper] Sentry loops started.")
96
  while self.is_running:
97
+ try:
98
+ # 1. إدارة قائمة المراقبة (للدخول)
99
+ async with self.lock:
100
+ watch_targets = list(self.watchlist.keys())
101
+
102
+ for sym in watch_targets:
103
+ if sym not in self.tasks:
104
+ print(f"🎯 [Sniper] Locking on target: {sym}")
105
+ self.data_cache[sym] = TacticalData(sym)
106
+ self.tasks[sym] = asyncio.create_task(self._monitor_for_entry(sym))
107
+
108
+ # 2. إدارة الصفقات المفتوحة (للخروج)
109
+ # (سيتم إضافتها لاحقاً: self._monitor_open_positions())
110
+
111
+ await asyncio.sleep(5) # دورة تفقد رئيسية سريعة
112
+ except Exception as e:
113
+ print(f"⚠️ [Sentry Loop Error] {e}")
114
+ await asyncio.sleep(5)
115
 
116
  async def stop_sentry_loops(self):
117
  self.is_running = False
118
  for t in self.tasks.values(): t.cancel()
119
  if self.exchange: await self.exchange.close()
120
+ print("🛡️ [Sniper] Sentry stood down.")
121
 
122
  async def update_sentry_watchlist(self, candidates):
123
+ """تحديث قائمة الأهداف من المستكشف"""
124
  async with self.lock:
125
+ # إضافة أهداف جديدة فقط
126
+ for cand in candidates:
127
+ sym = cand['symbol']
128
+ if sym not in self.watchlist and sym not in self.open_positions:
129
+ self.watchlist[sym] = cand
130
+ print(f"📋 [Sniper] New target acquired: {sym}")
131
 
132
+ # ==================================================================
133
+ # 🎯 منطق القناص (Entry Logic)
134
+ # ==================================================================
135
+ async def _monitor_for_entry(self, symbol):
136
+ """مراقبة لحظية مكثفة لهدف محدد بغرض الدخول"""
137
+ print(f"👁️ [Sniper] Monitoring {symbol} for tactical entry...")
138
+ consecutive_signals = 0
139
+
140
  while self.is_running and symbol in self.watchlist:
141
  try:
142
+ # 1. تحديث البيانات التكتيكية (5m + 1h + OrderBook)
143
+ tfs = ['5m', '1h']
144
+ tasks = [self.exchange.fetch_ohlcv(symbol, tf, limit=150) for tf in tfs]
145
+ tasks.append(self.exchange.fetch_order_book(symbol, limit=50))
146
  results = await asyncio.gather(*tasks, return_exceptions=True)
147
 
148
  data = self.data_cache[symbol]
149
+ valid_update = True
150
+ for i, res in enumerate(results[:-1]):
151
+ if isinstance(res, list):
152
+ data.update_ohlcv(tfs[i], res)
153
+ else:
154
+ valid_update = False
155
+
156
+ if isinstance(results[-1], dict):
157
  data.order_book = results[-1]
158
 
159
+ if not valid_update or not data.is_ready_for_analysis():
160
+ await asyncio.sleep(10) # انتظار تجميع البيانات
161
+ continue
162
+
163
+ # 2. التحليل اللحظي الجديد (Fresh Analysis)
164
+ # أ. تحليل Titan اللحظي (5m)
165
+ titan_score = 0.0
166
+ if self.titan:
167
+ # تجهيز البيانات لـ Titan (يحتاج قاموس بجميع الفريمات المتاحة)
168
+ titan_packet = {tf: list(data.ohlcv[tf]) for tf in tfs}
169
+ titan_res = await asyncio.to_thread(self.titan.predict, titan_packet)
170
+ titan_score = titan_res.get('score', 0.0)
171
+
172
+ # ب. تحليل الأنماط اللحظي (5m)
173
+ pattern_score = 0.0
174
+ bullish_pattern = False
175
+ if self.pattern_analyzer:
176
+ # تحليل آخر 100 شمعة 5 دقائق فقط
177
+ candles_5m = list(data.ohlcv['5m'])[-100:]
178
+ pat_res = await self.pattern_analyzer.detect_chart_patterns({'5m': candles_5m})
179
+ pattern_score = pat_res.get('pattern_confidence', 0.0)
180
+ if pat_res.get('pattern_detected') and 'BULLISH' in pat_res['pattern_detected'].upper():
181
+ bullish_pattern = True
182
+
183
+ # ج. تحليل زخم دفتر الطلبات (Order Book Imbalance)
184
+ ob_imbalance = self._calculate_ob_imbalance(data.order_book)
185
+ momentum_confirmed = ob_imbalance > 0.15 # طلبات الشراء أكثر بـ 15%
186
+
187
+ # 3. قرار الزناد (The Trigger)
188
+ # الشروط: Titan قوي جدًا OR (Titan جيد AND نمط إيجابي AND زخم دفتر طلبات)
189
+ sniper_fired = False
190
+ trigger_reason = ""
191
+
192
+ if titan_score >= self.SNIPER_ENTRY_THRESHOLD:
193
+ sniper_fired = True
194
+ trigger_reason = f"Titan Surge ({titan_score:.2f})"
195
+ elif titan_score >= 0.65 and bullish_pattern and momentum_confirmed:
196
+ sniper_fired = True
197
+ trigger_reason = f"Tactical Combo (Titan:{titan_score:.2f} + Pattern + OB:{ob_imbalance:.2f})"
198
+
199
+ if sniper_fired:
200
+ consecutive_signals += 1
201
+ print(f"🔥 [Sniper] {symbol} Signal detected! ({consecutive_signals}/2) - {trigger_reason}")
202
 
203
+ # نطلب تأكيد إشارتين متتاليتين لتقليل الإشارات الكاذبة
204
+ if consecutive_signals >= 2:
205
+ await self._execute_buy(symbol, trigger_reason, titan_score)
206
+ break # الخروج من حلقة المراقبة بعد الشراء
207
+ else:
208
+ consecutive_signals = max(0, consecutive_signals - 1) # تبريد العداد
209
 
210
+ await asyncio.sleep(30) # مراقبة كل 30 ثانية (سريع)
211
+
212
+ except asyncio.CancelledError:
213
+ break
214
  except Exception as e:
215
  print(f"⚠️ [Monitor Error] {symbol}: {e}")
216
+ await asyncio.sleep(60) # تهدئة عند الخطأ
217
+
218
+ def _calculate_ob_imbalance(self, ob):
219
+ """حساب عدم توازن دفتر الطلبات (ميل نحو الشراء أو البيع)"""
220
+ if not ob or 'bids' not in ob or 'asks' not in ob: return 0.0
221
+ # حساب حجم أفضل 10 طلبات وعروض
222
+ bids_vol = sum(b[1] for b in ob['bids'][:10])
223
+ asks_vol = sum(a[1] for a in ob['asks'][:10])
224
+ if (bids_vol + asks_vol) == 0: return 0.0
225
+ return (bids_vol - asks_vol) / (bids_vol + asks_vol)
226
+
227
+ # ==================================================================
228
+ # 🚀 التنفيذ وإدارة الصفقات (Execution & Mgmt)
229
+ # ==================================================================
230
+ async def _execute_buy(self, symbol, reason, score):
231
+ """تنفيذ الشراء الافتراضي وتسجيل الصفقة"""
232
+ try:
233
+ # 1. جلب السعر الحالي للتنفيذ
234
+ ticker = await self.exchange.fetch_ticker(symbol)
235
+ entry_price = ticker['last']
236
+
237
+ # 2. حساب حجم الصفقة (إدارة مخاطر بسيطة: 10% من رأس المال الوهمي)
238
+ portfolio = await self.r2.get_portfolio_state_async()
239
+ capital = portfolio.get('current_capital_usd', 1000.0)
240
+ trade_amount_usd = capital * 0.10
241
+ quantity = trade_amount_usd / entry_price
242
+
243
+ # 3. إنشاء سجل الصفقة
244
+ trade = {
245
+ "symbol": symbol,
246
+ "status": "OPEN",
247
+ "entry_price": entry_price,
248
+ "quantity": quantity,
249
+ "invested_usd": trade_amount_usd,
250
+ "entry_time": datetime.now(timezone.utc).isoformat(),
251
+ "entry_reason": reason,
252
+ "entry_score": score,
253
+ # أهداف أولية (يمكن للحارس تعديلها لاحقاً)
254
+ "tp_price": entry_price * 1.025, # هدف 2.5%
255
+ "sl_price": entry_price * 0.98, # وقف 2%
256
+ "highest_price": entry_price # لتتبع الوقف المتحرك
257
+ }
258
+
259
+ # 4. نقل من قائمة المراقبة إلى الصفقات المفتوحة
260
+ async with self.lock:
261
+ if symbol in self.watchlist:
262
+ del self.watchlist[symbol]
263
+ self.open_positions[symbol] = trade
264
+
265
+ # 5. حفظ في R2 (اختياري: يمكن حفظ الصفقات المفتوحة أيضاً)
266
+ # await self.r2.save_open_trades_async(list(self.open_positions.values()))
267
+
268
+ print(f"🚀 [EXECUTED] BUY {symbol} @ {entry_price} | Reason: {reason}")
269
+
270
+ # بدء مراقبة هذه الصفقة المفتوحة فوراً
271
+ asyncio.create_task(self._monitor_open_position(symbol))
272
+
273
+ except Exception as e:
274
+ print(f"❌ [Execution Failed] {symbol}: {e}")
275
+
276
+ async def _monitor_open_position(self, symbol):
277
+ """حارس الأرباح: مراقبة صفقة مفتوحة حتى الإغلاق"""
278
+ print(f"🛡️ [Guardian] Protecting position: {symbol}")
279
+ while self.is_running and symbol in self.open_positions:
280
+ try:
281
+ trade = self.open_positions[symbol]
282
+ ticker = await self.exchange.fetch_ticker(symbol)
283
+ current_price = ticker['last']
284
+
285
+ # تحديث أعلى سعر وصل له لتفعيل الوقف المتحرك
286
+ if current_price > trade['highest_price']:
287
+ trade['highest_price'] = current_price
288
+ # تفعيل وقف متحرك إذا تجاوز الربح 1%
289
+ if current_price >= trade['entry_price'] * 1.01:
290
+ new_sl = current_price * 0.995 # حجز 0.5% تحت أعلى قمة
291
+ if new_sl > trade['sl_price']:
292
+ trade['sl_price'] = new_sl
293
+ print(f"🛡️ [Guardian] {symbol} Trailing SL moved to {new_sl:.4f}")
294
+
295
+ # التحقق من شروط الخروج
296
+ close_reason = None
297
+ if current_price >= trade['tp_price']:
298
+ close_reason = "TAKE_PROFIT"
299
+ elif current_price <= trade['sl_price']:
300
+ close_reason = "STOP_LOSS"
301
+
302
+ if close_reason:
303
+ await self._execute_sell(symbol, current_price, close_reason)
304
+ break
305
+
306
+ await asyncio.sleep(5) # مراقبة لصيقة جداً للصفقات المفتوحة
307
+
308
+ except Exception as e:
309
+ print(f"⚠️ [Guardian Error] {symbol}: {e}")
310
  await asyncio.sleep(10)
311
 
312
+ async def _execute_sell(self, symbol, exit_price, reason):
313
+ """تنفيذ البيع وتسجيل التقارير النهائية"""
314
+ async with self.lock:
315
+ trade = self.open_positions.pop(symbol, None)
316
+
317
+ if trade:
318
+ # 1. حساب النتائج
319
+ gross_pnl = (exit_price - trade['entry_price']) * trade['quantity']
320
+ # خصم رسوم وهمية (0.1% دخول + 0.1% خروج)
321
+ fees = (trade['invested_usd'] + (exit_price * trade['quantity'])) * 0.001
322
+ net_pnl = gross_pnl - fees
323
+ pnl_percent = (net_pnl / trade['invested_usd']) * 100
324
+
325
+ # 2. تحديث بيانات الصفقة
326
+ trade.update({
327
+ "status": "CLOSED",
328
+ "exit_price": exit_price,
329
+ "exit_time": datetime.now(timezone.utc).isoformat(),
330
+ "exit_reason": reason,
331
+ "pnl_usd": net_pnl,
332
+ "pnl_percent": pnl_percent,
333
+ "fees_paid": fees
334
+ })
335
+
336
+ print(f"💰 [CLOSED] {symbol} ({reason}) | PnL: ${net_pnl:.2f} ({pnl_percent:.2f}%)")
337
+
338
+ # 3. حفظ التقارير عبر R2 (النظام الجديد)
339
+ # أ. حفظ الصفقة المغلقة وتحديث ملخص التداول
340
+ await self.r2.save_closed_trade_async(trade)
341
+ # ب. تحديث رصيد المحفظة
342
+ await self.r2.update_portfolio_balance_async(net_pnl)
343
+
344
+ if symbol in self.tasks:
345
+ del self.tasks[symbol]