Riy777 commited on
Commit
4a41cb1
·
verified ·
1 Parent(s): c91de17

Update periodic_tuner.py

Browse files
Files changed (1) hide show
  1. periodic_tuner.py +74 -277
periodic_tuner.py CHANGED
@@ -1,297 +1,94 @@
1
  # ============================================================
2
- # 🗓️ periodic_tuner.py (V4.3 - GEM-Architect: Timer Persistence)
3
  # ============================================================
4
 
5
- import asyncio
6
- import numpy as np
7
- import pandas as pd
8
- import pandas_ta as ta
9
- import time
10
  import logging
11
- import argparse
12
- import json
13
- from datetime import datetime, timedelta
14
-
15
- # استيراد محركات النظام
16
- from backtest_engine import HeavyDutyBacktester
17
- from ml_engine.data_manager import DataManager
18
- from ml_engine.processor import MLProcessor
19
- from learning_hub.adaptive_hub import AdaptiveHub
20
- from r2 import R2Service
21
-
22
- # ============================================================
23
- # 💎 THE GOLDEN LIST (52 Strategic Assets)
24
- # ============================================================
25
- STRATEGIC_COINS = [
26
- 'SOL/USDT', 'XRP/USDT', 'DOGE/USDT', 'ADA/USDT', 'AVAX/USDT', 'LINK/USDT',
27
- 'TON/USDT', 'INJ/USDT', 'APT/USDT', 'OP/USDT', 'ARB/USDT', 'SUI/USDT',
28
- 'SEI/USDT', 'MINA/USDT', 'MATIC/USDT', 'NEAR/USDT', 'RUNE/USDT', 'API3/USDT',
29
- 'FLOKI/USDT', 'BABYDOGE/USDT', 'SHIB/USDT', 'TRX/USDT', 'DOT/USDT', 'UNI/USDT',
30
- 'ONDO/USDT', 'SNX/USDT', 'HBAR/USDT', 'XLM/USDT', 'AGIX/USDT', 'IMX/USDT',
31
- 'LRC/USDT', 'KCS/USDT', 'ICP/USDT', 'SAND/USDT', 'AXS/USDT', 'APE/USDT',
32
- 'GMT/USDT', 'CHZ/USDT', 'CFX/USDT', 'LDO/USDT', 'FET/USDT', 'RPL/USDT',
33
- 'MNT/USDT', 'RAY/USDT', 'CAKE/USDT', 'SRM/USDT', 'PENDLE/USDT', 'ATOM/USDT'
34
- ]
35
 
36
  logger = logging.getLogger("TitanCore")
37
 
38
- # ============================================================
39
- # 👁️ MARKET SENSOR V3.2
40
- # ============================================================
41
- async def detect_dominant_regime(dm: DataManager, days_back=7):
42
- try:
43
- required_limit = 200 + days_back + 10
44
- logger.info(f"👁️ [Market Sensor] Analyzing Dominant Regime (Last {days_back} days)...")
 
 
 
45
 
46
- candles = await dm.exchange.fetch_ohlcv('BTC/USDT', '1d', limit=required_limit)
47
- if not candles or len(candles) < 200: return "RANGE"
 
48
 
49
- df = pd.DataFrame(candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
50
- close = df['close']
51
- df['sma50'] = ta.sma(close, length=50)
52
- df['sma200'] = ta.sma(close, length=200)
53
- adx_df = ta.adx(df['high'], df['low'], close, length=14)
54
- if adx_df is not None: df['adx'] = adx_df.iloc[:, 0]
55
- else: df['adx'] = 0.0
56
- df['vol_sma'] = df['volume'].rolling(30).mean()
57
-
58
- window_df = df.iloc[-days_back:].copy()
59
- if window_df.empty: return "RANGE"
60
-
61
- regime_counts = {"BULL": 0, "BEAR": 0, "RANGE": 0, "DEAD": 0}
62
- for index, row in window_df.iterrows():
63
- day_regime = "RANGE"
64
- price = row['close']
65
- sma = row['sma200']
66
- adx = row['adx']
67
- vol = row['volume']
68
- vol_avg = row['vol_sma']
69
-
70
- if pd.notna(vol_avg) and vol < (vol_avg * 0.4): day_regime = "DEAD"
71
- elif pd.notna(sma) and price > sma: day_regime = "BULL" if adx > 25 else "RANGE"
72
- elif pd.notna(sma) and price < sma: day_regime = "BEAR" if adx > 25 else "RANGE"
73
- regime_counts[day_regime] += 1
74
-
75
- dominant_regime = max(regime_counts, key=regime_counts.get)
76
- log_str = " | ".join([f"{k}:{v}" for k,v in regime_counts.items()])
77
- logger.info(f" 👁️ Regime Distribution: [{log_str}] -> Winner: {dominant_regime}")
78
- return dominant_regime
79
- except Exception as e:
80
- logger.error(f"⚠️ [Sensor Error] {e}")
81
- return "RANGE"
82
 
83
- # ============================================================
84
- # 🩺 SURGICAL TUNER (Autonomous)
85
- # ============================================================
86
- async def run_surgical_tuning(period_type="weekly", use_fixed_list=True):
87
- logger.info(f"🩺 [Auto-Tuner] Starting {period_type.upper()} optimization sequence...")
88
- r2 = R2Service()
89
- dm = DataManager(None, None, r2)
90
- proc = MLProcessor(dm)
91
- hub = AdaptiveHub(r2)
92
- try:
93
- await dm.initialize(); await proc.initialize(); await hub.initialize()
94
-
95
- open_trades = await r2.get_open_trades_async()
96
- if len(open_trades) > 0:
97
- logger.warning(" ⛔ [Auto-Tuner] Aborted: Active trades present.")
98
- return False
99
 
100
- days_back = 7 if period_type == 'weekly' else 30
101
- detected_regime = await detect_dominant_regime(dm, days_back=days_back)
102
- hub.current_market_regime = detected_regime
103
- asyncio.create_task(hub._save_state_to_r2())
104
 
105
- current_dna = hub.strategies.get(detected_regime)
106
- if not current_dna: return False
107
-
108
- tuning_coins = STRATEGIC_COINS if use_fixed_list else ['DOGE/USDT']
109
- logger.info(f" 🌌 Universe: {len(tuning_coins)} Strategic Assets.")
110
-
111
- opt = HeavyDutyBacktester(dm, proc)
112
- opt.TARGET_COINS = tuning_coins
113
 
114
- base_filters = current_dna.base_filters
115
- base_guards = current_dna.base_guards
116
- scan_range = 0.03 if period_type == 'weekly' else 0.05
117
- steps = 3
118
- def create_micro_grid(center_val):
119
- low = max(0.1, center_val - scan_range)
120
- high = min(0.99, center_val + scan_range)
121
- return np.linspace(low, high, steps)
122
 
123
- opt.GRID_RANGES = {
124
- 'TITAN': create_micro_grid(current_dna.model_weights.get('titan', 0.3)),
125
- 'ORACLE': create_micro_grid(base_filters['l3_oracle_thresh']),
126
- 'SNIPER': create_micro_grid(base_filters['l4_sniper_thresh']),
127
- 'PATTERN': [0.1, 0.5], 'L1_SCORE': [10.0],
128
- 'HYDRA_CRASH': create_micro_grid(base_guards['hydra_crash']),
129
- 'HYDRA_GIVEBACK': create_micro_grid(base_guards['hydra_giveback']),
130
- 'LEGACY_V2': create_micro_grid(base_guards['legacy_v2']),
131
- }
132
-
133
- end_date = datetime.now()
134
- start_date = end_date - timedelta(days=days_back)
135
- opt.set_date_range(start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d"))
136
-
137
- logger.info(f" 🚀 Optimizing for {detected_regime} (Last {days_back} days)...")
138
- best_config, stats = await opt.run_optimization(detected_regime)
139
-
140
- if best_config:
141
- new_deltas = {}
142
- new_deltas['l3_oracle_thresh'] = best_config.get('oracle_thresh') - base_filters['l3_oracle_thresh']
143
- new_deltas['l4_sniper_thresh'] = best_config.get('sniper_thresh') - base_filters['l4_sniper_thresh']
144
- new_deltas['hydra_crash'] = best_config.get('hydra_thresh') - base_guards['hydra_crash']
145
- new_deltas['hydra_giveback'] = best_config.get('hydra_thresh') - base_guards['hydra_giveback']
146
- new_deltas['legacy_v2'] = best_config.get('legacy_thresh') - base_guards['legacy_v2']
147
 
148
- logger.info(f" ✅ [Auto-Tuner] Success. Deltas: {new_deltas}")
149
- hub.update_periodic_delta(detected_regime, period_type, new_deltas)
150
- return True
151
- return False
152
-
153
- except Exception as e:
154
- logger.error(f"❌ [Auto-Tuner Error] {e}")
155
- return False
156
- finally:
157
- await dm.close()
158
 
159
- # ============================================================
160
- # 🕰️ THE SCHEDULER CLASS (Persistent)
161
- # ============================================================
162
- class AutoTunerScheduler:
163
- def __init__(self, trade_manager):
164
- self.trade_manager = trade_manager
165
- self.state_file = "scheduler_state.json"
166
-
167
- # التوقيتات (ستيم تحميلها من R2)
168
- self.last_weekly_run = None
169
- self.last_monthly_run = None
170
 
171
- # العدادات (Status Counters)
172
- self.weekly_count = 0
173
- self.monthly_count = 0
174
 
175
- self.is_running = False
176
- logger.info("🕰️ [Scheduler] Auto-Tuner Armed & Ready.")
177
-
178
- async def start_loop(self):
179
- await self._load_state() # Force Load on Start
180
- while True:
181
- try:
182
- await asyncio.sleep(3600)
183
- now = datetime.now()
184
-
185
- # WEEKLY (Monday 03:00 AM)
186
- if now.weekday() == 0 and 3 <= now.hour < 4:
187
- if self._needs_run('weekly'): await self._try_run('weekly')
188
-
189
- # MONTHLY (1st Day 04:00 AM)
190
- if now.day == 1 and 4 <= now.hour < 5:
191
- if self._needs_run('monthly'): await self._try_run('monthly')
192
-
193
- except Exception as e:
194
- logger.error(f"⚠️ [Scheduler Loop Error] {e}")
195
-
196
- async def _load_state(self):
197
- try:
198
- if self.trade_manager.r2:
199
- data = await self.trade_manager.r2.get_file_async(self.state_file)
200
- if data:
201
- state = json.loads(data)
202
- if state.get('last_weekly'):
203
- self.last_weekly_run = datetime.fromisoformat(state['last_weekly'])
204
- if state.get('last_monthly'):
205
- self.last_monthly_run = datetime.fromisoformat(state['last_monthly'])
206
-
207
- self.weekly_count = state.get('weekly_count', 0)
208
- self.monthly_count = state.get('monthly_count', 0)
209
- logger.info(f" 🕰️ [Scheduler] State Restored (W:{self.weekly_count} | M:{self.monthly_count}).")
210
- else:
211
- # ✅ FIX: إذا كان الملف جديداً، نبدأ التوقيت من "الآن" بدلاً من الانتظار
212
- logger.info(" 🕰️ [Scheduler] New Instance. Initializing Clocks...")
213
- self.last_weekly_run = datetime.now()
214
- self.last_monthly_run = datetime.now()
215
- await self._save_state()
216
- except Exception: pass
217
-
218
- async def _save_state(self):
219
- try:
220
- state = {
221
- "last_weekly": self.last_weekly_run.isoformat() if self.last_weekly_run else None,
222
- "last_monthly": self.last_monthly_run.isoformat() if self.last_monthly_run else None,
223
- "weekly_count": self.weekly_count,
224
- "monthly_count": self.monthly_count
225
- }
226
- if self.trade_manager.r2:
227
- await self.trade_manager.r2.upload_json_async(state, self.state_file)
228
- except Exception: pass
229
-
230
- def _needs_run(self, period_type):
231
- now = datetime.now()
232
- if period_type == 'weekly':
233
- if not self.last_weekly_run: return True
234
- return (now - self.last_weekly_run).days >= 6
235
- if period_type == 'monthly':
236
- if not self.last_monthly_run: return True
237
- return (now - self.last_monthly_run).days >= 25
238
- return False
239
-
240
- async def _try_run(self, period_type):
241
- if len(self.trade_manager.open_positions) > 0:
242
- logger.warning(f"⏳ [Scheduler] Postponing {period_type} run: Active trades present.")
243
- return
244
-
245
- self.is_running = True
246
- try:
247
- # 1. Run Optimization (Isolated)
248
- success = await run_surgical_tuning(period_type, use_fixed_list=True)
249
 
250
- if success:
251
- # 2. Update Timestamps & Counters
252
- if period_type == 'weekly':
253
- self.last_weekly_run = datetime.now()
254
- self.weekly_count += 1
255
- else:
256
- self.last_monthly_run = datetime.now()
257
- self.monthly_count += 1
258
-
259
- await self._save_state()
260
-
261
- # 3. 🔥 HOT RELOAD LIVE SYSTEM (The Final Sync)
262
- if self.trade_manager.learning_hub:
263
- logger.info(" 🔄 [Scheduler] Hot-Reloading Live DNA...")
264
- await self.trade_manager.learning_hub.initialize()
265
- logger.info(" ✨ [Scheduler] Live System Updated Successfully.")
266
 
267
- except Exception as e:
268
- logger.error(f"❌ [Scheduler Fail] {e}")
269
- finally:
270
- self.is_running = False
271
-
272
- # دالة جلب المقاييس للواجهة (محسنة)
273
- def get_status_metrics(self):
274
- def _fmt_time(last_dt):
275
- if not last_dt: return "Pending"
276
- diff = datetime.now() - last_dt
277
- d = diff.days
278
- h = diff.seconds // 3600
279
- return f"{d}d {h}h"
280
-
281
- # إذا كانت None (لم يتم التحميل بعد)، نعيد Init
282
- w_time = _fmt_time(self.last_weekly_run) if self.last_weekly_run else "Init..."
283
- m_time = _fmt_time(self.last_monthly_run) if self.last_monthly_run else "Init..."
284
-
285
- return {
286
- "weekly_timer": w_time,
287
- "weekly_count": self.weekly_count,
288
- "monthly_timer": m_time,
289
- "monthly_count": self.monthly_count,
290
- "is_running": self.is_running
291
- }
292
-
293
- if __name__ == "__main__":
294
- parser = argparse.ArgumentParser()
295
- parser.add_argument('--type', type=str, default='weekly', help='weekly or monthly')
296
- args = parser.parse_args()
297
- asyncio.run(run_surgical_tuning(args.type, use_fixed_list=True))
 
1
  # ============================================================
2
+ # 🗓️ periodic_tuner.py (V65.1 - GEM-Architect: Full-Spectrum Tuner)
3
  # ============================================================
4
 
 
 
 
 
 
5
  import logging
6
+ from typing import Dict, Any
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  logger = logging.getLogger("TitanCore")
9
 
10
+ class ContinuousTuner:
11
+ """
12
+ يقوم بمراقبة أداء كل 'نوع عملة'.
13
+ كل 30 صفقة، يقوم بتقييم معدل الربح (Win Rate) وتعديل العتبات.
14
+ التعديل محصور بـ +/- 0.02 لضمان الاستقرار.
15
+ """
16
+ def __init__(self, adaptive_hub):
17
+ self.hub = adaptive_hub
18
+ self.BATCH_SIZE = 30
19
+ self.ADJUST_STEP = 0.02
20
 
21
+ # أهداف الأداء
22
+ self.TARGET_WR_LOW = 60.0 # أقل من 60% نجاح -> تشديد (رفع العتبة)
23
+ self.TARGET_WR_HIGH = 80.0 # أكثر من 80% نجاح -> تخفيف (خفض العتبة لاقتناص فرص أكثر)
24
 
25
+ logger.info("🔧 [Continuous Tuner] Online. Batch Size: 30")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ async def register_trade_result(self, coin_type: str, is_win: bool):
28
+ """
29
+ يتم استدعاؤها من TradeManager عند إغلاق صفقة.
30
+ """
31
+ if coin_type not in self.hub.strategies:
32
+ return
 
 
 
 
 
 
 
 
 
 
33
 
34
+ dna = self.hub.strategies[coin_type]
 
 
 
35
 
36
+ # 1. تحديث العدادات
37
+ dna.trade_count += 1
38
+ if is_win: dna.wins += 1
 
 
 
 
 
39
 
40
+ logger.info(f" 📊 [Tuner] {coin_type} Trade Registered. Count: {dna.trade_count}/{self.BATCH_SIZE} | Win: {is_win}")
 
 
 
 
 
 
 
41
 
42
+ # 2. التحقق من اكتمال الدفعة (30 صفقة)
43
+ if dna.trade_count >= self.BATCH_SIZE:
44
+ await self._tune_strategy(dna)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ # تصفير العدادات للدورة القادمة
47
+ dna.trade_count = 0
48
+ dna.wins = 0
49
+
50
+ # حفظ التعديلات
51
+ await self.hub.save_state()
 
 
 
 
52
 
53
+ async def _tune_strategy(self, dna):
54
+ win_rate = (dna.wins / dna.trade_count) * 100
55
+ action = "HOLD"
 
 
 
 
 
 
 
 
56
 
57
+ # منطق التعديل (Micro-Adjustment Logic)
58
+ delta_change = 0.0
 
59
 
60
+ if win_rate < self.TARGET_WR_LOW:
61
+ # الأداء ضعيف -> نحتاج دقة أعلى -> نرفع العتبات (Tighten)
62
+ delta_change = self.ADJUST_STEP
63
+ action = f"TIGHTEN (+{self.ADJUST_STEP})"
64
+ elif win_rate > self.TARGET_WR_HIGH:
65
+ # الأداء ممتاز جداً -> ربما نضيع فرصاً -> نخفض العتبات قليلاً (Loosen)
66
+ delta_change = -self.ADJUST_STEP
67
+ action = f"LOOSEN (-{self.ADJUST_STEP})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ if delta_change != 0.0:
70
+ logger.info(f" ⚖️ [Tuner Action] {dna.name}: WR {win_rate:.1f}% -> {action}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ # 1. تطبيق التعديل على الفلاتر الرئيسية
73
+ keys_to_tune = ["l3_oracle_thresh", "l4_sniper_thresh", "l1_min_score"]
74
+ for k in keys_to_tune:
75
+ current_delta = dna.delta.get(k, 0.0)
76
+ # نحد التعديل التراكمي بـ +/- 0.10 كحد أقصى كلي
77
+ new_delta = max(-0.10, min(0.10, current_delta + delta_change))
78
+ dna.delta[k] = new_delta
79
+
80
+ # 2. تطبيق التعديل على الحراس (فقط في حالة Tighten)
81
+ # إذا كان الأداء سيئاً، نزيد حساسية الحراس قليلاً للحماية
82
+ if delta_change > 0:
83
+ keys_guard = [
84
+ "hydra_crash", "hydra_giveback", "hydra_stagnation",
85
+ "legacy_v2", "legacy_v3_hard", "legacy_v3_soft"
86
+ ]
87
+ for k in keys_guard:
88
+ if k in dna.guard_delta:
89
+ curr_g = dna.guard_delta.get(k, 0.0)
90
+ # زيادة طفيفة جداً للحراس، بحد أقصى +0.05
91
+ dna.guard_delta[k] = min(0.05, curr_g + 0.01)
92
+
93
+ else:
94
+ logger.info(f" ⚖️ [Tuner Action] {dna.name}: WR {win_rate:.1f}% -> Optimal range. No Change.")