Tradtesting / ml_engine /monte_carlo.py
Riy777's picture
Update ml_engine/monte_carlo.py
1cc8695
raw
history blame
6.9 kB
# 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")