//+------------------------------------------------------------------+ //| AIC2.mq5 | //| Copyright 2025, MyCompany | //+------------------------------------------------------------------+ #property strict #property version "1.11" #property description "Советник для проверки 'Распил Н4' на графике H1. Комбинации LP2B и SLP формируются отдельно. Для каждой комбинации проверяется, что обе основные свечи проходят уровень (open и close на противоположных сторонах). Если найдена группа из как минимум 3 комбинаций (расстояние между их вторыми свечами ≤10 баров и расстояние до текущей свечи ≤10 баров), а после самой новой комбинации идёт блок из 4 и более свечей с односторонним расположением – сигнал 'Распил = нет'." // Входные параметры input int MaxCandlesToScan = 80; // Сколько свечей сканировать (от самой старой до текущей) input bool DebugMode = true; // Включить отладку // Объекты панели на H1 #define OBJ_LABEL_RASPIL_H4 "label_RaspilH4" #define OBJ_VALUE_RASPIL_H4 "value_RaspilH4" // Координаты панели #define X_LABEL 10 #define X_VALUE 160 #define Y_RASPIL 92 // Перечисление для типа комбинации enum ComboType { NONE, LP2B, SLP }; // Прототипы функций void CreateRaspilH4Field_H1(); void DeleteRaspilH4Field(); bool GetH4SelectedLevel(double &levelPrice); bool IsCandlePiercing(int index, double level); bool IsCandleAbove(int index, double level); bool IsCandleBelow(int index, double level); bool IsLP2B(int index, double level, int &comboEndIndex); bool IsSLP_New(int startIndex, double level, int &comboEndIndex); void CollectCombinations(double level, int limit, int &comboCount, int &comboStart[], int &comboEnd[], ComboType &comboType[]); bool CheckRaspilH4(); void UpdateRaspilH4Field_H1(); //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { if(Period() == PERIOD_H1) CreateRaspilH4Field_H1(); else DeleteRaspilH4Field(); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { DeleteRaspilH4Field(); } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { if(Period() == PERIOD_H1) UpdateRaspilH4Field_H1(); else DeleteRaspilH4Field(); } //+------------------------------------------------------------------+ //| Создание панели "Распил Н4" на графике H1 | //+------------------------------------------------------------------+ void CreateRaspilH4Field_H1() { if(!ObjectCreate(0, OBJ_LABEL_RASPIL_H4, OBJ_LABEL, 0, 0, 0)) Print("Ошибка создания объекта ", OBJ_LABEL_RASPIL_H4); ObjectSetInteger(0, OBJ_LABEL_RASPIL_H4, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, OBJ_LABEL_RASPIL_H4, OBJPROP_XDISTANCE, X_LABEL); ObjectSetInteger(0, OBJ_LABEL_RASPIL_H4, OBJPROP_YDISTANCE, Y_RASPIL); ObjectSetInteger(0, OBJ_LABEL_RASPIL_H4, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, OBJ_LABEL_RASPIL_H4, OBJPROP_COLOR, clrLightSlateGray); ObjectSetString(0, OBJ_LABEL_RASPIL_H4, OBJPROP_TEXT, "Распил Н4:"); if(!ObjectCreate(0, OBJ_VALUE_RASPIL_H4, OBJ_LABEL, 0, 0, 0)) Print("Ошибка создания объекта ", OBJ_VALUE_RASPIL_H4); ObjectSetInteger(0, OBJ_VALUE_RASPIL_H4, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, OBJ_VALUE_RASPIL_H4, OBJPROP_XDISTANCE, X_VALUE); ObjectSetInteger(0, OBJ_VALUE_RASPIL_H4, OBJPROP_YDISTANCE, Y_RASPIL); ObjectSetInteger(0, OBJ_VALUE_RASPIL_H4, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, OBJ_VALUE_RASPIL_H4, OBJPROP_COLOR, clrLightSlateGray); ObjectSetString(0, OBJ_VALUE_RASPIL_H4, OBJPROP_TEXT, "-"); } //+------------------------------------------------------------------+ //| Удаление панели "Распил Н4" | //+------------------------------------------------------------------+ void DeleteRaspilH4Field() { if(ObjectFind(0, OBJ_LABEL_RASPIL_H4) >= 0) ObjectDelete(0, OBJ_LABEL_RASPIL_H4); if(ObjectFind(0, OBJ_VALUE_RASPIL_H4) >= 0) ObjectDelete(0, OBJ_VALUE_RASPIL_H4); } //+------------------------------------------------------------------+ //| Получение уровня – ищем объект с "Horizontal" и цветом Green | //+------------------------------------------------------------------+ bool GetH4SelectedLevel(double &levelPrice) { int total = ObjectsTotal(0); for(int i = 0; i < total; i++) { string name = ObjectName(0, i); if(StringFind(name, "Horizontal") != -1) { color objColor = (color)ObjectGetInteger(0, name, OBJPROP_COLOR); if(objColor == clrGreen) { levelPrice = ObjectGetDouble(0, name, OBJPROP_PRICE); if(DebugMode) Print("Level found: ", name, ", Price=", levelPrice); return true; } } } if(DebugMode) Print("Level not found"); return false; } //+------------------------------------------------------------------+ //| Проверка свечи на пробитие уровня | //+------------------------------------------------------------------+ bool IsCandlePiercing(int index, double level) { double open = iOpen(_Symbol, PERIOD_H4, index); double close = iClose(_Symbol, PERIOD_H4, index); bool piercing = ((open > level && close < level) || (open < level && close > level)); if(DebugMode) Print("IsCandlePiercing: index=", index, ", Open=", open, ", Close=", close, ", Level=", level, ", result=", piercing); return piercing; } //+------------------------------------------------------------------+ //| Проверка свечи: полностью выше или ниже уровня | //+------------------------------------------------------------------+ bool IsCandleAbove(int index, double level) { double open = iOpen(_Symbol, PERIOD_H4, index); double close = iClose(_Symbol, PERIOD_H4, index); bool above = (open > level && close > level); if(DebugMode) Print("IsCandleAbove: index=", index, ", result=", above); return above; } bool IsCandleBelow(int index, double level) { double open = iOpen(_Symbol, PERIOD_H4, index); double close = iClose(_Symbol, PERIOD_H4, index); bool below = (open < level && close < level); if(DebugMode) Print("IsCandleBelow: index=", index, ", result=", below); return below; } //+------------------------------------------------------------------+ //| Определение LP2B: проверка двух соседних свечей | //+------------------------------------------------------------------+ bool IsLP2B(int index, double level, int &comboEndIndex) { if(index - 1 < 1) return false; double open1 = iOpen(_Symbol, PERIOD_H4, index - 1); double close1 = iClose(_Symbol, PERIOD_H4, index - 1); double open2 = iOpen(_Symbol, PERIOD_H4, index); double close2 = iClose(_Symbol, PERIOD_H4, index); bool condition1 = (open1 < level && close1 > level && open2 > level && close2 < level); bool condition2 = (open1 > level && close1 < level && open2 < level && close2 > level); bool result = condition1 || condition2; if(DebugMode) Print("IsLP2B: indices=", index, " and ", index-1, ", result=", result); if(result) { comboEndIndex = index - 1; return true; } return false; } //+------------------------------------------------------------------+ //| Определение SLP: проверка по следующим правилам: | //| - Main1 должна удовлетворять условию: либо open < level и close > level (patternUp), либо open > level и close < level (patternDown); | //| - Затем от 1 до 10 предыдущих свечей (aux) должны быть с той же стороной от уровня, что и Main1; | //| - Наконец, Main2 должна иметь open и close на противоположной стороне уровня; | //| Дополнительно для SLP проверяется, что close первой основной свечи находится с противоположной стороны от предыдущей свечи. | //+------------------------------------------------------------------+ bool IsSLP_New(int startIndex, double level, int &comboEndIndex) { double main1Open = iOpen(_Symbol, PERIOD_H4, startIndex); double main1Close = iClose(_Symbol, PERIOD_H4, startIndex); bool patternUp = (main1Close > level); bool patternDown = (main1Close < level); if(main1Close == level) return false; double prevClose = iClose(_Symbol, PERIOD_H4, startIndex+1); // Проверка "close противоположно предыдущей свечи" if(patternUp && !(main1Close > level && prevClose < level)) { if(DebugMode) Print("IsSLP_New: Comparing main1Close=", main1Close, " with previous close=", prevClose, " - fails chess check (patternUp), index=", startIndex); return false; } if(patternDown && !(main1Close < level && prevClose > level)) { if(DebugMode) Print("IsSLP_New: Comparing main1Close=", main1Close, " with previous close=", prevClose, " - fails chess check (patternDown), index=", startIndex); return false; } int auxCount = 0; int lastAuxIndex = startIndex - 1; for(int i = startIndex - 1; i >= MathMax(1, startIndex - 10); i--) { double auxOpen = iOpen(_Symbol, PERIOD_H4, i); double auxClose = iClose(_Symbol, PERIOD_H4, i); if(patternUp) { if(auxOpen > level && auxClose > level) { auxCount++; lastAuxIndex = i; } else break; } else if(patternDown) { if(auxOpen < level && auxClose < level) { auxCount++; lastAuxIndex = i; } else break; } } if(auxCount < 1) { if(DebugMode) Print("IsSLP_New: недостаточно дополнительных свечей, auxCount=", auxCount); return false; } int main2Index = lastAuxIndex - 1; if(main2Index < 1) { if(DebugMode) Print("IsSLP_New: нет свечи для второй основной, main2Index=", main2Index); return false; } double main2Open = iOpen(_Symbol, PERIOD_H4, main2Index); double main2Close = iClose(_Symbol, PERIOD_H4, main2Index); if(patternUp) { if(main2Open > level && main2Close < level) { comboEndIndex = main2Index; if(DebugMode) Print("IsSLP_New (patternUp): Found SLP from index ", startIndex, " to index ", comboEndIndex, ", auxCount=", auxCount); return true; } else { if(DebugMode) Print("IsSLP_New (patternUp): Main2 fails: open=", main2Open, ", close=", main2Close, ", index=", main2Index); return false; } } else if(patternDown) { if(main2Open < level && main2Close > level) { comboEndIndex = main2Index; if(DebugMode) Print("IsSLP_New (patternDown): Found SLP from index ", startIndex, " to index ", comboEndIndex, ", auxCount=", auxCount); return true; } else { if(DebugMode) Print("IsSLP_New (patternDown): Main2 fails: open=", main2Open, ", close=", main2Close, ", index=", main2Index); return false; } } return false; } //+------------------------------------------------------------------+ //| Функция сбора комбинаций | //| Используем локальные массивы для comboStart, comboEnd и comboType. | //| Здесь массивы передаются по ссылке (с оператором &). | //+------------------------------------------------------------------+ void CollectCombinations(double level, int limit, int &comboCount, int &comboStart[], int &comboEnd[], ComboType &comboType[]) { // Инициализация локального массива used[] размером limit+1 bool used[]; ArrayResize(used, limit+1); for(int i = 0; i <= limit; i++) used[i] = false; comboCount = 0; for(int i = limit - 1; i >= 1; i--) { if(used[i]) continue; int thisComboEnd = -1; ComboType currentType = NONE; if(IsLP2B(i, level, thisComboEnd) && !used[i] && !used[thisComboEnd]) currentType = LP2B; else if(IsSLP_New(i, level, thisComboEnd) && !used[i] && !used[thisComboEnd]) currentType = SLP; if(currentType != NONE) { comboStart[comboCount] = i; comboEnd[comboCount] = thisComboEnd; comboType[comboCount] = currentType; if(DebugMode) { if(currentType == LP2B) Print("LP2B found: Combo[", comboCount+1, "] = ", i, " ", thisComboEnd); else Print("SLP found: Combo[", comboCount+1, "] = ", i, " ", thisComboEnd); } // Отмечаем свечи от thisComboEnd до i как использованные for(int k = thisComboEnd; k <= i; k++) used[k] = true; comboCount++; } } } //+------------------------------------------------------------------+ //| Основная функция проверки "Распил Н4" | //| Сканиует свечи от (MaxCandlesToScan-1) до 1 (текущая свеча = 0) | //| Собирает комбинации, сортирует их и проверяет соседние пары, | //| расстояние до текущей свечи и блок из 4 и более свечей после последней | //| комбинации. | //+------------------------------------------------------------------+ bool CheckRaspilH4() { double level; if(!GetH4SelectedLevel(level)) return false; if(DebugMode) Print("CheckRaspilH4: Level=", level); int totalCandles = Bars(_Symbol, PERIOD_H4); int limit = MathMin(totalCandles, MaxCandlesToScan + 1); int comboStart[100], comboEnd[100]; ComboType comboType[100]; int comboCount = 0; // Инициализируем массивы (100 элементов) for(int i = 0; i < 100; i++) { comboStart[i] = 0; comboEnd[i] = 0; comboType[i] = NONE; } CollectCombinations(level, limit, comboCount, comboStart, comboEnd, comboType); if(DebugMode) { Print("Total combinations found=", comboCount); for(int k = 0; k < comboCount; k++) { string typeStr = (comboType[k] == LP2B) ? "LP2B" : "SLP"; Print("Combo[", k+1, "]: start=", comboStart[k], ", end=", comboEnd[k], ", type=", typeStr); } } if(comboCount < 3) { if(DebugMode) Print("Найдено комбинаций меньше 3, распил = нет"); return false; } // Сортировка комбинаций по возрастанию comboEnd (чем меньше, тем ближе к текущей свече) int sortedStart[100], sortedEnd[100]; ComboType sortedType[100]; for(int i = 0; i < comboCount; i++) { sortedStart[i] = comboStart[i]; sortedEnd[i] = comboEnd[i]; sortedType[i] = comboType[i]; } for(int i = 0; i < comboCount-1; i++) { for(int j = i+1; j < comboCount; j++) { if(sortedEnd[i] > sortedEnd[j]) { int tempS = sortedStart[i]; int tempE = sortedEnd[i]; ComboType tempT = sortedType[i]; sortedStart[i] = sortedStart[j]; sortedEnd[i] = sortedEnd[j]; sortedType[i] = sortedType[j]; sortedStart[j] = tempS; sortedEnd[j] = tempE; sortedType[j] = tempT; } } } if(DebugMode) { Print("Sorted combinations (newest first):"); for(int i = 0; i < comboCount; i++) { string typeStr = (sortedType[i] == LP2B) ? "LP2B" : "SLP"; Print("Combo[", i+1, "]: start=", sortedStart[i], ", end=", sortedEnd[i], ", type=", typeStr); } } // Подсчёт количества соседних пар, где разница между comboEnd соседних комбинаций ≤10 баров int validPairsCount = 0; for(int i = 0; i < comboCount - 1; i++) { int diff = sortedEnd[i+1] - sortedEnd[i]; if(diff <= 10) validPairsCount++; } if(DebugMode) Print("Valid adjacent pairs count = ", validPairsCount); // Определяем расстояние от самой новой комбинации (с наименьшим sortedEnd) до текущей свечи (индекс 0) int distanceToCurrent = sortedEnd[0]; if(DebugMode) Print("Distance from newest combination (index=", sortedEnd[0], ") to current candle = ", distanceToCurrent); if(distanceToCurrent > 10) { if(DebugMode) Print("Расстояние от самой новой комбинации до текущей свечи превышает 10 баров, распил = нет"); return false; } // Проверка блока: если после самой новой комбинации идёт блок из 4 и более свечей (открытых и закрытых с одной стороны), // то распил отсутствует. bool blockFound = false; if(sortedEnd[0] - 4 >= 1) { int cnt = 0; for(int j = sortedEnd[0] - 1; j >= sortedEnd[0] - 4; j--) { if(IsCandleAbove(j, level) || IsCandleBelow(j, level)) cnt++; else break; } if(cnt >= 4) { if(DebugMode) Print("Обнаружен блок из 4 и более свечей после самой новой комбинации, распил отменяется"); blockFound = true; } } if(blockFound) return false; // Если непрерывная группа из как минимум 3 комбинаций найдена (то есть минимум 2 соседних пары) if(validPairsCount >= 2) { if(DebugMode) Print("Group found: from combo end index ", sortedEnd[validPairsCount], " to ", sortedEnd[0], ", groupSize=", validPairsCount+1); return true; } else { if(DebugMode) Print("Нет непрерывной группы из 3 комбинаций, распил = нет"); return false; } } //+------------------------------------------------------------------+ //| Обновление панели "Распил Н4" на графике H1 | //+------------------------------------------------------------------+ void UpdateRaspilH4Field_H1() { bool raspilExists = CheckRaspilH4(); string text = raspilExists ? "есть" : "нет"; ObjectSetString(0, OBJ_VALUE_RASPIL_H4, OBJPROP_TEXT, text); if(DebugMode) Print("UpdateRaspilH4Field_H1: Распил =", text); } //+------------------------------------------------------------------+