//+------------------------------------------------------------------+ //| Fomations.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void FormationTesting(const string symbol,const ENUM_TIMEFRAMES tf,FormArray &array,const int index){ for(uint i = 0,size = array.size();i < size;i++){ if(::iTime(symbol,tf,index) <= array[i]._time) continue; if(array[i]._isTested) continue; if(array[i]._isUp){ if((::iLow(symbol,tf,index) > array[i]._low_level_renge && ::iLow(symbol,tf,index) < array[i]._high_level_renge) || (::iLow(symbol,tf,index) < array[i]._low_level_renge && ::iClose(symbol,tf,index) > array[i]._low_level_renge)) array[i]._isTested = true; if(::iLow(symbol,tf,index) < array[i]._low_level_renge && ::iClose(symbol,tf,index) < array[i]._low_level_renge){ if(array[i]._type_formation == FORMATION_VALUE_GAP) array[i]._isTested = true; else array[i]._isUp = false; } } else{ if((::iHigh(symbol,tf,index) < array[i]._high_level_renge && ::iHigh(symbol,tf,index) > array[i]._low_level_renge) || (::iHigh(symbol,tf,index) > array[i]._high_level_renge && ::iClose(symbol,tf,index) < array[i]._high_level_renge)) array[i]._isTested = true; if(::iHigh(symbol,tf,index) > array[i]._high_level_renge && ::iClose(symbol,tf,index) > array[i]._high_level_renge){ if(array[i]._type_formation == FORMATION_VALUE_GAP) array[i]._isTested = true; else array[i]._isUp = true; } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void FormationCleaner(const double equator, const double low, const double high, FormArray &array) { if(array.isEmpty()) return; for(uint i = 0,size = array.size();i < size;i++) { if(array[i]._isTested) array.remove(i); if(array[i]._isUp) { if(array[i]._low_level_renge < low || array[i]._high_level_renge > equator) array.remove(i); } if(!array[i]._isUp) { if(array[i]._low_level_renge < equator || array[i]._high_level_renge > high) array.remove(i); } if(array[i]._high_level_renge <= 0 || array[i]._low_level_renge <= 0 || array[i]._time == 0) array.remove(i); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void FormationFind(const string symbol, const ENUM_TIMEFRAMES tf, const datetime StartTime, const datetime EndTime, FormArray &array) { int start_index = 0; int end_index = 0; for(int i = 0,size = ::iBars(symbol,tf);i < size;i++) { if(::iTime(symbol,tf,i) <= StartTime) { start_index = i; break; } } for(int i = 0,size = ::iBars(symbol,tf);i < size;i++) { if(::iTime(symbol,tf,i) <= EndTime) { end_index = i; break; } } if(start_index <= 0 && end_index <= 0) return; // проверочный принт // Print("StartIndex = ", start_index,"EndIndex = ",end_index); double h_level = 0.0, l_level = 0.0; for(int i = start_index; i > end_index; i--) { if(!array.isEmpty()){ FormationTesting(symbol,tf,array,i); } if(IsBockenVolumeUp(symbol,tf,i,h_level,l_level)) { Formation form; form._type_formation = FORMATION_VALUE_GAP; form._isUp = true; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,i); form.setEquator(); array.append(form); } if(IsBockenVolumeDn(symbol,tf,i,h_level,l_level)) { Formation form; form._type_formation = FORMATION_VALUE_GAP; form._isUp = false; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,i); form.setEquator(); array.append(form); } if(IsInsideBarUp(symbol,tf,i,h_level,l_level)) { Formation form; form._type_formation = FORMATION_INSIDE_BAR; form._isUp = true; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,i); form.setEquator(); array.append(form); } if(IsInsideBarDn(symbol,tf,i,h_level,l_level)) { Formation form; form._type_formation = FORMATION_INSIDE_BAR; form._isUp = false; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,i); form.setEquator(); array.append(form); } if(IsAbsorbingDn(symbol,tf,i,h_level,l_level)) { Formation form; form._type_formation = FORMATION_ABSORBING; form._isUp = false; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,i); form.setEquator(); array.append(form); } if(IsAbsorbingUp(symbol,tf,i,h_level,l_level)) { Formation form; form._type_formation = FORMATION_ABSORBING; form._isUp = true; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,i); form.setEquator(); array.append(form); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /*void FormationFind(const string symbol, const ENUM_TIMEFRAMES tf, FormArray &array) { double h_level = 0.0, l_level = 0.0; if(IsBockenVolumeUp(symbol,tf,1,h_level,l_level)) { Formation form; form._type_formation = FORMATION_VALUE_GAP; form._isUp = true; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,1); form.setEquator(); array.append(form); } if(IsBockenVolumeDn(symbol,tf,1,h_level,l_level)) { Formation form; form._type_formation = FORMATION_VALUE_GAP; form._isUp = false; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,1); form.setEquator(); array.append(form); } if(IsInsideBarUp(symbol,tf,1,h_level,l_level)) { Formation form; form._type_formation = FORMATION_INSIDE_BAR; form._isUp = true; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,1); form.setEquator(); array.append(form); } if(IsInsideBarDn(symbol,tf,1,h_level,l_level)) { Formation form; form._type_formation = FORMATION_INSIDE_BAR; form._isUp = false; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,1); form.setEquator(); array.append(form); } if(IsAbsorbingDn(symbol,tf,1,h_level,l_level)) { Formation form; form._type_formation = FORMATION_ABSORBING; form._isUp = false; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,1); form.setEquator(); array.append(form); } if(IsAbsorbingUp(symbol,tf,1,h_level,l_level)) { Formation form; form._type_formation = FORMATION_ABSORBING; form._isUp = true; form._isTested = false; form._high_level_renge = h_level; form._low_level_renge = l_level; form._time = ::iTime(symbol,tf,1); form.setEquator(); array.append(form); } }*/ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsAbsorbingUp(const string symbol,const ENUM_TIMEFRAMES tf,const int index,double &h,double &l) { int end = index + 1; if(!IsValidData(index,end,2)) return false; double open,close,preOpen,preClose; open = ::iOpen(symbol,tf,index); close = ::iClose(symbol,tf,index); preOpen = ::iOpen(symbol,tf,end); preClose = ::iClose(symbol,tf,end); if(preOpen > preClose && open < close) { if(DistPrices(open,close,symbol) > DistPrices(preOpen,preClose,symbol)) { if(preOpen <= close && preClose >= open) { h = preOpen; l = ::iLow(symbol,tf,index); return true; } } } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsAbsorbingDn(const string symbol,const ENUM_TIMEFRAMES tf,const int index,double &h,double &l) { int end = index + 1; if(!IsValidData(index,end,2)) return false; double open,close,preOpen,preClose; open = ::iOpen(symbol,tf,index); close = ::iClose(symbol,tf,index); preOpen = ::iOpen(symbol,tf,end); preClose = ::iClose(symbol,tf,end); if(preOpen < preClose && open > close) { if(DistPrices(open,close,symbol) > DistPrices(preOpen,preClose,symbol)) { if(preOpen >= close && preClose <= open) { h = ::iHigh(symbol,tf,index); l = preOpen; return true; } } } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsBockenVolumeUp(const string symbol, const ENUM_TIMEFRAMES tf, const int index, double &h, double &l, const int min = 0) { int end = index + 2; if(!IsValidData(index,end,3)) return false; h = ::iLow(symbol,tf,index); l = ::iHigh(symbol,tf,end); if(h > l) { double point = ::SymbolInfoDouble(symbol,SYMBOL_POINT); if(h - l >= min * point) return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsBockenVolumeDn(const string symbol, const ENUM_TIMEFRAMES tf, const int index, double &h, double &l, const int min = 0) { int end = index + 2; if(!IsValidData(index,end,3)) return false; l = ::iLow(symbol,tf,end); h = ::iHigh(symbol,tf,index); if(l > h) { double point = ::SymbolInfoDouble(symbol,SYMBOL_POINT); if(h - l >= min * point) return true; } return false; } //+------------------------------------------------------------------+ /*bool isCloseGapClosingUp(const int begin,const int end){ if(!IsValidData(begin,end,4)) return false; if(!IsBockenVolumeUp(begin)) return false; for(int i = begin - 2;i >= end;i++){ if(IsBockenVolumeUp(i)){ double level = ::iHigh(_Symbol,PERIOD_CURRENT,i); double volume= ::iHigh(_Symbol,PERIOD_CURRENT,i + 2); for(int y = i - 1;y >= end;i--){ if(::iOpen(_Symbol,PERIOD_CURRENT,y) < ::iClose(_Symbol,PERIOD_CURRENT,y)){ if(::iLow(_Symbol,PERIOD_CURRENT,y) <= volume && ::iClose(_Symbol,PERIOD_CURRENT,y) > level) return true; else continue; } else continue; } } else continue; } return false; } //+------------------------------------------------------------------+ bool isCloseGapClosingDn(const int begin,const int end){ if(!IsValidData(begin,end,4)) return false; if(!IsBockenVolumeDn(begin,end)) return false; for(int i = begin - 2;i >= end;i++){ if(IsBockenVolumeDn(i,i+2)){ double level = ::iLow(_Symbol,PERIOD_CURRENT,i); double volume= ::iLow(_Symbol,PERIOD_CURRENT,i + 2); for(int y = i - 1;y >= end;i--){ if(::iOpen(_Symbol,PERIOD_CURRENT,y) > ::iClose(_Symbol,PERIOD_CURRENT,y)){ if(::iLow(_Symbol,PERIOD_CURRENT,y) >= volume && ::iClose(_Symbol,PERIOD_CURRENT,y) < level) return true; else continue; } else continue; } } else continue; } return false; }*/ //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsInsideBarDn(const string symbol, const ENUM_TIMEFRAMES tf, const int index, double &h, double &l) { int end = index + 2; int bar = index + 1; if(!IsValidData(index,end,3)) return false; double open,close,open1,close1,open2,close2; open = ::iOpen(symbol,tf,index); close = ::iClose(symbol,tf,index); open1 = ::iOpen(symbol,tf,bar); close1 = ::iClose(symbol,tf,bar); open2 = ::iOpen(symbol,tf,end); close2 = ::iClose(symbol,tf,end); if(open < close && open1 > close1 && open2 < close2) { if(DistPrices(open1,close1,symbol) < DistPrices(open,close,symbol) && DistPrices(open1,close1,symbol) < DistPrices(open2,close2,symbol)) { if(close1 >= open && close1 >= open2 && open1 <= close && open1 <= close2) { h = ::iHigh(symbol,tf,bar); l = ::iLow(symbol,tf,bar); return true; } } } return false; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsInsideBarUp(const string symbol, const ENUM_TIMEFRAMES tf, const int index, double &h, double &l) { int end = index + 2; int bar = index + 1; if(!IsValidData(index,end,3)) return false; double open,close,open1,close1,open2,close2; open = ::iOpen(symbol,tf,index); close = ::iClose(symbol,tf,index); open1 = ::iOpen(symbol,tf,bar); close1 = ::iClose(symbol,tf,bar); open2 = ::iOpen(symbol,tf,end); close2 = ::iClose(symbol,tf,end); if(open > close && open1 < close1 && open2 < close2) { if(DistPrices(open1,close1,symbol) < DistPrices(open,close,symbol) && DistPrices(open1,close1,symbol) < DistPrices(open2,close2,symbol)) { if(close1 <= open && close1 <= open2 && open1 >= close && open1 >= close2) { h = ::iHigh(symbol,tf,bar); l = ::iLow(symbol,tf,bar); return true; } } } return false; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsValidData(const int begin,const int end,const int formation) { if(begin <= 0) { ::Print("Все свечи паттерна должны быть полностью сформированы\nТекущая незакрытая свеча не допускается"); return false; } if(begin >= end) { ::Print("Указанные индексы не соответсвуют логике"); return false; } if(::MathAbs(end - begin) + 1 < formation) { //::Print(end," - ",begin," = ",end - begin); ::Print("Нехватает свечей для анализа"); return false; } return true; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int DistPrices(double priceA, double priceB, string symbol = NULL) { return (int)(::MathAbs(priceA - priceB) / SymbolInfoDouble(symbol,SYMBOL_POINT)); } //+------------------------------------------------------------------+