Riy777 commited on
Commit
43d96ec
·
verified ·
1 Parent(s): da5681d

Update ml_engine/oracle_engine.py

Browse files
Files changed (1) hide show
  1. ml_engine/oracle_engine.py +110 -189
ml_engine/oracle_engine.py CHANGED
@@ -1,229 +1,150 @@
 
 
 
 
 
 
 
 
 
1
  import os
2
  import joblib
3
  import numpy as np
4
  import pandas as pd
5
- import pandas_ta as ta
6
  import lightgbm as lgb
7
  import warnings
8
- from typing import Dict, Any, List, Optional
9
 
10
- # --- [ إعدادات النظام ] ---
11
- warnings.filterwarnings('ignore', category=FutureWarning)
12
 
13
  class OracleEngine:
14
- def __init__(self, model_dir: str = "ml_models/Unified_Models_V1"):
15
- """
16
- Oracle V4.2: Config-Injectable Strategic Brain
17
- """
18
- self.model_dir = model_dir
19
- self.model_direction = None
20
- self.model_target = None
21
- self.model_strength = None
22
 
23
- # القيمة الافتراضية (سيتم تحديثها من Processor)
24
- self.confidence_threshold = 0.65
 
25
 
26
- self.feature_cols = []
27
- self.initialized = False
28
- print("🧠 [Oracle V4.2] Engine Instance Created.")
29
-
30
- # ✅ دالة لاستقبال الإعدادات المركزية
31
- def set_threshold(self, threshold: float):
32
- self.confidence_threshold = threshold
33
- # print(f"🔧 [Oracle] Threshold updated to: {self.confidence_threshold}")
34
 
35
  async def initialize(self):
36
- """تحميل النماذج وخريطة الميزات"""
37
  if self.initialized: return True
38
-
39
- print(f"🧠 [Oracle V4] Loading artifacts from {self.model_dir}...")
40
  try:
41
- feat_path = os.path.join(self.model_dir, "feature_columns.pkl")
42
- if not os.path.exists(feat_path):
43
- print(f"❌ [Oracle] Feature map missing: {feat_path}")
44
- return False
45
- self.feature_cols = joblib.load(feat_path)
46
-
47
- dir_path = os.path.join(self.model_dir, "lgbm_direction.txt")
48
- tgt_path = os.path.join(self.model_dir, "lgbm_target_class.txt")
49
- str_path = os.path.join(self.model_dir, "lgbm_strength.txt")
50
-
51
- if os.path.exists(dir_path):
52
- self.model_direction = lgb.Booster(model_file=dir_path)
53
- else:
54
  return False
55
-
56
- if os.path.exists(tgt_path):
57
- self.model_target = lgb.Booster(model_file=tgt_path)
58
-
59
- if os.path.exists(str_path):
60
- self.model_strength = lgb.Booster(model_file=str_path)
61
-
62
  self.initialized = True
63
- print(f"✅ [Oracle V4] Ready. Dynamic Threshold: {self.confidence_threshold}")
64
  return True
65
-
66
  except Exception as e:
67
  print(f"❌ [Oracle] Init Error: {e}")
68
  return False
69
 
70
- def _calculate_snapshot_features(self, df, tf_prefix):
71
- df = df.copy()
72
- df['close'] = df['close'].astype(float)
73
- df['volume'] = df['volume'].astype(float)
74
-
75
- if len(df) < 15:
76
- return pd.DataFrame()
77
-
78
  try:
79
- df[f'{tf_prefix}_slope'] = ta.slope(df['close'], length=7)
80
- df[f'{tf_prefix}_rsi'] = ta.rsi(df['close'], length=14)
81
- atr = ta.atr(df['high'], df['low'], df['close'], length=14)
82
- df[f'{tf_prefix}_atr_pct'] = atr / df['close']
83
- vol_mean = df['volume'].rolling(20).mean()
84
- vol_std = df['volume'].rolling(20).std()
85
- df[f'{tf_prefix}_vol_z'] = (df['volume'] - vol_mean) / (vol_std + 1e-9)
86
 
87
- cols = [f'{tf_prefix}_slope', f'{tf_prefix}_rsi', f'{tf_prefix}_atr_pct', f'{tf_prefix}_vol_z']
88
- return df[cols].ffill().bfill().fillna(0)
89
- except Exception as e:
90
- return pd.DataFrame()
91
-
92
- def _create_feature_vector(self, ohlcv_data: Dict[str, Any], titan_score: float, mc_score: float, pattern_score: float) -> Optional[pd.DataFrame]:
93
- try:
94
- raw_1h = ohlcv_data.get('1h')
95
- if not raw_1h or len(raw_1h) < 30: return None
96
-
97
- df_1h = pd.DataFrame(raw_1h, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
98
- df_15m = pd.DataFrame(ohlcv_data.get('15m', []), columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
99
- df_4h = pd.DataFrame(ohlcv_data.get('4h', []), columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
100
-
101
- feats_1h = self._calculate_snapshot_features(df_1h, "1h").iloc[-1:].reset_index(drop=True)
102
 
103
- if len(df_15m) > 20:
104
- feats_15m = self._calculate_snapshot_features(df_15m, "15m").iloc[-1:].reset_index(drop=True)
105
- else:
106
- feats_15m = pd.DataFrame(np.zeros((1, 4)), columns=[f'15m_{c}' for c in ['slope', 'rsi', 'atr_pct', 'vol_z']])
107
-
108
- if len(df_4h) > 20:
109
- feats_4h = self._calculate_snapshot_features(df_4h, "4h").iloc[-1:].reset_index(drop=True)
110
- else:
111
- feats_4h = pd.DataFrame(np.zeros((1, 4)), columns=[f'4h_{c}' for c in ['slope', 'rsi', 'atr_pct', 'vol_z']])
112
-
113
- vector = pd.concat([feats_1h, feats_15m, feats_4h], axis=1)
114
 
115
- vector['sim_titan_score'] = float(titan_score)
116
- vector['sim_mc_score'] = float(mc_score)
117
- vector['sim_pattern_score'] = float(pattern_score)
118
-
119
- final_vector = pd.DataFrame(columns=self.feature_cols)
120
- for col in self.feature_cols:
121
- if col in vector.columns:
122
- val = vector[col].iloc[0]
123
- final_vector.at[0, col] = float(val) if not pd.isna(val) else 0.0
124
- else:
125
- final_vector.at[0, col] = 0.0
126
 
127
- return final_vector.astype(float)
 
 
128
 
129
- except Exception as e:
130
- print(f"❌ Vector Creation Error: {e}")
131
- return None
132
-
133
- # ==========================================================================
134
- # 🔮 التنبؤ (Inference Logic - SPOT MODE)
135
- # ==========================================================================
136
- async def predict(self, symbol_data: Dict[str, Any]) -> Dict[str, Any]:
137
- """تحليل الفرصة باستخدام العتبة الديناميكية"""
138
  if not self.initialized:
139
- return {'action': 'WAIT', 'reason': 'Not initialized', 'confidence': 0.0}
140
 
141
  try:
142
- ohlcv = symbol_data.get('ohlcv')
143
- current_price = symbol_data.get('current_price', 0.0)
 
 
 
144
 
145
- titan = symbol_data.get('titan_score', 0.0)
146
- mc = symbol_data.get('mc_score', 0.0)
147
- patt = symbol_data.get('patterns_score', 0.0)
148
-
149
- features = self._create_feature_vector(ohlcv, titan, mc, patt)
150
- if features is None:
151
- return {'action': 'WAIT', 'reason': 'Features failed', 'confidence': 0.0}
152
-
153
- # 1. التنبؤ بالاتجاه
154
- dir_probs = self.model_direction.predict(features)[0]
155
 
156
- if isinstance(dir_probs, (np.ndarray, list)):
157
- prob_long = float(dir_probs[0])
158
- prob_short = float(dir_probs[1])
159
- else:
160
- prob_short = float(dir_probs)
161
- prob_long = 1.0 - prob_short
162
-
163
- if prob_short > prob_long:
164
- return {
165
- 'action': 'WAIT',
166
- 'reason': f'Bearish (Short Prob: {prob_short:.2f})',
167
- 'direction': 'SHORT',
168
- 'confidence': prob_long,
169
- 'short_confidence': prob_short
170
- }
171
-
172
- confidence = prob_long
173
-
174
- # 2. البوابة المنطقية (استخدام العتبة الديناميكية المحقونة)
175
- # ✅ هنا التغيير الجوهري: نستخدم self.confidence_threshold بدلاً من الثابت
176
- if confidence < self.confidence_threshold:
177
- return {
178
- 'action': 'WAIT',
179
- 'reason': f'Low Confidence ({confidence:.2f} < {self.confidence_threshold})',
180
- 'direction': 'LONG',
181
- 'confidence': confidence,
182
- 'short_confidence': prob_short
183
- }
184
-
185
- # 3. التنبؤ بالأهداف والقوة
186
- strength = 0.5
187
- if self.model_strength:
188
- strength = float(self.model_strength.predict(features)[0])
189
- strength = max(0.0, min(1.0, strength))
190
-
191
- tp_class_idx = 1
192
- if self.model_target:
193
- tgt_probs = self.model_target.predict(features)[0]
194
- tp_class_idx = np.argmax(tgt_probs)
195
 
196
- tp_labels = ['TP1', 'TP2', 'TP3', 'TP4']
197
- target_profile = tp_labels[tp_class_idx]
198
-
199
- # 4. حساب المستويات
200
- atr_pct_val = features['1h_atr_pct'].iloc[0]
201
- if atr_pct_val == 0: atr_pct_val = 0.02
202
-
203
- atr_abs = atr_pct_val * current_price
204
 
205
- tp_map = {
206
- 'TP1': current_price + (1.0 * atr_abs),
207
- 'TP2': current_price + (1.8 * atr_abs),
208
- 'TP3': current_price + (2.8 * atr_abs),
209
- 'TP4': current_price + (4.5 * atr_abs),
210
- }
211
 
212
- primary_tp = tp_map[target_profile]
213
- sl_price = current_price - (1.2 * atr_abs)
214
-
215
- return {
216
- 'action': 'WATCH',
217
- 'action_type': 'BUY',
218
- 'confidence': float(confidence),
219
- 'strength': float(strength),
220
- 'target_class': target_profile,
221
- 'primary_tp': float(primary_tp),
222
- 'sl_price': float(sl_price),
223
- 'tp_map': tp_map,
224
- 'analysis_summary': f"SPOT BUY (Conf: {confidence:.0%}) | Str: {strength:.2f} | Aim: {target_profile}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  }
 
 
 
 
 
 
 
 
226
 
227
  except Exception as e:
228
- print(f"❌ [Oracle] Prediction Error: {e}")
 
229
  return {'action': 'WAIT', 'reason': 'Error', 'confidence': 0.0}
 
1
+ # ==============================================================================
2
+ # 🧠 ml_engine/oracle_engine.py (V4.5 - LightGBM Golden Threshold Edition)
3
+ # ==============================================================================
4
+ # GEM-Architect Approved
5
+ # - Uses the trained LightGBM model.
6
+ # - Implements the "Golden Threshold" strategy (0.5% Predicted Return).
7
+ # - Integrates CNN probabilities + Market Context properly.
8
+ # ==============================================================================
9
+
10
  import os
11
  import joblib
12
  import numpy as np
13
  import pandas as pd
 
14
  import lightgbm as lgb
15
  import warnings
16
+ import traceback
17
 
18
+ warnings.filterwarnings('ignore')
 
19
 
20
  class OracleEngine:
21
+ def __init__(self, model_dir="ml_models/Unified_Models_V1"):
22
+ self.model_path = os.path.join(model_dir, "oracle_lgbm.txt")
23
+ self.model = None
24
+ self.initialized = False
 
 
 
 
25
 
26
+ # 🏆 THE GOLDEN CONFIGURATION (From Testing)
27
+ # Threshold 0.005 (0.5% return) gave 77% Win Rate.
28
+ self.CONFIDENCE_THRESHOLD = 0.005
29
 
30
+ # Context Features (Must match training)
31
+ self.ctx_features = ["ret_var_30", "ret_skew_30", "ret_kurt_30"]
32
+ # CNN Features inputs
33
+ self.cnn_cols = ["cnn_prob_neutral", "cnn_prob_loss", "cnn_prob_win"]
 
 
 
 
34
 
35
  async def initialize(self):
36
+ """Load LightGBM Model"""
37
  if self.initialized: return True
38
+
39
+ print(f"🧠 [Oracle] Loading Strategic Brain from {self.model_path}...")
40
  try:
41
+ if not os.path.exists(self.model_path):
42
+ print(f"❌ [Oracle] Model missing: {self.model_path}")
 
 
 
 
 
 
 
 
 
 
 
43
  return False
44
+
45
+ self.model = lgb.Booster(model_file=self.model_path)
 
 
 
 
 
46
  self.initialized = True
47
+ print(f"✅ [Oracle] Online. Strategy Threshold: >{self.CONFIDENCE_THRESHOLD*100:.1f}% Expected Return.")
48
  return True
49
+
50
  except Exception as e:
51
  print(f"❌ [Oracle] Init Error: {e}")
52
  return False
53
 
54
+ def _calc_context_features(self, df):
55
+ """Calculate statistical context features from OHLCV"""
 
 
 
 
 
 
56
  try:
57
+ if len(df) < 30: return np.zeros(3)
 
 
 
 
 
 
58
 
59
+ close = df['close'].values.astype(float)
60
+ prev_close = np.roll(close, 1); prev_close[0] = close[0]
61
+ log_ret = np.log(close / np.maximum(prev_close, 1e-9))
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
+ # Rolling 30 stats (Last value)
64
+ s = pd.Series(log_ret)
65
+ roll = s.rolling(30)
 
 
 
 
 
 
 
 
66
 
67
+ var = roll.var().iloc[-1]
68
+ skew = roll.skew().iloc[-1]
69
+ kurt = roll.kurt().iloc[-1]
 
 
 
 
 
 
 
 
70
 
71
+ return np.array([var, skew, kurt])
72
+ except:
73
+ return np.zeros(3)
74
 
75
+ async def predict(self, symbol_data: dict) -> dict:
76
+ """
77
+ Decision Core.
78
+ Input: symbol_data containing 'ohlcv' and 'titan_probs'.
79
+ """
 
 
 
 
80
  if not self.initialized:
81
+ return {'action': 'WAIT', 'reason': 'Oracle Not Init', 'confidence': 0.0}
82
 
83
  try:
84
+ # 1. Get Inputs
85
+ # Titan Probs: [Neutral, Loss, Win]
86
+ titan_probs = symbol_data.get('titan_probs')
87
+ if not titan_probs or len(titan_probs) != 3:
88
+ return {'action': 'WAIT', 'reason': 'No Titan Input', 'confidence': 0.0}
89
 
90
+ # Market Context (From 15m data)
91
+ ohlcv_15m = symbol_data.get('ohlcv', {}).get('15m')
92
+ if ohlcv_15m is None or ohlcv_15m.empty:
93
+ return {'action': 'WAIT', 'reason': 'No Market Data', 'confidence': 0.0}
 
 
 
 
 
 
94
 
95
+ # 2. Build Feature Vector
96
+ # Order: [cnn_p0, cnn_p1, cnn_p2, ctx_var, ctx_skew, ctx_kurt]
97
+ ctx_vals = self._calc_context_features(ohlcv_15m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ # Handle NaN/Inf
100
+ ctx_vals = np.nan_to_num(ctx_vals, nan=0.0)
 
 
 
 
 
 
101
 
102
+ input_vector = np.concatenate([titan_probs, ctx_vals]).reshape(1, -1)
 
 
 
 
 
103
 
104
+ # 3. Predict (Net Expected Return)
105
+ predicted_pnl = float(self.model.predict(input_vector)[0])
106
+
107
+ # 4. Decision Logic (The Golden Rule)
108
+ # Titan Win Prob (Raw Confidence)
109
+ cnn_win_prob = titan_probs[2]
110
+
111
+ # We combine Oracle PnL Prediction AND Titan Win Prob
112
+ # Oracle says "How much money?", Titan says "How likely?"
113
+
114
+ is_buy = False
115
+ reason = ""
116
+
117
+ if predicted_pnl > self.CONFIDENCE_THRESHOLD:
118
+ # Strong signal
119
+ is_buy = True
120
+ strength = "HIGH"
121
+ reason = f"Golden Setup (Exp. Ret: {predicted_pnl*100:.2f}%)"
122
+ elif predicted_pnl > (self.CONFIDENCE_THRESHOLD * 0.5) and cnn_win_prob > 0.7:
123
+ # Moderate return but very high certainty
124
+ is_buy = True
125
+ strength = "MODERATE"
126
+ reason = f"High Certainty (Win Prob: {cnn_win_prob:.2f})"
127
+ else:
128
+ reason = f"Weak Signal (Exp: {predicted_pnl*100:.2f}% < {self.CONFIDENCE_THRESHOLD*100:.1f}%)"
129
+
130
+ # 5. Build Result
131
+ result = {
132
+ 'confidence': float(cnn_win_prob), # For compatibility with old logic
133
+ 'oracle_score': float(predicted_pnl), # The real juice
134
+ 'target_class': "TP2" if predicted_pnl > 0.01 else "TP1",
135
+ 'action_type': 'BUY',
136
+ 'analysis_summary': f"Oracle: {predicted_pnl*100:.2f}% Return | Titan: {cnn_win_prob:.2f} Win"
137
  }
138
+
139
+ if is_buy:
140
+ result['action'] = 'WATCH' # System will upgrade to BUY after Governance
141
+ else:
142
+ result['action'] = 'WAIT'
143
+ result['reason'] = reason
144
+
145
+ return result
146
 
147
  except Exception as e:
148
+ print(f"❌ [Oracle] Inference Error: {e}")
149
+ traceback.print_exc()
150
  return {'action': 'WAIT', 'reason': 'Error', 'confidence': 0.0}