//+------------------------------------------------------------------+ //| FractalBWMFI_4Colors_ATR.mq4 | //+------------------------------------------------------------------+ #property copyright "Copyright © 2023-2026" #property link "https://www.mql5.com" #property version "2.00" #property strict #property indicator_separate_window #property indicator_buffers 4 #property indicator_plots 4 //--- Буфер 1: Green (Grow) - MFI↑ Volume↑ #property indicator_label1 "Grow" #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrGreen #property indicator_width1 3 //--- Буфер 2: Fade (Fake) - MFI↓ Volume↓ #property indicator_label2 "Fade" #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 clrYellow #property indicator_width2 3 //--- Буфер 3: Squat - MFI↑ Volume↓ #property indicator_label3 "Squat" #property indicator_type3 DRAW_HISTOGRAM #property indicator_color3 clrOrange #property indicator_width3 3 //--- Буфер 4: Stop - MFI↓ Volume↑ #property indicator_label4 "Stop" #property indicator_type4 DRAW_HISTOGRAM #property indicator_color4 clrRed #property indicator_width4 3 //--- Входные параметры input int ATR_Period = 14; // Период ATR input double ATR_Multiplier = 100.0; // Масштаб гистограммы (50-500) input bool ShowDebugPrints = false; // Включить отладочные выводы //--- Буферы double ExtGrowBuffer[]; // 🟢 Grow: MFI↑ Vol↑ double ExtFadeBuffer[]; // 🟡 Fade: MFI↓ Vol↓ double ExtSquatBuffer[]; // 🟠 Squat: MFI↑ Vol↓ double ExtStopBuffer[]; // 🔴 Stop: MFI↓ Vol↑ //--- Глобальные переменные для хранения состояния между вызовами double g_prevMFI = 0; long g_prevVolume = 0; int g_lastFractalIndex = -1; //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, ExtGrowBuffer); SetIndexBuffer(1, ExtFadeBuffer); SetIndexBuffer(2, ExtSquatBuffer); SetIndexBuffer(3, ExtStopBuffer); IndicatorShortName("Fractal BW MFI (4 Colors, ATR)"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < ATR_Period + 10) return(0); int limit = rates_total - prev_calculated; if(prev_calculated == 0) { // Первый запуск — очищаем буферы и сбрасываем состояние ArrayInitialize(ExtGrowBuffer, 0); ArrayInitialize(ExtFadeBuffer, 0); ArrayInitialize(ExtSquatBuffer, 0); ArrayInitialize(ExtStopBuffer, 0); limit = rates_total - 5; g_lastFractalIndex = -1; g_prevMFI = 0; g_prevVolume = 0; } if(limit > rates_total - 5) limit = rates_total - 5; if(limit < 0) limit = 0; //--- Если это не первый запуск, восстанавливаем предыдущие значения для сравнения double prevMFI = g_prevMFI; long prevVolume = g_prevVolume; int lastFractalIndex = g_lastFractalIndex; //--- Если у нас есть предыдущий фрактал, но нет значений для сравнения — пересчитаем их if(prev_calculated > 0 && lastFractalIndex != -1 && prevMFI == 0) { // Ищем фрактал ДО lastFractalIndex для получения предыдущих значений for(int i = lastFractalIndex + 1; i < rates_total - 2; i++) { double upper = iFractals(NULL, 0, MODE_UPPER, i); double lower = iFractals(NULL, 0, MODE_LOWER, i); if(upper > 0 || lower > 0) { // Пересчитываем MFI и объем для этого интервала double mfiSum = 0; long volSum = 0; int barsCount = 0; for(int k = i; k <= lastFractalIndex; k++) { double range = high[k] - low[k]; long vol = volume[k]; if(vol > 0) { mfiSum += range / (double)vol; volSum += vol; barsCount++; } } if(barsCount > 0) { prevMFI = mfiSum / (double)barsCount; prevVolume = volSum / barsCount; } break; } } } //--- Основной цикл: идём от старых баров к новым for(int i = rates_total - 5; i >= limit; i--) { double upper = iFractals(NULL, 0, MODE_UPPER, i); double lower = iFractals(NULL, 0, MODE_LOWER, i); bool isUpper = (upper > 0); bool isLower = (lower > 0); if(isUpper || isLower) { if(lastFractalIndex != -1 && lastFractalIndex > i) { //=== Считаем MFI и объем между фракталами === double mfiSum = 0.0; long volSum = 0; int barsCount = 0; for(int k = i; k <= lastFractalIndex; k++) { double range = high[k] - low[k]; long vol = volume[k]; if(vol > 0) { mfiSum += range / (double)vol; volSum += vol; barsCount++; } } double avgMFI_raw = (barsCount > 0) ? mfiSum / barsCount : 0.0; long avgVolume = (barsCount > 0) ? volSum / barsCount : 0; //=== Нормализация через ATR === double atrValue = iATR(NULL, 0, ATR_Period, i); double normalized = 0.0; if(atrValue > 0 && atrValue != EMPTY_VALUE) normalized = (avgMFI_raw / atrValue) * ATR_Multiplier; else normalized = avgMFI_raw * ATR_Multiplier; //=== Определяем цвет по схеме Билла Вильямса === if(prevMFI > 0 && prevVolume > 0) { bool mfiUp = (avgMFI_raw > prevMFI); bool volUp = (avgVolume > prevVolume); // Очищаем все буферы для этого бара ExtGrowBuffer[i] = 0; ExtFadeBuffer[i] = 0; ExtSquatBuffer[i] = 0; ExtStopBuffer[i] = 0; if(mfiUp && volUp) ExtGrowBuffer[i] = normalized; // 🟢 Grow else if(!mfiUp && !volUp) ExtFadeBuffer[i] = normalized; // 🟡 Fade else if(mfiUp && !volUp) ExtSquatBuffer[i] = normalized; // 🟠 Squat else if(!mfiUp && volUp) ExtStopBuffer[i] = normalized; // 🔴 Stop } else { // Первый фрактал — показываем зелёным ExtGrowBuffer[i] = normalized; } //=== Отладочные выводы (опционально) === if(ShowDebugPrints && (i % 200 == 0 || (rates_total - i) <= 10)) { string type = isUpper ? "UPPER" : "LOWER"; string colorName = ""; if(prevMFI > 0 && prevVolume > 0) { bool mfiUp = (avgMFI_raw > prevMFI); bool volUp = (avgVolume > prevVolume); if(mfiUp && volUp) colorName = "GREEN(Grow)"; else if(!mfiUp && !volUp) colorName = "YELLOW(Fade)"; else if(mfiUp && !volUp) colorName = "ORANGE(Squat)"; else colorName = "RED(Stop)"; } else colorName = "GREEN(First)"; PrintFormat("Fractal %s | Bar:%d | RawMFI:%.6f | AvgVol:%ld | ATR:%.5f | Norm:%.3f | Color:%s | Bars:%d", type, i, avgMFI_raw, avgVolume, atrValue, normalized, colorName, lastFractalIndex - i); } //=== Сохраняем текущие значения для следующего сравнения === prevMFI = avgMFI_raw; prevVolume = avgVolume; } // Обновляем индекс последнего фрактала lastFractalIndex = i; } } //=== Сохраняем состояние в глобальные переменные === g_lastFractalIndex = lastFractalIndex; g_prevMFI = prevMFI; g_prevVolume = prevVolume; return(rates_total); } //+------------------------------------------------------------------+