The "Stop distance must be greater than 0" error is caused by a chain of issues in the ATR calculation. Here's what you need to do: 1. Replace the GetValidATR function in EntrySystem.mqh The original code had a recursive call bug. Replace the entire EntrySystem.mqh file with the fixed version I provided above. 2. Update the GetATR method in TechnicalAnalysis.mqh Replace the GetATR method with the fixed version that includes fallback calculations. 3. Update the AnalyzeMarket method in TechnicalAnalysis.mqh Replace the AnalyzeMarket method with the enhanced version that ensures volatility is always valid. 4. Update the main EA file (ERMT_6.0.mq5) Replace the CheckEntrySignals() and UpdateMarketConditions() functions with the enhanced versions that include debugging and validation. Key Changes Made: Fixed Recursive Bug: The GetValidATR function was calling itself instead of using market.volatility Added Multiple Fallbacks: Primary: Use market.volatility from TechnicalAnalysis
887 lines
No EOL
31 KiB
MQL5
887 lines
No EOL
31 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| TechnicalAnalysis.mqh |
|
|
//| Technical Analysis Module v1.1 |
|
|
//| Added: ATR validation and fallback |
|
|
//+------------------------------------------------------------------+
|
|
|
|
#ifndef TECHNICAL_ANALYSIS_MQH
|
|
#define TECHNICAL_ANALYSIS_MQH
|
|
|
|
#include "DataTypes.mqh"
|
|
#include <Math/Stat/Math.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Technical Analysis Class |
|
|
//+------------------------------------------------------------------+
|
|
class CTechnicalAnalysis
|
|
{
|
|
private:
|
|
//--- Configuration
|
|
TechnicalConfig m_config;
|
|
|
|
//--- Indicator handles
|
|
int m_atr_handle;
|
|
int m_adx_handle;
|
|
int m_rsi_handle;
|
|
int m_ma200_handle;
|
|
|
|
//--- Cached levels
|
|
TechnicalLevel m_sr_levels[100];
|
|
int m_sr_count;
|
|
TechnicalLevel m_pivot_levels[10];
|
|
int m_pivot_count;
|
|
datetime m_last_calc_time;
|
|
|
|
//--- Helper methods
|
|
void CalculateSupportResistance();
|
|
void CalculatePivotPoints();
|
|
void CalculateFibonacciLevels();
|
|
void IdentifyMarketStructure();
|
|
void FindSwingPoints(double &highs[], double &lows[],
|
|
int &high_bars[], int &low_bars[]);
|
|
double CalculateLevelStrength(double price, int min_touches,
|
|
datetime last_touch);
|
|
bool IsLevelValid(double price, const TechnicalLevel &levels[],
|
|
int count, double tolerance);
|
|
void SortLevelsByStrength(TechnicalLevel &levels[], int count);
|
|
|
|
public:
|
|
CTechnicalAnalysis();
|
|
~CTechnicalAnalysis();
|
|
|
|
//--- Initialization
|
|
bool Initialize(const TechnicalConfig &config);
|
|
|
|
//--- Main analysis method
|
|
MarketConditions AnalyzeMarket();
|
|
|
|
//--- Specific analysis methods
|
|
void UpdateTechnicalLevels();
|
|
int GetNearestLevels(double price, TechnicalLevel &above[],
|
|
TechnicalLevel &below[], int max_count);
|
|
double GetTrendStrength();
|
|
ENUM_MARKET_CONDITION GetMarketCondition();
|
|
|
|
//--- Indicator values
|
|
double GetATR(int period = 14);
|
|
double GetADX();
|
|
double GetRSI();
|
|
double GetMA200();
|
|
|
|
//--- Level finders
|
|
double GetNearestSupport(double price);
|
|
double GetNearestResistance(double price);
|
|
double GetStrongestLevel(double price, double range);
|
|
|
|
//--- Pattern recognition
|
|
bool IsAtSupport(double price, double tolerance = 0);
|
|
bool IsAtResistance(double price, double tolerance = 0);
|
|
bool IsBreakout(double price, int lookback = 20);
|
|
bool IsFakeout(double price, int lookback = 5);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CTechnicalAnalysis::CTechnicalAnalysis()
|
|
{
|
|
m_atr_handle = INVALID_HANDLE;
|
|
m_adx_handle = INVALID_HANDLE;
|
|
m_rsi_handle = INVALID_HANDLE;
|
|
m_ma200_handle = INVALID_HANDLE;
|
|
m_sr_count = 0;
|
|
m_pivot_count = 0;
|
|
m_last_calc_time = 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTechnicalAnalysis::~CTechnicalAnalysis()
|
|
{
|
|
if(m_atr_handle != INVALID_HANDLE) IndicatorRelease(m_atr_handle);
|
|
if(m_adx_handle != INVALID_HANDLE) IndicatorRelease(m_adx_handle);
|
|
if(m_rsi_handle != INVALID_HANDLE) IndicatorRelease(m_rsi_handle);
|
|
if(m_ma200_handle != INVALID_HANDLE) IndicatorRelease(m_ma200_handle);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize technical analysis |
|
|
//+------------------------------------------------------------------+
|
|
bool CTechnicalAnalysis::Initialize(const TechnicalConfig &config)
|
|
{
|
|
m_config = config;
|
|
|
|
//--- Create indicator handles
|
|
m_atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
|
|
m_adx_handle = iADX(_Symbol, PERIOD_CURRENT, 14);
|
|
m_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
|
m_ma200_handle = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE);
|
|
|
|
if(m_atr_handle == INVALID_HANDLE || m_adx_handle == INVALID_HANDLE ||
|
|
m_rsi_handle == INVALID_HANDLE || m_ma200_handle == INVALID_HANDLE)
|
|
{
|
|
Print("TechnicalAnalysis: Failed to create indicator handles");
|
|
return false;
|
|
}
|
|
|
|
//--- Initial calculation of levels
|
|
UpdateTechnicalLevels();
|
|
|
|
Print("TechnicalAnalysis initialized: Lookback=", m_config.lookback);
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Analyze market conditions - FIXED VERSION |
|
|
//+------------------------------------------------------------------+
|
|
MarketConditions CTechnicalAnalysis::AnalyzeMarket()
|
|
{
|
|
MarketConditions conditions;
|
|
|
|
//--- Update technical levels if needed
|
|
if(TimeCurrent() - m_last_calc_time > 300) // Update every 5 minutes
|
|
{
|
|
UpdateTechnicalLevels();
|
|
}
|
|
|
|
//--- Get basic indicators with validation
|
|
conditions.volatility = GetATR();
|
|
|
|
// Critical fix: Ensure volatility is ALWAYS valid
|
|
if(conditions.volatility <= 0 || conditions.volatility == EMPTY_VALUE || !MathIsValidNumber(conditions.volatility))
|
|
{
|
|
Print("TechnicalAnalysis: Invalid ATR value (", conditions.volatility, "), calculating fallback");
|
|
|
|
// Try manual calculation
|
|
double sum = 0;
|
|
int period = 14;
|
|
bool calc_success = true;
|
|
|
|
for(int i = 1; i <= period; i++)
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
|
double close_prev = iClose(_Symbol, PERIOD_CURRENT, i + 1);
|
|
|
|
if(high > 0 && low > 0 && close_prev > 0)
|
|
{
|
|
double tr = MathMax(high - low, MathMax(MathAbs(high - close_prev), MathAbs(low - close_prev)));
|
|
sum += tr;
|
|
}
|
|
else
|
|
{
|
|
calc_success = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(calc_success && sum > 0)
|
|
{
|
|
conditions.volatility = sum / period;
|
|
Print("TechnicalAnalysis: Manual ATR calculation: ", conditions.volatility);
|
|
}
|
|
else
|
|
{
|
|
// Final fallback based on price
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(current_price > 0)
|
|
{
|
|
conditions.volatility = current_price * 0.001; // 0.1% of price
|
|
|
|
// Minimum 20 pips for forex
|
|
double min_volatility = 20 * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
if(conditions.volatility < min_volatility)
|
|
conditions.volatility = min_volatility;
|
|
|
|
Print("TechnicalAnalysis: Using price-based fallback ATR: ", conditions.volatility);
|
|
}
|
|
else
|
|
{
|
|
// Absolute last resort
|
|
conditions.volatility = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
Print("TechnicalAnalysis: Using absolute fallback: 50 points");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Log the final volatility for debugging
|
|
Print("TechnicalAnalysis: Final volatility = ", conditions.volatility, " (", conditions.volatility / _Point, " points)");
|
|
|
|
conditions.trend_strength = GetADX();
|
|
conditions.momentum = GetRSI();
|
|
conditions.spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
|
|
//--- Determine market condition
|
|
conditions.condition = GetMarketCondition();
|
|
|
|
//--- Check for news (simplified - would need news calendar integration)
|
|
conditions.news_event = false;
|
|
|
|
//--- Calculate liquidity score (based on volume)
|
|
long volume = iVolume(_Symbol, PERIOD_CURRENT, 0);
|
|
long avg_volume = 0;
|
|
for(int i = 1; i <= 20; i++)
|
|
avg_volume += iVolume(_Symbol, PERIOD_CURRENT, i);
|
|
avg_volume /= 20;
|
|
conditions.liquidity = (avg_volume > 0) ? (double)volume / avg_volume * 100 : 100;
|
|
|
|
//--- Copy S/R levels
|
|
conditions.sr_count = MathMin(m_sr_count, 20);
|
|
for(int i = 0; i < conditions.sr_count; i++)
|
|
{
|
|
conditions.sr_levels[i] = m_sr_levels[i];
|
|
}
|
|
|
|
return conditions;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update all technical levels |
|
|
//+------------------------------------------------------------------+
|
|
void CTechnicalAnalysis::UpdateTechnicalLevels()
|
|
{
|
|
if(m_config.use_sr)
|
|
CalculateSupportResistance();
|
|
|
|
if(m_config.use_pivot)
|
|
CalculatePivotPoints();
|
|
|
|
if(m_config.use_fib)
|
|
CalculateFibonacciLevels();
|
|
|
|
//--- Sort levels by strength
|
|
if(m_sr_count > 0)
|
|
SortLevelsByStrength(m_sr_levels, m_sr_count);
|
|
|
|
m_last_calc_time = TimeCurrent();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate support and resistance levels |
|
|
//+------------------------------------------------------------------+
|
|
void CTechnicalAnalysis::CalculateSupportResistance()
|
|
{
|
|
m_sr_count = 0;
|
|
|
|
//--- Find swing highs and lows
|
|
double highs[], lows[];
|
|
int high_bars[], low_bars[];
|
|
ArrayResize(highs, m_config.lookback);
|
|
ArrayResize(lows, m_config.lookback);
|
|
ArrayResize(high_bars, m_config.lookback);
|
|
ArrayResize(low_bars, m_config.lookback);
|
|
|
|
FindSwingPoints(highs, lows, high_bars, low_bars);
|
|
|
|
//--- Process swing highs as resistance
|
|
for(int i = 0; i < ArraySize(highs); i++)
|
|
{
|
|
if(highs[i] == 0) continue;
|
|
|
|
//--- Check if level already exists
|
|
if(!IsLevelValid(highs[i], m_sr_levels, m_sr_count, m_config.level_tolerance))
|
|
continue;
|
|
|
|
//--- Count touches
|
|
int touches = 0;
|
|
datetime last_touch = 0;
|
|
|
|
for(int j = 0; j < m_config.lookback; j++)
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, j);
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, j);
|
|
|
|
if(MathAbs(high - highs[i]) <= m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT) ||
|
|
MathAbs(low - highs[i]) <= m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT))
|
|
{
|
|
touches++;
|
|
if(last_touch == 0)
|
|
last_touch = iTime(_Symbol, PERIOD_CURRENT, j);
|
|
}
|
|
}
|
|
|
|
if(touches >= m_config.min_touches && m_sr_count < 100)
|
|
{
|
|
m_sr_levels[m_sr_count].price = highs[i];
|
|
m_sr_levels[m_sr_count].type = "resistance";
|
|
m_sr_levels[m_sr_count].strength = CalculateLevelStrength(highs[i], touches, last_touch);
|
|
m_sr_levels[m_sr_count].touches = touches;
|
|
m_sr_levels[m_sr_count].last_touch = last_touch;
|
|
m_sr_levels[m_sr_count].zone_width = m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
m_sr_levels[m_sr_count].description = StringFormat("Resistance %.5f (%d touches)", highs[i], touches);
|
|
m_sr_count++;
|
|
}
|
|
}
|
|
|
|
//--- Process swing lows as support
|
|
for(int i = 0; i < ArraySize(lows); i++)
|
|
{
|
|
if(lows[i] == 0) continue;
|
|
|
|
//--- Check if level already exists
|
|
if(!IsLevelValid(lows[i], m_sr_levels, m_sr_count, m_config.level_tolerance))
|
|
continue;
|
|
|
|
//--- Count touches
|
|
int touches = 0;
|
|
datetime last_touch = 0;
|
|
|
|
for(int j = 0; j < m_config.lookback; j++)
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, j);
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, j);
|
|
|
|
if(MathAbs(high - lows[i]) <= m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT) ||
|
|
MathAbs(low - lows[i]) <= m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT))
|
|
{
|
|
touches++;
|
|
if(last_touch == 0)
|
|
last_touch = iTime(_Symbol, PERIOD_CURRENT, j);
|
|
}
|
|
}
|
|
|
|
if(touches >= m_config.min_touches && m_sr_count < 100)
|
|
{
|
|
m_sr_levels[m_sr_count].price = lows[i];
|
|
m_sr_levels[m_sr_count].type = "support";
|
|
m_sr_levels[m_sr_count].strength = CalculateLevelStrength(lows[i], touches, last_touch);
|
|
m_sr_levels[m_sr_count].touches = touches;
|
|
m_sr_levels[m_sr_count].last_touch = last_touch;
|
|
m_sr_levels[m_sr_count].zone_width = m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
m_sr_levels[m_sr_count].description = StringFormat("Support %.5f (%d touches)", lows[i], touches);
|
|
m_sr_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate pivot points |
|
|
//+------------------------------------------------------------------+
|
|
void CTechnicalAnalysis::CalculatePivotPoints()
|
|
{
|
|
m_pivot_count = 0;
|
|
|
|
//--- Get daily OHLC
|
|
double daily_high = iHigh(_Symbol, PERIOD_D1, 1);
|
|
double daily_low = iLow(_Symbol, PERIOD_D1, 1);
|
|
double daily_close = iClose(_Symbol, PERIOD_D1, 1);
|
|
|
|
//--- Calculate pivot point
|
|
double pivot = (daily_high + daily_low + daily_close) / 3;
|
|
|
|
//--- Calculate support and resistance levels
|
|
double r1 = 2 * pivot - daily_low;
|
|
double s1 = 2 * pivot - daily_high;
|
|
double r2 = pivot + (daily_high - daily_low);
|
|
double s2 = pivot - (daily_high - daily_low);
|
|
double r3 = r1 + (daily_high - daily_low);
|
|
double s3 = s1 - (daily_high - daily_low);
|
|
|
|
//--- Add to technical levels if not already in S/R
|
|
if(IsLevelValid(pivot, m_sr_levels, m_sr_count, m_config.level_tolerance) && m_sr_count < 100)
|
|
{
|
|
m_sr_levels[m_sr_count].price = pivot;
|
|
m_sr_levels[m_sr_count].type = "pivot";
|
|
m_sr_levels[m_sr_count].strength = 75;
|
|
m_sr_levels[m_sr_count].touches = 0;
|
|
m_sr_levels[m_sr_count].last_touch = TimeCurrent();
|
|
m_sr_levels[m_sr_count].zone_width = m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
m_sr_levels[m_sr_count].description = "Daily Pivot";
|
|
m_sr_count++;
|
|
}
|
|
|
|
//--- Add R1, S1, etc. similarly...
|
|
double levels[] = {r1, r2, r3, s1, s2, s3};
|
|
string names[] = {"R1", "R2", "R3", "S1", "S2", "S3"};
|
|
string types[] = {"resistance", "resistance", "resistance", "support", "support", "support"};
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
if(IsLevelValid(levels[i], m_sr_levels, m_sr_count, m_config.level_tolerance) && m_sr_count < 100)
|
|
{
|
|
m_sr_levels[m_sr_count].price = levels[i];
|
|
m_sr_levels[m_sr_count].type = types[i];
|
|
m_sr_levels[m_sr_count].strength = 60 - i * 5; // R1/S1 stronger than R3/S3
|
|
m_sr_levels[m_sr_count].touches = 0;
|
|
m_sr_levels[m_sr_count].last_touch = TimeCurrent();
|
|
m_sr_levels[m_sr_count].zone_width = m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
m_sr_levels[m_sr_count].description = "Pivot " + names[i];
|
|
m_sr_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Fibonacci levels |
|
|
//+------------------------------------------------------------------+
|
|
void CTechnicalAnalysis::CalculateFibonacciLevels()
|
|
{
|
|
//--- Find recent significant high and low
|
|
int highest_bar = iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, m_config.lookback, 0);
|
|
int lowest_bar = iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, m_config.lookback, 0);
|
|
|
|
if(highest_bar < 0 || lowest_bar < 0) return;
|
|
|
|
double swing_high = iHigh(_Symbol, PERIOD_CURRENT, highest_bar);
|
|
double swing_low = iLow(_Symbol, PERIOD_CURRENT, lowest_bar);
|
|
double range = swing_high - swing_low;
|
|
|
|
//--- Fibonacci ratios
|
|
double fib_levels[] = {0.236, 0.382, 0.5, 0.618, 0.786};
|
|
string fib_names[] = {"23.6%", "38.2%", "50.0%", "61.8%", "78.6%"};
|
|
|
|
//--- Determine if uptrend or downtrend
|
|
bool uptrend = highest_bar < lowest_bar;
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
double fib_price;
|
|
string fib_type;
|
|
|
|
if(uptrend)
|
|
{
|
|
//--- Retracement levels in uptrend are support
|
|
fib_price = swing_low + range * fib_levels[i];
|
|
fib_type = "support";
|
|
}
|
|
else
|
|
{
|
|
//--- Retracement levels in downtrend are resistance
|
|
fib_price = swing_high - range * fib_levels[i];
|
|
fib_type = "resistance";
|
|
}
|
|
|
|
if(IsLevelValid(fib_price, m_sr_levels, m_sr_count, m_config.level_tolerance) && m_sr_count < 100)
|
|
{
|
|
m_sr_levels[m_sr_count].price = fib_price;
|
|
m_sr_levels[m_sr_count].type = fib_type;
|
|
m_sr_levels[m_sr_count].strength = 50 + (i == 2 ? 10 : 0); // 50% level stronger
|
|
m_sr_levels[m_sr_count].touches = 0;
|
|
m_sr_levels[m_sr_count].last_touch = TimeCurrent();
|
|
m_sr_levels[m_sr_count].zone_width = m_config.level_tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
m_sr_levels[m_sr_count].description = "Fib " + fib_names[i];
|
|
m_sr_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Find swing points |
|
|
//+------------------------------------------------------------------+
|
|
void CTechnicalAnalysis::FindSwingPoints(double &highs[], double &lows[],
|
|
int &high_bars[], int &low_bars[])
|
|
{
|
|
int swing_period = 5; // Look for 5-bar swings
|
|
int found_highs = 0, found_lows = 0;
|
|
|
|
for(int i = swing_period; i < m_config.lookback - swing_period; i++)
|
|
{
|
|
bool is_high = true, is_low = true;
|
|
double current_high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
double current_low = iLow(_Symbol, PERIOD_CURRENT, i);
|
|
|
|
//--- Check if it's a swing high
|
|
for(int j = 1; j <= swing_period; j++)
|
|
{
|
|
if(iHigh(_Symbol, PERIOD_CURRENT, i - j) >= current_high ||
|
|
iHigh(_Symbol, PERIOD_CURRENT, i + j) >= current_high)
|
|
{
|
|
is_high = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//--- Check if it's a swing low
|
|
for(int j = 1; j <= swing_period; j++)
|
|
{
|
|
if(iLow(_Symbol, PERIOD_CURRENT, i - j) <= current_low ||
|
|
iLow(_Symbol, PERIOD_CURRENT, i + j) <= current_low)
|
|
{
|
|
is_low = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(is_high && found_highs < ArraySize(highs))
|
|
{
|
|
highs[found_highs] = current_high;
|
|
high_bars[found_highs] = i;
|
|
found_highs++;
|
|
}
|
|
|
|
if(is_low && found_lows < ArraySize(lows))
|
|
{
|
|
lows[found_lows] = current_low;
|
|
low_bars[found_lows] = i;
|
|
found_lows++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate level strength |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::CalculateLevelStrength(double price, int touches, datetime last_touch)
|
|
{
|
|
double strength = 0;
|
|
|
|
//--- Base strength from touches
|
|
strength = MathMin(touches * 10, 50); // Max 50 points from touches
|
|
|
|
//--- Recency bonus
|
|
int bars_since_touch = iBarShift(_Symbol, PERIOD_CURRENT, last_touch);
|
|
if(bars_since_touch < 50)
|
|
strength += 20;
|
|
else if(bars_since_touch < 100)
|
|
strength += 10;
|
|
|
|
//--- Round number bonus
|
|
double normalized = price / SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
if(MathMod(normalized, 100) < 1) // Round to 100 points
|
|
strength += 15;
|
|
else if(MathMod(normalized, 50) < 1) // Round to 50 points
|
|
strength += 10;
|
|
else if(MathMod(normalized, 10) < 1) // Round to 10 points
|
|
strength += 5;
|
|
|
|
return MathMin(strength, 100);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if level is valid (not duplicate) |
|
|
//+------------------------------------------------------------------+
|
|
bool CTechnicalAnalysis::IsLevelValid(double price, const TechnicalLevel &levels[],
|
|
int count, double tolerance)
|
|
{
|
|
double tol = tolerance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
if(MathAbs(price - levels[i].price) <= tol)
|
|
return false; // Too close to existing level
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Sort levels by strength |
|
|
//+------------------------------------------------------------------+
|
|
void CTechnicalAnalysis::SortLevelsByStrength(TechnicalLevel &levels[], int count)
|
|
{
|
|
//--- Simple bubble sort
|
|
for(int i = 0; i < count - 1; i++)
|
|
{
|
|
for(int j = 0; j < count - i - 1; j++)
|
|
{
|
|
if(levels[j].strength < levels[j + 1].strength)
|
|
{
|
|
TechnicalLevel temp = levels[j];
|
|
levels[j] = levels[j + 1];
|
|
levels[j + 1] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get market condition |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_MARKET_CONDITION CTechnicalAnalysis::GetMarketCondition()
|
|
{
|
|
double adx = GetADX();
|
|
double ma200 = GetMA200();
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
//--- Check for trending market
|
|
if(adx > 25)
|
|
{
|
|
if(current_price > ma200)
|
|
return MARKET_TRENDING_UP;
|
|
else
|
|
return MARKET_TRENDING_DOWN;
|
|
}
|
|
//--- Check for breakout
|
|
else if(IsBreakout(current_price))
|
|
{
|
|
return MARKET_BREAKOUT;
|
|
}
|
|
//--- Check volatility
|
|
else
|
|
{
|
|
double atr = GetATR();
|
|
double avg_atr = 0;
|
|
double atr_buffer[20];
|
|
|
|
if(CopyBuffer(m_atr_handle, 0, 0, 20, atr_buffer) == 20)
|
|
{
|
|
for(int i = 0; i < 20; i++)
|
|
avg_atr += atr_buffer[i];
|
|
avg_atr /= 20;
|
|
|
|
if(atr > avg_atr * 1.5)
|
|
return MARKET_VOLATILE;
|
|
else if(atr < avg_atr * 0.5)
|
|
return MARKET_QUIET;
|
|
}
|
|
}
|
|
|
|
return MARKET_RANGING;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get ATR value - FIXED VERSION |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetATR(int period)
|
|
{
|
|
double buffer[1];
|
|
if(CopyBuffer(m_atr_handle, 0, 0, 1, buffer) > 0 && buffer[0] > 0 && buffer[0] != EMPTY_VALUE)
|
|
{
|
|
return buffer[0];
|
|
}
|
|
|
|
// Fallback calculation if handle fails
|
|
Print("TechnicalAnalysis: ATR handle failed, calculating manually");
|
|
|
|
double sum = 0;
|
|
for(int i = 1; i <= period; i++)
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
|
double close_prev = iClose(_Symbol, PERIOD_CURRENT, i + 1);
|
|
|
|
double tr = MathMax(high - low, MathMax(MathAbs(high - close_prev), MathAbs(low - close_prev)));
|
|
sum += tr;
|
|
}
|
|
|
|
double atr = sum / period;
|
|
|
|
// Validate result
|
|
if(atr <= 0 || !MathIsValidNumber(atr))
|
|
{
|
|
// Last resort: use 0.1% of current price
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
atr = price * 0.001;
|
|
Print("TechnicalAnalysis: Using price-based ATR fallback: ", atr);
|
|
}
|
|
|
|
return atr;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get ADX value |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetADX()
|
|
{
|
|
double buffer[1];
|
|
if(CopyBuffer(m_adx_handle, 0, 0, 1, buffer) > 0)
|
|
return buffer[0];
|
|
return 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get RSI value |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetRSI()
|
|
{
|
|
double buffer[1];
|
|
if(CopyBuffer(m_rsi_handle, 0, 0, 1, buffer) > 0)
|
|
return buffer[0];
|
|
return 50; // Neutral
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get MA200 value |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetMA200()
|
|
{
|
|
double buffer[1];
|
|
if(CopyBuffer(m_ma200_handle, 0, 0, 1, buffer) > 0)
|
|
return buffer[0];
|
|
return SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get trend strength |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetTrendStrength()
|
|
{
|
|
return GetADX();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get nearest support |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetNearestSupport(double price)
|
|
{
|
|
double nearest = 0;
|
|
double min_distance = DBL_MAX;
|
|
|
|
for(int i = 0; i < m_sr_count; i++)
|
|
{
|
|
if(m_sr_levels[i].type == "support" && m_sr_levels[i].price < price)
|
|
{
|
|
double distance = price - m_sr_levels[i].price;
|
|
if(distance < min_distance)
|
|
{
|
|
min_distance = distance;
|
|
nearest = m_sr_levels[i].price;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nearest;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get nearest resistance |
|
|
//+------------------------------------------------------------------+
|
|
double CTechnicalAnalysis::GetNearestResistance(double price)
|
|
{
|
|
double nearest = 0;
|
|
double min_distance = DBL_MAX;
|
|
|
|
for(int i = 0; i < m_sr_count; i++)
|
|
{
|
|
if(m_sr_levels[i].type == "resistance" && m_sr_levels[i].price > price)
|
|
{
|
|
double distance = m_sr_levels[i].price - price;
|
|
if(distance < min_distance)
|
|
{
|
|
min_distance = distance;
|
|
nearest = m_sr_levels[i].price;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nearest;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if price is at support |
|
|
//+------------------------------------------------------------------+
|
|
bool CTechnicalAnalysis::IsAtSupport(double price, double tolerance)
|
|
{
|
|
if(tolerance == 0)
|
|
tolerance = GetATR() * 0.1; // 10% of ATR
|
|
|
|
for(int i = 0; i < m_sr_count; i++)
|
|
{
|
|
if(m_sr_levels[i].type == "support")
|
|
{
|
|
if(MathAbs(price - m_sr_levels[i].price) <= tolerance)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if price is at resistance |
|
|
//+------------------------------------------------------------------+
|
|
bool CTechnicalAnalysis::IsAtResistance(double price, double tolerance)
|
|
{
|
|
if(tolerance == 0)
|
|
tolerance = GetATR() * 0.1; // 10% of ATR
|
|
|
|
for(int i = 0; i < m_sr_count; i++)
|
|
{
|
|
if(m_sr_levels[i].type == "resistance")
|
|
{
|
|
if(MathAbs(price - m_sr_levels[i].price) <= tolerance)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if breakout occurred |
|
|
//+------------------------------------------------------------------+
|
|
bool CTechnicalAnalysis::IsBreakout(double price, int lookback)
|
|
{
|
|
//--- Check if price broke above recent resistance
|
|
double highest = 0;
|
|
for(int i = 1; i <= lookback; i++)
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
if(high > highest)
|
|
highest = high;
|
|
}
|
|
|
|
if(price > highest && (price - highest) > GetATR() * 0.5)
|
|
return true;
|
|
|
|
//--- Check if price broke below recent support
|
|
double lowest = DBL_MAX;
|
|
for(int i = 1; i <= lookback; i++)
|
|
{
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, i);
|
|
if(low < lowest)
|
|
lowest = low;
|
|
}
|
|
|
|
if(price < lowest && (lowest - price) > GetATR() * 0.5)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get nearest levels above and below price |
|
|
//+------------------------------------------------------------------+
|
|
int CTechnicalAnalysis::GetNearestLevels(double price, TechnicalLevel &above[],
|
|
TechnicalLevel &below[], int max_count)
|
|
{
|
|
int above_count = 0, below_count = 0;
|
|
|
|
//--- Find levels above and below
|
|
for(int i = 0; i < m_sr_count; i++)
|
|
{
|
|
if(m_sr_levels[i].price > price && above_count < max_count)
|
|
{
|
|
above[above_count] = m_sr_levels[i];
|
|
above_count++;
|
|
}
|
|
else if(m_sr_levels[i].price < price && below_count < max_count)
|
|
{
|
|
below[below_count] = m_sr_levels[i];
|
|
below_count++;
|
|
}
|
|
}
|
|
|
|
//--- Sort by distance
|
|
for(int i = 0; i < above_count - 1; i++)
|
|
{
|
|
for(int j = i + 1; j < above_count; j++)
|
|
{
|
|
if(above[i].price > above[j].price)
|
|
{
|
|
TechnicalLevel temp = above[i];
|
|
above[i] = above[j];
|
|
above[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < below_count - 1; i++)
|
|
{
|
|
for(int j = i + 1; j < below_count; j++)
|
|
{
|
|
if(below[i].price < below[j].price)
|
|
{
|
|
TechnicalLevel temp = below[i];
|
|
below[i] = below[j];
|
|
below[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MathMax(above_count, below_count);
|
|
}
|
|
|
|
#endif // TECHNICAL_ANALYSIS_MQH
|
|
//+------------------------------------------------------------------+ |