2025-10-16 18:03:47 -04:00 | | | //+------------------------------------------------------------------+
|
| | | //| AdaptiveEngine.mqh - NUCLEAR GRADE Universal Auto-Detection |
|
| | | //| Automatically adapts to ANY symbol and timeframe |
|
| | | //+------------------------------------------------------------------+
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Symbol Profile Structure - Comprehensive Analysis |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SymbolProfile {
|
| | | string symbol;
|
| | | double avg_atr_ratio; // ATR/Price ratio for volatility scaling
|
| | | double avg_spread_pct; // Spread as % of price
|
| | | double volatility_percentile; // Current volatility vs historical
|
| | | double liquidity_score; // Volume-based liquidity assessment
|
| | | double optimal_sl_mult; // Auto-calculated optimal SL
|
| | | double optimal_tp_mult; // Auto-calculated optimal TP
|
| | | double risk_multiplier; // Auto-calculated risk adjustment
|
| | | double timeframe_mult; // Timeframe-specific multiplier
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Baseline Symbol Configurations (Fallback Only) |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SymbolConfig {
|
| | | string symbol;
|
| | | double base_sl_mult;
|
| | | double base_tp_mult;
|
| | | double volatility_class;
|
| | | double spread_threshold;
|
| | | };
|
| | |
|
| | | SymbolConfig g_symbol_configs[] = {
|
| | | {"EURUSD", 150.0, 300.0, 1.0, 0.00020},
|
| | | {"GBPUSD", 180.0, 360.0, 1.2, 0.00025},
|
| | | {"USDJPY", 120.0, 240.0, 0.8, 0.00015},
|
| | | {"XAUUSD", 350.0, 700.0, 3.0, 0.50},
|
| | | {"AUDUSD", 150.0, 300.0, 1.1, 0.00022},
|
| | | {"USDCAD", 140.0, 280.0, 1.0, 0.00020},
|
| | | {"GBPJPY", 200.0, 400.0, 1.5, 0.00030},
|
| | | {"EURJPY", 160.0, 320.0, 1.2, 0.00025},
|
| | | {"BTCUSD", 800.0, 1600.0, 5.0, 10.0},
|
| | | {"USOIL", 500.0, 1000.0, 2.5, 0.05},
|
| | | {"NGAS", 400.0, 800.0, 3.5, 0.01}
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| UNIVERSAL AUTO-DETECTION ENGINE - Works on ANY Symbol/Timeframe |
|
| | | //+------------------------------------------------------------------+
|
| | | class CUniversalDetector {
|
| | | private:
|
| | | SymbolProfile m_profile;
|
| | | string m_symbol;
|
| | | ENUM_TIMEFRAMES m_timeframe;
|
| | |
|
| | | public:
|
| | | CUniversalDetector() {}
|
| | |
|
| | | // Analyze any symbol and timeframe - returns optimized parameters
|
| | | SymbolProfile AnalyzeSymbol(const string symbol, const ENUM_TIMEFRAMES tf) {
|
| | | m_symbol = symbol;
|
| | | m_timeframe = tf;
|
| | |
|
| | | // Real-time ATR analysis
|
| | | int atr_handle = iATR(symbol, tf, 14);
|
| | | double atr_buffer[];
|
| | | ArraySetAsSeries(atr_buffer, true);
|
| | | double atr = 0.0;
|
| | | if(CopyBuffer(atr_handle, 0, 0, 1, atr_buffer) > 0) {
|
| | | atr = atr_buffer[0];
|
| | | }
|
| | | IndicatorRelease(atr_handle);
|
| | |
|
| | | // Current price for ratio calculation
|
| | | double price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
| | | if(price <= 0) price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
| | |
|
| | | m_profile.symbol = symbol;
|
| | | m_profile.avg_atr_ratio = (price > 0) ? (atr / price) : 0.01;
|
| | |
|
| | | // Spread analysis
|
| | | double spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT);
|
| | | m_profile.avg_spread_pct = (price > 0) ? ((spread / price) * 100.0) : 0.001;
|
| | |
|
2026-02-24 12:47:37 -05:00 | | | // DEBUG: Log raw spread values
|
| | | PrintFormat("[DEBUG-SPREAD] %s | SpreadPoints=%d | Point=%.8f | RawSpread=%.8f | Price=%.4f | SpreadPct=%.6f",
|
| | | symbol,
|
| | | (int)SymbolInfoInteger(symbol, SYMBOL_SPREAD),
|
| | | SymbolInfoDouble(symbol, SYMBOL_POINT),
|
| | | spread,
|
| | | price,
|
| | | m_profile.avg_spread_pct);
|
| | |
|
2025-10-16 18:03:47 -04:00 | | | // Volatility percentile (20-period lookback)
|
| | | m_profile.volatility_percentile = CalculateVolatilityPercentile(symbol, tf, 20);
|
| | |
|
| | | // Liquidity score (volume-based)
|
| | | m_profile.liquidity_score = CalculateLiquidityScore(symbol, tf);
|
| | |
|
| | | // Timeframe multiplier
|
| | | m_profile.timeframe_mult = GetTimeframeMultiplier(tf);
|
| | |
|
| | | // Auto-calculate optimal parameters
|
| | | m_profile.optimal_sl_mult = CalculateOptimalSL(m_profile);
|
| | | m_profile.optimal_tp_mult = CalculateOptimalTP(m_profile);
|
| | | m_profile.risk_multiplier = CalculateOptimalRisk(m_profile);
|
| | |
|
| | | return m_profile;
|
| | | }
|
| | |
|
| | | SymbolProfile GetProfile() { return m_profile; }
|
| | |
|
| | | private:
|
| | | // Calculate optimal SL based on symbol characteristics
|
| | | double CalculateOptimalSL(const SymbolProfile& profile) {
|
| | | double base_sl = 150.0; // Default for EURUSD-like pairs
|
| | |
|
| | | // ATR-based scaling: higher volatility = wider SL
|
| | | double atr_scaling = 1.0 + (profile.avg_atr_ratio * 50.0);
|
| | | if(atr_scaling > 5.0) atr_scaling = 5.0;
|
| | |
|
| | | // Spread adjustment
|
| | | double spread_adj = MathMax(1.0, profile.avg_spread_pct * 20.0);
|
| | | if(spread_adj > 3.0) spread_adj = 3.0;
|
| | |
|
| | | // Timeframe scaling
|
| | | double tf_mult = profile.timeframe_mult;
|
| | |
|
| | | // Combined calculation
|
| | | double optimal_sl = base_sl * atr_scaling * spread_adj * tf_mult;
|
| | |
|
| | | // Clamp to reasonable range (50-2000 pips)
|
| | | return MathMax(50.0, MathMin(2000.0, optimal_sl));
|
| | | }
|
| | |
|
| | | // Calculate optimal TP based on SL and volatility
|
| | | double CalculateOptimalTP(const SymbolProfile& profile) {
|
| | | double sl = CalculateOptimalSL(profile);
|
| | | double base_ratio = 2.0; // TP/SL ratio
|
| | |
|
| | | // Volatility bonus: higher volatility = higher TP potential
|
| | | double volatility_bonus = MapRange(profile.volatility_percentile, 0, 100, 0.0, 1.0);
|
| | |
|
| | | // Liquidity bonus: higher liquidity = tighter TP
|
| | | double liquidity_bonus = MapRange(profile.liquidity_score, 0, 100, 0.5, 0.0);
|
| | |
|
| | | double tp_ratio = base_ratio + volatility_bonus + liquidity_bonus;
|
| | | return sl * tp_ratio;
|
| | | }
|
| | |
|
| | | // Calculate optimal risk multiplier (AGGRESSIVE profile)
|
| | | double CalculateOptimalRisk(const SymbolProfile& profile) {
|
| | | double base_risk = 1.5; // AGGRESSIVE: 1.5%
|
| | |
|
| | | // Reduce risk for high volatility
|
| | | double volatility_penalty = MapRange(profile.volatility_percentile, 0, 100, 1.2, 0.6);
|
| | |
|
| | | // Reduce risk for wide spreads
|
| | | double spread_penalty = MapRange(profile.avg_spread_pct, 0, 0.1, 1.0, 0.5);
|
| | | if(spread_penalty < 0.5) spread_penalty = 0.5;
|
| | |
|
| | | double optimal_risk = base_risk * volatility_penalty * spread_penalty;
|
| | |
|
| | | // Clamp to AGGRESSIVE range (0.5% - 3.0%)
|
| | | return MathMax(0.5, MathMin(3.0, optimal_risk));
|
| | | }
|
| | |
|
| | | // Calculate volatility percentile
|
| | | double CalculateVolatilityPercentile(const string symbol, const ENUM_TIMEFRAMES tf, const int lookback) {
|
| | | int atr_handle = iATR(symbol, tf, 14);
|
| | | double atr_values[];
|
| | | ArraySetAsSeries(atr_values, true);
|
| | |
|
| | | if(CopyBuffer(atr_handle, 0, 0, lookback, atr_values) < lookback) {
|
| | | IndicatorRelease(atr_handle);
|
| | | return 50.0; // Default to median
|
| | | }
|
| | |
|
| | | double current_atr = atr_values[0];
|
| | | int count_below = 0;
|
| | |
|
| | | for(int i = 0; i < lookback; i++) {
|
| | | if(atr_values[i] <= current_atr) count_below++;
|
| | | }
|
| | |
|
| | | IndicatorRelease(atr_handle);
|
| | | return (double)count_below / (double)lookback * 100.0;
|
| | | }
|
| | |
|
| | | // Calculate liquidity score based on volume
|
| | | double CalculateLiquidityScore(const string symbol, const ENUM_TIMEFRAMES tf) {
|
| | | long volume[];
|
| | | ArraySetAsSeries(volume, true);
|
| | |
|
| | | if(CopyTickVolume(symbol, tf, 0, 20, volume) < 20) {
|
| | | return 50.0; // Default to median
|
| | | }
|
| | |
|
| | | double avg_volume = 0.0;
|
| | | for(int i = 0; i < 20; i++) {
|
| | | avg_volume += (double)volume[i];
|
| | | }
|
| | | avg_volume /= 20.0;
|
| | |
|
| | | double current_volume = (double)volume[0];
|
| | | double ratio = (avg_volume > 0) ? (current_volume / avg_volume) : 1.0;
|
| | |
|
| | | return MathMin(100.0, ratio * 50.0);
|
| | | }
|
| | |
|
| | | // Timeframe multiplier
|
| | | double GetTimeframeMultiplier(const ENUM_TIMEFRAMES tf) {
|
| | | switch(tf) {
|
| | | case PERIOD_M1: return 0.3;
|
| | | case PERIOD_M5: return 0.5;
|
| | | case PERIOD_M15: return 0.75;
|
| | | case PERIOD_M30: return 1.0;
|
| | | case PERIOD_H1: return 1.5;
|
| | | case PERIOD_H4: return 2.5;
|
| | | case PERIOD_D1: return 4.0;
|
| | | case PERIOD_W1: return 8.0;
|
| | | default: return 1.0;
|
| | | }
|
| | | }
|
| | |
|
| | | // Map value from input range to output range
|
| | | double MapRange(const double value, const double in_min, const double in_max,
|
| | | const double out_min, const double out_max) {
|
| | | if(in_max == in_min) return out_min;
|
| | | double mapped = out_min + (value - in_min) * (out_max - out_min) / (in_max - in_min);
|
| | | return MathMax(out_min, MathMin(out_max, mapped));
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| LIVE ADAPTATION ENGINE - Evolves Parameters Based on Performance|
|
| | | //+------------------------------------------------------------------+
|
| | | class CLiveAdaptation {
|
| | | private:
|
| | | struct PerformanceMetrics {
|
| | | double rolling_pf; // Rolling profit factor
|
| | | double rolling_wr; // Rolling win rate
|
| | | double rolling_dd; // Rolling max drawdown
|
| | | double rolling_vol; // Rolling volatility
|
| | | int trade_count; // Total trades
|
| | | datetime last_update; // Last update time
|
| | | };
|
| | |
|
| | | PerformanceMetrics m_metrics;
|
| | |
|
| | | public:
|
| | | CLiveAdaptation() {
|
| | | m_metrics.rolling_pf = 1.0;
|
| | | m_metrics.rolling_wr = 0.5;
|
| | | m_metrics.rolling_dd = 0.0;
|
| | | m_metrics.rolling_vol = 1.0;
|
| | | m_metrics.trade_count = 0;
|
| | | m_metrics.last_update = 0;
|
| | | }
|
| | |
|
| | | // Update from live trading performance
|
| | | void UpdateFromLiveTrading(const double pf, const double wr, const double dd, const double vol) {
|
| | | m_metrics.rolling_pf = pf;
|
| | | m_metrics.rolling_wr = wr;
|
| | | m_metrics.rolling_dd = dd;
|
| | | m_metrics.rolling_vol = vol;
|
| | | m_metrics.trade_count++;
|
| | | m_metrics.last_update = TimeCurrent();
|
| | | }
|
| | |
|
| | | // Calculate adaptive adjustment multiplier
|
| | | double CalculateAdaptiveAdjustment() {
|
| | | if(m_metrics.trade_count < 10) return 1.0; // Need minimum sample
|
| | |
|
| | | // Multi-factor adjustment
|
| | | double pf_component = MapRange(m_metrics.rolling_pf, 1.9, 3.0, 0.8, 1.2);
|
| | | double wr_component = MapRange(m_metrics.rolling_wr, 0.5, 1.0, 0.9, 1.1);
|
| | | double dd_component = MapRange(m_metrics.rolling_dd, 0.0, 0.05, 1.2, 0.7);
|
| | | double vol_component = MapRange(m_metrics.rolling_vol, 0.5, 2.0, 1.1, 0.9);
|
| | |
|
| | | // Weighted average
|
| | | double adjustment = (pf_component * 0.4 + wr_component * 0.3 + dd_component * 0.2 + vol_component * 0.1);
|
| | |
|
| | | // Clamp to reasonable range (0.7 - 1.3)
|
| | | return MathMax(0.7, MathMin(1.3, adjustment));
|
| | | }
|
| | |
|
| | | double GetProfitFactor() { return m_metrics.rolling_pf; }
|
| | | double GetWinRate() { return m_metrics.rolling_wr; }
|
| | | double GetDrawdown() { return m_metrics.rolling_dd; }
|
| | | int GetTradeCount() { return m_metrics.trade_count; }
|
| | |
|
| | | private:
|
| | | double MapRange(const double value, const double in_min, const double in_max,
|
| | | const double out_min, const double out_max) {
|
| | | if(in_max == in_min) return out_min;
|
| | | double mapped = out_min + (value - in_min) * (out_max - out_min) / (in_max - in_min);
|
| | | return MathMax(out_min, MathMin(out_max, mapped));
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| ENHANCED ADAPTIVE ENGINE - Integration Layer |
|
| | | //+------------------------------------------------------------------+
|
| | | class CAdaptiveEngine {
|
| | | private:
|
| | | double m_current_atr;
|
| | | double m_current_spread;
|
| | | double m_volatility_percentile;
|
| | | double m_spread_percentile;
|
| | | CUniversalDetector m_detector;
|
| | | SymbolProfile m_current_profile;
|
| | |
|
| | | public:
|
| | | CAdaptiveEngine() {
|
| | | m_current_atr = 0.0;
|
| | | m_current_spread = 0.0;
|
| | | m_volatility_percentile = 50.0;
|
| | | m_spread_percentile = 50.0;
|
| | | }
|
| | |
|
| | | // Calculate symbol-specific multipliers using UNIVERSAL DETECTOR
|
| | | void CalculateDynamicParameters(const string symbol, const ENUM_TIMEFRAMES timeframe) {
|
| | | // Use nuclear-grade universal detector
|
| | | m_current_profile = m_detector.AnalyzeSymbol(symbol, timeframe);
|
| | |
|
| | | // Legacy compatibility - extract key metrics
|
| | | int atr_handle = iATR(symbol, timeframe, 14);
|
| | | double atr_buffer[];
|
| | | ArraySetAsSeries(atr_buffer, true);
|
| | | if(CopyBuffer(atr_handle, 0, 0, 1, atr_buffer) > 0) {
|
| | | m_current_atr = atr_buffer[0];
|
| | | }
|
| | | IndicatorRelease(atr_handle);
|
| | |
|
| | | m_current_spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT);
|
| | | m_volatility_percentile = m_current_profile.volatility_percentile;
|
| | | m_spread_percentile = m_current_profile.avg_spread_pct;
|
| | |
|
| | | // Log auto-detection results
|
| | | PrintFormat("🎯 [AUTO-DETECT] %s %s | SL=%.1f TP=%.1f Risk=%.2f%% | ATR Ratio=%.4f Spread=%.3f%% Vol=%d%%",
|
| | | symbol, EnumToString(timeframe),
|
| | | m_current_profile.optimal_sl_mult,
|
| | | m_current_profile.optimal_tp_mult,
|
| | | m_current_profile.risk_multiplier,
|
| | | m_current_profile.avg_atr_ratio,
|
| | | m_current_profile.avg_spread_pct * 100,
|
| | | (int)m_current_profile.volatility_percentile);
|
| | | }
|
| | |
|
| | | // Get adjusted stop loss in pips - USES UNIVERSAL DETECTOR RESULTS
|
| | | double GetDynamicSL(const string symbol, const ENUM_TIMEFRAMES timeframe) {
|
| | | // Return optimal SL calculated by universal detector
|
| | | if(m_current_profile.symbol == symbol) {
|
| | | return m_current_profile.optimal_sl_mult;
|
| | | }
|
| | |
|
| | | // Fallback: recalculate
|
| | | SymbolProfile profile = m_detector.AnalyzeSymbol(symbol, timeframe);
|
| | | return profile.optimal_sl_mult;
|
| | | }
|
| | |
|
| | | // Get adjusted take profit in pips - USES UNIVERSAL DETECTOR RESULTS
|
| | | double GetDynamicTP(const string symbol, const ENUM_TIMEFRAMES timeframe) {
|
| | | // Return optimal TP calculated by universal detector
|
| | | if(m_current_profile.symbol == symbol) {
|
| | | return m_current_profile.optimal_tp_mult;
|
| | | }
|
| | |
|
| | | // Fallback: recalculate
|
| | | SymbolProfile profile = m_detector.AnalyzeSymbol(symbol, timeframe);
|
| | | return profile.optimal_tp_mult;
|
| | | }
|
| | |
|
| | | // Get risk-adjusted position size - USES UNIVERSAL DETECTOR RESULTS
|
| | | double GetDynamicRisk(const string risk_profile) {
|
| | | // Use optimal risk from universal detector (already tuned for AGGRESSIVE)
|
| | | if(m_current_profile.symbol != "") {
|
| | | return m_current_profile.risk_multiplier;
|
| | | }
|
| | |
|
| | | // Fallback to manual calculation
|
| | | double base_risk = 1.0;
|
| | | if(risk_profile == "CONSERVATIVE") base_risk = 0.5;
|
| | | else if(risk_profile == "MODERATE") base_risk = 1.0;
|
| | | else if(risk_profile == "AGGRESSIVE") base_risk = 1.5;
|
| | |
|
| | | // Reduce risk in high volatility
|
| | | double volatility_penalty = MapRange(m_volatility_percentile, 0, 100, 1.2, 0.6);
|
| | |
|
| | | return base_risk * volatility_penalty;
|
| | | }
|
| | |
|
| | | // Get current symbol profile
|
| | | SymbolProfile GetCurrentProfile() {
|
| | | return m_current_profile;
|
| | | }
|
| | |
|
| | | private:
|
| | | double GetSymbolBaseSL(const string symbol) {
|
| | | for(int i = 0; i < ArraySize(g_symbol_configs); i++) {
|
| | | if(StringFind(symbol, g_symbol_configs[i].symbol) >= 0) {
|
| | | return g_symbol_configs[i].base_sl_mult;
|
| | | }
|
| | | }
|
| | | return 10.0; // Default fallback
|
| | | }
|
| | |
|
| | | double GetSymbolBaseTP(const string symbol) {
|
| | | for(int i = 0; i < ArraySize(g_symbol_configs); i++) {
|
| | | if(StringFind(symbol, g_symbol_configs[i].symbol) >= 0) {
|
| | | return g_symbol_configs[i].base_tp_mult;
|
| | | }
|
| | | }
|
| | | return 20.0; // Default fallback
|
| | | }
|
| | |
|
| | | double GetTimeframeMultiplier(const int timeframe) {
|
| | | switch(timeframe) {
|
| | | case PERIOD_M1: return 0.25;
|
| | | case PERIOD_M5: return 0.5;
|
| | | case PERIOD_M15: return 0.75;
|
| | | case PERIOD_M30: return 1.0;
|
| | | case PERIOD_H1: return 1.5;
|
| | | case PERIOD_H4: return 2.5;
|
| | | case PERIOD_D1: return 5.0;
|
| | | default: return 1.0;
|
| | | }
|
| | | }
|
| | |
|
| | | double CalculateATRPercentile(const string symbol, const ENUM_TIMEFRAMES tf, const int lookback) {
|
| | | int atr_handle = iATR(symbol, tf, 14);
|
| | | double atr_values[];
|
| | | ArraySetAsSeries(atr_values, true);
|
| | |
|
| | | if(CopyBuffer(atr_handle, 0, 0, lookback, atr_values) < lookback) {
|
| | | IndicatorRelease(atr_handle);
|
| | | return 50.0; // Default to median on error
|
| | | }
|
| | |
|
| | | double current_atr = atr_values[0];
|
| | | IndicatorRelease(atr_handle);
|
| | |
|
| | | return CalculatePercentile(atr_values, current_atr);
|
| | | }
|
| | |
|
| | | double CalculateSpreadPercentile(const string symbol, const int lookback) {
|
| | | double spread_values[];
|
| | | ArrayResize(spread_values, lookback);
|
| | |
|
| | | for(int i = 0; i < lookback; i++) {
|
| | | spread_values[i] = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT);
|
| | | }
|
| | |
|
| | | return CalculatePercentile(spread_values,
|
| | | SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT));
|
| | | }
|
| | |
|
| | | double CalculatePercentile(const double &values[], const double current_value) {
|
| | | int count = 0;
|
| | | for(int i = 0; i < ArraySize(values); i++) {
|
| | | if(values[i] <= current_value) count++;
|
| | | }
|
| | | return (double)count / ArraySize(values) * 100.0;
|
| | | }
|
| | |
|
| | | double MapRange(const double value, const double in_min, const double in_max,
|
| | | const double out_min, const double out_max) {
|
| | | return out_min + (value - in_min) * (out_max - out_min) / (in_max - in_min);
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Machine Learning Adaptation Engine |
|
| | | //+------------------------------------------------------------------+
|
| | | class CMLAdaptationEngine {
|
| | | private:
|
| | | double m_recent_pf;
|
| | | double m_recent_wr;
|
| | | double m_recent_dd;
|
| | | double m_recent_vol;
|
| | |
|
| | | public:
|
| | | CMLAdaptationEngine() {
|
| | | m_recent_pf = 1.2;
|
| | | m_recent_wr = 0.5;
|
| | | m_recent_dd = 0.1;
|
| | | m_recent_vol = 1.0;
|
| | | }
|
| | |
|
| | | // Calculate ML-based parameter adjustment
|
| | | double CalculateAdjustment(const double profit_factor, const double win_rate,
|
| | | const double max_drawdown, const double volatility) {
|
| | |
|
| | | // Performance score (0-100)
|
| | | double perf_score = 0.0;
|
| | | perf_score += profit_factor * 25.0; // Max 50
|
| | | perf_score += win_rate * 50.0; // Max 50
|
| | | perf_score -= max_drawdown * 200.0; // Penalty for drawdown
|
| | | perf_score = MathMax(0.0, MathMin(100.0, perf_score));
|
| | |
|
| | | // Volatility adjustment factor
|
| | | double vol_factor = MapRange(volatility, 0.5, 3.0, 1.2, 0.8);
|
| | |
|
| | | // Combined adjustment
|
| | | double adjustment = 1.0 + (perf_score - 50.0) / 100.0 * vol_factor;
|
| | | return MathMax(0.5, MathMin(2.0, adjustment));
|
| | | }
|
| | |
|
| | | // Get recent performance metrics
|
| | | double GetRecentProfitFactor(const int lookback_trades) {
|
| | | // Implementation would query KnowledgeBase or trade history
|
| | | return 1.2 + (MathRand() % 100 - 50) / 500.0; // Simulated: 1.0 to 1.4
|
| | | }
|
| | |
|
| | | double GetRecentWinRate(const int lookback_trades) {
|
| | | // Implementation would query KnowledgeBase or trade history
|
| | | return 0.45 + (MathRand() % 20) / 100.0; // Simulated: 0.45 to 0.65
|
| | | }
|
| | |
|
| | | double GetRecentMaxDrawdown(const int lookback_trades) {
|
| | | // Implementation would query KnowledgeBase or trade history
|
| | | return 0.05 + (MathRand() % 15) / 100.0; // Simulated: 0.05 to 0.20
|
| | | }
|
| | |
|
| | | double GetRecentVolatility(const int lookback_bars) {
|
| | | // Calculate recent volatility from price data
|
| | | return 1.0 + (MathRand() % 100 - 50) / 100.0; // Simulated: 0.5 to 1.5
|
| | | }
|
| | |
|
| | | private:
|
| | | double MapRange(const double value, const double in_min, const double in_max,
|
| | | const double out_min, const double out_max) {
|
| | | if(in_max == in_min) return out_min;
|
| | | double mapped = out_min + (value - in_min) * (out_max - out_min) / (in_max - in_min);
|
| | | return MathMax(out_min, MathMin(out_max, mapped));
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Helper Functions |
|
| | | //+------------------------------------------------------------------+
|
| | | double GetSymbolSLMultiplier(const string symbol) {
|
| | | for(int i = 0; i < ArraySize(g_symbol_configs); i++) {
|
| | | if(StringFind(symbol, g_symbol_configs[i].symbol) >= 0) {
|
| | | return g_symbol_configs[i].base_sl_mult;
|
| | | }
|
| | | }
|
| | | return 10.0; // Default fallback
|
| | | }
|
| | |
|
| | | double GetSymbolTPMultiplier(const string symbol) {
|
| | | for(int i = 0; i < ArraySize(g_symbol_configs); i++) {
|
| | | if(StringFind(symbol, g_symbol_configs[i].symbol) >= 0) {
|
| | | return g_symbol_configs[i].base_tp_mult;
|
| | | }
|
| | | }
|
| | | return 20.0; // Default fallback
|
| | | }
|