Разработка инструментария для анализа Price Action (Часть 36): Прямой доступ Python к потокам рыночных данных MetaTrader 5
Содержание
- Введение
- Обзор архитектуры системы
- Подробный разбор бэкенда на Python
- Архитектура клиентского советника на MQL5
- Оценка и показатели производительности
- Заключение
Введение
В нашей предыдущей статье мы показали, как простой скрипт на MQL5 может передавать исторические бары в Python, создавать признаки, обучать модель машинного обучения, а затем отправлять сигналы обратно в MetaTrader для исполнения, избавляя от ручного экспорта CSV, анализа в Excel и проблем с контролем версий. Трейдеры получили сквозной пайплайн, который преобразовывал исходные данные минутных баров в статистически обоснованные точки входа с динамически рассчитанными уровнями стоп-лосса (SL) и тейк-профита (TP).
Эта система решала три основные проблемы алгоритмической торговли:
- Фрагментация данных. Больше не нужно копировать и вставлять CSV-файлы или разбираться со сложными табличными формулами – график MetaTrader 5 напрямую взаимодействует с Python.
- Запаздывающая аналитика. Автоматизация создания признаков и инференса модели позволила перейти от реактивной торговли к проактивной, основанной на актуальных данных, за счет получения сигналов в реальном времени.
- Непоследовательное управление рисками. Добавление SL/TP на основе ATR и в тестирование на исторических данных, и в торговлю в реальном времени гарантировало, что все сделки подчиняются правилам, скорректированным по волатильности, сохраняя ваше преимущество.

Однако передача данных в Python через советник может добавлять задержки и усложнять систему. В новой версии Python сам выступает клиентом MetaTrader 5, используя библиотеку MetaTrader 5 для прямого получения и обновления данных. Такой подход устраняет ожидание таймера советника: Python может получать данные по запросу, эффективно записывать их в хранилище Parquet и асинхронно выполнять ресурсоемкие вычисления.
Опираясь на эту основу, наш улучшенный инструмент – гибрид Python и MQL5 – предлагает еще более широкие возможности:
- На стороне Python – получение данных MetaTrader 5 в реальном времени через нативную библиотеку, расширенное создание признаков (например, z-оценки всплесков, разности MACD, полосы ATR, дельты тренда Prophet) и пайплайн градиентного бустинга, учитывающий временную структуру данных и переобучающийся на скользящих окнах, – все это доступно через легковесный Flask API.
- На стороне MQL5 – надежный советник с REST-опросом и логикой повторных попыток, панелью на графике с сигналами, уровнями уверенности и статусом подключения, стрелками входа и выхода, а также опциональным автоматическим исполнением ордеров по строгим правилам управления рисками.
Если первая статья была лишь проверкой концепции, то данный фреймворк, готовый к использованию в production-среде, заметно сокращает время настройки, ускоряет цикл обратной связи и позволяет торговать увереннее и точнее, опираясь на данные. Приступим.
Обзор архитектуры системы
Ниже представлена общая схема того, как данные и сигналы проходят между MetaTrader 5 и вашим сервисом на Python, а затем перечислены ключевые задачи каждого компонента:

Терминал MetaTrader 5
Терминал MetaTrader 5 выступает основным интерфейсом для работы с рынком и платформой для построения графиков. Он хранит текущие и исторические ценовые бары выбранного символа и предоставляет среду исполнения для советника. Через встроенную функцию WebRequest() советник периодически обменивается данными с Python-сервисом, а последние бары получает локально и отображает на графике входящие сигналы, линии SL/TP и стрелки входа/выхода. Терминал MetaTrader 5 отвечает за размещение ордеров (если оно включено), управление локальными объектами (панелями, стрелками, метками) и визуализацию результатов работы системы на стороне пользователя.
Поток данных на стороне Python
Вместо того чтобы полагаться на советник для отправки данных по барам, компонент Python Data Feed использует официальную библиотеку MetaTrader 5 Python, чтобы по запросу получать и исторические, и текущие данные OHLC по минутным барам. Сначала компонент создает сжатое хранилище Parquet для сохранения данных за прошедшие дни, а затем дописывает в него новые бары по мере их поступления. Такая схема устраняет зависимость от интервалов таймера в MQL5 и гарантирует, что сервис Python всегда имеет немедленный доступ ко всей ценовой истории в произвольном порядке, необходимой и для тестирования на исторических данных, и для инференса в реальном времени.
Создание признаков
Как только исходные бары становятся доступны в памяти или на диске, слой создания признаков преобразует их в статистически значимые входные данные для машинного обучения. Он вычисляет нормализованные z-оценки всплесков, разницу гистограммы MACD, значения RSI за 14 периодов, ATR за 14 периодов для оценки волатильности и динамические полосы индикатора Envelopes на основе EMA. Кроме того, он использует библиотеку Prophet для оценки дельты тренда на минутном уровне, что позволяет уловить склонность рынка к возврату к среднему или к продолжению тренда. Этот автоматизированный пайплайн гарантирует, что и текущие, и исторические данные проходят одинаковую обработку, сохраняя согласованность модели.
Модель машинного обучения
В основе системы лежит классификатор градиентного бустинга, обернутый в пайплайн scikit-learn со стандартизацией признаков. Модель обучается на скользящих окнах прошлых баров, используя TimeSeriesSplit, чтобы избежать смещения из-за заглядывания вперед (т.н. ошибки опережения), и RandomizedSearchCV для оптимизации гиперпараметров. Метки формируются по движению цены на десять минут вперед: затем движения классифицируются как BUY, SELL или WAIT на основе настраиваемого порога. Обученная модель сериализуется в model.pkl, что обеспечивает низкую задержку при загрузке и инференсе как в тестировании на исторических данных, так и при работе в реальном времени.
Flask API
Flask API служит связующим звеном между Python-экосистемой анализа данных и советником MQL5. Он предоставляет единый эндпоинт /analyze, который принимает JSON-пакет с последними ценами закрытия и временными метками, применяет пайплайн признаков и загруженную модель для вычисления вероятностей классов и возвращает краткий JSON-ответ с signal, sl, tp и conf (величиной уверенности). Этот легковесный REST-интерфейс можно упаковать в контейнер или развернуть на любом сервере, отделив вычислительные ресурсы Python от среды выполнения MetaTrader и упростив масштабирование.
Советник MQL5
На стороне клиента советник MQL5 отвечает исключительно за взаимодействие с пользователем и исполнение сделок. Он периодически опрашивает Flask API, разбирает входящие JSON, записывает все сигналы и во вкладку "Эксперты", и в локальный CSV-файл, а также обновляет панель на графике, где показаны текущий сигнал, уровень уверенности, статус подключения и временная метка. Когда поступает корректный сигнал на покупку, продажу или закрытие, советник рисует стрелки и линии SL/TP и, если EnableTrading имеет значение true, размещает или закрывает ордера через класс CTrade. Благодаря переносу всей аналитической части в Python советник остается легковесным, отзывчивым и простым в сопровождении.
Подробный разбор бэкенда на Python
В основе нашего бэкенда лежит надежный пайплайн загрузки данных, который использует официальный Python-пакет MetaTrader 5. При первом запуске сервис выполняет начальную загрузку: получает данные OHLC по минутным барам за последние дни и записывает их в сжатый файл Parquet. Столбцовый формат Parquet и сжатие Zstandard обеспечивают очень быстрое чтение срезов временных рядов и при этом минимальный расход дискового пространства. После этого простое инкрементальное обновление дописывает только новые бары, избегая лишних загрузок и гарантируя, что и инференс в реальном времени, и тестирование на исторических данных работают с актуальным единым источником данных.
import datetime as dt import pandas as pd import MetaTrader5 as mt5 from pathlib import Path PARQUET_FILE = "hist.parquet.zst" DAYS_TO_PULL = 60 UTC = dt.timezone.utc def bootstrap(): """Fetch last DAYS_TO_PULL days of M1 bars and write to Parquet.""" now = dt.datetime.utcnow() start = now - dt.timedelta(days=DAYS_TO_PULL) mt5.initialize() mt5.symbol_select("Boom 300 Index", True) bars = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1, start.replace(tzinfo=UTC), now.replace(tzinfo=UTC)) df = pd.DataFrame(bars) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time').to_parquet(PARQUET_FILE, compression='zstd') def append_new_bars(): """Append only the newest bars since last timestamp.""" df = pd.read_parquet(PARQUET_FILE) last = df.index[-1] now = dt.datetime.utcnow() new = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1, last.replace(tzinfo=UTC) + dt.timedelta(minutes=1), now.replace(tzinfo=UTC)) if new: new_df = pd.DataFrame(new) new_df['time'] = pd.to_datetime(new_df['time'], unit='s') merged = pd.concat([df, new_df.set_index('time')]) merged[~merged.index.duplicated()].to_parquet(PARQUET_FILE, compression='zstd')
Когда исходные бары уже загружены, наш пайплайн вычисляет набор признаков, предназначенных для оценки моментума, волатильности и экстремальных движений. Мы превращаем первую разность цены в показатель "z-spike", деля ее на 20-барное скользящее стандартное отклонение, чтобы выделить резкие ценовые всплески. Разница гистограммы MACD и индикатор RSI с 14 периодом позволяют оценить соответственно тренд и состояние перекупленности/перепроданности, а ATR с 14 периодом измеряет текущую волатильность. EMA с периодом 20 задает полосы индикатора Envelopes (EMA×0.997 и EMA×1.003), которые адаптируются к меняющимся режимам рынка. Наконец, библиотека Prophet обрабатывает весь ряд цен закрытия, чтобы прогнозировать дельту тренда на минутном уровне, позволяя учитывать более тонкие, зависящие от времени сезонность и дрейф.
import numpy as np import pandas as pd import ta from prophet import Prophet def engineer(df: pd.DataFrame) -> pd.DataFrame: df = df.copy() # z-spike (20-bar rolling std) df['r'] = df['close'].diff() df['z_spike'] = df['r'] / (df['r'].rolling(20).std() + 1e-9) # MACD histogram diff, RSI, ATR df['macd'] = ta.trend.macd_diff(df['close']) df['rsi'] = ta.momentum.rsi(df['close'], window=14) df['atr'] = ta.volatility.average_true_range(df['high'], df['low'], df['close'], window=14) # EMA envelopes ema = df['close'].ewm(span=20).mean() df['env_low'] = ema * 0.997 df['env_up'] = ema * 1.003 # Prophet trend delta (minute-level) if len(df) > 200: m = Prophet(daily_seasonality=False, weekly_seasonality=False) m.fit(pd.DataFrame({'ds': df.index, 'y': df['close']})) df['delta'] = m.predict(m.make_future_dataframe(periods=0, freq='min'))['yhat'] - df['close'] else: df['delta'] = 0.0 return df.dropna()
Мы формулируем задачу прогнозирования как классификацию на три класса: "BUY", если цена вырастет больше чем на заданный порог в течение следующих десяти минут, "SELL", если она упадет больше чем на тот же порог, и "WAIT" во всех остальных случаях. После того как метки назначены, признаки и сами метки разбиваются на скользящие временные окна для обучения. Пайплайн scikit-learn сначала стандартизирует каждый признак, а затем обучает классификатор GradientBoostingClassifier. Гиперпараметры (скорость обучения, число деревьев, максимальная глубина) оптимизируются с помощью RandomizedSearchCV в рамках схемы перекрестной валидации TimeSeriesSplit, что исключает ошибку опережения. Лучшая модель сериализуется в model.pkl и сразу готова к инференсу с низкой задержкой.
import numpy as np import joblib from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import GradientBoostingClassifier from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV LOOKAHEAD_MIN = 10 LABEL_THRESHOLD = 0.0015 FEATS = ['z_spike','macd','rsi','atr','env_low','env_up','delta'] def label_and_train(df: pd.DataFrame): # Look-ahead return chg = (df['close'].shift(-LOOKAHEAD_MIN) - df['close']) / df['close'] df['label'] = np.where(chg > LABEL_THRESHOLD, 1, np.where(chg < -LABEL_THRESHOLD, 2, 0)) X = df[FEATS].dropna() y = df.loc[X.index, 'label'] pipe = Pipeline([ ('scaler', StandardScaler()), ('gb', GradientBoostingClassifier(random_state=42)) ]) param_dist = { 'gb__learning_rate': [0.01, 0.05, 0.1], 'gb__n_estimators': [300, 500, 700], 'gb__max_depth': [2, 3, 4] } cv = TimeSeriesSplit(n_splits=5) rs = RandomizedSearchCV(pipe, param_dist, n_iter=12, cv=cv, scoring='roc_auc_ovr', n_jobs=-1, random_state=42) rs.fit(X, y) joblib.dump(rs.best_estimator_, 'model.pkl')Чтобы связать Python и MetaTrader, мы публикуем единственный Flask-эндпоинт – /analyze. Клиенты отправляют JSON-пакет, содержащий символ, массив цен закрытия и соответствующие временные метки UNIX. Эндпоинт заново прогоняет этот пакет через наш пайплайн признаков, загружает предварительно обученную модель, вычисляет вероятности классов, определяет сигнал с максимальным уровнем уверенности и динамически рассчитывает уровни стоп-лосса и тейк-профита по признаку ATR. Ответ представляет собой компактный JSON-объект:
from flask import Flask, request, jsonify import joblib import pandas as pd app = Flask(__name__) model = joblib.load('model.pkl') @app.route('/analyze', methods=['POST']) def analyze(): payload = request.get_json(force=True) closes = payload['prices'] times = pd.to_datetime(payload['timestamps'], unit='s') df = pd.DataFrame({'close': closes}, index=times) # duplicate open/high/low for completeness df[['open','high','low']] = df[['close']] feat = engineer(df).iloc[-1:] probs = model.predict_proba(feat[FEATS])[0] p_buy, p_sell = probs[1], probs[2] signal = ('BUY' if p_buy > 0.55 else 'SELL' if p_sell > 0.55 else 'WAIT') atr = feat['atr'] entry = feat['close'] sl = entry - atr if signal=='BUY' else entry + atr tp = entry + 2*atr if signal=='BUY' else entry - 2*atr return jsonify(signal=signal, sl=round(sl,5), tp=round(tp,5), conf=round(max(p_buy,p_sell),2)) if __name__ == '__main__': app.run(port=5000)
Архитектура клиентского советника MQL5
Основной цикл советника работает либо по событию таймера (OnTimer), либо при проверке нового бара; в нем вызывается WebRequest для отправки и получения HTTP-сообщений. Сначала он получает последние N баров через CopyRates, преобразует массив MqlRates в UTF-8 JSON-пакет с символом и последовательностью цен закрытия и задает необходимые HTTP-заголовки. Если WebRequest() завершается неудачей (возвращает ≤0), советник получает GetLastError(), увеличивает счетчик повторных попыток, пишет ошибку в лог и откладывает дальнейшие запросы до исчерпания попыток или до следующего срабатывания таймера. Успешные ответы (код состояния ≥200) сбрасывают счетчик повторных попыток и обновляют lastStatus. Такой подход обеспечивает надежную асинхронную обработку сигналов без блокировки потока графика и без сбоев из-за кратковременных сетевых проблем.
// In OnTimer() or OnNewBar(): MqlRates rates[]; // Copy the last N bars into `rates` if(CopyRates(_Symbol, _Period, 0, InpBufferBars, rates) != InpBufferBars) return; ArraySetAsSeries(rates, true); // Build payload string payload = "{"; payload += StringFormat("\"symbol\":\"%s\",", _Symbol); payload += "\"prices\":["; for(int i=0; i<InpBufferBars; i++) { payload += DoubleToString(rates[i].close, _digits); if(i < InpBufferBars-1) payload += ","; } payload += "]}"; // Send request string headers = "Content-Type: application/json\r\nAccept: application/json\r\n\r\n"; char req[], resp[]; int len = StringToCharArray(payload, req, 0, WHOLE_ARRAY, CP_UTF8); ArrayResize(req, len); ArrayResize(resp, 8192); int status = WebRequest("POST", InpServerURL, headers, "", InpTimeoutMs, req, len, resp, headers); if(status <= 0) { int err = GetLastError(); PrintFormat("WebRequest error %d (attempt %d/%d)", err, retryCount+1, MaxRetry); ResetLastError(); retryCount = (retryCount+1) % MaxRetry; lastStatus = StringFormat("Err%d", err); return; } retryCount = 0; lastStatus = StringFormat("HTTP %d", status);
Как только корректный JSON-ответ будет разобран и из него извлечены signal, sl и tp, советник обновляет панель на графике и рисует новые стрелки и линии. Панель представляет собой один объект OBJ_RECTANGLE_LABEL с четырьмя текстовыми метками, которые показывают символ, текущий сигнал, код состояния HTTP и временную метку. Для сделок советник сначала удаляет все существующие объекты с нужным префиксом, а затем создает новую стрелку (OBJ_ARROW) на уровне текущей цены, используя разные коды и цвета для покупки (зеленая вверх), продажи (красная вниз) и закрытия (оранжевая). Горизонтальные линии (OBJ_HLINE) отмечают уровни стоп-лосса и тейк-профита, окрашенные соответственно в красный и зеленый цвет. Благодаря уникальному для графика префиксу у каждого объекта, а также их удалению при смене сигнала или деинициализации график остается чистым и не перегруженным.
// Panel (rectangle + labels) void DrawPanel() { const string pid = "SigPanel"; if(ObjectFind(0, pid) < 0) ObjectCreate(0, pid, OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, pid, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, pid, OBJPROP_XDISTANCE, PanelX); ObjectSetInteger(0, pid, OBJPROP_YDISTANCE, PanelY); ObjectSetInteger(0, pid, OBJPROP_XSIZE, PanelW); ObjectSetInteger(0, pid, OBJPROP_YSIZE, PanelH); ObjectSetInteger(0, pid, OBJPROP_BACK, true); ObjectSetInteger(0, pid, OBJPROP_BGCOLOR, PanelBG); ObjectSetInteger(0, pid, OBJPROP_COLOR, PanelBorder); string lines[4] = { StringFormat("Symbol : %s", _Symbol), StringFormat("Signal : %s", lastSignal), StringFormat("Status : %s", lastStatus), StringFormat("Time : %s", TimeToString(TimeLocal(), TIME_MINUTES)) }; for(int i=0; i<4; i++) { string lbl = pid + "_L" + IntegerToString(i); if(ObjectFind(0, lbl) < 0) ObjectCreate(0, lbl, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, lbl, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, lbl, OBJPROP_XDISTANCE, PanelX + 6); ObjectSetInteger(0, lbl, OBJPROP_YDISTANCE, PanelY + 4 + i*(TxtSize+2)); ObjectSetString(0, lbl, OBJPROP_TEXT, lines[i]); ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, TxtSize); ObjectSetInteger(0, lbl, OBJPROP_COLOR, TxtColor); } } // Arrows & SL/TP lines void ActOnSignal(ESignal code, double sl, double tp) { // remove old arrows/lines for(int i=ObjectsTotal(0)-1; i>=0; i--) if(StringFind(ObjectName(0,i), objPrefix) == 0) ObjectDelete(0, ObjectName(0,i)); // arrow int arrCode = (code==SIG_BUY ? 233 : code==SIG_SELL ? 234 : 158); color clr = (code==SIG_BUY ? clrLime : code==SIG_SELL ? clrRed : clrOrange); string name = objPrefix + "Arr_" + TimeToString(TimeCurrent(), TIME_SECONDS); ObjectCreate(0, name, OBJ_ARROW, 0, TimeCurrent(), SymbolInfoDouble(_Symbol, SYMBOL_BID)); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrCode); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); // SL line if(sl > 0) { string sln = objPrefix + "SL_" + name; ObjectCreate(0, sln, OBJ_HLINE, 0, 0, sl); ObjectSetInteger(0, sln, OBJPROP_COLOR, clrRed); } // TP line if(tp > 0) { string tpn = objPrefix + "TP_" + name; ObjectCreate(0, tpn, OBJ_HLINE, 0, 0, tp); ObjectSetInteger(0, tpn, OBJPROP_COLOR, clrLime); } }
Фактическое размещение ордеров контролируется флагом EnableTrading, поэтому можно без труда переключаться между режимом только визуализации и режимом реального исполнения. Перед отправкой любого рыночного ордера советник проверяет PositionSelect(_Symbol), чтобы не открывать дублирующую позицию. Для сигнала BUY вызывается CTrade.Buy() с аргументами FixedLots, SL и TP; для SELL – CTrade.Sell(); а для сигнала CLOSE – CTrade.PositionClose(). Допустимое проскальзывание (SlippagePoints) учитывается при выходе из позиции. Эта минимальная логика с хранением состояния гарантирует, что вы не откроете одно и то же направление дважды и что все ордера будут соответствовать заранее заданным параметрам риска.
void ExecuteTrade(ESignal code, double sl, double tp) { if(!EnableTrading) return; bool hasPosition = PositionSelect(_Symbol); if(code == SIG_BUY && !hasPosition) trade.Buy(FixedLots, _Symbol, 0, sl, tp); else if(code == SIG_SELL && !hasPosition) trade.Sell(FixedLots, _Symbol, 0, sl, tp); else if(code == SIG_CLOSE && hasPosition) trade.PositionClose(_Symbol, SlippagePoints); }
Установка и настройка
Прежде чем начать получать сигналы в реальном времени, нужно подготовить и среду Python, и платформу MetaTrader 5. Начните с установки Python 3.8 или новее на тот же компьютер, где работает терминал MetaTrader 5. Создайте и активируйте виртуальное окружение (выполните команду python -m venv venv, а затем выполните venv\Scripts\activate на Windows или source venv/bin/activate на macOS/Linux), а затем установите все зависимости командой:В MetaTrader 5 откройте
Например, http://127.0.0.1:5000 в поле "Разрешить WebRequest для следующих URL". Этот шаг с белым списком критически важен – без него MetaTrader 5 будет молча отбрасывать все POST-запросы.
В папке проекта скопируйте скрипт сервиса Python (например, market_ai_engine.py) в выбранную рабочую директорию. В начале скрипта задайте торговый символ скрипта (MAIN_SYMBOL), учетные данные MetaTrader 5 (LOGIN_ID, PASSWORD, SERVER) и пути к файлам (PARQUET_FILE, MODEL_FILE). Если нужен нестандартный порт для Flask-сервера, его можно передать через параметр --port при запуске сервиса.
Чтобы установить советник, поместите скомпилированный файл EA.ex5 (или исходный файл .mq5) в каталог MQL5 → Experts вашего MetaTrader 5.
Перезапустите MetaTrader 5 или обновите "Навигатор", чтобы советник появился в списке. Перетащите его на график M1 того же символа, который задан в Python. Во входных параметрах советника укажите для InpServerURL значение http://127.0.0.1:5000/analyze, а затем задайте InpBufferBars (например, 60), InpPollInterval (например, 60 секунд) и InpTimeoutMs (например, 5000 мс). Сначала оставьте EnableTrading выключенным, чтобы проверять сигналы без исполнения реальных ордеров.
Наконец, запустите Python-бэкенд в следующей последовательности:
1. python market_ai_engine.py bootstrap
2. python market_ai_engine.py collect
3. python market_ai_engine.py train
4. python market_ai_engine.py serve --port 5000
Когда сервер Flask запущен, а в MetaTrader включен AutoTrading, советник начинает запрашивать сигналы в реальном времени, рисовать на графике стрелки и линии SL/TP и, когда вы будете готовы, открывать сделки по заранее заданным правилам риска.
Устранение неполадок
Если советник не показывает данные или всегда возвращает "WAIT", убедитесь, что URL вашего API добавлен в белый список в настройках советников MetaTrader 5. В смешанных средах HTTP/HTTPS используйте для локального тестирования обычный HTTP (127.0.0.1), а для production-среды переключайтесь на HTTPS с доверенным сертификатом. Убедитесь, что часы сервера и терминала MetaTrader 5 синхронизированы (либо оба в UTC, либо в одинаковом часовом поясе), чтобы запросы баров не смещались. Наконец, убедитесь, что AutoTrading включен и что никакие глобальные разрешения или другие советники не блокируют работу вашего советника.
Оценка и показатели производительности
Чтобы начать работу с гибридной системой машинного обучения Python–MQL5, сначала нужно выполнить начальную загрузку исторических данных командой:
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py bootstrap 2025-08-04 23:39:23 | INFO | Bootstrapped historical data: 86394 rows
После начальной загрузки непрерывное обновление можно поддерживать командой:
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py collect 2025-08-04 23:41:01 | INFO | Appended 2 new bars 2025-08-04 23:42:01 | INFO | Appended 1 new bars 2025-08-04 23:43:01 | INFO | Appended 1 new bars
Когда набор данных уже актуален, следующим шагом становится обучение модели.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py train 23:48:44 - cmdstanpy - INFO - Chain [1] start processing 23:51:24 - cmdstanpy - INFO - Chain [1] done processing 2025-08-05 02:59:08 | INFO | Model training complete
Чтобы оценить, как модель может показать себя в реальной торговле, можно провести тестирование на исторических данных командой:
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py backtest --days 30 06:57:33 - cmdstanpy - INFO - Chain [1] start processing 06:58:30 - cmdstanpy - INFO - Chain [1] done processing 2025-08-05 06:59:20 | INFO | Backtest results saved to backtest_results_30d.csv
Результаты тестирования на исторических данных
Ниже приведены отдельные метрики и сведения о сделках, полученные по итогам 30-дневного тестирования на исторических данных:

График совокупного капитала во времени

Вот сводка ключевых метрик:
* **Average Entry Price:** 3099.85 * **Average Exit Price:** 3096.53 * **Average PNL (Profit and Loss):** 3.32 * **Total PNL:** 195.69 * **Average Cumulative Equity:** 96.34 * **First Trade Time:** 2025-07-11 14:18:00 * **Last Trade Time:** 2025-07-27 02:00:00
Процент выигрышных сделок:
win_rate 72.88135528564453 The win rate is 72.88%. This means that approximately 73% of the trades resulted in a profit.
После того как модель обучена и проверена, можно запустить сервер инференса в реальном времени командой:
2025-08-05 12:41:53 | INFO | analyze: signal=%s, sl=%.5f, tp=%.5f 127.0.0.1 - - [05/Aug/2025 12:41:53] "POST /analyze HTTP/1.1" 200 -
В MetaTrader советник нужно правильно настроить для опроса Python-сервера. Во входных параметрах советника нужно задать URL сервера (например, http://127.0.0.1:5000/analyze), а сам советник должен быть прикреплен к тому же символу и тому же таймфрейму, на которых обучалась модель, обычно к M1. После запуска советник будет периодически получать сигналы, отображать их на графике в виде стрелок и при необходимости исполнять сделки по строгим правилам риска.
2025.08.05 12:41:53.532 trained model (1) (Boom 300 Index,M1) >>> JSON: {"symbol":"Boom 300 Index","prices":[2701.855,2703.124, 2704.408,2705.493,2705.963,2696.806,2698.278,2699.877,2701.464,2702.788,2691.762,2693.046,2694.263,2695.587,2696.863,2698. 179,2699.775,2701.328,2702.888,2698.471,2699.887,2695.534,2696.952,2698.426,2699.756,2699.552,2700.954,2702.131,2703.571, 2699.549,2700.868,2702.567,2703.798,2705.067,2706.874,2698.084,2699.538,2700.856,2702.227,2703.692,2705.102,2706.188,2707.609,2709.001, 2710.335,2711.716,2712.919,2712.028,2713.529,2715.052,2716.578,2717. 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) <<< HTTP 200 hdr: 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) {"conf":0.43,"signal":"WAIT","sl":2725.04317,"tp":2720.18266} 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) [2025.08.05 12:41:53] Signal → WAIT | SL=2725.04317 | TP=2720.18266 | Conf=0.43
Заключение
В этой статье мы создали советник MQL5, который опирается на сильные стороны Python в анализе данных. Вот что у вас теперь есть:- поток данных – Python-компонент получает минутные бары из MetaTrader 5 и записывает их в Parquet, а советник запрашивает сигналы и отображает их на графике.
- создание признаков – система вычисляет spike-z, MACD, RSI, ATR, полосы индикатора Envelopes и даже дельты тренда на основе Prophet;
- моделирование и выдача сигналов: вы обучаете модель градиентного бустинга с учетом временной структуры данных и публикуете прогнозы через Flask.
- Работа на графике: Советник MQL5 использует эти сигналы, чтобы рисовать стрелки и линии SL/TP, а также может автоматически открывать и закрывать сделки.
Вы можете попробовать другой алгоритм (XGBoost, LSTM и т.д.), точнее настроить правила управления рисками или упаковать сервис Python в Docker для более аккуратного развертывания. С такой основой вы можете улучшать результаты тестирования на исторических данных и развивать автоматизированные стратегии дальше.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19065
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Статистический арбитраж на основе коинтегрированных акций (Часть 9): Бэктестирование обновлений весов портфеля
Статистический арбитраж на основе коинтегрированных акций (Часть 8): Сравнение собственных векторов на скользящих окнах для ребалансировки портфеля
Интеграция MQL5 с пакетами обработки данных (Часть 5): Адаптивное обучение и гибкость
Архитектура машинного обучения в MetaTrader 5 (Часть 6): Проектирование системы кэширования промышленного уровня
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования