Spaces:
Paused
Paused
| # ml_engine/monte_carlo.py (V10.3 - Robust Debugging & GARCH Fallback) | |
| import numpy as np | |
| import pandas as pd | |
| import traceback | |
| import logging | |
| # محاولة استيراد arch بشكل آمن | |
| try: | |
| from arch import arch_model | |
| ARCH_AVAILABLE = True | |
| except ImportError: | |
| ARCH_AVAILABLE = False | |
| print("⚠️ [Monte Carlo] 'arch' library not found. Running in fallback mode.") | |
| def _sanitize_results_for_json(results_dict): | |
| if isinstance(results_dict, dict): | |
| return {k: _sanitize_results_for_json(v) for k, v in results_dict.items()} | |
| elif isinstance(results_dict, list): | |
| return [_sanitize_results_for_json(v) for v in results_dict] | |
| elif isinstance(results_dict, np.ndarray): | |
| return results_dict.tolist() | |
| elif isinstance(results_dict, (np.float64, np.float32)): | |
| return float(results_dict) | |
| elif isinstance(results_dict, (np.int64, np.int32)): | |
| return int(results_dict) | |
| else: | |
| return results_dict | |
| class MonteCarloAnalyzer: | |
| def __init__(self): | |
| self.simulation_results = {} | |
| def generate_1h_price_distribution_simple(self, closes_np: np.ndarray) -> dict: | |
| """ | |
| محاكاة سريعة وبسيطة للإغلاق القادم. | |
| """ | |
| try: | |
| if len(closes_np) < 20: | |
| return {'mc_prob_gain': 0.5, 'error': True, 'msg': 'Insufficient data < 20'} | |
| current_price = closes_np[-1] | |
| if current_price <= 0: return {'mc_prob_gain': 0.5, 'error': True} | |
| log_returns = np.log(closes_np[1:] / closes_np[:-1]) | |
| log_returns = log_returns[~np.isnan(log_returns) & ~np.isinf(log_returns)] | |
| if len(log_returns) < 10: return {'mc_prob_gain': 0.5, 'error': True} | |
| mean_return = np.mean(log_returns) | |
| std_return = np.std(log_returns) | |
| # محاكاة بسيطة | |
| drift = (mean_return - 0.5 * std_return**2) | |
| simulated_returns = np.random.normal(drift, std_return, 1000) | |
| simulated_prices = current_price * np.exp(simulated_returns) | |
| prob_gain = np.mean(simulated_prices > current_price) | |
| var95_price = np.percentile(simulated_prices, 5) | |
| var95_pct = (current_price - var95_price) / current_price | |
| return { | |
| 'mc_prob_gain': float(prob_gain), | |
| 'mc_var_95_pct': float(var95_pct), | |
| 'error': False | |
| } | |
| except Exception: | |
| return {'mc_prob_gain': 0.5, 'error': True} | |
| async def generate_1h_distribution_advanced(self, ohlcv_data, target_profit_percent=0.0): | |
| """ | |
| (V10.3) النسخة المتقدمة مع تصحيح الأخطاء وآلية السقوط (Fallback). | |
| الهدف: حساب احتمالية أن يكون السعر بعد ساعة > السعر الحالي (target=0.0). | |
| """ | |
| debug_info = {} | |
| try: | |
| # 1. التحقق من البيانات | |
| if not ohlcv_data or '1h' not in ohlcv_data: | |
| print("⚠️ [MC Advanced] No 1h data provided.") | |
| return {'probability_of_gain': 0.5, 'error': True, 'reason': 'NO_DATA'} | |
| candles = ohlcv_data['1h'] | |
| if len(candles) < 30: # تم تقليل الشرط ليتوافق مع app.py | |
| print(f"⚠️ [MC Advanced] Insufficient candles: {len(candles)} < 30") | |
| return {'probability_of_gain': 0.5, 'error': True, 'reason': 'INSUFFICIENT_DATA'} | |
| # 2. تحضير الداتا فريم | |
| df = pd.DataFrame(candles, columns=['ts', 'o', 'h', 'l', 'c', 'v']) | |
| df[['c']] = df[['c']].astype(float) | |
| current_price = df['c'].iloc[-1] | |
| # حساب العوائد اللوغاريتمية | |
| df['log_ret'] = np.log(df['c'] / df['c'].shift(1)) | |
| rets = df['log_ret'].replace([np.inf, -np.inf], np.nan).dropna() | |
| if len(rets) < 20: | |
| return {'probability_of_gain': 0.5, 'error': True, 'reason': 'NOT_ENOUGH_RETURNS'} | |
| # 3. تقدير التقلب (Volatility Estimation) | |
| vol_est = np.std(rets.iloc[-30:]) # القيمة الافتراضية | |
| model_used = 'Simple_StdDev' | |
| # محاولة استخدام GARCH إذا كان متاحاً | |
| if ARCH_AVAILABLE: | |
| try: | |
| # نقوم بتكبير القيم لتجنب مشاكل التقارب (Scaling) | |
| am = arch_model(rets * 100, vol='Garch', p=1, q=1, dist='t', rescale=False) | |
| res = am.fit(disp='off', show_warning=False) | |
| # التنبؤ بالتقلب للفترة القادمة | |
| forecast = res.forecast(horizon=1) | |
| # استعادة القيمة (Scaling back) | |
| vol_est = np.sqrt(forecast.variance.iloc[-1, 0]) / 100 | |
| model_used = 'GARCH(1,1)' | |
| except Exception as e: | |
| # فشل GARCH ليس كارثياً، نعود للانحراف المعياري | |
| # print(f" -> [MC Debug] GARCH failed ({e}), using Simple Volatility.") | |
| pass | |
| debug_info['volatility'] = vol_est | |
| debug_info['model'] = model_used | |
| # 4. المحاكاة (Simulation) | |
| # Drift Assuming random walk with drift | |
| recent_mean = np.mean(rets.iloc[-30:]) | |
| drift = (recent_mean - 0.5 * vol_est**2) | |
| num_sims = 2000 | |
| # استخدام توزيع t-student للأطراف السمينة (Fat Tails) | |
| random_shocks = np.random.standard_t(df=10, size=num_sims) | |
| simulated_returns = drift + vol_est * random_shocks | |
| simulated_prices = current_price * np.exp(simulated_returns) | |
| # 5. حساب الاحتمالات | |
| # هل السعر المستقبلي >= السعر الحالي + الهدف؟ | |
| target_price = current_price * (1 + target_profit_percent) | |
| prob_gain = np.mean(simulated_prices >= target_price) | |
| var95 = current_price - np.percentile(simulated_prices, 5) | |
| return _sanitize_results_for_json({ | |
| 'probability_of_gain': prob_gain, | |
| 'risk_metrics': {'VaR_95_value': var95}, | |
| 'simulation_model': model_used, | |
| 'debug': debug_info, | |
| 'error': False | |
| }) | |
| except Exception as e: | |
| print(f"❌ [MC Advanced FATAL] {e}") | |
| traceback.print_exc() | |
| return {'probability_of_gain': 0.5, 'error': True, 'reason': str(e)} | |
| print("✅ ML Module: Monte Carlo V10.3 (Robust GARCH Fallback) loaded") |