557 lines
36 KiB
MQL5
557 lines
36 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Fomations.mqh |
|
|
//| Copyright 2024, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
|
|
#include <FormationContainers.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
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));
|
|
}
|
|
//+------------------------------------------------------------------+
|