//+------------------------------------------------------------------+ //| ExpCodePatternFinder.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Перечисления | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Структуры | //+------------------------------------------------------------------+ struct SPatternStep // Структура одного бара паттерна { int code; // Код Лиховидова datetime time; // Время бара double open; // Цена открытия double high; // Цена High double low; // Цена Low double close; // Цена Close double open_base; // Цена открытия базовой точки datetime time_base; // Время базовой точки double rel_pos; // Позиция относительно базовой точки (Open[i] - open_base) / ATR }; struct SPattern // Структура паттерна { SPatternStep steps[]; // Глубина паттерна ENUM_POSITION_TYPE type; // POSITION_TYPE_BUY (Low ZZ) или POSITION_TYPE_SELL (High ZZ) datetime time; // Время экстремума //--- Устанавливает глубину паттерна bool SetSteps(const int size){ return(ArrayResize(steps,size)==size); } }; struct SCandleParts // Структура кода Лиховидова { int body; // Градация тела (0-3) int us; // Верхняя тень (0-3) int ls; // Нижняя тень (0-3) ENUM_POSITION_TYPE direction; // Направление (BUY/SELL) }; struct SMatchResult // Структура результатов поиска { int best_idx; // Индекс лучшего паттерна string signal; // Текст сигнала int cnt_buy; // Количество совпадений BUY int cnt_sell; // Количество совпадений SELL double avg_buy; // Среднее сходство BUY double avg_sell; // Среднее сходство SELL //--- Конструктор SMatchResult() : best_idx(WRONG_VALUE), signal("UNCERTAIN"), cnt_buy(0), cnt_sell(0), avg_buy(0), avg_sell(0) {} }; //+------------------------------------------------------------------+ //| Макроподстановки | //+------------------------------------------------------------------+ #define ATTEMPTS_MAX 10 // Максимальное количество попыток получения базы паттернов #define MAX_POS_DIST 0.5 // 0.5 ATR - максимальный предел смещения (0% сходства вертикальной позиции) //+------------------------------------------------------------------+ //| Входные параметры | //+------------------------------------------------------------------+ //--- ZigZag sinput string InpParamZZ = "---"; // --- ZigZag Parameters --- input int InpZZDepth = 12; // ZZ Depth input int InpZZDeviation = 5; // ZZ Deviation input int InpZZBackstep = 3; // ZZ Back Step //--- Candle Code sinput string InpParamCC = "---"; // --- Candle Code Parameters --- input int InpCCBBLength = 55; // Candle Code BB Length (Thresholds) input double InpCCBBNumDevs = 0.3; // Candle Code BB Deviations input int InpCCAvgLength = 9; // Candle Code Average Length (SMA) //--- ATR sinput string InpParamATR = "---"; // --- ATR Parameters --- input int InpATRPeriod = 14; // ATR Period //--- Параметры поиска паттернов sinput string InpParamPattSrc= "---"; // --- Pattern search Parameters --- input int InpPatternDepth= 5; /* Pattern Depth (Bars) */ // Размер паттерна в барах input uint InpMaxGradeDiff= 1; /* Maximum difference in gradations (0-3) */ // Максимальная разница по градациям тела/теней для совпадения (0-3) input double InpMinCandleSim= 50.0; /* Candles similarity (0-100%) */ // Сходство свечей (0-100%) input double InpMinPosSim = 50.0; /* Relative position similarity (0-100%) */ // Сходство относительного расположения (0-100%) input double InpTotalSim = 65.0; /* Final pattern similarity threshold (0-100%) */ // Итоговый порог похожести паттерна (0-100%) //--- Параметры окна паттерна sinput string InpParamPattWnd= "---"; // --- Pattern window Parameters --- input int InpWndX = 30; /* Pattern Window X offset */ // Смещение окна по оси X input int InpWndY = 30; /* Pattern Window Y offset */ // Смещение окна по оси Y input int InpWndW = 500; /* Pattern Window Width */ // Ширина окна паттерна input int InpWndH = 250; /* Pattern Window Height */ // Высота окна паттерна //--- Параметры текста статистики sinput string InpParamStatsTxt= "---"; // --- Statistics text Parameters --- input int InpStatsX = 10; /* Statistics X offset */ // Смещение текста по оси X input int InpStatsY = 20; /* Statistics Y offset */ // Смещение текста по оси Y input int InpStatsStep = 15; /* Statistics Line Step */ // Вертикальный шаг между строками input bool InpStatsBack = true; /* Use background for statistics */ // Использовать подложку для текста статистики //--- Параметры ручного режима sinput string InpParamManMode= "---"; // --- Manual mode Parameters --- input bool InpManualMode = true; /* Manual bar selection (Ctrl + Click) */ // Включить ручной выбор бара input color InpAnalysColor = clrGray; /* Analysis line color */ // Цвет линии выбранного бара //--- Параметры советника sinput string InpParamExp = "---"; // --- Expert Parameters --- input ulong InpMagic = 123; /* Magic Number */ // Идентификатор эксперта //+------------------------------------------------------------------+ //| Глобальные переменные | //+------------------------------------------------------------------+ int handle_zz; // Хэндл ZigZag int handle_cc; // Хэндл Candle Code int handle_atr; // Хэндл ATR int zz_depth; // ZZ Depth int zz_dev; // ZZ Deviation int zz_back; // ZZ Back Step int cc_bb_len; // Candle Code BB Length (Thresholds) int cc_avg_len; // Candle Code Average Length (SMA) int atr_period; // ATR Period int ExtPatternDepth; // Размер паттерна в барах uint ExtMaxGradeDiff; // Максимальная разница по градациям тела/теней для совпадения (0-3) double ExtMinCandleSim; // Сходство свечей (0-100%) double ExtMinPosSim; // Сходство относительного расположения (0-100%) double ExtTotalSim; // Итоговый порог похожести паттерна (0-100%) int attempts_count; // Счётчик попыток сбора базы паттернов SPattern patterns[]; // Массив паттернов SMatchResult match_result; // Структура результатов поиска bool base_is_ready; // Флаг готовности базы паттернов string prefix; // Префикс имён объектов int start_search_index; // Бар начала поиска //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create timer EventSetTimer(60); //--- Инициализируем массив паттернов ArrayFree(patterns); //--- Устанавливаем и корректируем входные параметры индикаторов //--- ZigZag zz_depth=(InpZZDepth <1 ? 12 : InpZZDepth); zz_dev=(InpZZDeviation <1 ? 5 : InpZZDeviation); zz_back=(InpZZBackstep <1 ? 3 : InpZZBackstep); //--- Candle Code cc_bb_len=(InpCCBBLength <1 ? 55 : InpCCBBLength); cc_avg_len=(InpCCAvgLength<1 ? 9 : InpCCAvgLength); //--- ATR atr_period=(InpATRPeriod<1 ? 14 : InpATRPeriod); //--- Создаём хэндлы индикаторов //--- ATR handle_atr=iATR(_Symbol,_Period,atr_period); if(handle_atr==INVALID_HANDLE) { PrintFormat("%s: Failed to create iATR(%d) handle",__FUNCTION__,atr_period); return INIT_FAILED; } //--- ZigZag \MQL5\Indicators\Examples\ZigZag.mq5 handle_zz=iCustom(_Symbol,_Period,"Examples\\ZigZag",zz_depth,zz_dev,zz_back); if(handle_zz==INVALID_HANDLE) { PrintFormat("%s: Failed to create ZigZag(%d,%d,%d) handle",__FUNCTION__,zz_depth,zz_dev,zz_back); return INIT_FAILED; } //--- CandleCode handle_cc=iCustom(_Symbol,_Period,"Shared Projects\\Article-21319-MQL5-CandleCode-Indicator\\CandleCode",cc_bb_len,InpCCBBNumDevs,cc_avg_len); if(handle_cc==INVALID_HANDLE) { PrintFormat("%s: Failed to create CandleCode(%d,%.1f,%d) handle",__FUNCTION__,cc_bb_len,InpCCBBNumDevs,cc_avg_len); return INIT_FAILED; } //--- Инициализируем флаг готовности и количество попыток получения базы паттернов base_is_ready=false; attempts_count=0; //--- Корректируем введённые значения поиска паттернов ExtPatternDepth=(InpPatternDepth<0 ? 5 : InpPatternDepth); ExtMinCandleSim=(InpMinCandleSim<0 ? 80.0 : InpMinCandleSim>100 ? 100.0 : InpMinCandleSim); ExtMinPosSim=(InpMinPosSim<0 ? 80.0 : InpMinPosSim>100 ? 100.0 : InpMinPosSim); ExtMaxGradeDiff=int(InpMaxGradeDiff>3 ? 3 : InpMaxGradeDiff); ExtTotalSim=(InpTotalSim<0 ? 85.0 : InpTotalSim>100 ? 100.0 : InpTotalSim); start_search_index=1; prefix=MQLInfoString(MQL_PROGRAM_NAME)+(string)InpMagic; //--- Пытаемся сформировать базу паттернов сразу при инициализации base_is_ready=CreatePatternBase(ATTEMPTS_MAX,ExtPatternDepth,cc_bb_len,attempts_count,patterns); //--- Если база готова - ищем паттерны и выводим статистику if(base_is_ready) { int total=ArraySize(patterns); PrintFormat("%s: The pattern database has been successfully created. Total patterns: %d",__FUNCTION__,total); FindAndDisplayMatches(ExtPatternDepth,ExtTotalSim,ExtMinCandleSim,ExtMinPosSim,ExtMaxGradeDiff,patterns,match_result,prefix+"_BestMatchWindow", InpWndX, InpWndY, InpWndW, InpWndH,InpStatsX, InpStatsY, InpStatsStep,InpStatsBack,start_search_index,InpAnalysColor); } //--- Всё успешно return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy timer EventKillTimer(); //--- Освобождаем массив паттернов ArrayFree(patterns); //--- Удаляем графические объекты ObjectsDeleteAll(ChartID(),prefix); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Если база паттернов ещё не готова - пытаемся создать if(!base_is_ready) { if(CreatePatternBase(ATTEMPTS_MAX,ExtPatternDepth,cc_bb_len,attempts_count,patterns)) { base_is_ready=true; int total=ArraySize(patterns); PrintFormat("%s: The pattern database has been successfully created. Total patterns: %d",__FUNCTION__,total); } } //--- Если база всё еще не готова - выходим до следующего тика if(!base_is_ready) return; //--- Поиск совпадений при появлении нового бара if(IsNewBar()) { //--- Если работа в автоматическом режиме, устанавливаем индекс на прошлый бар (с индексом 1) if(!InpManualMode) { //--- Выполняем поиск и визуализацию результатов, начиная от первого бара PrintFormat("%s: New Bar. Automatic search for matches from index 1",__FUNCTION__); start_search_index=1; FindAndDisplayMatches(ExtPatternDepth,ExtTotalSim,ExtMinCandleSim,ExtMinPosSim,ExtMaxGradeDiff,patterns,match_result,prefix+"_BestMatchWindow", InpWndX,InpWndY,InpWndW,InpWndH,InpStatsX,InpStatsY,InpStatsStep,InpStatsBack,start_search_index,InpAnalysColor); } } } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- } //+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { //--- } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam) { //--- Если режим автоматический - уходим if(!InpManualMode) return; //--- Обработка клика по графику в ручном режиме if(id==CHARTEVENT_CLICK) { ChartClickHandler(lparam,dparam,start_search_index); } } //+------------------------------------------------------------------+ //| Возвращает флаг открытия нового бара | //+------------------------------------------------------------------+ bool IsNewBar(void) { static datetime lastbar=0; datetime bar=iTime(_Symbol,_Period,0); if(bar==lastbar || bar==0) return false; lastbar=bar; return true; } //+------------------------------------------------------------------+ //| Возвращает данные цен с указанного бара в заданном количестве | //+------------------------------------------------------------------+ bool GetPriceData(const int index,const int count,MqlRates &array[]) { ResetLastError(); if(CopyRates(_Symbol,_Period,index,count,array)!=count) { PrintFormat("%s: CopyRates(%d,%d) failed. Error %d",__FUNCTION__,index,count,GetLastError()); return false; } return true; } //+------------------------------------------------------------------+ //| Возвращает данные индикатора ATR | //| с указанного бара в заданном количестве | //+------------------------------------------------------------------+ bool GetATRData(const int index,const int count,double &array[]) { ResetLastError(); if(CopyBuffer(handle_atr,0,index,count,array)!=count) { PrintFormat("%s: CopyBuffer(%d,%d) failed. Error %d",__FUNCTION__,index,count,GetLastError()); return false; } return true; } //+------------------------------------------------------------------+ //| Возвращает данные ZigZag в заданном количестве | //| с указанного буфера и бара | //+------------------------------------------------------------------+ bool GetZZData(const int buffer,const int index,const int count,double &array[]) { if(buffer>2) { PrintFormat("%s: Error: Invalid buffer number (%d). Must be 0 - 2",__FUNCTION__,buffer); return false; } ResetLastError(); if(CopyBuffer(handle_zz,buffer,index,count,array)!=count) { PrintFormat("%s: CopyBuffer(%d,%d) failed. Error %d",__FUNCTION__,index,count,GetLastError()); return false; } return true; } //+------------------------------------------------------------------+ //| Возвращает данные индикатора Candle Code | //| с указанного бара в заданном количестве | //+------------------------------------------------------------------+ bool GetCandleCodeData(const int index,const int count,double &array[]) { ResetLastError(); if(CopyBuffer(handle_cc,0,index,count,array)!=count) { PrintFormat("%s: CopyBuffer(%d,%d) failed. Error %d",__FUNCTION__,index,count,GetLastError()); return false; } return true; } //+------------------------------------------------------------------+ //| Сбор паттернов по всей истории | //+------------------------------------------------------------------+ bool CollectHistoryData(const int pattern_size,const int limit,SPattern &array[]) { //--- Проверяем наличие необходимой истории int bars_total=iBars(_Symbol,_Period); if(bars_total0) ArrayFree(array); //--- Массивы для получения данных MqlRates rates_hist[]; double atr_hist[],zz0_hist[],zz1_hist[],zz2_hist[],cc_hist[]; //--- Массивы как таймсерии ArraySetAsSeries(rates_hist,true); ArraySetAsSeries(atr_hist,true); ArraySetAsSeries(zz0_hist,true); ArraySetAsSeries(zz1_hist,true); ArraySetAsSeries(zz2_hist,true); ArraySetAsSeries(cc_hist,true); //--- Получаем исторические данные цен и индикаторов if(!GetPriceData(0,bars_total,rates_hist)) return false; if(!GetATRData(0,bars_total,atr_hist)) return false; if(!GetZZData(0,0,bars_total,zz0_hist)) return false; if(!GetZZData(1,0,bars_total,zz1_hist)) return false; if(!GetZZData(2,0,bars_total,zz2_hist)) return false; if(!GetCandleCodeData(0,bars_total,cc_hist)) return false; bool is_first_found=false; // Флаг для пропуска текущего излома ZZ //--- Проходим по всей истории for(int i=0; i=64); code_struct.direction=(bullish ? POSITION_TYPE_BUY : POSITION_TYPE_SELL); //--- Извлечение геометрических составляющих (градации 0-3) int temp=code-(bullish ? 64 : 0); int raw_body=temp/16; // Тело int raw_us=(temp%16)/4; // Верхняя тень int raw_ls=temp%4; // Нижняя тень //--- Приведение к единому формату (0 - минимум, 3 - максимум) //--- Согласно спецификации индикатора, инверсия шкал различается для каждого элемента //--- Тело: инвертировано только у медвежьих свечей (0 - максимум) code_struct.body=(bullish ? raw_body : 3-raw_body); //--- Верхняя тень: в исходном коде всегда имеет прямую шкалу (0 - минимум) code_struct.us=raw_us; //--- Нижняя тень: в исходном коде всегда имеет обратную шкалу (3 - минимум) code_struct.ls=3-raw_ls; } //+------------------------------------------------------------------+ //| Сравнивает две свечи по форме и расположению | //+------------------------------------------------------------------+ double CalculateStepSimilarity(const SPatternStep &base_step, // Данные эталонной свечи из истории const SPatternStep &curr_step, // Данные текущей свечи с рынка const double min_candle_sim, // Минимально допустимый процент сходства формы const double min_pos_sim, // Минимально допустимый процент сходства позиции const int max_grade_diff) // Максимально допустимая разница по градациям тела/теней { //--- Разбираем оба кода на физические параметры свечей SCandleParts b,c; DecomposeCode(base_step.code,b); DecomposeCode(curr_step.code,c); //--- Сравниваем цвет. Если направление не совпало - свечи разные, возвращаем 0 if(b.direction!=c.direction) return 0.0; //--- Настраиваемый фильтр: если различие по телу или теням больше max_grade_diff - свечи считаются непохожими if(fabs(b.body-c.body)>max_grade_diff || fabs(b.us-c.us)>max_grade_diff || fabs(b.ls-c.ls)>max_grade_diff) return 0.0; //--- Вычисляем, насколько сильно различаются размеры тел и теней обеих свечей int diff_total=fabs(b.body-c.body)+fabs(b.us-c.us)+fabs(b.ls-c.ls); double s_candle=100.0*(1.0-(double)diff_total/9.0); //--- Вычисляем, насколько сильно отличаются вертикальные позиции свечей относительно базовой свечи double diff_pos=fabs(base_step.rel_pos-curr_step.rel_pos); double s_pos=fmax(0.0,100.0*(1.0-diff_pos/MAX_POS_DIST)); //--- Возвращаем взвешенную оценку сходства: баланс между формой свечи (50%) и её положением в ATR (50%) return(s_candle*0.5+s_pos*0.5); } //+------------------------------------------------------------------+ //| Возвращает паттерн, сформированный на текущих рыночных данных | //+------------------------------------------------------------------+ bool GetCurrentPattern(SPattern &pattern,const int start_index,const int depth) { //--- Устанавливаем размер (глубину) данных паттерна if(!pattern.SetSteps(depth)) return false; //--- Получаем базовые данные (цену и волатильность) для точки отсчёта MqlRates r_base[]; double atr_val[]; if(!GetPriceData(start_index,1,r_base)) return false; if(!GetATRData(start_index,1,atr_val)) return false; //--- Сохраняем значения для расчёта относительного расположения свечей в паттерне double open_base=r_base[0].open; double atr_base=atr_val[0]; //--- Проверка на нулевую волатильность if(atr_base==0) return false; //--- Собираем данные по каждой свече паттерна for(int j=0; j=0) ObjectDelete(chart_id,back_name); } //--- Цикл создания объектов строк for(int i=0; i=0 ? clrBlue : StringFind(lines[i],"SELL")>=0 ? clrDarkRed : clrDarkSlateGray); ObjectSetInteger(chart_id,name,OBJPROP_COLOR,clr); } //--- Дублируем информацию в основной журнал и перерисовываем указанный график Print(stats_text); ChartRedraw(chart_id); } //+------------------------------------------------------------------+ //| Выводит окно чарта с центровкой на историческом паттерне | //+------------------------------------------------------------------+ long ShowPatternWindow(string name,int x,int y,int width,int height,const SPattern &pattern) { //--- Вычисляем актуальный индекс бара по времени из структуры int idx=iBarShift(_Symbol,_Period,pattern.time); //--- Если бар не найден в доступной истории - выходим if(idx==WRONG_VALUE) { PrintFormat("%s: Bar with time %s not found in available history",__FUNCTION__,TimeToString(pattern.time)); return WRONG_VALUE; } //--- Проверяем наличие объекта окна, создаём, если его нет if(ObjectFind(0,name)<0) { if(!ObjectCreate(ChartID(),name,OBJ_CHART,0,0,0)) { PrintFormat("%s: ObjectCreate(\"%s\",OBJ_CHART) failed",__FUNCTION__,name); return WRONG_VALUE; } //--- Устанавливаем свойства окна объекта-графика ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,width); ObjectSetInteger(0,name,OBJPROP_YSIZE,height); } //--- Управление объектом графиком через его идентификатор long id=ObjectGetInteger(ChartID(),name,OBJPROP_CHART_ID); if(id>WRONG_VALUE) { //--- Синхронизируем символ и период графика if(ChartSymbol(id)!=_Symbol || ChartPeriod(id)!=_Period) ChartSetSymbolPeriod(id,_Symbol,_Period); //--- Свойства графика объекта ChartSetInteger(id,CHART_AUTOSCROLL,false); ChartSetInteger(id,CHART_SHIFT,false); ChartSetInteger(id,CHART_SCALE,ChartGetInteger(ChartID(),CHART_SCALE)); //--- Устанавливаем графическую метку на изломе ZZ double price=(pattern.type==POSITION_TYPE_SELL) ? pattern.steps[0].high : pattern.steps[0].low; CreateMarker(id,pattern.time,price,pattern.type); //--- Получаем количество видимых баров для позиции метки излома ZZ int visible_bars=(int)ChartGetInteger(id,CHART_VISIBLE_BARS); //--- Если статистика выводится на подложку, то позиция излома ZZ находится во второй трети окна int center=visible_bars/(InpStatsBack ? 3 : 2); //--- Рассчитываем итоговое смещение для центровки излома ZZ относительно конца истории int offset=idx-center; if(offset<0) offset=0; //--- Смещаем график от конца истории на рассчитанное смещение ChartNavigate(id,CHART_END,-offset); //--- Перерисовываем график объекта ChartRedraw(id); } //--- Возвращаем идентификатор графика объекта return id; } //+------------------------------------------------------------------+ //| Создает или перемещает вертикальную линию начала анализа | //+------------------------------------------------------------------+ void CreateAnalysisLine(const datetime time,const color clr) { //--- Формируем имя объекта string name=prefix+"_AnalysisLine"; //--- Если линии нет - создаем её if(ObjectFind(0,name)<0) { if(!ObjectCreate(0,name,OBJ_VLINE,0,time,0)) { PrintFormat("%s: ObjectCreate(%s,OBJ_VLINE) failed",__FUNCTION__,name); return; } //--- Устанавливаем свойства линии ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_RAY,true); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); ObjectSetInteger(0,name,OBJPROP_BACK,false); } //--- Обновляем положение, цвет и тултип ObjectSetInteger(0,name,OBJPROP_TIME,time); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); string tooltip=StringFormat("The beginning of the search for\nZZ patterns (%s)",TimeToString(time)); ObjectSetString(0,name,OBJPROP_TOOLTIP,tooltip); //--- Перерисовываем основной график ChartRedraw(0); } //+------------------------------------------------------------------+ //| Формирует базу паттернов из истории | //+------------------------------------------------------------------+ bool CreatePatternBase(const int attempts_limit, // Максимальное количество попыток сбора const int pattern_depth, // Глубина паттерна в барах const int hist_limit, // Ограничение глубины поиска по истории int &attempts_cnt, // Счётчик выполненных попыток SPattern &base_array[]) // Массив для хранения собранных паттернов { static bool warned=false; // было ли сообщение в журнал //--- Если лимит попыток исчерпан - выходим if(attempts_cnt>=attempts_limit) { if(!warned) { PrintFormat("%s: Error. Maximum attempts (%d) reached. Check data",__FUNCTION__,attempts_limit); warned=true; } return false; } //--- Увеличиваем счётчик и пробуем собрать базу attempts_cnt++; PrintFormat("%s: Attempt %d to collect pattern base...",__FUNCTION__,attempts_cnt); //--- Если данные не получены - очищаем массив и ждем следующей попытки if(!CollectHistoryData(pattern_depth,hist_limit,base_array)) { ArrayFree(base_array); return false; } //--- База успешно сформирована return true; } //+------------------------------------------------------------------+ //| Осуществляет поиск совпадений текущего рынка с базой паттернов | //+------------------------------------------------------------------+ void FindMatches(const int pattern_depth, // Глубина паттерна в барах const double total_sim, // Порог сходства паттерна (0-100%) const SPattern&base_array[], // База исторических паттернов SMatchResult &result, // Структура для записи результатов const int start_idx, // Индекс бара для снятия слепка const datetime max_time, // Время отсечки будущего const double min_candle_sim, // Сходство свечей (0-100%) const double min_pos_sim, // Сходство относительного расположения (0-100%) const int max_grade_diff) // Максимальная разница по градациям { //--- Если передан пустой массив - выходим int total=ArraySize(base_array); if(total==0) { PrintFormat("%s: Error. Empty array passed",__FUNCTION__); return; } //--- Обнуляем структуру перед заполнением ZeroMemory(result); result.best_idx=WRONG_VALUE; //--- Получаем текущий паттерн ("слепок" рынка) от указанного индекса SPattern curr_pattern; if(!GetCurrentPattern(curr_pattern,start_idx,pattern_depth)) return; double max_res=0; double sum_buy=0, sum_sell=0; //--- Проходим по всей базе паттернов for(int i=0; i=max_time) continue; //--- Сравниваем текущий паттерн с историческим образцом double res=ComparePatterns(base_array[i],curr_pattern,min_candle_sim,min_pos_sim,max_grade_diff); //--- Если сходство ниже порога - идём далее if(resmax_res) { max_res=res; result.best_idx=i; } } //--- Рассчитываем итоговую статистику по найденным совпадениям int total_matches=result.cnt_buy+result.cnt_sell; if(total_matches>0) { result.avg_buy =(result.cnt_buy > 0 ? sum_buy /result.cnt_buy : 0); result.avg_sell=(result.cnt_sell> 0 ? sum_sell/result.cnt_sell : 0); //--- Формируем текст доминирующего сигнала result.signal=(result.cnt_buy>result.cnt_sell) ? "PREDICT BUY" : (result.cnt_sell>result.cnt_buy) ? "PREDICT SELL" : "UNCERTAIN"; } } //+------------------------------------------------------------------+ //| Выполняет поиск совпадений и выводит результаты визуально | //+------------------------------------------------------------------+ void FindAndDisplayMatches(const int pattern_depth, // Глубина паттерна в барах const double total_sim, // Порог сходства паттерна (0-100%) const double min_candle_sim, // Сходство свечей (0-100%) const double min_pos_sim, // Минимально допустимый процент сходства позиции const int max_grade_diff, // Максимальная разница по градациям const SPattern &base_array[], // База исторических паттернов SMatchResult &match_res, // Структура для записи результатов поиска const string wnd_name, // Имя вложенного окна чарта const int wnd_x, // Координата X окна паттерна const int wnd_y, // Координата Y окна паттерна const int wnd_w, // Ширина окна паттерна const int wnd_h, // Высота окна паттерна const int stats_x, // Координата X блока статистики const int stats_y, // Координата Y первой строки const int stats_step, // Вертикальный шаг между строками const bool use_back, // Флаг использования подложки const int start_idx, // Индекс бара начала поиска const color line_clr) // Цвет линии анализа (ДОБАВЛЕНО) { //--- Определяем время выбранного бара для фильтрации паттернов справа от времени бара ResetLastError(); datetime max_time=iTime(_Symbol,_Period,start_idx); int err=GetLastError(); if(max_time==0 && err!=0) { PrintFormat("%s: iTime(%s,%s,%d) failed. Error %d", __FUNCTION__,_Symbol,StringSubstr(EnumToString(_Period),7),start_idx,err); return; } //--- Выполняем поиск совпадений с учетом временной отсечки FindMatches(pattern_depth,total_sim,base_array,match_res,start_idx,max_time,min_candle_sim,min_pos_sim,max_grade_diff); //--- Рисуем вертикальную линию начала анализа на основном графике CreateAnalysisLine(max_time,line_clr); //--- Если найдено подходящее совпадение - визуализируем его if(match_res.best_idx>WRONG_VALUE) { //--- Создаем/обновляем вложенное окно чарта long id=ShowPatternWindow(prefix+wnd_name,wnd_x,wnd_y,wnd_w,wnd_h,base_array[match_res.best_idx]); //--- Если идентификатор окна получен - выводим в него блок статистики if(id>WRONG_VALUE) DisplayStatistics(id,match_res.signal, match_res.cnt_buy, match_res.cnt_sell, match_res.avg_buy, match_res.avg_sell, base_array[match_res.best_idx], match_res.best_idx, base_array.Size(), stats_x, stats_y, stats_step, use_back); } //--- Если совпадений нет - выведем в лог сообщение else PrintFormat("%s: No matches found for the configuration at %s",__FUNCTION__,TimeToString(max_time)); } //+------------------------------------------------------------------+ //| Обрабатывает выбор бара на графике для ручного поиска | //+------------------------------------------------------------------+ void ChartClickHandler(const long lparam,const double dparam,int &start_idx) { //--- Проверяем, зажата ли клавиша Ctrl в момент клика if(TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL)<0) { datetime click_time=0; double click_price=0; int sub_window=0; //--- Преобразуем координаты клика во время и цену ResetLastError(); if(!ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sub_window,click_time,click_price)) { PrintFormat("%s: ChartXYToTimePrice() failed. Error %d",__FUNCTION__,GetLastError()); return; } //--- Находим индекс бара по времени клика int bar_idx=iBarShift(_Symbol,_Period,click_time); if(bar_idx==WRONG_VALUE) { PrintFormat("%s: Error. iBarShift() failed for time %s",__FUNCTION__,TimeToString(click_time)); return; } //--- Записываем новый индекс в переменную, переданную по ссылке start_idx=bar_idx; //--- Отобразим в журнале событие PrintFormat("%s: Manual mode. Analysis bar changed to: %d (%s)", __FUNCTION__,start_idx,TimeToString(click_time)); //--- Запускаем поиск и визуализацию от выбранного бара на графике FindAndDisplayMatches(ExtPatternDepth,ExtTotalSim,ExtMinCandleSim,ExtMinPosSim,ExtMaxGradeDiff,patterns,match_result,prefix+"BestMatchWindow", InpWndX,InpWndY,InpWndW,InpWndH,InpStatsX,InpStatsY,InpStatsStep,InpStatsBack,start_idx,InpAnalysColor); } } //+------------------------------------------------------------------+