English Deutsch 日本語
preview
Разработка инструментария для анализа Price Action (Часть 36): Прямой доступ Python к потокам рыночных данных MetaTrader 5

Разработка инструментария для анализа Price Action (Часть 36): Прямой доступ Python к потокам рыночных данных MetaTrader 5

MetaTrader 5Торговые системы |
380 0
Christian Benjamin
Christian Benjamin

Содержание


Введение

В нашей предыдущей статье мы показали, как простой скрипт на MQL5 может передавать исторические бары в Python, создавать признаки, обучать модель машинного обучения, а затем отправлять сигналы обратно в MetaTrader для исполнения, избавляя от ручного экспорта CSV, анализа в Excel и проблем с контролем версий. Трейдеры получили сквозной пайплайн, который преобразовывал исходные данные минутных баров в статистически обоснованные точки входа с динамически рассчитанными уровнями стоп-лосса (SL) и тейк-профита (TP).

Эта система решала три основные проблемы алгоритмической торговли:

  1. Фрагментация данных. Больше не нужно копировать и вставлять CSV-файлы или разбираться со сложными табличными формулами – график MetaTrader 5 напрямую взаимодействует с Python.
  2. Запаздывающая аналитика. Автоматизация создания признаков и инференса модели позволила перейти от реактивной торговли к проактивной, основанной на актуальных данных, за счет получения сигналов в реальном времени.
  3. Непоследовательное управление рисками. Добавление 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), а затем установите все зависимости командой:
pip install MetaTrader 5 pandas numpy ta prophet scikit-learn flask loguru joblib
Затем настройте брандмауэр Windows так, чтобы он разрешал исходящие HTTP-запросы для python.exe и terminal64.exe. Если вы планируете развертывание по HTTPS в production-среде, установите SSL-сертификат в хранилище доверенных корневых сертификатов, чтобы MetaTrader 5 принимал защищенные соединения.

В MetaTrader 5 откройте

Сервис → Настройки → Советники, включите импорт DLL и добавьте ваш локальный хост API.

Например, 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, сначала нужно выполнить начальную загрузку исторических данных командой:

python market_ai_engine.py bootstrap
Эта команда инициализирует локальный набор данных, загружая последние 60 дней баров M1 (1-минутных) напрямую из MetaTrader 5 с помощью нативной библиотеки MetaTrader 5 для Python. Данные хранятся в сжатом файле Parquet (hist.parquet.zst), который обеспечивает быстрый доступ с диска и экономичное хранение. Этот шаг нужно выполнить только один раз – если только вы не захотите полностью сбросить исторические данные.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py bootstrap
2025-08-04 23:39:23 | INFO | Bootstrapped historical data: 86394 rows

После начальной загрузки непрерывное обновление можно поддерживать командой:

python market_ai_engine.py collect
Так набор данных остается актуальным в реальном времени. Хотя этот сборщик может работать в фоновом режиме, он нужен только в том случае, если вы хотите поддерживать поток данных непрерывно обновляемым без повторной начальной загрузки перед каждым обучением.
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

Когда набор данных уже актуален, следующим шагом становится обучение модели. 

python market_ai_engine.py train
Эта команда запускает полный пайплайн машинного обучения. Скрипт читает файл Parquet, применяет методы создания признаков (такие как обнаружение всплесков, разности MACD, RSI, полосы ATR, полосы индикатора Envelopes и тренды на основе Prophet), а затем использует GradientBoostingClassifier в связке с RandomizedSearchCV для обучения прогностической модели на скользящем окне недавних данных. В результате получается сериализованная модель, сохраненная как model.pkl, готовая к инференсу.
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

Чтобы оценить, как модель может показать себя в реальной торговле, можно провести тестирование на исторических данных командой:

python market_ai_engine.py backtest --days 30
Это моделирует генерацию сигналов и результаты сделок за последние 30 дней, рассчитывая такие метрики, как точность сигналов, соотношение прибыльных и убыточных сделок, а также общую прибыльность. Результаты сохраняются в CSV-файл для удобного анализа и помогают понять, соответствует ли стратегия системы вашим ожиданиям.
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.

После того как модель обучена и проверена, можно запустить сервер инференса в реальном времени командой:

python market_ai_engine.py serve --port 5000
Эта команда запускает легковесный Flask API, который принимает входящие запросы от советника MQL5. Когда советник обращается к эндпоинту /analyze, сервер мгновенно получает последний бар, рассчитывает по нему признаки, запускает модель и возвращает прогноз с соответствующими значениями SL, TP и значением уверенности в формате JSON. Этот сервер служит мостом в реальном времени между вашим графиком и ИИ-движком.
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, а также может автоматически открывать и закрывать сделки.
По ходу работы вы разобрались и с практическими нюансами – от парсинга JSON с дополнительными данными во Flask до ограничения частоты HTTP-запросов в советнике – и увидели, как разделение ответственности помогает сохранять систему сопровождаемой и расширяемой.
Вы можете попробовать другой алгоритм (XGBoost, LSTM и т.д.), точнее настроить правила управления рисками или упаковать сервис Python в Docker для более аккуратного развертывания. С такой основой вы можете улучшать результаты тестирования на исторических данных и развивать автоматизированные стратегии дальше.






Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Скрипт Quarters Drawer
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Currency Strength Meter 
PAQ Analysis Tool 
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep Opening Range Breakout Tool Boom and Crash Interceptor CCI Zer-Line EA
Candlestick Recognition Candlestick Detection using TA-Lib Candle Range Tool MetaTrader 5 Data Ingestor Model Training and Deployment Use of Python Lib

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19065

Прикрепленные файлы |
Статистический арбитраж на основе коинтегрированных акций (Часть 9): Бэктестирование обновлений весов портфеля Статистический арбитраж на основе коинтегрированных акций (Часть 9): Бэктестирование обновлений весов портфеля
В данной статье описывается использование файлов CSV для тестирования на исторических данных обновлений весов портфеля в стратегии, основанной на возврате к среднему значению и использующей статистический арбитраж на основе коинтегрированных акций. Это включает в себя как заполнение базы данных результатами сравнения собственных векторов в скользящих окнах (RWEC), так и сравнение отчетов по бэктесту. В то же время в статье подробно описывается роль каждого параметра RWEC и его влияние на общий результат бэктеста, а также показывается, как сравнение относительного просадки может помочь нам в дальнейшей оптимизации этих параметров.
Статистический арбитраж на основе коинтегрированных акций (Часть 8): Сравнение собственных векторов на скользящих окнах для ребалансировки портфеля Статистический арбитраж на основе коинтегрированных акций (Часть 8): Сравнение собственных векторов на скользящих окнах для ребалансировки портфеля
В этой статье предлагается использовать сравнение собственных векторов со скользящим окном для ранней диагностики дисбаланса и ребалансировки портфеля в рамках стратегии статистического арбитража, основанной на возврате к среднему (mean-reversion) и коинтегрированных акциях. Данный метод противопоставляется традиционной валидации с помощью ADF-теста на внутривыборочных и вневыборочных данных (IS/OOS), показывая, что сдвиги собственных векторов могут сигнализировать о необходимости ребалансировки даже в тех случаях, когда IS/OOS ADF всё ещё указывает на стационарность спреда. Хотя этот метод предназначен в первую очередь для мониторинга торговли в реальном времени, в статье делается вывод, что сравнение собственных векторов также может быть интегрировано в систему скоринга — хотя его реальный вклад в эффективность стратегии ещё предстоит проверить.
Интеграция MQL5 с пакетами обработки данных (Часть 5): Адаптивное обучение и гибкость Интеграция MQL5 с пакетами обработки данных (Часть 5): Адаптивное обучение и гибкость
В этой части основное внимание уделяется созданию гибкой, адаптивной торговой модели, обученной на исторических данных XAUUSD. Мы подготовим ее к экспорту в ONNX и потенциальной интеграции в системы реальной торговли.
Архитектура машинного обучения в MetaTrader 5 (Часть 6): Проектирование системы кэширования промышленного уровня Архитектура машинного обучения в MetaTrader 5 (Часть 6): Проектирование системы кэширования промышленного уровня
Устали смотреть на индикаторы выполнения вместо того, чтобы тестировать торговые стратегии? Традиционное кэширование не подходит для финансового машинного обучения, что приводит к потере результатов вычислений и вынуждает вас к повторному запуску, что вызывает раздражение. Мы разработали сложную архитектуру кэширования, учитывающую специфику финансовых данных — временные зависимости, сложные структуры данных и постоянную угрозу смещения look-ahead. Наша трёхслойная система обеспечивает значительное повышение скорости, автоматически отбрасывая устаревшие результаты и предотвращая утечку ценных данных. Хватит ждать результатов расчетов — начинайте действовать в темпе, которого требуют рынки.