Разработка инструментария для анализа Price Action (Часть 32): Модуль распознавания свечных паттернов на Python (II) – Распознавание с помощью Ta-Lib
Содержание
- Введение
- В продолжение предыдущего разбора
- Знакомство с TA-Lib
- Разбор работы системы
- Тестирование и результаты
- Заключение
Введение
В своей предыдущей статье я показал, как использовать Python для распознавания свечных паттернов. Сначала мы использовали ручные методы распознавания, при которых каждый свечной паттерн описывался в коде явно. В этом подходе, когда MQL5 отправляет в Python данные, включая цену открытия, цену закрытия, цену максимума, цену минимума и временную метку, Python обрабатывает их и с помощью вычислений определяет паттерн.
В этой статье мы развиваем этот подход, интегрируя библиотеку TA-Lib для Python, которая умеет автоматически распознавать более 60 свечных паттернов. Мы также будем использовать ее вместе с mplfinance и matplotlib для построения свечных графиков.
Ниже приведен обзор основных используемых библиотек:
from flask import Flask, reque st, jsonify, send_file import numpy as np import pandas as pd import talib import json import matplotlib.pyplot as plt import mplfinance as mpf
Ниже кратко разберем каждую из них.
Flask
from flask import Flask, request, jsonify, send_file
- Легковесный веб-фреймворк для Python, используемый для создания веб-приложений и API.
- Основной класс для создания экземпляра приложения Flask.
jsonify – преобразует структуры данных Python в формат JSON для ответа.
send_file – отправляет файлы с сервера клиенту; полезно, например, при скачивании сгенерированных изображений или отчетов.
import numpy as np
- NumPy – базовая библиотека для численных вычислений в Python.
- np – стандартный алиас для NumPy.
Библиотека эффективно обрабатывает операции с массивами, математические функции и преобразование данных.
import pandas as pd
- Pandas – мощная библиотека для обработки и преобразования данных.
- pd – стандартный алиас.
Используется для работы со структурами данных, такими как DataFrames, которые хорошо подходят для обработки временных рядов, включая свечные данные.
import talib
- TA-Lib – библиотека технического анализа для Python.
Предоставляет функции для автоматического расчета широкого спектра технических индикаторов и распознавания свечных паттернов.
import json
- Встроенная библиотека Python для работы с данными JSON.
Разбирает данные JSON из запросов или преобразует объекты Python в JSON для ответа.
import matplotlib.pyplot as plt
- Matplotlib – библиотека для построения графиков.
- pyplot – модуль Matplotlib, предоставляющий интерфейс в стиле MATLAB.
- plt – стандартный алиас.
- Используется для создания статических, анимированных и интерактивных графиков, включая свечные графики.
import mplfinance as mpf
- mplfinance – специализированная библиотека для визуализации финансовых данных, особенно свечных и OHLC-графиков.
- mpf – алиас, используемый для удобства.
- Упрощает построение финансовых графиков и предоставляет расширенные возможности настройки.
Ниже приведены преимущества использования TA-Lib для распознавания паттернов:
| Преимущество | Описание |
|---|---|
| Автоматизация | Автоматически распознает более 60 свечных паттернов без необходимости вручную прописывать логику каждого из них. |
| Эффективность | Более быстрое распознавание паттернов, подходящее для анализа в реальном времени. |
| Точность | Использует хорошо протестированные алгоритмы для надежного распознавания паттернов. |
| Совместимость | Легко интегрируется с другими библиотеками Python для анализа и визуализации. |
| Простота использования | Упрощает реализацию за счет простых и понятных вызовов функций. |
В продолжение предыдущего разбора
Как я уже упоминал, предыдущая система представляла собой систему для распознавания свечных паттернов, написанную вручную на Python. Давайте кратко разберем, как была устроена эта система.
- Советник MetaTrader 5 ("координатор на стороне графика")
В MetaTrader 5 советник отслеживает появление каждого нового бара, а затем сразу собирает данные о цене открытия, цене максимума, цене минимума, цене закрытия и временной метке последних 31 свечи. Затем он упаковывает их в компактный JSON-пакет и через WebRequest отправляет в локальный сервис распознавания паттернов. Когда сервер возвращает массив названий свечных паттернов,
MQL5 EA ➔ (OHLC JSON POST) ➔ Python Server
советник автоматически размещает каждую метку на максимуме соответствующего бара, используя выбранные вами размер шрифта и цвет, и вызывает оповещение MetaTrader 5 для каждого валидного паттерна. На каждом тике также удаляются предыдущие метки, чтобы график оставался чистым, а при удалении советник убирает все созданные им объекты, чтобы после него ничего не оставалось.
MQL5 EA draws labels & fires alerts
- Сервер Python Flask: модуль распознавания паттернов
На стороне Python я создал приложение Flask с эндпоинтом /patterns. Оно принимает JSON-массив с данными OHLC, преобразует временные метки UNIX в дату и время по UTC и строит pandas DataFrame для надежной индексации. Затем я применяю на чистом Python набор классических правил свечного анализа – доджи, молот, поглощение, харами, утренняя звезда, вечерняя звезда и другие, – с четкими и тестируемыми функциями для каждого паттерна.
def detect_patterns(df): pats = ["None"]*len(df) for i in range(len(df)): o,h,l,c = df.iloc[i][["OPEN","HIGH","LOW","CLOSE"]] body = abs(c-o); rng = max(h-l,1e-6) lower = min(o,c)-l; upper = h-max(o,c) # doji if body/rng <= 0.1: pats[i] = "doji"; continue # hammer if lower>=2*body and upper<=body: pats[i] = "hammer"; continue # shooting star if upper>=2*body and lower<=body: pats[i] = "shootingstar"; continue # bullish engulfing if i>0: po,pc = df.iloc[i-1][["OPEN","CLOSE"]] if pc<po and c>o and o<=pc and c>=po: pats[i] = "bullishengulfing"; continue # bearish engulfing if i>0: po,pc = df.iloc[i-1][["OPEN","CLOSE"]] if pc>po and c<o and o>=pc and c<=po: pats[i] = "bearishengulfing"; continue # harami if i>0: po,pc = df.iloc[i-1][["OPEN","CLOSE"]] if pc<po and o<c and o>pc and c<po: pats[i] = "bullishharami"; continue if pc>po and o>c and o<pc and c>po: pats[i] = "bearishharami"; continue # morning star if i>1: o1,c1 = df.iloc[i-2][["OPEN","CLOSE"]] o2,c2 = df.iloc[i-1][["OPEN","CLOSE"]] if c1<o1 and abs(c2-o2)<(df.iloc[i-1]["HIGH"]-df.iloc[i-1]["LOW"])*0.3 \ and c>o2 and c>(o1+c1)/2: pats[i] = "morningstar"; continue # evening star if i>1: o1,c1 = df.iloc[i-2][["OPEN","CLOSE"]] o2,c2 = df.iloc[i-1][["OPEN","CLOSE"]] if c1>o1 and abs(c2-o2)<(df.iloc[i-1]["HIGH"]-df.iloc[i-1]["LOW"])*0.3 \ and c<o2 and c<(o1+c1)/2: pats[i] = "eveningstar"; continue return pats
После маркировки каждого бара я подсчитываю все случаи, отличные от "None", формирую краткую сводку в логе и измеряю время обработки в миллисекундах. Наконец, я возвращаю объект JSON, содержащий полный список названий паттернов, по одному на каждый бар, и лог. Такое разделение оставляет тяжелую обработку данных на стороне Python, где ее проще поддерживать и расширять, тогда как MetaTrader 5 отвечает за ввод-вывод данных графика и визуализацию.
Python Server ➔ (patterns & log JSON) ➔ MQL5 EAЗнакомство с Ta-Lib
TA-Lib (Technical Analysis Library) – это библиотека с открытым исходным кодом, широко используемая трейдерами, инвесторами и аналитиками для выполнения сложных технических расчетов и разработки торговых стратегий. Изначально разработанная Марио Фортье, эта библиотека написана на ANSI C и включает обширный набор из более чем 200 технических индикаторов, включая ADX, MACD, RSI, Stochastic Oscillator, Bollinger Bands и другие, а также возможность распознавать более 60 свечных паттернов. Ее ядро на C/C++ предоставляет API, доступный в том числе через Python, что упрощает интеграцию в различные приложения. С момента первого выпуска в 2001 году под лицензией BSD библиотека TA-Lib зарекомендовала себя как стабильный и надежный инструмент, а ее алгоритмы выдержали проверку временем и по-прежнему широко используются как в проектах с открытым исходным кодом, так и в коммерческих проектах.
В нашей системе обширный набор функций распознавания паттернов из TA-Lib встроен в аналитический пайплайн на Python для автоматизации распознавания свечных паттернов. Система динамически загружает все нужные функции распознавания свечных паттернов из TA-Lib, что позволяет ей выявлять более 60 различных паттернов во входящих рыночных данных. Сочетая надежные алгоритмы TA-Lib с пользовательской логикой фильтрации, система точно определяет бычьи и медвежьи сигналы, которые затем наносятся на свечные графики с помощью mplfinance. Весь этот процесс реализован в виде Flask-сервиса, что упрощает обработку данных в реальном времени, распознавание паттернов и визуализацию. Этот подход показывает, как возможности TA-Lib можно использовать вместе с современными инструментами Python для создания сложных автоматизированных систем торгового анализа, органично дополняющих стратегии MQL5.
Ниже приведено пошаговое руководство по установке TA-Lib на компьютер.
Шаг 1: скачайте и установите файл TA-Lib .whl
- Для Windows:
Скачайте предварительно скомпилированные бинарные файлы из надежного источника. Скачайте подходящий .whl-файл, например TA_Lib‑0.4.0‑cp39‑cp39‑win_amd64.whl, совместимый с Python 3.9 в 64-разрядной Windows. Под "подходящим" .whl-файлом имеется в виду файл, который соответствует и версии Python, и архитектуре вашей системы, чтобы обеспечить совместимость.
Запустите установку через pip:
pip install path\to\your\downloaded\file.whl
- Для macOS:
Установите C-библиотеку TA-Lib с помощью Homebrew:
brew install ta-lib
- Для Linux (Debian/Ubuntu):
Установите зависимости через apt:
sudo apt-get update sudo apt-get install libta-lib0-dev
Шаг 2: установите Python-обертку для TA-Lib
После установки библиотеки на C установите Python-обертку:
pip install ta-lib
Шаг 3: проверьте установку
Откройте оболочку Python и выполните:
import talib print(talib.__version__)
Если ошибок не возникло и версия вывелась на экран, значит установка прошла успешно.
Давайте посмотрим, какие свечные паттерны доступны в установленной версии библиотеки TA-Lib.
CDL2CROWS CDL3BLACKCROWS CDL3INSIDE CDL3LINESTRIKE CDL3OUTSIDE CDL3STARSINSOUTH CDL3WHITESOLDIERS CDLABANDONEDBABY CDLADVANCEBLOCK CDLBELTHOLD CDLBREAKAWAY CDLCLOSINGMARUBOZU CDLCONCEALBABYSWALL CDLCOUNTERATTACK CDLDARKCLOUDCOVER CDLDOJI CDLDOJISTAR CDLDRAGONFLYDOJI CDLENGULFING CDLEVENINGDOJISTAR CDLEVENINGSTAR CDLGAPSIDESIDEWHITE CDLGRAVESTONEDOJI CDLHAMMER CDLHANGINGMAN CDLHARAMI CDLHARAMICROSS CDLHIGHWAVE CDLHIKKAKE CDLHIKKAKEMOD CDLHOMINGPIGEON CDLIDENTICAL3CROWS CDLINNECK CDLINVERTEDHAMMER CDLKICKING CDLKICKINGBYLENGTH CDLLADDERBOTTOM CDLLONGLEGGEDDOJI CDLLONGLINE CDLMARUBOZU CDLMATCHINGLOW CDLMATHOLD CDLMORNINGDOJISTAR CDLMORNINGSTAR CDLONNECK CDLPIERCING CDLRICKSHAWMAN CDLRISEFALL3METHODS CDLSEPARATINGLINES CDLSHOOTINGSTAR CDLSHORTLINE CDLSPINNINGTOP CDLSTALLEDPATTERN CDLSTICKSANDWICH CDLTAKURI CDLTASUKIGAP CDLTHRUSTING CDLTRISTAR CDLUNIQUE3RIVER CDLUPSIDEGAP2CROWS CDLXSIDEGAP3METHODS
Хотя большинство трейдеров хорошо знают такие классические паттерны, как доджи и поглощение, некоторые менее известные формации тоже могут указывать на тонкие изменения в рыночной психологии. Паттерн "брошенный младенец" (Abandoned Baby) – это редкий разворотный паттерн из трех баров: доджи отрывается гэпом от предыдущей свечи, а затем еще один гэп в противоположную сторону оставляет все три бара "отделенными" друг от друга, что нередко предвещает решительный разворот тренда. Паттерн "сэндвич" (Stick Sandwich) формируется, когда две сильные свечи одного цвета окружают небольшой бар противоположного цвета, при этом последний бар закрывается на уровне закрытия первого или очень близко к нему; такой "сэндвич" показывает, что, несмотря на кратковременное движение против тренда, исходный импульс сохраняется. Паттерн "разрыв Тасуки" (Tasuki Gap) – это паттерн продолжения из трех баров, в котором исходный гэп по направлению тренда частично, но не полностью, перекрывается третьим баром, подчеркивая нежелание рынка закрывать этот гэп и подтверждая силу текущего тренда.
Еще два менее известных паттерна, "хиккаке" (Hikkake) и "захват за пояс" (Belt Hold), добавляют картине дополнительные нюансы. Паттерн "хиккаке", название которого по-японски означает "ловушка", начинается с внутреннего бара, за которым следует ложный пробой, заманивающий трейдеров в одну сторону, после чего цена разворачивается и закрывается за противоположной границей, оставляя в ловушке участников, вставших не в ту сторону, и ускоряя основной тренд. В отличие от него, "захват за пояс" – это одиночная сильная свеча, идущая против предыдущего движения: бычий "захват за пояс" открывается на минимуме или рядом с ним в нисходящем тренде, а затем уверенно закрывается выше, тогда как медвежий "захват за пояс" открывается на максимуме или рядом с ним в восходящем тренде и затем резко закрывается ниже. Поскольку "захват за пояс" отражает резкую смену контроля внутри одного бара и не требует, чтобы предыдущий бар был "поглощен", этот паттерн может служить четким и сильным сигналом разворота на ключевых уровнях поддержки и сопротивления.
В TA-Lib каждая функция свечного паттерна начинается с префикса CDL-, что является сокращением от "Candle". Этот префикс указывает на то, что функция реализует один из классических алгоритмов распознавания свечных паттернов, например CDLDOJI, CDLENGULFING, CDLHAMMER и т.д. Когда вы вызываете одну из этих функций CDL* с массивами Open-High-Low-Close (OHLC), она возвращает целочисленную последовательность, в которой ненулевые значения указывают на наличие соответствующей свечной формации, а знак указывает на ее направление.
Разбор работы системы
В этом разделе я подробно разберу работу системы и отдельно покажу роли скриптов MQL5 и Python. В основе этой схемы лежит четко организованный цикл "запрос-ответ": каждый раз, когда закрывается новая свеча, советник MQL5 берет значения цены открытия, цены максимума, цены минимума, цены закрытия и временной метки по последним 60 барам, упаковывает их в компактный JSON-пакет и отправляет его в локальный Flask-сервис на эндпоинт /patterns.
Затем советник ждет ответа сервера, который теперь включает два параллельных массива длиной 60: один для паттернов, то есть названий свечных формаций, и второй для сигналов – "bullish" или "bearish", – после чего сразу разбирает этот JSON. Затем в MetaTrader 5 советник сначала удаляет все существующие метки, а потом заново создает текстовые объекты на максимуме каждого бара, где был найден реальный паттерн, окрашивая их в зеленый цвет для бычьих сигналов и в красный – для медвежьих, после чего вызывает функцию Alert().
Тем временем эндпоинт Flask принимает JSON, нормализует его до pandas DataFrame, применяет более 60 детекторов паттернов TA-Lib, помечает каждый бар, определяет бычье или медвежье направление и возвращает оба массива, а также краткий лог и метрику времени выполнения. Такое четкое разделение – MQL5 для ввода-вывода данных на графике и оповещений, Python для нормализации данных и логики паттернов – делает обе части системы специализированными, эффективными и удобными для расширения.

- Разбор советника на MQL5
При первой загрузке советник сразу записывает в лог стартовое сообщение с напоминанием добавить эндпоинт Flask в белый список в настройках советников MetaTrader 5. Выполнив этот шаг один раз, вы гарантируете, что последующие HTTP-запросы к вашему локальному сервису распознавания паттернов будут разрешены, и предотвратите скрытые сбои во время выполнения.
int OnInit() { Log("EA started – allow WebRequest to: " + InpURL); return INIT_SUCCEEDED; }
На каждом входящем тике советник сравнивает временную метку текущей свечи с индексом 0 с временной меткой последнего обработанного бара. Если с момента предыдущей проверки не закрылся новый бар, функция просто завершает работу, избегая лишних операций и гарантируя, что анализ паттернов выполняется только один раз для каждой закрытой свечи.
void OnTick() { datetime bar = iTime(_Symbol, InpTF, 0); if(bar == 0 || bar == g_lastBar) return; g_lastBar = bar; // … (continue processing) }
Как только подтверждается новый бар, советник проходит в обратном порядке по последним 60 свечам, извлекая для каждого бара цену открытия, цену максимума, цену минимума, цену закрытия и временную метку UNIX. Эти значения временно сохраняются в массивах фиксированного размера, а затем преобразуются в компактный JSON-объект, который содержит символ, таймфрейм и все 60 наборов данных OHLC и временных меток.
double o[BARS], h[BARS], l[BARS], c[BARS]; long t[BARS]; for(int i = 0; i < BARS; i++) { int sh = i + 1; o[i] = iOpen(_Symbol, InpTF, sh); h[i] = iHigh(_Symbol, InpTF, sh); l[i] = iLow(_Symbol, InpTF, sh); c[i] = iClose(_Symbol, InpTF, sh); t[i] = (long)iTime(_Symbol, InpTF, sh); } string json = StringFormat( "{\"symbol\":\"%s\",\"timeframe\":%d,\"time\":[%s],\"open\":[%s],\"high\":[%s],\"low\":[%s],\"close\":[%s]}", _Symbol, InpTF, CSVInt(t), CSV(o), CSV(h), CSV(l), CSV(c) ); Log("JSON-OUT: " + json);
Когда JSON-объект сформирован, советник отправляет POST-запрос на настроенный URL Flask и ждет ответа до истечения заданного таймаута. Он перехватывает любые сетевые ошибки или ошибки доступа, записывая подробные сообщения в лог, если запрос не удался; в противном случае получает JSON-ответ сервера в виде текстовой строки для дальнейшей обработки.
char body[]; StringToCharArray(json, body, 0, StringLen(json)); char reply[]; string hdr = "Content-Type: application/json\r\n", respHdr; int code = WebRequest("POST", InpURL, hdr, InpTimeout, body, reply, respHdr); if(code == -1) { Log("WebRequest failed: " + IntegerToString(GetLastError())); return; } string resp = CharArrayToString(reply, 0, -1, CP_UTF8); Log("HTTP " + IntegerToString(code) + " RESP: " + resp);
После того как HTTP-вызов завершен, советник ищет в ответе два массива: "patterns" и "signals". Он использует простые процедуры поиска по строке и разбиения, чтобы преобразовать эти JSON-массивы обратно в массивы строк MQL5, и перед продолжением проверяет, что каждый массив содержит ровно 60 элементов.
string patTxt, sigTxt, patt[], sigs[]; if(!ExtractArray(resp, "patterns", patTxt) || !ExtractArray(resp, "signals", sigTxt) || !ParseArray(patTxt, patt) || !ParseArray(sigTxt, sigs) || ArraySize(patt) != BARS || ArraySize(sigs) != BARS) { Log("Malformed patterns or signals"); return; }
Для каждого из 60 баров советник проверяет соответствующие значения паттерна и сигнала. Каждый раз, когда обнаруживается реальный паттерн, то есть не "None", советник вычисляет нужное смещение свечи, удаляет старые пометки и создает новую текстовую метку на максимуме бара. Метки кодируются цветом – лаймовый для бычьих сигналов и красный для медвежьих, – и каждая вызывает понятное оповещение MetaTrader 5 с символом, таймфреймом, направлением, названием паттерна и временной меткой.
for(int i = 0; i < BARS; i++) { string pat = patt[i], sig = sigs[i]; if(pat == "" || pat == "None") continue; int shift = BARS - i; datetime tm = iTime(_Symbol, InpTF, shift); double y = iHigh(_Symbol, InpTF, shift); string obj = PREFIX + IntegerToString(shift); ObjectCreate(0, obj, OBJ_TEXT, 0, tm, y); ObjectSetString(0, obj, OBJPROP_TEXT, pat); color col = (sig == "bullish" ? clrLime : clrRed); ObjectSetInteger(0, obj, OBJPROP_COLOR, col); ObjectSetInteger(0, obj, OBJPROP_FONTSIZE, InpFontSize); ObjectSetInteger(0, obj, OBJPROP_SELECTABLE, false); Alert(StringFormat("%s %s %s pattern '%s' at %s", _Symbol, EnumToString(InpTF), sig, pat, TimeToString(tm, TIME_DATE|TIME_MINUTES))); }
Когда советник удаляется или MetaTrader 5 закрывается, процедура деинициализации проходит по всем объектам графика с префиксом меток советника и удаляет их. Это гарантирует, что после отключения советника на графике не останется устаревших меток и рабочее пространство останется чистым.
void OnDeinit(const int reason) { for(int i = ObjectsTotal(0,0,-1)-1; i >= 0; i--) { string n = ObjectName(0, i, 0, -1); if(StringFind(n, PREFIX) == 0) ObjectDelete(0, n); } Log("EA removed"); }
- Разбор Flask-сервера на Python
Когда запускается сервис Flask, он настраивает логирование уровня INFO, чтобы записывать каждый входящий запрос и внутреннее событие и в консоль, и в лог-файл с ротацией. Сразу после этого скрипт просматривает библиотеку TA-Lib и динамически собирает в словарь в памяти все функции свечных паттернов, имена которых начинаются с "CDL". Благодаря этому сервис автоматически поддерживает все текущие и будущие паттерны TA-Lib без ручной доработки: как только библиотека обновляется и в ней появляются новые паттерны, они сразу становятся доступны системе.
app = Flask(__name__) logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s') app.logger.setLevel(logging.INFO) # load all TA‑Lib candlestick functions CDL_FUNCS = { name: getattr(talib, name) for name in talib.get_functions() if name.startswith("CDL") }
Каждый раз, когда советник отправляет POST на эндпоинт /patterns, сервер считывает необработанное тело запроса, удаляет нулевые байты, которые может добавлять MetaTrader 5, а затем пытается декодировать запрос как JSON. Если парсинг не удался или отсутствует хотя бы один из обязательных массивов open, high, low, close или time, сервер немедленно возвращает понятную ошибку HTTP 400 с поясняющим сообщением. Проверяя данные на этом раннем этапе, сервер не дает некорректным или неполным данным пройти дальше по пайплайну и вызвать неочевидные ошибки на следующих этапах.
@app.route('/patterns', methods=['POST']) def patterns(): app.logger.info("Received /patterns request") try: raw = request.data if b'\x00' in raw: raw = raw.split(b'\x00', 1)[0] data = json.loads(raw.decode('utf-8')) except Exception as e: return jsonify(error="Invalid JSON", details=str(e)), 400
После успешной проверки JSON сервер извлекает пять ключевых массивов – временные метки и четыре ценовые серии – и разворачивает их таким образом, чтобы индексу 0 соответствовал самый старый бар. Затем он преобразует список временных меток UNIX в pandas DatetimeIndex, чтобы все дальнейшие операции корректно учитывали время. Если длина массивов не совпадает или в них есть нечисловые значения, сервис сразу фиксирует проблему и возвращает ошибку, гарантируя, что до логики распознавания доходят только строго согласованные и корректно типизированные данные.
try: symbol = data.get('symbol', 'Instrument') ts = data.get('time', []) open_ = np.array(data['open'][::-1], dtype=float) high = np.array(data['high'][::-1], dtype=float) low = np.array(data['low'][::-1], dtype=float) close = np.array(data['close'][::-1], dtype=float) idx = pd.to_datetime(np.array(ts[::-1], dtype='int64'), unit='s') app.logger.info(f"Loaded {len(open_)} bars for {symbol}") except KeyError as ke: return jsonify(error=f"Missing field {ke}"), 400 except Exception as e: return jsonify(error="Bad field format", details=str(e)), 400
Получив чистые массивы NumPy, сервис проходит по всем функциям свечных паттернов TA-Lib и применяет каждую ко всей серии. Любой ненулевой результат фиксируется как кандидат на срабатывание паттерна. Две дополнительные вспомогательные процедуры – одна для дополнительной проверки "харами", другая для уточнения поглощений, – применяются условно на основе флагов запроса, включая более строгие критерии там, где это нужно. Поскольку каждый вызов TA-Lib обернут в блок try/except, одна проблемная функция или неожиданная форма данных не может вызвать сбой всего сервиса; в таком случае процедура просто пропускает эту функцию и продолжает обработку остальных.
n = len(open_) all_hits = [[] for _ in range(n)] for name, func in CDL_FUNCS.items(): try: res = func(open_, high, low, close) except Exception: continue for i, v in enumerate(res): if v == 0: continue if name == "CDLHARAMI" and fh and not is_manual_harami(open_, close, i, tol=HARAMI_TOL): continue if name == "CDLENGULFING" and fe and not is_manual_engulfing(open_, close, i, tol=ENGULF_TOL): continue all_hits[i].append((name, v))
Поскольку на одном баре могут сработать сразу несколько паттернов, в случаях конфликтов и необходимости выбрать доминирующий паттерн сервис использует небольшой настраиваемый список приоритетов, где, например, у поглощений приоритет выше, чем у харами, а у харами – выше, чем у доджи. Если ни один из приоритетных паттернов не найден, выбирается тот, у которого наибольшее абсолютное значение результата TA-Lib. Числовой знак выбранного результата затем сопоставляется с меткой "bullish" или "bearish". Такой детерминированный подход – один паттерн на один бар – гарантирует, что каждая свеча получает один четкий сигнал, упрощая дальнейшую разметку графика и логику оповещений.
PRIORITY = ["CDLENGULFING", "CDLHARAMI", "CDLDOJI"] detected = [None] * n signals = [None] * n for i, hits in enumerate(all_hits): if not hits: continue pick = next(((nm, val) for pat in PRIORITY for nm, val in hits if nm == pat), None) if pick is None: pick = max(hits, key=lambda x: abs(x[1])) nm, val = pick detected[i] = nm signals[i] = "bullish" if val > 0 else "bearish"
Затем скрипт строит pandas DataFrame из четырех массивов цен и datetime-индекса, а после этого добавляет два новых столбца: один для названия обнаруженного паттерна, другой – для сигнала bullish/bearish. Чтобы подготовить визуальную разметку, скрипт вычисляет небольшой отступ на основе общего ценового диапазона и формирует список наложений с маркерами: зеленые стрелки вверх размещаются чуть ниже минимумов баров для бычьих сигналов, а красные стрелки вниз – чуть выше максимумов баров для медвежьих. Такое позиционирование маркеров относительно динамического диапазона графика помогает избежать визуального шума и сохраняет единообразие на разных инструментах и таймфреймах.
df = pd.DataFrame({"Open": open_, "High": high, "Low": low, "Close": close}, index=idx)
df["Pattern"] = pd.Series(detected, index=idx).fillna("None")
df["Signal"] = pd.Series(signals, index=idx).fillna("")
df.sort_index(inplace=True)
adds = []
price_rng = df["High"].max() - df["Low"].min()
for tstamp, row in df.iterrows():
if row["Pattern"] == "None":
continue
if row["Signal"] == "bullish":
y, marker, color = row["Low"] - price_rng*0.005, "^", "green"
else:
y, marker, color = row["High"] + price_rng*0.005, "v", "red"
adds.append(mpf.make_addplot(
[y if i == tstamp else np.nan for i in df.index],
type="scatter", marker=marker, markersize=80, color=color
))Наконец, сервис задает пользовательскую цветовую схему свечей, например зеленый цвет для растущих баров и красный для падающих, а затем строит полноценный свечной график с помощью mplfinance, включая ранее подготовленные маркеры. Для удобства чтения добавляется легенда, поясняющая и направление свечей, и маркеры паттернов. Фигура подгоняется по размеру для оптимальной четкости, сохраняется в уникальный PNG-файл в директории сервера, а затем закрывается для освобождения памяти. JSON-ответ возвращает развернутые списки паттернов и сигналов, так что индекс 0 снова соответствует самому новому бару, а также необязательную сводку лога и имя файла сохраненного графика, готового для немедленного использования советником или интеграции в другие аналитические инструменты.
mc = make_marketcolors(up='green', down='red', edge='inherit', wick='inherit') style = make_mpf_style(marketcolors=mc, base_mpf_style='default') fig, axes = mpf.plot(df, type="candle", style=style, title=f"{symbol} Patterns", addplot=adds, volume=False, returnfig=True, tight_layout=True) axes[0].legend(handles=[ Line2D([0],[0],color='green',lw=4,label='Buy Candle'), Line2D([0],[0],color='red',lw=4,label='Sell Candle'), Line2D([0],[0],marker='^',linestyle='None',color='green',markersize=12,label='Buy Signal'), Line2D([0],[0],marker='v',linestyle='None',color='red',markersize=12,label='Sell Signal') ], loc='upper left', frameon=True) fname = f"pattern_chart_{uuid.uuid4().hex[:8]}.png" fig.savefig(path := os.path.join(os.path.dirname(__file__), fname), dpi=100) plt.close(fig) app.logger.info(f"Chart saved: {fname}") return jsonify( patterns=[p or "None" for p in detected[::-1]], signals =[s or "none" for s in signals [::-1]], log=[], chart=fname )
Тестирование и результаты
Ниже представлены результаты тестирования.
Тестирование на Step Index
Логи MQL5
Вкладка "Эксперты" в MQL5 показывает логи: данные, отправленные в Python, полученные паттерны и название каждого паттерна, обнаруженного на текущем баре. Эта информация обновляется при закрытии каждого нового бара и сопровождается оповещением.
2025.07.15 22:56:50.294 Candlestick Label 2 (Step Index,M15) [CSLAB] JSON-OUT: {"symbol":"Step Index","timeframe":0,"time":[1752558300,1752559200, 1752560100,1752561000,1752561900,1752562800,1752563700,1752564600,1752565500,1752566400,1752567300,1752568200, 1752569100,1752570000,1752570900,1752571800,1752572700,1752573600,1752574500,1752575400,1752576300,1752577200,1752578100,1752579000, 1752579900,1752580800,1752581700,1752582600,1752583500,1752584400,1752585300,1752586200,1752587100,1752588000,1752588900,1752589800, 1752590700,1752591600,1752592500,1752593400,17525943 2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15) [CSLAB] HTTP 200 RESP: {"chart":"pattern_chart_e06f7a61.png","log":[],"patterns": ["CDLENGULFING","CDLHARAMI","None","CDLCLOSINGMARUBOZU","CDLHARAMI","CDLHIKKAKE","CDLENGULFING","CDLHIGHWAVE","None","CDLLONGLINE", "CDLHIGHWAVE","CDLSHORTLINE","CDLSHORTLINE","CDLHIKKAKE","CDLHARAMI","CDLENGULFING","CDLDOJI","None","None","None","CDLCLOSINGMARUBOZU", "None","CDLBELTHOLD","None","CDLHARAMI","CDLBELTHOLD","CDLHIKKAKE","CDLENGULFING","CDLHIKKAKE","CDLHIKKAKE","None","CDLDOJI","CDLHAMMER", "CDLHARAMI","CDLENGULFING" 2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15) 2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15) Alert: Step Index PERIOD_CURRENT bullish pattern 'CDLENGULFING' at 2025.07.15 05:45
Логи командной строки
- POST /patterns – клиент, то есть ваш советник MQL5, отправил HTTP POST-запрос на эндпоинт /patterns.
- HTTP/1.1 – в запросе использовался протокол HTTP 1.1.
- 200 – сервер вернул код состояния 200, то есть "OK" – ваш эндпоинт успешно обработал запрос.
2025-07-15 22:54:52,246 INFO Received /patterns request 2025-07-15 22:54:52,250 INFO Loaded 60 bars for Step Index 2025-07-15 22:54:52,808 INFO Chart saved: pattern_chart_a77f2eec.png 2025-07-15 22:54:52,810 INFO 127.0.0.1 - - [15/Jul/2025 22:54:52] "POST /patterns HTTP/1.1" 200 - 2025-07-15 22:55:00,040 INFO Received /patterns request
Ниже показаны графики для тех же обнаруженных паттернов, иллюстрирующие поведение Step Index во времени. Графики построены с помощью Matplotlib, TA-Lib и mplfinance; на них показаны и ценовые бары, и сигналы свечных паттернов, выявленные нашей системой:
Свечи
- Зеленые тела свечей показывают периоды, когда цена закрытия была выше цены открытия, то есть бычьи свечи.
- Красные тела свечей показывают периоды, когда цена закрытия была ниже цены открытия, то есть медвежьи свечи.
Сигналы
- Зеленые маркеры под барами обозначают сигналы на покупку, то есть бычьи паттерны.
- Красные маркеры над барами обозначают сигналы на продажу, то есть медвежьи паттерны.

Тестирование на Crash 1000 Index

Логи MQL5
2025.07.15 23:30:00.299 Candlestick Label 2 (Crash 1000 Index,M15) [CSLAB] JSON-OUT: {"symbol":"Crash 1000 Index","timeframe":0,"time":[1752561000,1752561900,
1752562800,1752563700,1752564600,1752565500,1752566400,1752567300,1752568200,1752569100,1752570000,1752570900,1752571800,1752572700,1752573600,1752574500,1752575400
,1752576300,1752577200,1752578100,1752579000,1752579900,1752580800,1752581700,1752582600,1752583500,1752584400,1752585300,1752586200,1752587100,1752588000,1752588900,
1752589800,1752590700,1752591600,1752592500,1752593400,1752594300,1752595200,1752596100,17
2025.07.15 23:30:02.749 Candlestick Label 2 (Crash 1000 Index,M15) [CSLAB] HTTP 200 RESP: {"chart":"pattern_chart_ebd72b47.png","log":[],"patterns":
["CDLCLOSINGMARUBOZU","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLHIKKAKE","CDLENGULFING","CDLBELTHOLD","CDLHARAMI","CDLENGULFING","None","CDLBELTHOLD",
"None","CDLBELTHOLD","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLBELTHOLD","CDLBELTHOLD","CDLDOJI","CDLBELTHOLD","None",
"CDLSHORTLINE","CDLSHORTLINE","None","CDLENGULFING","CDLHARAMI","CDLCLOSINGMARUBOZU","None","CDLMATCHINGL
2025.07.15 23:30:02.749 Candlestick Label 2 (Crash 1000 Index,M15)
2025.07.15 23:30:02.769 Candlestick Label 2 (Crash 1000 Index,M15) Alert: Crash 1000 Index PERIOD_CURRENT bearish pattern 'CDLCLOSINGMARUBOZU' at 2025.07.15 06:30
Логи командной строки
2025-07-15 23:30:02,719 INFO Chart saved: pattern_chart_ebd72b47.png 2025-07-15 23:30:02,735 INFO 127.0.0.1 - - [15/Jul/2025 23:30:02] "POST /patterns HTTP/1.1" 200 -
График свечных паттернов, построенный Python-сервисом
Этот график наглядно показывает, какие сигналы дает каждый обнаруженный паттерн и что происходит после этого.

Заключение
В данной архитектуре MetaTrader 5 отвечает за свои сильные стороны – получение рыночных данных в реальном времени, управление графическими объектами и встроенные оповещения, – а легкий Flask-сервис с библиотеками TA-Lib и mplfinance берет на себя все вычисления по свечным паттернам и построение аккуратных визуализаций. Передавая 60 баров OHLC и временные метки в формате JSON, советник получает анализ за доли секунды, не утяжеляя собственную часть системы, а Python-бэкенд возвращает и подробные метки паттернов, и готовые графики с аннотациями для просмотра или дальнейшей передачи.
Такое четкое разделение ролей делает систему по-настоящему модульной: можно независимо дорабатывать правила распознавания паттернов, настраивать допуски фильтрации сигналов и расширять логику оповещений советника без риска нежелательных побочных эффектов. Упаковка Flask-сервиса – например, в Docker-контейнер для предсказуемого развертывания или за API-шлюзом с аутентификацией – еще больше повышает переносимость и безопасность.
Советую применить этот подход к выбранным вами торговым инструментам, поэкспериментировать с пользовательскими наложениями индикаторов и встроить сгенерированные графики в более сложные аналитические платформы. Благодаря такому гибридному подходу вы задействуете сильные стороны как MQL5, так и современных библиотек Python, превращая ручной анализ графиков в точный автоматизированный процесс.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18824
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Как создать и оптимизировать торговую систему на основе циклов (Detrended Price Oscillator — DPO)
Использование регрессии Ренко-баров с корректировкой ошибок
Разработка инструментария для анализа Price Action (Часть 33): Инструмент на основе теории свечного диапазона
Создание самооптимизирующихся советников на MQL5 (Часть 16): Идентификация линейных систем на основе обучения с учителем
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования