mql5/Experts/Advisors/Modules/TechnicalAnalysis.mqh

887 lines
31 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+