English
preview
Анализ CSV-данных (Часть 3): Построение аналитического пайплайна на Python для CSV-экспорта из MetaTrader 5

Анализ CSV-данных (Часть 3): Построение аналитического пайплайна на Python для CSV-экспорта из MetaTrader 5

MetaTrader 5Статистика и анализ |
90 0
Ushana Kevin Iorkumbul
Ushana Kevin Iorkumbul

Введение

Тестер стратегий MetaTrader 5 предоставляет большой объём данных для оценки стратегий. Он формирует подробные журналы сделок, метрики динамики капитала и результаты перебора параметров. Однако встроенный уровень визуализации не обеспечивает детализации, необходимой для углублённого количественного анализа. Встроенный инструмент отчётности предлагает лишь статические кривые капитала, списки сделок и базовую сводную статистику. Кроме того, хотя результаты оптимизации включают двумерные и трёхмерные диаграммы рассеяния для пар параметров, каждое из этих представлений ограничено отображением только одной метрики.

В результате в платформе отсутствуют встроенные средства для:

  • Сравнения поведения стратегии на нескольких инструментах при едином наборе параметров.
  • Профилирования распределения сроков восстановления после просадок.
  • Построения временных карт результативности для различных торговых сессий.

Для разработчиков, управляющих мультиактивными портфелями, эти ограничения создают структурные слепые зоны в анализе независимо от того, торгуют ли они валютными парами, сырьевыми товарами или индексами. Терминал формирует необходимые данные о результатах, но удобно исследовать и анализировать их внутри платформы невозможно.

Чтобы устранить это ограничение, экспортируйте данные о сделках и оптимизации в CSV с помощью файловых операций MQL5. После экспорта их можно обрабатывать в экосистеме анализа данных Python. Библиотеки pandas, matplotlib и seaborn предоставляют необходимую основу и позволяют строить именно те аналитические графики, которых не хватает в MetaTrader 5.

В статье представлено техническое руководство по созданию пяти отдельных визуализаций для анализа портфелей и стратегий. Каждый график закрывает конкретный аналитический пробел, а каждый пример содержит практически применимую реализацию на Python. В заключительном разделе о развёртывании показано, как объединить отдельные функции визуализации в единый аналитический модуль, непосредственно обрабатывающий нормализованные CSV-файлы экспорта.

Примечание: статья основана на инфраструктуре файлового ввода-вывода, созданной в Части 2.



Раздел 1. Практическая ценность пользовательского извлечения данных из CSV

Стандартные показатели отчётов MetaTrader 5 охватывают основные характеристики: чистую прибыль, максимальную просадку, фактор прибыли, математическое ожидание выигрыша и коэффициент Шарпа. Они подходят для быстрого первичного отбора, но недостаточны для глубокого структурного исследования. За приемлемыми витринными показателями стратегии могут скрываться серьёзные недостатки. Например, убытки могут концентрироваться в определённую торговую сессию, на более волатильных инструментах сигналы могут систематически деградировать, а чрезмерная чувствительность к параметрам способна привести к полному обвалу результатов даже при небольших изменениях рыночной среды.

Пользовательское логирование в CSV решает эту проблему, фиксируя показатели, которые MetaTrader 5 самостоятельно не рассчитывает. Во время тестирования или работы на реальном счёте MQL5-скрипт может записывать в файл любую вычисляемую переменную с требуемой детализацией. Можно отслеживать флаги ложных разворотов на уровне отдельных сделок, задержку индикатора на каждом баре, скользящие окна нисходящей волатильности и оценки качества пересечений с дополнительной разметкой. В результате такие пользовательские поля превращают непрозрачный алгоритмический цикл в детализированный журнал показателей, индексированный по времени.

Базовый этап извлечения данных в текстовый CSV-формат достаточно прост. Последовательность FileOpen и FileWrite внутри советника или индикатора записывает структурированные строки с выбранным разделителем. Строка заголовка задаёт имена столбцов, которые pandas считывает напрямую. После этого становится доступен весь аналитический инструментарий экосистемы Python: сводные таблицы, оценки ядерной плотности, групповые агрегации, линии двумерной регрессии и многопанельные компоновки графиков.

Главное преимущество специализированного CSV-логирования перед встроенным экспортом MetaTrader 5 заключается в детальном контексте: можно записывать, какой компонент сигнала вызвал вход, какова была задержка индикатора на этом баре, к какой сессии относилась сделка и насколько капитал был ниже своего скользящего пика.



Раздел 2. Настройка аналитической среды

Все пять визуализаций используют нормализованный CSV-файл экспорта со следующей структурой полей:

Symbol, Timeframe, Indicator_Name, Filter_Period, Net_Profit_USD,
Sortino_Ratio, Max_Drawdown_PCT, False_Flips_Whipsaws, Avg_Lag_On_Turn_Bars,
Trade_Count, IS_Score, OOS_Score, Entry_Hour, Entry_DayOfWeek,
Drawdown_Duration_Bars, Equity_Peak, Equity_Trough

Минимальный блок экспорта MQL5 для формирования этой схемы:

//--- MQL5 CSV Export Block (place inside OnTick or OnTradeTransaction)
int fileHandle = FileOpen("analytics_export.csv",FILE_WRITE|FILE_CSV|FILE_ANSI,',');

if(fileHandle != INVALID_HANDLE)
  {
   //--- Write header row once on first execution
   FileWrite(fileHandle,
             "Symbol","Timeframe","Indicator_Name","Filter_Period",
             "Net_Profit_USD","Sortino_Ratio","Max_Drawdown_PCT",
             "False_Flips_Whipsaws","Avg_Lag_On_Turn_Bars",
             "Trade_Count","IS_Score","OOS_Score",
             "Entry_Hour","Entry_DayOfWeek",
             "Drawdown_Duration_Bars","Equity_Peak","Equity_Trough");

   //--- Write data row from computed variables
   FileWrite(fileHandle,
             _Symbol,EnumToString(Period()),indicatorName,filterPeriod,
             netProfit,sortinoRatio,maxDrawdownPct,
             falseFlips,avgLagBars,
             tradeCount,inSampleScore,outOfSampleScore,
             entryHour,entryDOW,
             drawdownDuration,equityPeak,equityTrough);

   FileClose(fileHandle);
  }

Примечание: Полная реализация класса CCSVExporter для этого пайплайна приведена в Части 2 данной серии.

В начале каждого аналитического модуля Python необходимо подключить следующие библиотеки и добавить блок определения путей:

import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import seaborn as sns
from scipy import stats
from scipy.stats import gaussian_kde

# Path Configuration
# MT5 terminal sandbox default path (Windows):
# C:\Users\<user>\AppData\Roaming\MetaQuotes\Terminal\<InstanceID>\MQL5\Files\
# Adjust DATA_PATH to point to your local MT5 Files directory.

BASE_DIR   = os.path.dirname(os.path.abspath(__file__))
DATA_PATH  = os.path.join(BASE_DIR, "exports", "analytics_export.csv")
OUTPUT_DIR = os.path.join(BASE_DIR, "charts")
os.makedirs(OUTPUT_DIR, exist_ok=True)

# Load the normalized analytics export
df = pd.read_csv(DATA_PATH)

# Global Style Configuration
plt.rcParams.update({
    "figure.facecolor"  : "#0d1117",
    "axes.facecolor"    : "#161b22",
    "axes.edgecolor"    : "#30363d",
    "axes.labelcolor"   : "#c9d1d9",
    "xtick.color"       : "#8b949e",
    "ytick.color"       : "#8b949e",
    "text.color"        : "#c9d1d9",
    "grid.color"        : "#21262d",
    "grid.linestyle"    : "--",
    "grid.linewidth"    : 0.6,
    "font.family"       : "monospace",
    "font.size"         : 9,
})

Все изображения сохраняются в OUTPUT_DIR и имеют ширину не более 980 пикселей (ширина figsize — 9,8 дюйма при 100 DPI).

Перед запуском программ Python из этой статьи установите необходимые библиотеки, выполнив в терминале следующую команду:

pip install pandas numpy matplotlib seaborn scipy

Примечание: модуль os входит в стандартную библиотеку Python и не требует отдельной установки.


Раздел 3. График 1. Матрица соответствия параметров для нескольких активов (расходящаяся столбчатая диаграмма)

Структурная необходимость

MetaTrader 5 выполняет оптимизацию по одному инструменту за один запуск оптимизации. Чтобы проверить, работает ли логика конфигурации параметров — например, фильтра с периодом 10 — устойчиво на нескольких активах, приходится выполнять трудоёмкую последовательность действий. Разработчику приходится запускать отдельные тесты для каждого инструмента и вручную сопоставлять полученные отчёты. Это заметно усложняет работу, а наглядное параллельное сравнение метрик между инструментами через штатный интерфейс фактически невозможно.

Матрица соответствия параметров для нескольких активов решает эту проблему, фиксируя одно значение параметра и отображая все нормализованные показатели каждого инструмента на расходящейся горизонтальной столбчатой диаграмме. Значения выше базового уровня уходят вправо, а ниже него — влево. В результате получается полный визуальный срез результатов по инструментам, который сразу показывает, какие активы положительно реагируют на выбранную конфигурацию, а какие выявляют структурную несовместимость. Такое представление особенно полезно при принятии решений о запуске советника в составе портфеля, когда универсальность параметров является обязательным условием.

Реализация на Python (Chart1.py)

def plot_multi_asset_parameter_matrix(df: pd.DataFrame,
                                      target_period: int = 30,
                                      save_path: str = None):
    """
    Multi-Asset Parameter Match Matrix (Diverging Bar Chart)

    Filters the analytics export to a single Filter_Period and constructs
    a diverging horizontal bar plot comparing normalized performance metrics
    across all unique Symbol values present in that subset.

    Parameters
    ----------
    df            : Full analytics DataFrame from CSV export.
    target_period : The Filter_Period integer to isolate for comparison.
    save_path     : File path for saving the output figure (optional).
    """

    # --- Data Preparation
    subset = df[df["Filter_Period"] == target_period].copy()

    if subset.empty:
        raise ValueError(f"No records found for Filter_Period = {target_period}.")

    # Metrics to include in the comparison matrix
    metric_cols = ["Net_Profit_USD", "Sortino_Ratio", "Max_Drawdown_PCT",
                   "False_Flips_Whipsaws", "Trade_Count"]

    # Invert Max_Drawdown and False_Flips so higher = better across all fields
    subset["Max_Drawdown_PCT"]     = -subset["Max_Drawdown_PCT"]
    subset["False_Flips_Whipsaws"] = -subset["False_Flips_Whipsaws"]

    # Z-score normalization per metric column so all axes share a common scale
    for col in metric_cols:
        col_mean = subset[col].mean()
        col_std  = subset[col].std()
        subset[col + "_z"] = (subset[col] - col_mean) / (col_std + 1e-9)

    z_cols    = [c + "_z" for c in metric_cols]
    symbols   = subset["Symbol"].unique()
    n_metrics = len(metric_cols)

    # Aggregate to symbol-level mean z-scores where multiple timeframes exist
    agg = subset.groupby("Symbol")[z_cols].mean().reset_index()

    # --- Figure Layout
    fig, axes = plt.subplots(1, n_metrics,
                             figsize=(8.167, max(3.5, len(symbols) * 0.55 + 1.5)),
                             sharey=True)
    fig.suptitle(
        f"Multi-Asset Parameter Match Matrix  |  Filter Period: {target_period}",
        fontsize=11, fontweight="bold", color="#e6edf3", y=1.01
    )

    display_labels = {
        "Net_Profit_USD_z"       : "Net Profit (Normalized)",
        "Sortino_Ratio_z"        : "Sortino Ratio",
        "Max_Drawdown_PCT_z"     : "Max DD (Inverted)",
        "False_Flips_Whipsaws_z" : "Whipsaws (Inverted)",
        "Trade_Count_z"          : "Trade Count",
    }

    bar_colors_pos = "#3fb950"   # Green for positive z-score
    bar_colors_neg = "#f85149"   # Red for negative z-score

    for ax_idx, (ax, z_col) in enumerate(zip(axes, z_cols)):
        values     = agg[z_col].values
        sym_labels = agg["Symbol"].values
        y_pos      = np.arange(len(sym_labels))

        colors = [bar_colors_pos if v >= 0 else bar_colors_neg for v in values]
        bars   = ax.barh(y_pos, values, color=colors, height=0.62, alpha=0.88)

        ax.axvline(0, color="#484f58", linewidth=0.9, linestyle="-")
        ax.set_title(display_labels.get(z_col, z_col),
                     fontsize=8, color="#8b949e", pad=6)
        ax.tick_params(axis="x", labelsize=7)
        ax.grid(axis="x", alpha=0.4)
        ax.set_xlim(-3.5, 3.5)
        ax.spines[["top", "right"]].set_visible(False)

        # Annotate each bar with its raw z-score
        for bar_rect, val in zip(bars, values):
            label_x = bar_rect.get_width() + (0.08 if val >= 0 else -0.08)
            ha       = "left" if val >= 0 else "right"
            ax.text(label_x, bar_rect.get_y() + bar_rect.get_height() / 2,
                    f"{val:+.2f}", va="center", ha=ha, fontsize=6.5,
                    color="#8b949e")

        if ax_idx == 0:
            ax.set_yticks(y_pos)
            ax.set_yticklabels(sym_labels, fontsize=8)

    fig.text(0.5, -0.03,
             "Z-score deviation from group mean  |  Higher = Structurally Stronger",
             ha="center", fontsize=7.5, color="#8b949e", style="italic")

    plt.tight_layout()

    if save_path:
        plt.savefig(save_path, dpi=120, bbox_inches="tight",
                    facecolor=fig.get_facecolor())
    plt.show()


# Execute
plot_multi_asset_parameter_matrix(
    df,
    target_period=30,
    save_path=os.path.join(OUTPUT_DIR, "Multi_Asset_Parameter_Matrix.png")
)

Рис. 1. Матрица параметров для нескольких активов

Рис. 1. Матрица параметров для нескольких активов


Раздел 4. График 2. Задержка сигнала и частота ложных срабатываний (совмещённая диаграмма рассеяния с линиями тренда)

Структурная необходимость

Любой сглаживающий фильтр, применяемый к ценовым данным, связан с одним и тем же фундаментальным компромиссом. Уменьшение задержки делает сигнал более отзывчивым, но увеличивает число ложных пересечений, вызванных рыночным шумом. Увеличение задержки, напротив, подавляет шум, однако откладывает корректные входы и выходы. Такое запаздывание напрямую снижает временную ценность каждого торгового сигнала. Хотя эта зависимость хорошо известна теоретически, на практике её редко визуализируют по реальным данным тестирования.

MetaTrader 5 показывает количество ложных срабатываний только в том случае, если советник явно рассчитывает и записывает этот показатель. Но даже после логирования встроенной визуализации, одновременно отображающей зависимость между задержкой и ложными срабатываниями для нескольких конфигураций, нет. Без такого двумерного представления разработчику сложно оценить эффективность фильтра, определить, находится ли выбранный период в зоне высокого шума или приближается к структурной границе, и обнаружить оптимальную область, где одновременно минимальны и задержка, и частота ложных пересечений.

Эти задачи решает совмещённая диаграмма рассеяния с наложенными линиями тренда. Для каждой экспортированной точки по оси X откладывается средняя задержка в барах, а по оси Y — абсолютное количество ложных срабатываний. Линии тренда, построенные отдельно для каждой категории индикаторов, показывают характер зависимости для каждого класса фильтров. Экспоненциальные средние, адаптивные фильтры и простые скользящие средние формируют в этом пространстве разные траектории. Точки в левом нижнем квадранте соответствуют конфигурациям с наилучшим соотношением задержки и шума, поскольку и задержка, и количество ложных срабатываний остаются низкими.

Реализация на Python (Chart2.py)

def plot_lag_vs_whipsaw(df: pd.DataFrame, save_path: str = None):
    """
    Signal Lag vs. Whipsaw Frequency (Joint Scatter with Trend Lines)

    Plots Avg_Lag_On_Turn_Bars (X) against False_Flips_Whipsaws (Y) for all
    records in the export. Each unique Indicator_Name receives a distinct color
    and a fitted OLS trend line to expose per-category efficiency trajectories.

    Parameters
    ----------
    df        : Full analytics DataFrame from CSV export.
    save_path : File path for saving the output figure (optional).
    """

    # --- Data Preparation
    required = ["Avg_Lag_On_Turn_Bars", "False_Flips_Whipsaws", "Indicator_Name"]
    plot_df  = df[required].dropna()

    indicators = plot_df["Indicator_Name"].unique()
    palette    = sns.color_palette("husl", len(indicators))
    color_map  = dict(zip(indicators, palette))

    # --- Figure Setup
    fig, ax = plt.subplots(figsize=(8.167, 5.4))
    fig.suptitle(
        "Signal Lag vs. Whipsaw Frequency  |  Alpha-Efficiency Boundary Analysis",
        fontsize=11, fontweight="bold", color="#e6edf3"
    )

    for indicator in indicators:
        mask   = plot_df["Indicator_Name"] == indicator
        x_vals = plot_df.loc[mask, "Avg_Lag_On_Turn_Bars"].values
        y_vals = plot_df.loc[mask, "False_Flips_Whipsaws"].values
        color  = color_map[indicator]

        ax.scatter(x_vals, y_vals, color=color, label=indicator,
                   alpha=0.75, s=52, linewidths=0.4,
                   edgecolors="white", zorder=3)

        # OLS trend line per indicator category (requires >= 2 points)
        if len(x_vals) >= 2:
            slope, intercept, r_value, p_value, _ = stats.linregress(x_vals, y_vals)
            x_line = np.linspace(x_vals.min(), x_vals.max(), 120)
            y_line = slope * x_line + intercept
            ax.plot(x_line, y_line, color=color, linewidth=1.5,
                    linestyle="--", alpha=0.7, zorder=2)
            ax.text(x_line[-1] + 0.15, y_line[-1],
                    f"R²={r_value**2:.2f}",
                    fontsize=7, color=color, va="center", alpha=0.9)

    # --- Quadrant Reference Lines
    x_med = plot_df["Avg_Lag_On_Turn_Bars"].median()
    y_med = plot_df["False_Flips_Whipsaws"].median()
    ax.axvline(x_med, color="#484f58", linewidth=0.8, linestyle=":")
    ax.axhline(y_med, color="#484f58", linewidth=0.8, linestyle=":")

    ax.text(plot_df["Avg_Lag_On_Turn_Bars"].min() + 0.1,
            plot_df["False_Flips_Whipsaws"].min() + 0.5,
            "Optimal Region (Low Lag | Low Noise)",
            fontsize=7.5, color="#3fb950", style="italic", alpha=0.8)

    ax.set_xlabel("Average Lag on Turn (Bars)", fontsize=9, labelpad=8)
    ax.set_ylabel("Whipsaw Count (False Flips)", fontsize=9, labelpad=8)
    ax.grid(True, alpha=0.35)
    ax.spines[["top", "right"]].set_visible(False)

    ax.legend(title="Indicator Class", fontsize=8,
              title_fontsize=8, loc="upper left",
              framealpha=0.25, edgecolor="#30363d")

    plt.tight_layout()

    if save_path:
        plt.savefig(save_path, dpi=120, bbox_inches="tight",
                    facecolor=fig.get_facecolor())
    plt.show()


# Execute
plot_lag_vs_whipsaw(
    df,
    save_path=os.path.join(OUTPUT_DIR, "Lag_vs_Whipsaw_Scatter.png")
)

Рис. 2. Диаграмма рассеяния задержки и ложных срабатываний

Рис. 2. Диаграмма рассеяния задержки и ложных срабатываний


Раздел 5. График 3. Профиль деградации результатов Walk-Forward (парный график наклонов)

Структурная необходимость

Результаты оптимизации подвержены переобучению. Набор параметров, демонстрирующий исключительно высокую доходность на обучающей выборке, часто оказывается подогнанным под конкретный исторический участок данных. Способность такой конфигурации обобщаться на ранее не наблюдавшиеся рыночные условия — отдельный вопрос. К сожалению, сводные отчёты оптимизации отвечают на него плохо. Штатный вывод MetaTrader 5 показывает лишь комбинации параметров, занявшие верхние позиции по целевой функции на обучающей выборке, но не демонстрирует, как те же комбинации проявили себя на зарезервированном участке вне выборки.

Walk-Forward-валидация является стандартным методом оценки способности к обобщению на новые данные, а парный график наклонов — оптимальный формат визуализации для этой задачи. Каждая конфигурация стратегии представлена линией, соединяющей две точки: слева показана оценка на обучающей выборке, справа — оценка вне выборки. Наклон кодирует направление и величину изменения результата. Горизонтальные или плавно снижающиеся линии указывают на структурную логику, сохраняющую работоспособность на новых данных, тогда как резкие падения являются классическим признаком переоптимизации. К таким переобученным наборам параметров следует относиться скептически, независимо от привлекательности их основных сводных показателей тестирования.

Умеренный наклон деградации сам по себе не означает, что стратегия будет прибыльной в реальной торговле. График лишь выявляет стратегии с неустойчивой исторической подгонкой до того, как в них будет вложен капитал.

Реализация на Python (Chart3.py)

def plot_walkforward_degradation(df: pd.DataFrame,
                                  top_n: int = 20,
                                  save_path: str = None):
    """
    Walk-Forward Performance Degradation Profile (Paired Slope Chart)

    Draws a slope chart pairing IS_Score (In-Sample) with OOS_Score
    (Out-of-Sample) for the top_n configurations ranked by IS_Score.
    Line color encodes degradation magnitude: green = moderate,
    orange = significant, red = collapse.

    Parameters
    ----------
    df        : Full analytics DataFrame from CSV export.
    top_n     : Number of top in-sample configurations to display.
    save_path : File path for saving the output figure (optional).
    """

    required = ["IS_Score", "OOS_Score", "Symbol", "Filter_Period", "Indicator_Name"]
    plot_df  = df[required].dropna().copy()
    plot_df  = plot_df.nlargest(top_n, "IS_Score").reset_index(drop=True)

    plot_df["Degradation"] = plot_df["OOS_Score"] / (plot_df["IS_Score"].abs() + 1e-9)

    def assign_color(ratio):
        if ratio >= 0.70:
            return "#3fb950"    # Retained >= 70% of IS performance
        elif ratio >= 0.35:
            return "#d29922"    # Moderate degradation (35–70%)
        else:
            return "#f85149"    # Collapse below 35% of IS performance

    plot_df["LineColor"] = plot_df["Degradation"].apply(assign_color)

    fig, ax = plt.subplots(figsize=(8.167, 6.2))
    fig.suptitle(
        "Walk-Forward Performance Degradation Profile  |  IS vs OOS Structural Decay",
        fontsize=11, fontweight="bold", color="#e6edf3"
    )

    x_left  = 0.0
    x_right = 1.0
    y_min = min(plot_df["IS_Score"].min(), plot_df["OOS_Score"].min()) - 0.05
    y_max = max(plot_df["IS_Score"].max(), plot_df["OOS_Score"].max()) + 0.05

    for _, row in plot_df.iterrows():
        y_is  = row["IS_Score"]
        y_oos = row["OOS_Score"]
        color = row["LineColor"]
        label = f"{row['Symbol']} | {row['Indicator_Name']} | P={int(row['Filter_Period'])}"

        ax.plot([x_left, x_right], [y_is, y_oos],
                color=color, linewidth=1.4, alpha=0.80, zorder=2)
        ax.scatter([x_left],  [y_is],  color=color, s=32, zorder=3, alpha=0.9)
        ax.scatter([x_right], [y_oos], color=color, s=32, zorder=3,
                   alpha=0.9, marker="D")
        ax.text(x_left - 0.03, y_is, label,
                ha="right", va="center", fontsize=6.2, color="#8b949e")

    ax.axvline(x_left,  color="#484f58", linewidth=0.7, linestyle="--", zorder=1)
    ax.axvline(x_right, color="#484f58", linewidth=0.7, linestyle="--", zorder=1)
    ax.text(x_left,  y_max + 0.01, "In-Sample Score",
            ha="center", fontsize=9, fontweight="bold", color="#c9d1d9")
    ax.text(x_right, y_max + 0.01, "Out-of-Sample Score",
            ha="center", fontsize=9, fontweight="bold", color="#c9d1d9")

    from matplotlib.lines import Line2D
    legend_handles = [
        Line2D([0],[0], color="#3fb950", lw=2, label="Moderate Decay (>=70% retained)"),
        Line2D([0],[0], color="#d29922", lw=2, label="Significant Decay (35–70%)"),
        Line2D([0],[0], color="#f85149", lw=2, label="Collapse (<35% retained)"),
    ]
    ax.legend(handles=legend_handles, fontsize=8,
              loc="upper center", bbox_to_anchor=(0.5, -0.04),
              ncol=3, framealpha=0.2, edgecolor="#30363d")

    ax.set_xlim(-0.65, 1.55)
    ax.set_ylim(y_min, y_max + 0.12)
    ax.set_xticks([])
    ax.set_ylabel("Performance Score (Normalized)", fontsize=9, labelpad=8)
    ax.grid(axis="y", alpha=0.25)
    ax.spines[["top", "right", "bottom"]].set_visible(False)
    plt.tight_layout()

    if save_path:
        plt.savefig(save_path, dpi=120, bbox_inches="tight",
                    facecolor=fig.get_facecolor())
    plt.show()


# Execute
plot_walkforward_degradation(
    df, top_n=20,
    save_path=os.path.join(OUTPUT_DIR, "WalkForward_Degradation_Slope.png")
)

Рис. 3. Наклон деградации результатов Walk-Forward

Рис. 3. Наклон деградации результатов Walk-Forward


Раздел 6. График 4. Распределение глубины и длительности максимальной просадки (KDE в Seaborn)

Структурная необходимость

MetaTrader 5 представляет максимальную просадку двумя точечными сводными показателями: абсолютной суммой в долларах в самой глубокой точке и величиной этой точки в процентах от максимума капитала. Эти значения описывают наиболее тяжёлое отдельное событие, но ничего не говорят о статистическом распределении поведения просадок по всей истории теста.

Стратегия с заявленной максимальной просадкой 12% могла достичь её в результате единственного изолированного экстремального события, после которого профиль восстановления мог быть практически ровным. Но возможен и другой вариант: десятки интервалов просадки от 8% до 11% с восстановлением, растянутым на недели. Второй сценарий приводит к хроническому подрыву капитала, полностью скрытому итоговой цифрой. Для расчёта размера позиции и оценки допустимого риска форма распределения просадок намного важнее одного порогового значения.

График KDE преобразует зарегистрированные эпизоды снижения капитала в сглаженные кривые плотности вероятности для глубины и продолжительности просадки. Это устраняет артефакты разбиения гистограммы на интервалы и упрощает сравнение профилей восстановления.

Реализация на Python (Chart4.py)

def plot_drawdown_distribution(df: pd.DataFrame, save_path: str = None):
    """
    Maximum Drawdown Duration and Depth Distribution (Seaborn KDE)

    Generates two KDE distribution overlays on a dual-panel figure:
      Left panel:  KDE of Max_Drawdown_PCT across all records.
      Right panel: KDE of Drawdown_Duration_Bars.

    Parameters
    ----------
    df        : Full analytics DataFrame from CSV export.
    save_path : File path for saving the output figure (optional).
    """

    dd_depth    = df["Max_Drawdown_PCT"].dropna().abs().values
    dd_duration = df["Drawdown_Duration_Bars"].dropna().values

    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8.167, 5.0))
    fig.suptitle(
        "Drawdown Distribution Profile  |  Depth and Duration Density Estimation",
        fontsize=11, fontweight="bold", color="#e6edf3"
    )

    # --- Left Panel: Drawdown Depth KDE
    sns.kdeplot(dd_depth, ax=ax1,
                color="#f85149", linewidth=2.2, fill=True, alpha=0.18)
    ax1.set_xlabel("Drawdown Depth (%)", fontsize=9, labelpad=7)
    ax1.set_ylabel("Probability Density", fontsize=9, labelpad=7)
    ax1.set_title("Depth Distribution", fontsize=9, color="#8b949e", pad=6)

    for pct, pct_val in [(50, np.percentile(dd_depth, 50)),
                         (90, np.percentile(dd_depth, 90)),
                         (95, np.percentile(dd_depth, 95))]:
        ax1.axvline(pct_val, color="#d29922", linewidth=0.9, linestyle=":", alpha=0.8)
        ax1.text(pct_val + 0.1, ax1.get_ylim()[1] * 0.92,
                 f"P{pct}: {pct_val:.1f}%", fontsize=6.5, color="#d29922", ha="left")

    ax1.hist(dd_depth, bins=30, density=True,
             color="#f85149", alpha=0.10, edgecolor="none", zorder=0)
    ax1.grid(True, alpha=0.3)
    ax1.spines[["top", "right"]].set_visible(False)

    # --- Right Panel: Drawdown Duration KDE
    sns.kdeplot(dd_duration, ax=ax2,
                color="#388bfd", linewidth=2.2, fill=True, alpha=0.18)
    ax2.set_xlabel("Drawdown Duration (Bars)", fontsize=9, labelpad=7)
    ax2.set_ylabel("Probability Density", fontsize=9, labelpad=7)
    ax2.set_title("Duration Distribution", fontsize=9, color="#8b949e", pad=6)

    for pct, pct_val in [(50, np.percentile(dd_duration, 50)),
                         (90, np.percentile(dd_duration, 90)),
                         (95, np.percentile(dd_duration, 95))]:
        ax2.axvline(pct_val, color="#d29922", linewidth=0.9, linestyle=":", alpha=0.8)
        ax2.text(pct_val + 0.5, ax2.get_ylim()[1] * 0.92,
                 f"P{pct}: {pct_val:.0f}b", fontsize=6.5, color="#d29922", ha="left")

    ax2.hist(dd_duration, bins=30, density=True,
             color="#388bfd", alpha=0.10, edgecolor="none", zorder=0)
    ax2.grid(True, alpha=0.3)
    ax2.spines[["top", "right"]].set_visible(False)

    plt.tight_layout()

    if save_path:
        plt.savefig(save_path, dpi=120, bbox_inches="tight",
                    facecolor=fig.get_facecolor())
    plt.show()


# Execute
plot_drawdown_distribution(
    df,
    save_path=os.path.join(OUTPUT_DIR, "Drawdown_Distribution_KDE.png")
)

Рис. 4. KDE-распределение просадок

Рис. 4. KDE-распределение просадок


Раздел 7. График 5. Внутридневные и внутринедельные кластеры альфа-доходности (тепловая карта по часам и дням)

Структурная необходимость

Алгоритмические системы сталкиваются с изменяющимися режимами ликвидности на валютных и товарных рынках. Азиатская сессия характеризуется иной структурой волатильности и направленной склонностью, чем открытие Лондона или период пересечения лондонской и нью-йоркской сессий. Стратегия, настроенная по агрегированным 24-часовым данным, может давать сильно искажённую картину: вся её доходность может концентрироваться в узком двухчасовом окне, тогда как в остальные сессии она теряет капитал. Аналогично положительный итоговый результат способен скрывать систематические убытки по понедельникам и пятницам, которые компенсируются тремя прибыльными днями.

MetaTrader 5 не формирует временную сетку результативности. Историю сделок можно сортировать и фильтровать вручную, но агрегация по часам и дням требует внешней обработки. Предлагаемая тепловая карта даёт именно такое представление. Данные сделок организуются в двумерную матрицу: строки соответствуют торговым часам от 0 до 23 UTC, а столбцы — дням недели с понедельника по пятницу. Интенсивность цвета каждой ячейки кодирует чистую прибыль или среднюю доходность сделок, выполненных в соответствующем сочетании часа и дня.

Такая компоновка выявляет зависимость от торговых сессий и аномальные внутринедельные временные интервалы, а также даёт основу для настройки сессионных фильтров или снижения экспозиции в слабые периоды.

Реализация на Python (Chart5.py)

def plot_intraday_heatmap(df: pd.DataFrame,
                           metric: str = "Net_Profit_USD",
                           save_path: str = None):
    """
    Intraday and Intraweek Alpha Clusters (Hour-by-Day Performance Heatmap)

    Aggregates the chosen performance metric by Entry_Hour (0–23) and
    Entry_DayOfWeek (0=Monday, 4=Friday), then renders a seaborn heatmap
    with annotated cell values and session zone overlays.

    Parameters
    ----------
    df        : Full analytics DataFrame from CSV export.
    metric    : Column to aggregate into the heatmap cells.
    save_path : File path for saving the output figure (optional).
    """

    required = ["Entry_Hour", "Entry_DayOfWeek", metric]
    plot_df  = df[required].dropna().copy()
    plot_df["Entry_Hour"]      = plot_df["Entry_Hour"].astype(int)
    plot_df["Entry_DayOfWeek"] = plot_df["Entry_DayOfWeek"].astype(int)

    pivot = plot_df.pivot_table(index="Entry_Hour", columns="Entry_DayOfWeek",
                                values=metric, aggfunc="mean")
    pivot = pivot.reindex(index=range(24), columns=range(5), fill_value=0.0)

    day_labels = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri"}
    pivot.rename(columns=day_labels, inplace=True)

    fig, ax = plt.subplots(figsize=(8.167, 7.6))
    fig.suptitle(
        f"Intraday Alpha Cluster Heatmap  |  {metric} by Session Hour and Weekday",
        fontsize=11, fontweight="bold", color="#e6edf3"
    )

    cmap = sns.diverging_palette(10, 130, as_cmap=True)

    sns.heatmap(pivot, ax=ax, cmap=cmap, center=0,
                linewidths=0.4, linecolor="#21262d",
                annot=True, fmt=".0f",
                annot_kws={"size": 6.5, "color": "#e6edf3"},
                cbar_kws={"label": metric, "shrink": 0.75, "pad": 0.02})

    # --- Session Zone Overlays (approximate UTC ranges)
    session_zones = [
        (0,  9,  "Asian",    "#388bfd", 0.06),
        (7,  16, "London",   "#3fb950", 0.06),
        (13, 22, "New York", "#d29922", 0.06),
    ]

    for row_start, row_end, label, color, alpha in session_zones:
        ax.add_patch(
            plt.Rectangle((0, row_start), len(pivot.columns), row_end - row_start,
                           fill=True, color=color, alpha=alpha, zorder=0)
        )
        ax.text(-0.35, (row_start + row_end) / 2, label,
                va="center", ha="right", fontsize=7,
                color=color, rotation=90, style="italic")

    ax.set_xlabel("Weekday", fontsize=9, labelpad=7)
    ax.set_ylabel("Entry Hour (UTC)", fontsize=9, labelpad=7)
    ax.tick_params(axis="both", labelsize=8)
    ax.set_yticklabels([f"{h:02d}:00" for h in range(24)], fontsize=7, rotation=0)

    cbar = ax.collections[0].colorbar
    cbar.ax.yaxis.label.set_color("#c9d1d9")
    cbar.ax.tick_params(labelcolor="#8b949e")

    plt.tight_layout()

    if save_path:
        plt.savefig(save_path, dpi=120, bbox_inches="tight",
                    facecolor=fig.get_facecolor())
    plt.show()


# Execute
plot_intraday_heatmap(
    df, metric="Net_Profit_USD",
    save_path=os.path.join(OUTPUT_DIR, "Intraday_Performance_Heatmap.png")
)

Рис. 5. Внутридневная тепловая карта результативности

Рис. 5. Внутридневная тепловая карта результативности


Раздел 8. Архитектура автоматизированного развёртывания

Каждая из пяти приведённых выше функций работает независимо. Однако их можно объединить в единый вызываемый аналитический модуль, исключив ручной запуск отдельных этапов. Тогда весь набор визуализаций будет обрабатываться для любого нового файла экспорта, а все графики можно будет сформировать одной командой.

Следующая обёртка объединяет все функции построения графиков и запускает их в заданной последовательности. Конфигурация полностью централизована в верхней части файла, а все выходные пути формируются из одной корневой переменной. Благодаря такой структуре для переноса папки экспорта достаточно изменить одну строку.

# MT5 Analytics Suite  |  Unified Execution Module
# Reads:  exports/analytics_export.csv
# Writes: charts/  (PNG files, 120 DPI, max width 8.167 inches)

import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import seaborn as sns
from scipy import stats
from scipy.stats import gaussian_kde
from matplotlib.lines import Line2D


BASE_DIR   = os.path.dirname(os.path.abspath(__file__))
DATA_PATH  = os.path.join(BASE_DIR, "exports", "analytics_export.csv")
OUTPUT_DIR = os.path.join(BASE_DIR, "charts")
os.makedirs(OUTPUT_DIR, exist_ok=True)

plt.rcParams.update({
    "figure.facecolor" : "#0d1117",  "axes.facecolor"   : "#161b22",
    "axes.edgecolor"   : "#30363d",  "axes.labelcolor"  : "#c9d1d9",
    "xtick.color"      : "#8b949e",  "ytick.color"      : "#8b949e",
    "text.color"       : "#c9d1d9",  "grid.color"       : "#21262d",
    "grid.linestyle"   : "--",       "grid.linewidth"   : 0.6,
    "font.family"      : "monospace","font.size"        : 9,
})


def load_analytics_export(path: str) -> pd.DataFrame:
    if not os.path.isfile(path):
        raise FileNotFoundError(
            f"Export file not found at:\n  {path}\n\n"
            "Ensure the MQL5 EA has written to this location or adjust DATA_PATH."
        )
    df = pd.read_csv(path)
    print(f"[Loaded] {len(df):,} records from {os.path.basename(path)}")
    return df


# --- Paste all five chart functions here as defined in the sections above ---
# plot_multi_asset_parameter_matrix()
# plot_lag_vs_whipsaw()
# plot_walkforward_degradation()
# plot_drawdown_distribution()
# plot_intraday_heatmap()


def run_full_suite(data_path=DATA_PATH, output_dir=OUTPUT_DIR,
                   wf_top_n=20, target_period=10):
    df = load_analytics_export(data_path)

    chart_map = {
        "Multi_Asset_Parameter_Matrix.png": lambda: plot_multi_asset_parameter_matrix(
            df, target_period=target_period,
            save_path=os.path.join(output_dir, "Multi_Asset_Parameter_Matrix.png")),
        "Lag_vs_Whipsaw_Scatter.png": lambda: plot_lag_vs_whipsaw(
            df, save_path=os.path.join(output_dir, "Lag_vs_Whipsaw_Scatter.png")),
        "WalkForward_Degradation_Slope.png": lambda: plot_walkforward_degradation(
            df, top_n=wf_top_n,
            save_path=os.path.join(output_dir, "WalkForward_Degradation_Slope.png")),
        "Drawdown_Distribution_KDE.png": lambda: plot_drawdown_distribution(
            df, save_path=os.path.join(output_dir, "Drawdown_Distribution_KDE.png")),
        "Intraday_Performance_Heatmap.png": lambda: plot_intraday_heatmap(
            df, metric="Net_Profit_USD",
            save_path=os.path.join(output_dir, "Intraday_Performance_Heatmap.png")),
    }

    for chart_name, chart_fn in chart_map.items():
        print(f"\n[Rendering] {chart_name}")
        try:
            chart_fn()
        except Exception as exc:
            print(f"  [Warning] {chart_name} skipped: {exc}")

    print(f"\n[Complete] All charts written to: {output_dir}")


if __name__ == "__main__":
    run_full_suite()

Определение пути к файловой песочнице терминала MetaTrader 5

Функция `FileOpen` в MetaTrader 5 записывает файлы только в определённый каталог — подпапку `MQL5\Files\` внутри папки данных терминала. Модуль Python можно напрямую связать с этим расположением, полностью отказавшись от ручного копирования файлов. Для этого достаточно заменить определение `BASE_DIR` автоматическим механизмом поиска пути терминала:

import os

# Windows: locate the MT5 terminal data folder automatically
MT5_APPDATA = os.path.join(
    os.environ.get("APPDATA", ""),
    "MetaQuotes", "Terminal"
)

# List all terminal instance directories
terminal_instances = [
    d for d in os.listdir(MT5_APPDATA)
    if os.path.isdir(os.path.join(MT5_APPDATA, d))
]

# Use the first instance, or specify the exact InstanceID string
INSTANCE_ID = terminal_instances[0]

MT5_FILES_DIR = os.path.join(MT5_APPDATA, INSTANCE_ID, "MQL5", "Files")
DATA_PATH     = os.path.join(MT5_FILES_DIR, "analytics_export.csv")

Советник записывает новый CSV-файл при каждом тестировании или запланированном запуске, а модуль Python при следующем выполнении считывает данные непосредственно из этого каталога. Такая прямая интеграция исключает промежуточные действия и необходимость вручную копировать файлы.


Раздел 9. Направления расширения и дальнейшего развития

Описанный в статье пайплайн закрывает пять конкретных пробелов визуализации, но его базовая архитектура остаётся легко расширяемой. Её можно адаптировать к широкому спектру дополнительных аналитических задач и без труда дополнять в будущем.

Анализ параметров в скользящем окне: Текущая матрица параметров агрегирует данные за всю доступную историю. Естественным расширением станет добавление измерения скользящего окна. Вместо единой агрегированной z-оценки по каждому инструменту график будет изменяться по последовательным 60- или 90-дневным окнам. Это позволит отслеживать согласованность конфигурации параметров между активами во времени и видеть, сохраняется ли она либо ухудшается при смене рыночных режимов.

Автоматические триггеры пакетного экспорта из MQL5: Триггер на основе EventScheduler в MQL5 может напрямую запускать модуль Python через `ShellExecuteW`. Тогда советник и скрипт Python не придётся запускать вручную: процесс будет автоматически срабатывать после каждого прохода оптимизации или через заданные календарные интервалы в реальной торговле. Советник записывает обновлённый CSV-файл и вызывает исполняемый файл Python, после чего без участия пользователя формируется новый набор графиков.

//--- Trigger Python analytics suite from within MQL5
string pythonPath = "C:\\Python311\\python.exe";
string scriptPath = "C:\\MT5_Analytics\\run_analytics_suite.py";
string command    = pythonPath + " " + scriptPath;

ShellExecuteW(NULL, "open", "cmd.exe",
              "/c " + command, NULL, SW_HIDE);

Сравнительные наложения нескольких прогонов: Сейчас модуль обрабатывает один файл экспорта, но загрузчик можно расширить так, чтобы он принимал список путей. Тогда график деградации Walk-Forward и KDE-распределения смогут накладывать результаты разных версий советника, обеспечивая прямое наглядное сравнение структурных изменений между итерациями алгоритма.

Отображение статистической значимости: Сейчас диаграмма рассеяния задержки и ложных срабатываний показывает значения R² для каждой линии тренда. Полезным расширением станет применение перестановочного тестирования или бутстреп-доверительных интервалов к каждому наклону регрессии. Это позволит формально оценить достоверность тренда и определить, отличается ли наблюдаемая зависимость эффективности от случайных колебаний при заданном уровне значимости альфа.


Заключение

Пять представленных в руководстве визуализаций устраняют конкретные ограничения встроенной системы отчётности MetaTrader 5:

  • Матрица соответствия параметров: выявляет различия в поведении стратегии между активами, которые не видны в отчётах по одному инструменту.
  • Диаграмма задержки сигнала: количественно оценивает реальный компромисс между отзывчивостью и шумом по данным тестирования.
  • Диаграмма наклонов Walk-Forward: выделяет признаки переобучения и помогает выявлять наборы параметров, неспособные к обобщению.
  • KDE-распределения просадок: показывают вероятностное распределение эпизодов снижения капитала, которое скрывается за точечными статистическими показателями.
  • Внутридневная тепловая карта: определяет, является ли преимущество алгоритма равномерным или сосредоточено в отдельных сессиях ликвидности.

Эти графики не заменяют проверку статистических гипотез и сами по себе не являются единственным основанием для внедрения стратегии. Их задача — снизить трудоёмкость анализа на исследовательском этапе. Вместо сопоставления нескольких статических отчётов разработчик может изучить единое визуальное представление и определить закономерности, требующие более глубокого анализа.

Среда исполнения MQL5 формирует данные о результатах, а Python выполняет их обработку и визуализацию. Создание такого автоматизированного процесса позволяет полностью обойти ограничения штатной визуализации терминала и напрямую встроить результаты тестирования в стандартный процесс количественного анализа.


Программы, используемые в статье:

# Название Тип Описание
1 Chart1.py Скрипт Python Сравнивает нормализованные показатели результативности нескольких торговых инструментов для выбранного периода фильтра. Показатели масштабируются с помощью z-оценок и отображаются на расходящейся столбчатой диаграмме, что позволяет быстро увидеть сильные и слабые стороны стратегии на разных активах.
2 Chart2.py Скрипт Python Строит зависимость Avg_Lag_On_Turn_Bars от False_Flips_Whipsaws для каждой записи экспорта. Каждому классу индикаторов назначается отдельный цвет и линия тренда, построенная методом OLS. Значение R² на каждой линии показывает силу зависимости между задержкой и шумом для соответствующей категории фильтра. Левый нижний квадрант обозначает целевую область, где оба показателя одновременно минимальны.
3 Chart3.py Скрипт Python Строит парный график наклонов, соединяя оценку каждой конфигурации на обучающей выборке слева с её оценкой вне выборки справа. Цвет линии кодирует величину деградации: зелёный означает умеренное снижение, янтарный — значительное, красный — обвал. Каждая запись содержит обозначение инструмента, класса индикатора и периода фильтра.
4 Chart4.py Скрипт Python Формирует по данным аналитического экспорта две расположенные рядом панели оценки ядерной плотности. Левая показывает распределение глубины просадок, правая — распределение их продолжительности в барах. На обе панели наносятся маркеры процентилей P50, P90 и P95, а столбцы гистограммы под каждой сглаженной кривой дают дополнительное представление о частоте наблюдений.
5 Chart5.py Скрипт Python Агрегирует выбранный показатель результативности по Entry_Hour и Entry_DayOfWeek, затем строит тепловую карту seaborn с числовыми значениями в ячейках. Полупрозрачные горизонтальные полосы обозначают приблизительные временные окна азиатской, лондонской и нью-йоркской сессий по UTC. Каждая ячейка показывает среднюю чистую прибыль всех сделок, открытых в соответствующем пересечении часа и дня недели.
6 Unified_Execution_Module.py Скрипт Python Точка входа автоматизации для всего набора визуализаций. Модуль один раз загружает файл аналитического экспорта, автоматически определяет путь к файлам терминала MetaTrader 5 и последовательно вызывает все пять функций построения графиков. Каждый график сохраняется в заданном выходном каталоге с шириной 980 пикселей. Один запуск из командной строки полностью обновляет весь набор графиков для нового файла экспорта.
 7  analytics_export.csv Пример CSV-файла Нормализованный пример данных, записываемый советником MQL5 в MetaTrader 5. Он служит единым источником данных для всех пяти графиков и содержит записи по сделкам и конфигурациям для всех отслеживаемых показателей.
 8 CSV_Data_Analysis_Part3.zip  ZIP-архив  ZIP-архив со всеми приложенными файлами и путями к ним относительно корневого каталога терминала.

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

Прикрепленные файлы |
Chart1.py (5.54 KB)
Chart2.py (4.47 KB)
Chart3.py (5.12 KB)
Chart4.py (4.29 KB)
Chart5.py (4.33 KB)
Разработка самовосстанавливающегося советника в MQL5 (Часть 2): Виртуальная защита сделок, устойчивая к перезапуску Разработка самовосстанавливающегося советника в MQL5 (Часть 2): Виртуальная защита сделок, устойчивая к перезапуску
Постройте уровень виртуальной защиты, устойчивый к перезапускам, поверх механизма персистентности SQLite из Части 1. Советник восстанавливает скрытые стоп-лосс и тейк-профит после перезапуска, сверяет текущую цену с восстановленными уровнями выхода и в зависимости от результата либо закрывает позиции, либо продолжает ими управлять. В результате формируется согласованный сценарий восстановления, который обнаруживает управляемые позиции и обеспечивает безопасное управление в рабочем режиме.
Нейросети в трейдинге: От единственного прогноза к пространству рыночных сценариев (Окончание) Нейросети в трейдинге: От единственного прогноза к пространству рыночных сценариев (Окончание)
Материал завершает адаптацию фреймворка ORION к трейдингу: сценарии рынка формируются Market Encoder, а Актёр на их основе управляет объёмом, TP и SL с учётом состояния счёта; Критики дают оценку действий. Описаны процедуры офлайн‑ и онлайн‑обучения и контроль утечки будущего. Проверка проводится в тестере стратегий на отложенном периоде.
Возможности Мастера MQL5, которые вам нужно знать (Часть 80): Использование паттернов Ишимоку и ADX Уайлдера в обучении с подкреплением TD3 Возможности Мастера MQL5, которые вам нужно знать (Часть 80): Использование паттернов Ишимоку и ADX Уайлдера в обучении с подкреплением TD3
Эта статья продолжает тему части 74, где мы рассматривали сочетание Ишимоку и ADX в рамках обучения с учителем, перенося фокус на обучение с подкреплением. Индикаторы Ишимоку и ADX дополняют друг друга: первый помогает строить карту уровней поддержки и сопротивления, а второй – определять силу тренда. В этой статье мы рассмотрим, как можно использовать алгоритм Twin Delayed Deep Deterministic Policy Gradient (TD3) с этим набором индикаторов. Как и в предыдущих частях серии, реализация выполняется в пользовательском классе сигнала, предназначенном для интеграции с Мастером MQL5, что упрощает сборку советника.
Автоматизация торговых стратегий в MQL5 (Часть 33): Создание системы распознавания гармонического паттерна "Shark" с использованием Price Action Автоматизация торговых стратегий в MQL5 (Часть 33): Создание системы распознавания гармонического паттерна "Shark" с использованием Price Action
В этой статье мы разрабатываем систему распознавания паттернов "Shark" (акула) на языке MQL5, которая определяет бычьи и медвежьи гармонические паттерны "Shark" с использованием точек разворота и коэффициентов Фибоначчи, совершая сделки с настраиваемыми уровнями входа, стоп-лосса и тейк-профита на основе выбранных пользователем параметров. Мы улучшим работу трейдеров с помощью визуальной обратной связи, используя графические объекты, такие как треугольники, линии тренда и подписи, для наглядного отображения структуры паттерна X-A-B-C-D.