Spaces:
Paused
Paused
Update learning_hub/adaptive_hub.py
Browse files- learning_hub/adaptive_hub.py +64 -43
learning_hub/adaptive_hub.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# ==============================================================================
|
| 2 |
# 🧠 learning_hub/adaptive_hub.py
|
| 3 |
-
# (V60.1 - GEM-Architect:
|
| 4 |
# ==============================================================================
|
| 5 |
|
| 6 |
import json
|
|
@@ -10,7 +10,7 @@ import traceback
|
|
| 10 |
from collections import deque
|
| 11 |
from typing import Dict, Any, List
|
| 12 |
|
| 13 |
-
# محاولة استيراد SystemLimits
|
| 14 |
try:
|
| 15 |
from ml_engine.processor import SystemLimits
|
| 16 |
except ImportError:
|
|
@@ -52,19 +52,9 @@ class StrategyDNA:
|
|
| 52 |
def get_final_guards(self):
|
| 53 |
final = {}
|
| 54 |
for k, v in self.base_guards.items():
|
|
|
|
| 55 |
d_fast = self.delta_guards_fast.get(k, 0.0)
|
| 56 |
-
# Periodic tuner updates base directly or needs delta logic?
|
| 57 |
-
# V3.3 tuner updates delta via update_periodic_delta, so we assume deltas exist for guards too?
|
| 58 |
-
# Note: For simplicity, Periodic Tuner currently updates 'Deltas' passed to hub.
|
| 59 |
-
# We need to support Weekly/Monthly deltas for guards too if Tuner sends them.
|
| 60 |
-
# Let's keep it simple: Base + Fast + (Weekly/Monthly if implemented in map)
|
| 61 |
-
# Since Tuner V3.3 passes new_deltas map, we handle it in update_periodic_delta
|
| 62 |
-
|
| 63 |
val = v + d_fast
|
| 64 |
-
# Check if we stored weekly/monthly for guards in a generic way?
|
| 65 |
-
# Currently structure is separate. To avoid complexity, we rely on Base updates from Tuner
|
| 66 |
-
# OR we assume Tuner updates delta_weekly dict which contains ALL keys.
|
| 67 |
-
|
| 68 |
final[k] = max(0.1, min(0.99, val))
|
| 69 |
return final
|
| 70 |
|
|
@@ -89,31 +79,75 @@ class AdaptiveHub:
|
|
| 89 |
self.dna_file_key = "learning/strategic_dna_v60_layered.json"
|
| 90 |
self.current_market_regime = "RANGE"
|
| 91 |
self.strategies: Dict[str, StrategyDNA] = {}
|
| 92 |
-
print("🧠 [AdaptiveHub V60.1]
|
| 93 |
|
| 94 |
async def initialize(self):
|
| 95 |
try:
|
| 96 |
if self.r2:
|
| 97 |
data_bytes = await self.r2.get_file_async(self.dna_file_key)
|
| 98 |
if data_bytes:
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
else:
|
| 101 |
self._create_default_dna()
|
|
|
|
| 102 |
else:
|
| 103 |
self._create_default_dna()
|
| 104 |
self._inject_current_parameters()
|
| 105 |
-
except Exception:
|
|
|
|
| 106 |
self._create_default_dna()
|
| 107 |
|
| 108 |
def _create_default_dna(self):
|
| 109 |
-
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
d_filters_bull = {"l1_min_score": 0.60, "l3_oracle_thresh": 0.75, "l4_sniper_thresh": 0.50}
|
| 112 |
|
| 113 |
-
self.strategies["BULL"] = StrategyDNA(
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
def _load_from_dict(self, data):
|
| 119 |
for key, val in data.get("strategies", {}).items():
|
|
@@ -132,41 +166,31 @@ class AdaptiveHub:
|
|
| 132 |
self.current_market_regime = data.get("current_regime", "RANGE")
|
| 133 |
|
| 134 |
# ============================================================
|
| 135 |
-
# 🧪 STRATEGIC BACKTEST LINK (Base Updater)
|
| 136 |
# ============================================================
|
| 137 |
def submit_challenger(self, regime: str, new_config: dict, new_stats: dict) -> bool:
|
| 138 |
-
"""
|
| 139 |
-
Called by HeavyDutyBacktester (Strategic Run).
|
| 140 |
-
Updates BASE values and resets Deltas (Clean Slate).
|
| 141 |
-
"""
|
| 142 |
if regime not in self.strategies: return False
|
| 143 |
champion = self.strategies[regime]
|
| 144 |
|
| 145 |
-
# Logic to accept new config (Always accept if from manual Strategic run)
|
| 146 |
print(f" ⚖️ [Strategic Update] Updating BASE DNA for {regime}...")
|
| 147 |
|
| 148 |
-
#
|
| 149 |
champion.model_weights['titan'] = new_config['w_titan']
|
| 150 |
champion.model_weights['structure'] = new_config['w_struct']
|
| 151 |
-
|
| 152 |
-
# 2. Update Base Filters
|
| 153 |
champion.base_filters['l1_min_score'] = new_config['thresh']
|
| 154 |
champion.base_filters['l3_oracle_thresh'] = new_config.get('oracle_thresh', 0.65)
|
| 155 |
champion.base_filters['l4_sniper_thresh'] = new_config.get('sniper_thresh', 0.40)
|
| 156 |
-
|
| 157 |
-
# 3. Update Base Guards
|
| 158 |
champion.base_guards['hydra_crash'] = new_config['hydra_thresh']
|
| 159 |
-
champion.base_guards['hydra_giveback'] = new_config['hydra_thresh']
|
| 160 |
champion.base_guards['legacy_v2'] = new_config['legacy_thresh']
|
| 161 |
-
|
| 162 |
champion.backtest_performance = new_stats
|
| 163 |
|
| 164 |
-
#
|
| 165 |
for k in champion.delta_weekly: champion.delta_weekly[k] = 0.0
|
| 166 |
for k in champion.delta_monthly: champion.delta_monthly[k] = 0.0
|
| 167 |
for k in champion.delta_fast: champion.delta_fast[k] = 0.0
|
| 168 |
|
| 169 |
-
#
|
| 170 |
asyncio.create_task(self._save_state_to_r2())
|
| 171 |
self._inject_current_parameters()
|
| 172 |
return True
|
|
@@ -180,14 +204,13 @@ class AdaptiveHub:
|
|
| 180 |
dna.trade_buffer.append(trade_data)
|
| 181 |
|
| 182 |
if len(dna.trade_buffer) >= 100:
|
| 183 |
-
print(f"🎓 [Fast Learner] Batch of 100 trades reached
|
| 184 |
self._process_fast_learning_batch(dna)
|
| 185 |
dna.trade_buffer.clear()
|
| 186 |
await self._save_state_to_r2()
|
| 187 |
self._inject_current_parameters()
|
| 188 |
|
| 189 |
def _process_fast_learning_batch(self, dna: StrategyDNA):
|
| 190 |
-
# Simple Logic: Check Oracle & Sniper
|
| 191 |
models = ['l3_oracle_thresh', 'l4_sniper_thresh']
|
| 192 |
performance = {m: {'wins': 0, 'total': 0, 'pnl_sum': 0.0} for m in models}
|
| 193 |
|
|
@@ -220,10 +243,8 @@ class AdaptiveHub:
|
|
| 220 |
|
| 221 |
if win_rate < TARGET_HIT_LOW and avg_pnl < 0:
|
| 222 |
dna.delta_fast[model] = min(dna.delta_fast[model] + 0.01, 0.05)
|
| 223 |
-
print(f" 📉 {model} performing poorly (WR {win_rate:.2%}). Tightening +1%")
|
| 224 |
elif win_rate > TARGET_HIT_HIGH and avg_pnl > 0.5:
|
| 225 |
dna.delta_fast[model] = max(dna.delta_fast[model] - 0.01, -0.05)
|
| 226 |
-
print(f" 📈 {model} performing great (WR {win_rate:.2%}). Loosening -1%")
|
| 227 |
|
| 228 |
# ============================================================
|
| 229 |
# 🗓️ PERIODIC UPDATES (Weekly/Monthly)
|
|
@@ -235,9 +256,7 @@ class AdaptiveHub:
|
|
| 235 |
target_dict = dna.delta_weekly if update_type == 'weekly' else dna.delta_monthly
|
| 236 |
limit = 0.03 if update_type == 'weekly' else 0.05
|
| 237 |
|
| 238 |
-
# Handles both Filters and Guards based on key names
|
| 239 |
for k, v in new_deltas.items():
|
| 240 |
-
# If key exists in filters or guards, update it
|
| 241 |
if k in dna.base_filters or k in dna.base_guards:
|
| 242 |
target_dict[k] = max(-limit, min(limit, v))
|
| 243 |
|
|
@@ -257,6 +276,7 @@ class AdaptiveHub:
|
|
| 257 |
|
| 258 |
mw = dna.model_weights
|
| 259 |
total_w = sum(mw.values()) or 1.0
|
|
|
|
| 260 |
SystemLimits.L2_WEIGHT_TITAN = mw.get("titan", 0.4) / total_w
|
| 261 |
SystemLimits.L2_WEIGHT_PATTERNS = mw.get("structure", 0.3) / total_w
|
| 262 |
|
|
@@ -268,6 +288,7 @@ class AdaptiveHub:
|
|
| 268 |
SystemLimits.L4_OB_WALL_RATIO = dna.ob_settings.get("wall_ratio_limit", 0.4)
|
| 269 |
|
| 270 |
SystemLimits.HYDRA_CRASH_THRESH = final_guards.get('hydra_crash', 0.85)
|
|
|
|
| 271 |
SystemLimits.LEGACY_V2_PANIC_THRESH = final_guards.get('legacy_v2', 0.95)
|
| 272 |
|
| 273 |
print(f" 💉 [System Updated] {self.current_market_regime} | Oracle: {SystemLimits.L3_CONFIDENCE_THRESHOLD:.3f} | Sniper: {SystemLimits.L4_ENTRY_THRESHOLD:.3f}")
|
|
|
|
| 1 |
# ==============================================================================
|
| 2 |
# 🧠 learning_hub/adaptive_hub.py
|
| 3 |
+
# (V60.1 - GEM-Architect: Golden Values + Full Sync)
|
| 4 |
# ==============================================================================
|
| 5 |
|
| 6 |
import json
|
|
|
|
| 10 |
from collections import deque
|
| 11 |
from typing import Dict, Any, List
|
| 12 |
|
| 13 |
+
# محاولة استيراد SystemLimits لتحديث النظام الحي
|
| 14 |
try:
|
| 15 |
from ml_engine.processor import SystemLimits
|
| 16 |
except ImportError:
|
|
|
|
| 52 |
def get_final_guards(self):
|
| 53 |
final = {}
|
| 54 |
for k, v in self.base_guards.items():
|
| 55 |
+
# For guards, we rely on Base updates mostly, but structure supports delta
|
| 56 |
d_fast = self.delta_guards_fast.get(k, 0.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
val = v + d_fast
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
final[k] = max(0.1, min(0.99, val))
|
| 59 |
return final
|
| 60 |
|
|
|
|
| 79 |
self.dna_file_key = "learning/strategic_dna_v60_layered.json"
|
| 80 |
self.current_market_regime = "RANGE"
|
| 81 |
self.strategies: Dict[str, StrategyDNA] = {}
|
| 82 |
+
print("🧠 [AdaptiveHub V60.1] Golden DNA Injection Active.")
|
| 83 |
|
| 84 |
async def initialize(self):
|
| 85 |
try:
|
| 86 |
if self.r2:
|
| 87 |
data_bytes = await self.r2.get_file_async(self.dna_file_key)
|
| 88 |
if data_bytes:
|
| 89 |
+
saved_data = json.loads(data_bytes)
|
| 90 |
+
self._load_from_dict(saved_data)
|
| 91 |
+
print(" 📂 Loaded DNA from R2.")
|
| 92 |
else:
|
| 93 |
self._create_default_dna()
|
| 94 |
+
print(" ⚠️ No R2 file. Created Golden Defaults.")
|
| 95 |
else:
|
| 96 |
self._create_default_dna()
|
| 97 |
self._inject_current_parameters()
|
| 98 |
+
except Exception as e:
|
| 99 |
+
print(f" ❌ Init Error: {e}")
|
| 100 |
self._create_default_dna()
|
| 101 |
|
| 102 |
def _create_default_dna(self):
|
| 103 |
+
"""
|
| 104 |
+
💎 GEM-Architect: الحقن المباشر للقيم الذهبية (Golden Values)
|
| 105 |
+
"""
|
| 106 |
+
|
| 107 |
+
# 1. BULL MARKET (Golden Config)
|
| 108 |
+
d_guards_bull = {"hydra_crash": 0.60, "hydra_giveback": 0.60, "legacy_v2": 0.85}
|
| 109 |
d_filters_bull = {"l1_min_score": 0.60, "l3_oracle_thresh": 0.75, "l4_sniper_thresh": 0.50}
|
| 110 |
|
| 111 |
+
self.strategies["BULL"] = StrategyDNA(
|
| 112 |
+
"BULL",
|
| 113 |
+
{"titan": 0.50, "structure": 0.10},
|
| 114 |
+
{"wall_ratio_limit": 0.60},
|
| 115 |
+
d_filters_bull,
|
| 116 |
+
d_guards_bull
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
# 2. BEAR MARKET (Golden Config)
|
| 120 |
+
d_guards_bear = {"hydra_crash": 0.60, "hydra_giveback": 0.60, "legacy_v2": 0.85}
|
| 121 |
+
d_filters_bear = {"l1_min_score": 0.60, "l3_oracle_thresh": 0.75, "l4_sniper_thresh": 0.30} # Sniper lower
|
| 122 |
+
|
| 123 |
+
self.strategies["BEAR"] = StrategyDNA(
|
| 124 |
+
"BEAR",
|
| 125 |
+
{"titan": 0.10, "structure": 0.10},
|
| 126 |
+
{"wall_ratio_limit": 0.30},
|
| 127 |
+
d_filters_bear,
|
| 128 |
+
d_guards_bear
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
# 3. RANGE/DEAD (Conservative Defaults)
|
| 132 |
+
d_guards_def = {"hydra_crash": 0.70, "hydra_giveback": 0.70, "legacy_v2": 0.90}
|
| 133 |
+
d_filters_range = {"l1_min_score": 0.65, "l3_oracle_thresh": 0.70, "l4_sniper_thresh": 0.40}
|
| 134 |
+
|
| 135 |
+
self.strategies["RANGE"] = StrategyDNA(
|
| 136 |
+
"RANGE",
|
| 137 |
+
{"titan": 0.30, "structure": 0.30},
|
| 138 |
+
{"wall_ratio_limit": 0.40},
|
| 139 |
+
d_filters_range,
|
| 140 |
+
d_guards_def
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
d_filters_dead = {"l1_min_score": 0.80, "l3_oracle_thresh": 0.80, "l4_sniper_thresh": 0.50}
|
| 144 |
+
self.strategies["DEAD"] = StrategyDNA(
|
| 145 |
+
"DEAD",
|
| 146 |
+
{"titan": 0.10, "structure": 0.10},
|
| 147 |
+
{"wall_ratio_limit": 0.20},
|
| 148 |
+
d_filters_dead,
|
| 149 |
+
{"hydra_crash": 0.50, "hydra_giveback": 0.50, "legacy_v2": 0.80}
|
| 150 |
+
)
|
| 151 |
|
| 152 |
def _load_from_dict(self, data):
|
| 153 |
for key, val in data.get("strategies", {}).items():
|
|
|
|
| 166 |
self.current_market_regime = data.get("current_regime", "RANGE")
|
| 167 |
|
| 168 |
# ============================================================
|
| 169 |
+
# 🧪 STRATEGIC BACKTEST LINK (Base Updater)
|
| 170 |
# ============================================================
|
| 171 |
def submit_challenger(self, regime: str, new_config: dict, new_stats: dict) -> bool:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
if regime not in self.strategies: return False
|
| 173 |
champion = self.strategies[regime]
|
| 174 |
|
|
|
|
| 175 |
print(f" ⚖️ [Strategic Update] Updating BASE DNA for {regime}...")
|
| 176 |
|
| 177 |
+
# Update Base
|
| 178 |
champion.model_weights['titan'] = new_config['w_titan']
|
| 179 |
champion.model_weights['structure'] = new_config['w_struct']
|
|
|
|
|
|
|
| 180 |
champion.base_filters['l1_min_score'] = new_config['thresh']
|
| 181 |
champion.base_filters['l3_oracle_thresh'] = new_config.get('oracle_thresh', 0.65)
|
| 182 |
champion.base_filters['l4_sniper_thresh'] = new_config.get('sniper_thresh', 0.40)
|
|
|
|
|
|
|
| 183 |
champion.base_guards['hydra_crash'] = new_config['hydra_thresh']
|
| 184 |
+
champion.base_guards['hydra_giveback'] = new_config['hydra_thresh']
|
| 185 |
champion.base_guards['legacy_v2'] = new_config['legacy_thresh']
|
|
|
|
| 186 |
champion.backtest_performance = new_stats
|
| 187 |
|
| 188 |
+
# Reset Deltas
|
| 189 |
for k in champion.delta_weekly: champion.delta_weekly[k] = 0.0
|
| 190 |
for k in champion.delta_monthly: champion.delta_monthly[k] = 0.0
|
| 191 |
for k in champion.delta_fast: champion.delta_fast[k] = 0.0
|
| 192 |
|
| 193 |
+
# Save & Inject
|
| 194 |
asyncio.create_task(self._save_state_to_r2())
|
| 195 |
self._inject_current_parameters()
|
| 196 |
return True
|
|
|
|
| 204 |
dna.trade_buffer.append(trade_data)
|
| 205 |
|
| 206 |
if len(dna.trade_buffer) >= 100:
|
| 207 |
+
print(f"🎓 [Fast Learner] Batch of 100 trades reached. Analyzing...")
|
| 208 |
self._process_fast_learning_batch(dna)
|
| 209 |
dna.trade_buffer.clear()
|
| 210 |
await self._save_state_to_r2()
|
| 211 |
self._inject_current_parameters()
|
| 212 |
|
| 213 |
def _process_fast_learning_batch(self, dna: StrategyDNA):
|
|
|
|
| 214 |
models = ['l3_oracle_thresh', 'l4_sniper_thresh']
|
| 215 |
performance = {m: {'wins': 0, 'total': 0, 'pnl_sum': 0.0} for m in models}
|
| 216 |
|
|
|
|
| 243 |
|
| 244 |
if win_rate < TARGET_HIT_LOW and avg_pnl < 0:
|
| 245 |
dna.delta_fast[model] = min(dna.delta_fast[model] + 0.01, 0.05)
|
|
|
|
| 246 |
elif win_rate > TARGET_HIT_HIGH and avg_pnl > 0.5:
|
| 247 |
dna.delta_fast[model] = max(dna.delta_fast[model] - 0.01, -0.05)
|
|
|
|
| 248 |
|
| 249 |
# ============================================================
|
| 250 |
# 🗓️ PERIODIC UPDATES (Weekly/Monthly)
|
|
|
|
| 256 |
target_dict = dna.delta_weekly if update_type == 'weekly' else dna.delta_monthly
|
| 257 |
limit = 0.03 if update_type == 'weekly' else 0.05
|
| 258 |
|
|
|
|
| 259 |
for k, v in new_deltas.items():
|
|
|
|
| 260 |
if k in dna.base_filters or k in dna.base_guards:
|
| 261 |
target_dict[k] = max(-limit, min(limit, v))
|
| 262 |
|
|
|
|
| 276 |
|
| 277 |
mw = dna.model_weights
|
| 278 |
total_w = sum(mw.values()) or 1.0
|
| 279 |
+
|
| 280 |
SystemLimits.L2_WEIGHT_TITAN = mw.get("titan", 0.4) / total_w
|
| 281 |
SystemLimits.L2_WEIGHT_PATTERNS = mw.get("structure", 0.3) / total_w
|
| 282 |
|
|
|
|
| 288 |
SystemLimits.L4_OB_WALL_RATIO = dna.ob_settings.get("wall_ratio_limit", 0.4)
|
| 289 |
|
| 290 |
SystemLimits.HYDRA_CRASH_THRESH = final_guards.get('hydra_crash', 0.85)
|
| 291 |
+
SystemLimits.HYDRA_GIVEBACK_THRESH = final_guards.get('hydra_giveback', 0.70)
|
| 292 |
SystemLimits.LEGACY_V2_PANIC_THRESH = final_guards.get('legacy_v2', 0.95)
|
| 293 |
|
| 294 |
print(f" 💉 [System Updated] {self.current_market_regime} | Oracle: {SystemLimits.L3_CONFIDENCE_THRESHOLD:.3f} | Sniper: {SystemLimits.L4_ENTRY_THRESHOLD:.3f}")
|