Riy777 commited on
Commit
a836b38
·
1 Parent(s): 69e65e0

Create xgboost_pattern_v2.py

Browse files
Files changed (1) hide show
  1. ml_engine/xgboost_pattern_v2.py +174 -0
ml_engine/xgboost_pattern_v2.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ml_engine/xgboost_pattern_v2.py
2
+ # (Pipeline لتجهيز البيانات لنماذج XGBoost V2)
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import pandas_ta as ta
7
+ import logging
8
+
9
+ # إعداد التسجيل (Logging) لتتبع الأخطاء الصامتة
10
+ logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s')
11
+ logger = logging.getLogger(__name__)
12
+
13
+ try:
14
+ from hurst import compute_Hc
15
+ HURST_AVAILABLE = True
16
+ except ImportError:
17
+ logger.warning("مكتبة 'hurst' غير موجودة. سيتم استخدام قيمة افتراضية 0.5 لمؤشر Hurst.")
18
+ HURST_AVAILABLE = False
19
+
20
+ # === دوال مساعدة (مطابقة تماماً لما استخدم في التدريب) ===
21
+
22
+ def _zv(x):
23
+ """حساب Z-Score الآمن (يتجنب القسمة على صفر)"""
24
+ with np.errstate(divide='ignore', invalid='ignore'):
25
+ x = np.asarray(x, dtype="float32")
26
+ m = np.nanmean(x)
27
+ s = np.nanstd(x) + 1e-9
28
+ x_norm = (x - m) / s
29
+ return np.nan_to_num(x_norm, nan=0.0).astype("float32")
30
+
31
+ def ema_np_safe(x, n):
32
+ """حساب المتوسط المتحرك الأسي (EMA) بشكل آمن وسريع باستخدام Numpy"""
33
+ x = np.asarray(x, dtype="float32")
34
+ k = 2.0 / (n + 1.0)
35
+ out = np.empty_like(x)
36
+ out[0] = x[0] if not np.isnan(x[0]) else 0.0
37
+ for i in range(1, len(x)):
38
+ val = x[i] if not np.isnan(x[i]) else out[i-1]
39
+ out[i] = out[i-1] + k * (val - out[i-1])
40
+ return out
41
+
42
+ def mc_simple_fast(closes_np: np.ndarray, target_profit=0.005):
43
+ """نسخة سريعة وآمنة من محاكاة مونت كارلو البسيطة (للميزات الإحصائية)"""
44
+ try:
45
+ if len(closes_np) < 30: return 0.5, 0.0
46
+ c = closes_np
47
+ cur = float(c[-1])
48
+ if cur <= 0: return 0.5, 0.0
49
+
50
+ # حساب العوائد اللوغاريتمية
51
+ lr = np.diff(np.log1p(c))
52
+ lr = lr[np.isfinite(lr)]
53
+ if len(lr) < 20: return 0.5, 0.0
54
+
55
+ mu = np.mean(lr)
56
+ sigma = np.std(lr)
57
+ if sigma < 1e-9: return 0.5, 0.0 # تجنب التقلب الصفري
58
+
59
+ # محاكاة سريعة (500 مسار) باستخدام توزيع t-student (كما في التدريب)
60
+ n_sims = 500
61
+ drift = (mu - 0.5 * sigma**2)
62
+ diffusion = sigma * np.random.standard_t(df=10, size=n_sims)
63
+ sim_prices = cur * np.exp(drift + diffusion)
64
+
65
+ var95 = np.percentile(sim_prices, 5)
66
+ var95_pct = (cur - var95) / (cur + 1e-9)
67
+ prob_gain = np.mean(sim_prices >= cur * (1 + target_profit))
68
+
69
+ return float(prob_gain), float(var95_pct)
70
+ except Exception:
71
+ return 0.5, 0.0
72
+
73
+ # ============================================================
74
+ # === الدالة الرئيسية: تجهيز البيانات للنظام الحي (V6 Pipeline) ===
75
+ # ============================================================
76
+ def transform_candles_for_ml(df_window: pd.DataFrame):
77
+ """
78
+ تحويل نافذة من الشموع (200 شمعة) إلى متجه ميزات جاهز لنموذج ML.
79
+ Input: DataFrame (must have columns: 'open', 'high', 'low', 'close', 'volume')
80
+ Output: Numpy Array shape (1, 3803) or None if failed.
81
+ """
82
+ try:
83
+ # التأكد من وجود بيانات كافية (نحتاج 200 شمعة بالضبط للحفاظ على الشكل)
84
+ if len(df_window) < 200:
85
+ # في حالة البيانات الناقصة، يمكننا إما الرفض أو التعبئة بأصفار (الرفض أسلم)
86
+ # logger.warning(f"بيانات غير كافية للتحويل: {len(df_window)} < 200")
87
+ return None
88
+
89
+ df = df_window.iloc[-200:].copy() # نضمن أخذ آخر 200 فقط
90
+
91
+ # تحويل الأعمدة إلى numpy arrays (float32)
92
+ o = df["open"].to_numpy(dtype="float32")
93
+ h = df["high"].to_numpy(dtype="float32")
94
+ l = df["low"].to_numpy(dtype="float32")
95
+ c = df["close"].to_numpy(dtype="float32")
96
+ v = df["volume"].to_numpy(dtype="float32")
97
+
98
+ # --- 1. الميزات الأساسية (5 ميزات × 200) ---
99
+ base = np.stack([o, h, l, c, v], axis=1) # Shape: (200, 5)
100
+ base_z = _zv(base)
101
+
102
+ # --- 2. ميزات إضافية (2 ميزة × 200) ---
103
+ lr = np.zeros_like(c)
104
+ lr[1:] = np.diff(np.log1p(c)) # Log Returns
105
+ rng = (h - l) / (c + 1e-9) # Range
106
+ extra = np.stack([lr, rng], axis=1) # Shape: (200, 2)
107
+ extra_z = _zv(extra)
108
+
109
+ # --- 3. المؤشرات الفنية (12 ميزة × 200) - "المحرك المُدرّع V7" ---
110
+ ema9 = ema_np_safe(c, 9)
111
+ ema21 = ema_np_safe(c, 21)
112
+ ema50 = ema_np_safe(c, 50)
113
+ ema200 = ema_np_safe(c, 200)
114
+ slope21 = np.concatenate([[0.0], np.diff(ema21)])
115
+ slope50 = np.concatenate([[0.0], np.diff(ema50)])
116
+
117
+ # استخدام try...except لكل مؤشر من مكتبة pandas_ta لضمان المتانة
118
+ try: rsi = ta.rsi(pd.Series(c), length=14).fillna(50).to_numpy(dtype="float32")
119
+ except: rsi = np.full_like(c, 50.0, dtype="float32")
120
+
121
+ try:
122
+ macd_data = ta.macd(pd.Series(c), fast=12, slow=26, signal=9)
123
+ macd_line = macd_data.iloc[:, 0].fillna(0).to_numpy(dtype="float32")
124
+ macd_hist = macd_data.iloc[:, 2].fillna(0).to_numpy(dtype="float32")
125
+ except:
126
+ macd_line = np.zeros_like(c, dtype="float32")
127
+ macd_hist = np.zeros_like(c, dtype="float32")
128
+
129
+ try: atr = ta.atr(pd.Series(h), pd.Series(l), pd.Series(c), length=14).fillna(0).to_numpy(dtype="float32")
130
+ except: atr = np.zeros_like(c, dtype="float32")
131
+
132
+ try:
133
+ bb = ta.bbands(pd.Series(c), length=20, std=2)
134
+ # BB%B: موقع السعر بالنسبة للنطاق
135
+ bb_p = ((c - bb.iloc[:, 0]) / (bb.iloc[:, 2] - bb.iloc[:, 0] + 1e-9)).fillna(0.5).to_numpy(dtype="float32")
136
+ except: bb_p = np.full_like(c, 0.5, dtype="float32")
137
+
138
+ try: obv = ta.obv(pd.Series(c), pd.Series(v)).fillna(0).to_numpy(dtype="float32")
139
+ except: obv = np.zeros_like(c, dtype="float32")
140
+
141
+ # تجميع المؤشرات الـ 12 وتطبيق Z-Score عليها
142
+ indicators = np.stack([
143
+ ema9, ema21, ema50, ema200, slope21, slope50,
144
+ rsi, macd_line, macd_hist, atr / (c + 1e-9), bb_p, obv
145
+ ], axis=1) # Shape: (200, 12)
146
+ indicators_z = _zv(indicators)
147
+
148
+ # --- 4. الدمج والتسطيح (Flattening) ---
149
+ # الشكل النهائي قبل التسطيح: (200, 5 + 2 + 12) = (200, 19)
150
+ X_seq = np.concatenate([base_z, extra_z, indicators_z], axis=1)
151
+ X_seq_flat = X_seq.reshape(1, -1) # Shape: (1, 3800)
152
+
153
+ # --- 5. الميزات الثابتة (3 ميزات) ---
154
+ try: mc_p, mc_var = mc_simple_fast(c[-100:]) # آخر 100 شمعة للمحاكاة السريعة
155
+ except: mc_p, mc_var = 0.5, 0.0
156
+
157
+ hurst_val = 0.5
158
+ if HURST_AVAILABLE:
159
+ try: hurst_val = compute_Hc(c[-100:], kind='price', simplified=True)[0]
160
+ except: pass
161
+
162
+ X_stat = np.array([[mc_p, mc_var, hurst_val]], dtype="float32") # Shape: (1, 3)
163
+
164
+ # --- 6. الدمج النهائي ---
165
+ X_final = np.concatenate([X_seq_flat, X_stat], axis=1) # Shape: (1, 3803)
166
+
167
+ # استبدال أي NaN متبقي بـ 0 للأمان التام
168
+ X_final = np.nan_to_num(X_final, nan=0.0, posinf=0.0, neginf=0.0)
169
+
170
+ return X_final
171
+
172
+ except Exception as e:
173
+ # logger.error(f"Pipeline Error during transformation: {e}")
174
+ return None