//+------------------------------------------------------------------+ //| TechnicalAnalysis.mqh | //| Technical Analysis Module v1.1 | //| Added: ATR validation and fallback | //+------------------------------------------------------------------+ #ifndef TECHNICAL_ANALYSIS_MQH #define TECHNICAL_ANALYSIS_MQH #include "DataTypes.mqh" #include //+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+