English Deutsch 日本語
preview
Создание самооптимизирующихся советников в MQL5 (Часть 9): Двойное пересечение скользящих средних

Создание самооптимизирующихся советников в MQL5 (Часть 9): Двойное пересечение скользящих средних

MetaTrader 5Примеры |
35 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

В нашей серии статей мы рассмотрели различные подходы к уменьшению запаздывания в классической стратегии пересечения скользящих средних.

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

В данном обсуждении мы рассматриваем ещё один уникальный подход, который ранее не изучали. Как и в большинстве задач в жизни и математике, существует несколько способов решения одной и той же проблемы, и каждый из них имеет свои преимущества и недостатки. Сравнивая эти альтернативы, мы стремимся понять, насколько сильно мы можем контролировать запаздывание системы.

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

Проблема исходной формы заключается в том, что подтверждение сигналов на вход часто поступает слишком поздно — уже после начала движения — что приводит к запоздалым входам или упущенным возможностям. 

Рисунок 1: Визуализация нашей стратегии пересечения скользящих средних на дневном таймфрейме.

Предлагаемый здесь подход не является совершенно новым. На самом деле дискреционные трейдеры уже давно используют похожую логику. Основная идея заключается в том, чтобы сначала наблюдать за пересечениями на более высоком таймфрейме (например, дневном графике, как показано на Рисунке 1). Однако мы не входим в рынок сразу после этого сигнала. Вместо этого, заметив пересечение на старшем таймфрейме, мы переходим на более низкий — например, M30, как показано на рисунке 2 — и ищем аналогичные сигналы пересечения, соответствующие наблюдаемому на старшем таймфрейме.

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

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

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

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

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

Рисунок 2: Визуализация нашей стратегии пересечения скользящих средних на младшем таймфрейме (M30). 

Первым шагом мы определим несколько системных констант, которые должны оставаться неизменными на протяжении этапа разработки нашего приложения. Для простоты мы зафиксируем таймфреймы: дневной график будет выступать в роли старшего таймфрейма, M15 — младшего, а H4 — таймфрейма, используемого для расчёта уровня стоп-лосса.

В дальнейшем можно рассмотреть возможность преобразования этих системных констант в настраиваемые параметры, которые генетический оптимизатор сможет использовать для поиска наилучших точек входа. Однако на начальном этапе мы оставим эти значения фиксированными, чтобы упростить разработку и быстрее приступить к тестированию стратегии.
//+------------------------------------------------------------------+
//|                                             Double Crossover.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
//--- System time frames
#define TF_1            PERIOD_D1
#define TF_2            PERIOD_M15
#define TF_3            PERIOD_H4
#define LOT_MULTILPLE   1
    

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

//+------------------------------------------------------------------+
//| Custom enumerations                                              |
//+------------------------------------------------------------------+
//--- What trading style should we follow when opening our positions, trend following or mean reverting?
enum STRATEGY_MODES  {
     TREND = 0,             //Trend Following Mode
     MEAN_REVERTING = 1     //Mean Reverting Mode
};

//--- Which time frame should we consult, when determining if we should close our position?
enum CLOSING_TIME_FRAME {
     HIGHER_TIME_CLOSE = 0, //Close on higher time frames
     LOWER_TIME_CLOSE = 1   //Close on lower time frames
};

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

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "Technical Indicators"
input int ma_1_period = 10;   //Higher Time Frame Period
input int ma_1_gap    = 20;   //Higher Time Frame Period Gap

input int ma_2_period = 10;   //Lower Time Frame Period
input int ma_2_gap    = 20;   //Lower Time Frame Period Gap

input group "Strategy Settings"
input STRATEGY_MODES     strategy_mode = 0;  //Strategy Operation Mode
input CLOSING_TIME_FRAME closing_tf    = 0;  //Strategy Closing Timeframe

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

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int      ma_c_1_handle,ma_c_2_handle,ma_c_3_handle,ma_c_4_handle;
double   ma_c_1[],ma_c_2[],ma_c_3[],ma_c_4[];
double   volume_min;
double   bid,ask;
int      state;

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

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

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

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   volume_min     = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   ma_c_2_handle  = iMA(Symbol(),TF_1,ma_1_period,0,MODE_SMA,PRICE_CLOSE);
   ma_c_1_handle  = iMA(Symbol(),TF_1,(ma_1_period + ma_1_gap),0,MODE_SMA,PRICE_CLOSE);
   ma_c_4_handle  = iMA(Symbol(),TF_2,ma_2_period,0,MODE_SMA,PRICE_CLOSE);
   ma_c_3_handle  = iMA(Symbol(),TF_2,(ma_2_period + ma_2_gap),0,MODE_SMA,PRICE_CLOSE);
   state          = -1;
//---
   return(INIT_SUCCEEDED);
  }

Когда приложение больше не используется, мы освобождаем ресурсы индикаторов для экономии памяти.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(ma_c_1_handle);
   IndicatorRelease(ma_c_2_handle);
   IndicatorRelease(ma_c_3_handle);
   IndicatorRelease(ma_c_4_handle);
  }

При поступлении новых ценовых данных мы проверяем, сформировалась ли новая свеча на младшем таймфрейме (в данном случае M15). Если новая свеча обнаружена, мы обновляем внутреннее состояние системы.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   datetime current_time = iTime(Symbol(),TF_2,0);
   static datetime time_stamp;

   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      update();
     }
  }
//+------------------------------------------------------------------+

Одним из ключевых компонентов является функция, отвечающая за поиск торговых сигналов. Эта функция принимает параметр padding, который определяет размер стоп-лосса для позиций. Значение padding рассчитывается родительской функцией на основе исторических диапазонов рынка. В качестве меры предосторожности функция сначала проверяет, что общее количество открытых позиций равно нулю, чтобы избежать чрезмерной торговли.

Если на старшем таймфрейме происходит бычье пересечение и стратегия работает в режиме следования за трендом, функция ищет соответствующее бычье пересечение на младшем таймфрейме. Если же используется режим возврата к среднему, система ищет противоположный сигнал (то есть медвежье пересечение) и открывает позицию против него. Аналогичная логика применяется и для медвежьих входов: при медвежьем пересечении на старшем таймфрейме мы ищем соответствующий сигнал на младшем таймфрейме в зависимости от выбранного режима.

//+------------------------------------------------------------------+
//| Find a trading signal                                            |
//+------------------------------------------------------------------+
void find_setup(double padding)
  {
   if(PositionsTotal() == 0)
     {
      //--- Reset the system state
      state = -1;

      //--- Bullish on the higher time frame
      if(ma_c_1[0] > ma_c_2[0])
        {
         //--- Trend following mode
         if((ma_c_3[0] > ma_c_4[0]) && (strategy_mode == 0))
           {
            Trade.Buy(volume_min,Symbol(),ask,(bid - padding),0,"");
            state = 1;
           }
         //--- Mean reverting mode
         if((ma_c_3[0] < ma_c_4[0]) && (strategy_mode == 1))
           {
            Trade.Buy(volume_min,Symbol(),ask,(bid - padding),0,"");
            state = 1;
           }
        }

      //--- Bearish on the higher time frame
      if(ma_c_1[0] < ma_c_2[0])
        {
         //--- Trend following mode
         if((ma_c_3[0] < ma_c_4[0]) && (strategy_mode == 0))
           {
            Trade.Sell(volume_min,Symbol(),bid,(ask + padding),0,"");
            state = 0;
           }
         //--- Mean reverting mode
         if((ma_c_3[0] > ma_c_4[0]) && (strategy_mode == 1))
           {
            Trade.Sell(volume_min,Symbol(),bid,(ask + padding),0,"");
            state = 0;
           }
        }
     }
  }

После открытия позиции вызывается отдельная функция управления позициями, которая также работает в двух режимах — в зависимости от того, хотим ли мы закрывать сделки на основе сигналов младшего или старшего таймфрейма. Состояние системы обновляется при открытии позиций и определяет направление торговли. Например, если состояние равно нулю, это означает, что была открыта короткая позиция. Если затем первая скользящая средняя оказывается выше второй (что указывает на бычий импульс на дневном графике), и если управление выходом осуществляется по старшему таймфрейму, позиция будет закрыта. В противном случае, при управлении по младшему таймфрейму, мы ожидаем соответствующего пересечения уже на нём. Сразу определить, какой вариант выхода эффективнее, невозможно, поэтому необходимо протестировать оба подхода.

//+------------------------------------------------------------------+
//| Manage our open positions                                        |
//+------------------------------------------------------------------+
void manage_setup(void)
  {
   if(closing_tf == 0)
     {
      if((state ==0) && (ma_c_1[0] > ma_c_2[0]))
         Trade.PositionClose(Symbol());
      if((state ==1) && (ma_c_1[0] < ma_c_2[0]))
         Trade.PositionClose(Symbol());
     }

   else
      if(closing_tf == 1)
        {
         if((state ==0) && (ma_c_3[0] > ma_c_4[0]))
            Trade.PositionClose(Symbol());
         if((state ==1) && (ma_c_3[0] < ma_c_4[0]))
            Trade.PositionClose(Symbol());
        }
  }

Наконец, нам нужна функция для обновления ключевых системных переменных, таких как текущие цены Bid и Ask Также мы будем отслеживать последние 10 максимумов и минимумов на третьем таймфрейме — таймфрейме риска. В данном примере это H4, который удобно располагается между M15 и дневным графиком. Это делает его подходящим для оценки рыночного риска и установки стоп-лоссов, которые не будут ни слишком близкими, ни слишком удалёнными от точки входа.

//+------------------------------------------------------------------+
//| Update our technical indicators and positions                    |
//+------------------------------------------------------------------+
void update(void)
  {
   
   //Update technical indicators and market readings
   CopyBuffer(ma_c_2_handle,0,0,1,ma_c_2);
   CopyBuffer(ma_c_1_handle,0,0,1,ma_c_1);
   CopyBuffer(ma_c_4_handle,0,0,1,ma_c_4);
   CopyBuffer(ma_c_3_handle,0,0,1,ma_c_3);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   vector high = vector::Zeros(10);
   vector low = vector::Zeros(10);
   low.CopyRates(Symbol(),TF_3,COPY_RATES_LOW,0,10);
   high.CopyRates(Symbol(),TF_3,COPY_RATES_HIGH,0,10);
   vector var = high - low;
   double padding = var.Mean();

   //Find an open position
   if(PositionsTotal() == 0)
      find_setup(padding);

   //Manage our open positions
   else
      if(PositionsTotal() > 0)
         manage_setup();
  }
//+------------------------------------------------------------------+

Чтобы приступить к бэктестированию, сначала необходимо выбрать созданный нами экспертный советник — double crossover EX5. Затем выбираем инструмент EURUSD, таймфрейм M1 и период тестирования с января 2020 года по текущий момент — всего пять лет. Форвард-тестирование будем проводить на половине доступных данных, чтобы сохранить реалистичность результатов. 

Рисунок 3: Выбор нашего торгового приложения и периода обучения.

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

Рисунок 4: Выбор условий моделирования рынка.

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

Рисунок 5: Выбор параметров стратегии для оптимизации с помощью генетического алгоритма.

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

Рисунок 6: Результаты бэктестирования из нашего первоначального теста.

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

Рисунок 7: Результаты форвард-теста из первоначального эксперимента показывают, что большинство стратегий не являются стабильными.

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

Рисунок 8: Лишь небольшая часть стратегий, найденных генетическим оптимизатором, оказалась прибыльной в обоих тестах — это плохой признак. 


Дальнейшие улучшения

Одним из веских аргументов в пользу улучшения стратегии является предоставление генетическому оптимизатору контроля над таймфреймом, который используется для расчета стоп-лосса и параметров риска. Кроме того, мы позволим оптимизатору самому решать, какое количество исторических баров следует использовать в расчете стоп-лосса. В нашей первой попытке мы предполагали, что 10 баров на таймфрейме H4 будет достаточно. Однако теперь мы позволим оптимизатору корректировать эту настройку и проверим, улучшит ли это изменение общие показатели производительности.
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "Money Management Settings"
input ENUM_TIMEFRAMES TF_3 =    PERIOD_H4; //Risk Time Frame
input int HISTORICAL_BARS  =           10; //Historical bars for risk calculation

Нам также необходимо внести изменения в код. В предыдущей версии метод update отвечал за расчет отступа для каждой позиции. В этой новой версии за расчет отступа будет отвечать отдельная функция, так как теперь мы хотим, чтобы стоп-лосс был "скользящим" и следовал за прибыльными позициями.

//+------------------------------------------------------------------+
//| Get the stop loss size to use                                    |
//+------------------------------------------------------------------+
double get_padding(void)
  {
   vector high = vector::Zeros(10);
   vector low = vector::Zeros(10);
   low.CopyRates(Symbol(),TF_3,COPY_RATES_LOW,0,HISTORICAL_BARS);
   high.CopyRates(Symbol(),TF_3,COPY_RATES_HIGH,0,HISTORICAL_BARS);
   vector var = high - low;
   double padding = var.Mean();
   return(padding);
  }

Метод update изменится соответствующим образом. Отступ (padding) теперь будет рассчитываться с помощью метода get_padding. Та часть метода update, которая изначально идентифицировала позиции, теперь будет вызывать функцию, ответственную за поиск торгового сетапа, и другой метод для управления открытыми позициями. 

//+------------------------------------------------------------------+
//| Update our technical indicators and positions                    |
//+------------------------------------------------------------------+
void update(void)
  {

//Update technical indicators and market readings
   CopyBuffer(ma_c_2_handle,0,0,1,ma_c_2);
   CopyBuffer(ma_c_1_handle,0,0,1,ma_c_1);
   CopyBuffer(ma_c_4_handle,0,0,1,ma_c_4);
   CopyBuffer(ma_c_3_handle,0,0,1,ma_c_3);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double padding = get_padding();

//Find an open position
   if(PositionsTotal() == 0)
      find_setup(padding);

//Manage our open positions
   else
      if(PositionsTotal() > 0)
         manage_setup();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Manage our open positions                                        |
//+------------------------------------------------------------------+
void manage_setup(void)
  {
  //Does the position exist?
  if(PositionSelect(Symbol()))
   {
      //Get the current stop loss
      double current_sl = PositionGetDouble(POSITION_SL);
      double padding = get_padding();
      double new_sl;
      
   
      //Sell position
      if((state == 0))
      {
         new_sl = (ask + padding);
         
         if(new_sl < current_sl) Trade.PositionModify(Symbol(),new_sl,0);
      }

      //Buy position
      if((state == 1))
      {
         new_sl = (bid - padding);
         
         if(new_sl > current_sl) Trade.PositionModify(Symbol(),new_sl,0);
      }
  
  
   if(closing_tf == 0)
     {
      if((state ==0) && (ma_c_1[0] > ma_c_2[0]))
         Trade.PositionClose(Symbol());
      if((state ==1) && (ma_c_1[0] < ma_c_2[0]))
         Trade.PositionClose(Symbol());
     }

   else
      if(closing_tf == 1)
        {
         if((state ==0) && (ma_c_3[0] > ma_c_4[0]))
            Trade.PositionClose(Symbol());
         if((state ==1) && (ma_c_3[0] < ma_c_4[0]))
            Trade.PositionClose(Symbol());
        }
   }
  }

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

Рисунок 9: Попытка улучшить наши первоначальные результаты. Несмотря на то что мы позволили генетическому оптимизатору контролировать настройки рисков, наши новые результаты всё еще не были прибыльными в обоих тестах.

К сожалению, мы снова столкнулись с той же проблемой. Когда мы оптимизировали настройки параметров риска, наше приложение было прибыльным только в форвард-тесте и не смогло показать прибыль в бэктесте. 

Рисунок 10: Наши новые результаты по-прежнему не были прибыльными в обоих тестах. 



Заключение

Это упражнение многому нас научило. На примере стратегии пересечения двух скользящих средних мы увидели, что действительно можем контролировать степень запаздывания, хотя последствия таких изменений не всегда очевидны сразу. Возможно, нам стоит рассмотреть вариант повторного запуска оптимизации со всеми доступными параметрами одновременно. Вероятно, настройка одного параметра при фиксации остальных — не самый оптимальный подход. Поиск по всем параметрам сразу может дать более стабильные результаты.

В нашей последующей дискуссии, после проведения полного цикла оптимизации, мы построим статистические модели на основе наиболее прибыльных конфигураций. Это может помочь нам еще больше сократить запаздывание. Однако на данный момент мы заложили отличный фундамент. Помните, что оптимизация не дает никаких гарантий, а ИИ не заменит трудолюбивых разработчиков. Мы должны повторять процедуру оптимизации до тех пор, пока генетический оптимизатор не предложит нам группы стратегий, прибыльных в обоих тестах (бэктест и форвард), иначе оптимизация, скорее всего, будет преждевременной.

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

Прикрепленные файлы |
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Преодоление проблем доступности в торговых инструментах на MQL5 (Часть I): Как добавить контекстные голосовые оповещения в индикаторы MQL5 Преодоление проблем доступности в торговых инструментах на MQL5 (Часть I): Как добавить контекстные голосовые оповещения в индикаторы MQL5
В этой статье рассматривается ориентированное на доступность усовершенствование, выходящее за рамки оповещений терминала по умолчанию, путем использования управления ресурсами MQL5 для предоставления контекстной голосовой обратной связи. Вместо общих звуковых сигналов индикатор сообщает о том, что произошло и почему, позволяя трейдерам понимать рыночные события, не полагаясь исключительно на визуальное наблюдение. Такой подход особенно ценен для трейдеров с ослабленным зрением, но он также полезен занятым или многозадачным пользователям, предпочитающим взаимодействие со свободными руками.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Создание самооптимизирующихся советников в MQL5 (Часть 8): Анализ нескольких стратегий (3) — Политика взвешенного голосования Создание самооптимизирующихся советников в MQL5 (Часть 8): Анализ нескольких стратегий (3) — Политика взвешенного голосования
В этой статье исследуется, как определение оптимального количества стратегий в ансамбле может стать сложной задачей, которую проще решить с помощью генетического оптимизатора MetaTrader 5. Сеть MQL5 Cloud также используется как ключевой ресурс для ускорения бэктестинга и оптимизации. В целом, наше обсуждение здесь подготавливает почву для разработки статистических моделей, позволяющих оценивать и улучшать торговые стратегии на основе результатов работы нашего первоначального ансамбля.