558 lines
21 KiB
MQL5
558 lines
21 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
|
|
// 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);
|
|
|
|
// 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
|
|
}
|