English
preview
Реализация торговой стратегии Rapid-Fire с использованием индикаторов Parabolic SAR и простой скользящей средней (SMA) на MQL5

Реализация торговой стратегии Rapid-Fire с использованием индикаторов Parabolic SAR и простой скользящей средней (SMA) на MQL5

MetaTrader 5Торговые системы | 24 марта 2025, 08:28
282 6
Allan Munene Mutiiria
Allan Munene Mutiiria

Введение

В настоящей статье мы расскажем вам о важных процессах тестирования и оптимизации нашего торгового алгоритма. Мы будем использовать Тестер стратегий MQL5 для тестирования нашего советника на исторических данных. Проще говоря, мы проверим, насколько хорошо наш алгоритм работал бы в прошлом по сравнению с реальными рыночными условиями. Обсуждая эффективность нашего советника, мы также расскажем о том, как интерпретировать различные показатели эффективности (например, коэффициент прибыли, просадку, коэффициент выигрыша и т.д.) и что они говорят нам о надежности и прибыльности нашего алгоритма. К концу настоящей статьи у нас будет гораздо более четкое представление о том, как реализовать торговую стратегию rapid-fire и что нужно для обеспечения ее успеха. Следующие темы будут актуальны на протяжении всей статьи:

  1. Введение в Торговую стратегию Rapid-Fire
  2. Понимание индикаторов: Parabolic SAR и SMA
  3. Реализация средствами MQL5M
  4. Тестирование и оптимизация
  5. Заключение


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

Ключом к эффективности этой стратегии является использование технических индикаторов, таких как Parabolic SAR и Простая скользящая средняя, которые помогают выявлять развороты трендов и сглаживать ценовые данные. Эти индикаторы, интегрированные в высокочастотный торговый алгоритм, позволяют стратегии быстро адаптироваться к изменениям рынка, сохраняя точность генерации сигналов и исполнения сделок. При надлежащей реализации торговля со стратегией rapid-fire может принести быструю прибыль, хотя и требует тщательного управления затратами и рисками.


Понимание индикаторов: Parabolic SAR и SMA

Эффективная стратегия rapid-fire требует понимания важных технических индикаторов, определяющих торговые решения. Два ориентира, которые полезны в этом отношении, - это Parabolic SAR (Стоп-переворот) и Простая скользящая средняя (SMA). SMA - один из старейших и наиболее часто используемых индикаторов, следующих за трендом. По сравнению с SMA, Parabolic SAR является относительно новым, но, безусловно, не менее известным инструментом; оба этих индикатора очень удобны для определения рыночных условий и сигнализируют о потенциальных торговых возможностях.

Разработанный для отслеживания трендов, индикатор Parabolic SAR предназначен для поиска и идентификации потенциальных разворотов ценового направления. Он работает путем построения ряда точек либо выше, либо ниже цены относительно того места, где цена находится в отношении SAR (Стоп-переворот). Если включить индикатор Parabolic SAR в нашу торговую стратегию, мы сможем интерпретировать его относительно последовательности точек, чтобы принять решение о покупке, продаже или сокращении позиции на рынке. Если цена находится выше точек SAR, мы находимся в бычьем тренде; если цена находится ниже точек SAR, мы находимся в медвежьем тренде. Необходимо использовать параметры индикаторов по умолчанию. Ниже показано, как это делается:

PARABOLIC SAR SETTINGS

Настройка индикатора Parabolic SAR:

PARABOLIC SAR SETUP

Еще одной важной частью нашей торговой стратегии является Простая скользящая средняя (SMA). SMA собирает данные о ценах за определенный период и "сглаживает" их. Это дает нам гораздо более наглядное представление об общей тенденции. Мы также могли бы использовать SMA, чтобы получить еще более простое представление о тренде. Если SMA направлена вверх, то мы могли бы сказать в очень простых терминах, что рынок находится в восходящем тренде. Если SMA направлена вниз, то мы могли бы сказать, что рынок находится в нисходящем тренде. SMA - это трендовый фильтр. Он подсказывает нам, следует ли искать длинные позиции (восходящий тренд), короткие позиции (нисходящий тренд) или вообще не открывать сделки (когда цена колеблется вокруг плоской линии SMA). Следует использовать простую скользящую среднюю за период 60. Ниже показано, как это делается:

SMA SETTINGS

Настройка индикатора Простая скользящая средняя:

SMA SETUP

При совместном использовании Parabolic SAR и Простая скользящая средняя дают полную картину рыночных условий. SAR можно использовать для получения немедленных сигналов о том, что может произойти с текущим трендом. Индикатор SAR может, и довольно часто так и происходит, сигнализировать о развороте тренда еще до того, как он произойдет. Индикатор SMA анализирует больше данных за более длительный период и, следовательно, с большей уверенностью подтверждает направление тренда. В сочетании они получаются такими, как показано ниже:

PARABOLIC SAR & SMA COMBINED SETUP

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


Реализация средствами MQL5

После изучения всех теорий о торговой стратегии Rapid-Fire давайте автоматизируем теорию и создадим советника (EA) на MetaQuotes Language 5 (MQL5) для MetaTrader 5 (MT5).

Для создания советника (EA), в вашем терминале MetaTrader 5 перейдите на вкладку «Инструменты» и выберите языковой редактор MetaQuotes или просто нажмите клавишу F4 на клавиатуре. Кроме того, вы можете щелкнуть иконку IDE (интегрированная среда разработки) на панели инструментов. Откроется среда разработки на MQL5, которая позволяет писать торговых роботов, технические индикаторы, скрипты и библиотеки функций.

OPEN METAEDITOR

После открытия MetaEditor, на панели инструментов выберите "Файл" - "Новый файл" или нажмите CTRL + N, чтобы создать новый документ. Также вы можете нажать на иконку "Создать" в панели инструментов. Откроется окно Мастера MQL.

CREATE NEW EA

В открывшемся Мастере выберите Советник (шаблон) и нажмите Далее.

Мастер MQL

В общих свойствах укажите имя файла вашего советника. Чтобы указать или создать папку, если она не существует, используйте обратную косую черту перед именем советника. Например, по умолчанию указана папка «Experts\». Это значит, что наш советник будет создан в папке Experts. Остальные разделы довольно просты, но вы можете перейти по ссылке в нижней части Мастера, чтобы узнать детали.

NEW EA NAME

После указания имени файла советника нажмите "Далее" > "Далее" > "Готово". Теперь мы готовы к воплощению стратегии в коде.

Во-первых, начнем с определения некоторых метаданных о советнике (EA). Сюда относятся: название советника, информация об авторских правах и ссылка на веб-сайт MetaQuotes. Мы также указываем версию советника, которая установлена на "1.00".

//+------------------------------------------------------------------+
//|                                               #1. RAPID FIRE.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+

#property copyright "Copyright 2024, MetaQuotes Ltd."   // Set the copyright property for the Expert Advisor
#property link      "https://www.mql5.com"              // Set the link property for the Expert Advisor
#property version   "1.00"                              // Set the version of the Expert Advisor

При загрузке программы отображается информация, которая соответствует приведенной ниже.

EA METADATA

Во-первых, мы включаем торговый экземпляр, используя  #include  в начале исходного кода. Он даст нам доступ к классу CTrade, который мы будем использовать для создания торгового объекта. Это критически важно, т.к. это нужно для открытия сделок.

#include <Trade/Trade.mqh>   // Include the MQL5 standard library for trading operations
CTrade obj_Trade;            // Create an instance of the CTrade class to handle trade operations

Препроцессор заменит строку  #include <Trade/Trade.mqh> содержимым файла Trade.mqh. Угловые скобки указывают, что файл Trade.mqh будет взят из стандартного каталога (обычно это каталог_установки_терминала\MQL5\Include). Текущий каталог не включен в поиск. Строку можно разместить в любом месте программы, но обычно все включения размещаются в начале исходного кода для лучшей структуры кода и удобства ссылок. Благодаря разработчикам MQL5 объявление объекта obj_Trade класса  CTrade  предоставит нам легкий доступ к методам, содержащимся в этом классе.

Класс CTRADE

Нам понадобится: создать хэндлы индикаторов, чтобы мы могли включить необходимые индикаторы в стратегию.

int handleSMA = INVALID_HANDLE, handleSAR = INVALID_HANDLE;  // Initialize handles for SMA and SAR indicators

Здесь мы сначала объявляем и инициализируем две целевые переменные - "handleSMA" и "handleSAR". Они будут служить в качестве хэндлов для двух из технических индикаторов, которые мы планируем использовать в нашем советнике. В MQL5 хэндл - это уникальный идентификатор, присваиваемый индикатору, когда мы создаем его с помощью определенных функций MQL5. Мы будем использовать идентификаторы для обозначения нераскрашенных индикаторов в любом месте программы. Мы также присвоили оба идентификатора константе INVALID_HANDLE, которая гарантирует, что программа не будет ссылаться на недопустимый хэндл, если мы поленимся и забудем проверить логику выполнения программы после ее компиляции. Оба индикатора будут выполнять свою работу без каких-либо проблем, если мы будем обращаться с ними корректно и не будем ссылаться на недопустимый хэндл или делать какие-либо глупости, не проверив предварительно хэндлы.

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

double sma_data[], sar_data[];  // Arrays to store SMA and SAR indicator data

Здесь мы определяем два динамических массива, "sma_data" и "sar_data", которым мы будем присваивать точки данных, генерируемые индикаторами SMA (Простая скользящая средняя) и SAR (Parabolic SAR). Мы присваиваем этим массивам самые последние значения, рассчитанные каждым индикатором, позволяя им свободно ссылаться на данные и анализировать их при генерации торговых сигналов. Торговые сигналы могут генерироваться при пересечении сигнальной линии. Чтобы сгенерировать торговый сигнал с текущим трендом или разворотом текущего тренда, мы можем использовать прошлые значения, хранящиеся в этих массивах. Используя эти прошлые значения, мы сможем более точно интерпретировать наши индикаторы и динамику цены торгуемого актива.

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

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   // OnInit is called when the EA is initialized on the chart
//...
}

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

   handleSMA = iMA(_Symbol,PERIOD_M1,60,0,MODE_SMA,PRICE_CLOSE);  // Create an SMA (Simple Moving Average) indicator handle for the M1 timeframe

Здесь мы используем функцию iMA , чтобы создать хэндл для индикатора Простая скользящая средняя (SMA). Данный хэндл предоставит нам доступ к значениям SMA для любого торгового символа и таймфрейма, которые мы укажем. Здесь мы настраиваем его для текущего торгового символа (_Symbol) на 1-минутном графике (PERIOD_M1) на протяжении 60 баров. Параметр "0" указывает на отсутствие сдвига в расчете SMA, в то время как требование к расчету режима задано с помощью константы MODE_SMA; следовательно, мы будем вычислять простую скользящую среднюю. Наконец, мы указываем тип цены PRICE_CLOSE, что означает, что индикатор будет основан на ценах закрытия каждого бара. Мы сохраняем полученный хэндл в переменной "handleSMA". Далее мы создаем хэндл для индикатора Parabolic SAR.

   handleSAR = iSAR(_Symbol,PERIOD_M1,0.02,0.2);                  // Create a SAR (Parabolic SAR) indicator handle for the M1 timeframe

Аналогично, мы создаем хэндл для индикатора Parabolic SAR (SAR), используя функцию iSAR . Функция iSAR возвращает значения Parabolic SAR для определенного торгового символа и таймфрейма. В нашем случае мы используем его для текущего торгового символа (_Symbol) на 1-минутном графике (PERIOD_M1). Параметры 0.02 и 0.2 определяют шаг и максимальное значение SAR, что означает, что они определяют, насколько чувствительным будет индикатор к изменениям цены. Полученный в результате хэндл позволит нам получить доступ к данным SAR, которые будут играть центральную роль в нашей первой торговой стратегии, основанной на рыночных тенденциях и разворотах цен.

По умолчанию хэндл индикатора начинает движение с 10 и перемещается с интервалом в 1. Таким образом, если мы выведем значения индикатора, у нас должно получиться 10 и 11, так как у нас есть два хэндла. Чтобы наглядно представить их значения, давайте запишем их.

   Print("SMA Handle = ",handleSMA);
   Print("SAR Handle = ",handleSAR);

Значения, которые мы получаем, приведены ниже:

HANDLE VALUES

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

   // Check if the handles for either the SMA or SAR are invalid (indicating failure)
   if (handleSMA == INVALID_HANDLE || handleSAR == INVALID_HANDLE){
      Print("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW");  // Print error message in case of failure
      return (INIT_FAILED);  // Return failure code, stopping the EA from running
   }

В этом разделе мы настраиваем механизм для отслеживания критических ошибок, возникающих при создании хэндлов индикаторов SMA и SAR. Если эти хэндлы не будут созданы успешно, советник не сможет корректно работать. Таким образом, мы проверяем, осталось ли значение индикаторов "handleSMA" или "handleSAR" равным INVALID_HANDLE, что указывало бы на неудачную инициализацию любого из этих двух индикаторов. Если обнаружится, что любой из этих хэндлов неверен, выводим сообщение об ошибке ("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW.") и возвращаем значение INIT_FAILED из функции инициализации. Эти действия в совокупности представляют собой изящную процедуру обработки ошибок и служат для того, чтобы советник мог принимать небезопасные торговые решения. Затем надо настроить массивы хранения в виде временных рядов. Это достигается с помощью следующего фрагмента кода:

   // Configure the SMA and SAR data arrays to work as series, with the newest data at index 0
   ArraySetAsSeries(sma_data,true);
   ArraySetAsSeries(sar_data,true);

Мы настроили наши массивы хранения так, чтобы они работали как данные временных рядов, при этом самая свежая информация располагается по индексу 0. Мы выполняем это, используя функцию ArraySetAsSeries и два массива ("sma_data" и "sar_data") в качестве первого аргумента, а true - в качестве второго аргумента, чтобы обеспечить, что массивы обрабатываются как серии. Это необходимый шаг для доступа к последним значениям индикатора в обычные моменты принятия решений в торговой логике, когда данные, представленные индикатором, были бы переданы в процедуру принятия торговых решений. Следующая часть логики обращается как к текущим, так и к прошлым значениям для двух индикаторов. Наконец, если мы достигаем этой точки, это означает, что все инициализировано корректно, и, таким образом, мы возвращаем успешный экземпляр инициализации.

   return(INIT_SUCCEEDED);  // Return success code to indicate successful initialization

До этого момента все в разделе инициализации работало корректно. Полный исходный код, отвечающий за инициализацию программы, выглядит следующим образом:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   // OnInit is called when the EA is initialized on the chart

   handleSMA = iMA(_Symbol,PERIOD_M1,60,0,MODE_SMA,PRICE_CLOSE);  // Create an SMA (Simple Moving Average) indicator handle for the M1 timeframe
   handleSAR = iSAR(_Symbol,PERIOD_M1,0.02,0.2);                  // Create a SAR (Parabolic SAR) indicator handle for the M1 timeframe
   
   Print("SMA Handle = ",handleSMA);
   Print("SAR Handle = ",handleSAR);
   
   // Check if the handles for either the SMA or SAR are invalid (indicating failure)
   if (handleSMA == INVALID_HANDLE || handleSAR == INVALID_HANDLE){
      Print("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW");  // Print error message in case of failure
      return (INIT_FAILED);  // Return failure code, stopping the EA from running
   }

   // Configure the SMA and SAR data arrays to work as series, with the newest data at index 0
   ArraySetAsSeries(sma_data,true);
   ArraySetAsSeries(sar_data,true);

   return(INIT_SUCCEEDED);  // Return success code to indicate successful initialization
}

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

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   // OnDeinit is called when the EA is removed from the chart or terminated
//...
}

Функция OnDeinit запускается при удалении советника (EA) с графика или при выключении терминала. Мы должны использовать этот обработчик событий для обеспечения корректного обслуживания и управления ресурсами. Когда советник завершает работу, мы должны освободить все хэндлы для индикаторов, которые мы создали на этапе инициализации. Если бы мы этого не сделали, то могли бы игнорировать те области памяти, которые использовали что было бы неэффективно. Мы, конечно, не хотели рисковать, игнорируя какие-либо ресурсы, которые нам не нужны. Вот почему функция OnDeinit важна и почему этапы очистки имеют решающее значение в любой среде программирования.

   // Release the indicator handles to free up resources
   IndicatorRelease(handleSMA);
   IndicatorRelease(handleSAR);

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

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   // OnDeinit is called when the EA is removed from the chart or terminated

   // Release the indicator handles to free up resources
   IndicatorRelease(handleSMA);
   IndicatorRelease(handleSAR);
}

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

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   // OnTick is called whenever there is a new market tick (price update)

//...

}

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

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

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);  // Get and normalize the Ask price
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);  // Get and normalize the Bid price

Здесь мы получаем самые актуальные цены спроса и предложения на торгуемый инструмент. Для получения этих цен мы пользуемся функцией SymbolInfoDouble. Для получения цены предложения указываем SYMBOL_ASK, а для получения цены предложения указываем SYMBOL_BID. После получения цен используем функцию NormalizeDouble, чтобы округлить цены до количества знаков после запятой, определенного параметром _Digits. Данный шаг имеет решающее значение, поскольку он гарантирует, что наши торговые операции будут осуществляться с использованием цен, которые как стандартизированы, так и точны. Если бы мы не округлили цены, неточности с плавающей запятой могли бы привести к ошибочным результатам при расчете цены операции. Затем мы копируем значения индикатора для использования в анализе и торговых операциях.

   // Retrieve the last 3 values of the SMA indicator into the sma_data array
   if (CopyBuffer(handleSMA,0,0,3,sma_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SMA FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SMA data
      return;  // Exit the function if not enough data is available
   }

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

Подобным образом мы используем функцию CopyBuffer для получения данных индикатора SAR. Используем следующий фрагмент кода.

   // Retrieve the last 3 values of the SAR indicator into the sar_data array
   if (CopyBuffer(handleSAR,0,0,3,sar_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SAR FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SAR data
      return;  // Exit the function if not enough data is available
   }

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

BAR AND BUFFER INDICES

Напечатаем их данные в журнале, чтобы можно было проверить, правильно ли мы получаем их данные.

   ArrayPrint(sma_data,_Digits," , ");
   ArrayPrint(sar_data,_Digits," , ");

Мы используем функцию ArrayPrint для отображения содержимого массивов "sma_data" и "sar_data". ArrayPrint является базовой функцией, которая выводит содержимое массива на терминал. Это хорошо для подготовки простых визуализаций данных, с которыми ведется работа. Для использования этой функции нужно передать ей три фрагмента информации:

  • Массив, который вы хотите напечатать ("sms_data" или "sar_data").
  • Точность, которую вы хотите получить для отображаемых значений (_Digits), определяет, сколько знаков после запятой будет отображено.
  • Между напечатанными значениями (", ") в выходных данных должен использоваться разделитель.

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

DATA CONFIRMATION

На рисунке мы видим, что данные правильно извлечены и сохранены. Данные, выделенные красным цветом, представляют собой данные скользящей средней, в то время как данные, выделенные синим цветом, представляют данные SAR. Судя по перекрестию, мы указываем на второй бар и индекс 1. Теперь ясно, что мы получаем данные 3, извлекая значения данных в соответствии с запросом. Мы можем перейти к получению необходимых значений бара.

   // Get the low prices for the current and previous bars on the M1 timeframe
   double low0 = iLow(_Symbol,PERIOD_M1,0);  // Low of the current bar
   double low1 = iLow(_Symbol,PERIOD_M1,1);  // Low of the previous bar

Здесь мы извлекаем низкие цены из текущего и предыдущего баров на 1-минутном таймфрейме, используя функцию iLow. Первый параметр, _Symbol, автоматически настраивается в соответствии с торговым инструментом, с которым работает советник (например, EUR/USD). Второй параметр, PERIOD_M1, сообщает системе, что мы работаем на 1-минутном графике; это важно, поскольку стратегия rapid-fire основана на совершении краткосрочных сделок, основанных на быстром движении цены. Третий параметр, 0, в "iLow(_Symbol, PERIOD_M1, 0)" указывает на то, что мы хотим получить низкую цену текущего бара, который находится в процессе формирования. Аналогично, в "iLow(_Symbol, PERIOD_M1, 1)" 1 указывает на то, что мы хотим получить низкую цену предыдущего завершенного бара. Мы сохраняем полученные значения в переменных типа данных "double»: "low0" и "low1" соответственно.

Аналогично, чтобы получить высокие цены для текущего и предыдущего баров, используется аналогичная логика.

   // Get the high prices for the current and previous bars on the M1 timeframe
   double high0 = iHigh(_Symbol,PERIOD_M1,0);  // High of the current bar
   double high1 = iHigh(_Symbol,PERIOD_M1,1);  // High of the previous bar

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

   Print("High bar 0 = ",high0,", High bar 1 = ",high1);
   Print("Low bar 0 = ",low0,", Low bar 1 = ",low1);

Подтверждение данных производится следующим образом:

HIGH & LOW DATA CONFIRMATION

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

   // Define a static variable to track the last time a signal was generated
   static datetime signalTime = 0;
   datetime currTime0 = iTime(_Symbol,PERIOD_M1,0);  // Get the time of the current bar

В этом разделе мы создаем переменную "signalTime", чтобы отслеживать, когда был сгенерирован последний торговый сигнал. Мы делаем эту переменную статической, чтобы обеспечить сохранение ей своего значения между вызовами функции OnTick. Если бы "signalTime" не имела статической продолжительности хранения, она выделялась бы каждый раз при вызове OnTick, а её значение исчезало бы после завершения выполнения OnTick. Мы хотим, чтобы время сигнала устанавливалось только один раз для каждого бара и сохраняло свое значение до начала следующего бара. Таким образом, мы устанавливаем её равным 0 в начале, чтобы она имела значение только после генерирования первого сигнала. Чтобы она не генерировала второй (или множественный) сигнал в течение одного и того же бара, мы сравним время текущего бара со значением, хранящимся в переменной. После этого мы можем определить торговую логику.

   // Check for BUY signal conditions:
   // - Current SAR is below the current low (bullish)
   // - Previous SAR was above the previous high (bullish reversal)
   // - SMA is below the current Ask price (indicating upward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0
      && sma_data[0] < Ask && PositionsTotal() == 0){
      Print("BUY SIGNAL @ ",TimeCurrent());  // Print buy signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
   }

Здесь мы проверяем условия подачи сигнала на покупку, основываясь на одновременном поведении индикаторов Parabolic SAR и SMA, а также на нескольких других ограничениях. Чтобы получить бычий сигнал от Parabolic SAR, текущее значение SAR ("sar_data[0]") должно быть ниже текущей минимальной цены ("low0"). В то же время, предыдущее значение SAR ("sar_data[1]") должно было быть выше предыдущей максимальной цены ("high 1"). Чтобы получить бычий сигнал SMA, SMA должна быть ниже текущей цены предложения ("sma_data[0] < Ask").

Кроме того, мы проверяем, нет ли других открытых позиций (PositionsTotal == 0). Мы бы не хотели открывать множество сделок одновременно. Затем мы убеждаемся, что сигнал не был сгенерирован для текущего бара, сравнивая "signalTime" с "currTime0". Если все эти проверки проходят успешно, мы выводим сообщение о подаче сигнала на покупку. Мы также обновляем переменную времени сигнала до текущего времени. Этот изящный маленький трюк позволяет нам ограничить генерацию сигнала всего одним разом на бар. В конечном счете, мы хотим убедиться, что советник реагирует только на определенные рыночные условия и не выполняет никакой ненужной работы. Результаты, которые мы получаем, приведены ниже:

BUY SIGNAL

Поскольку мы получили сигнал, давайте перейдем к открытию позиций на покупку.

      obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point);  // Execute a buy order with a lot size of 0.01, stop loss and take profit

Здесь мы выполняем ордер на покупку, используя метод "Buy" из класса CTrade (представленный созданным нами объектом "obj_Trade"). Вот разбивка параметров:

  • 0.01: Это размер лота сделки. Здесь мы указываем небольшой размер позиции - 0,01 лота.
  • _Symbol: Представляет торговый символ (валютная пара, акция и т.д.), к которому привязан советник. Символ определяется автоматически с помощью встроенной переменной _Symbol.
  • Цена предложения (Ask): Это цена, по которой будет исполнен ордер на покупку, которая является текущей ценой предложения на рынке. Цена предложения обычно применяется для сделок на покупку.
  • Ask - 150 * _Point: Это уровень стоп-лосса. Мы устанавливаем стоп-лосс на 150 пунктов ниже цены предложения, чтобы ограничить потенциальные убытки в случае, если рынок пойдет против сделки.
  • Ask + 100 * _Point: Это уровень тейк-профита. Мы устанавливаем тейк-профит на 100 пунктов выше цены предложения, что означает, что сделка автоматически закроется, когда рынок достигнет этого уровня прибыли.

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

   // Check for BUY signal conditions:
   // - Current SAR is below the current low (bullish)
   // - Previous SAR was above the previous high (bullish reversal)
   // - SMA is below the current Ask price (indicating upward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0
      && sma_data[0] < Ask && PositionsTotal() == 0){
      Print("BUY SIGNAL @ ",TimeCurrent());  // Print buy signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point);  // Execute a buy order with a lot size of 0.01, stop loss and take profit
   }

Аналогичная логика применяется для подтверждения сигнала на продажу и открытия ордера на продажу.

   // Check for SELL signal conditions:
   // - Current SAR is above the current high (bearish)
   // - Previous SAR was below the previous low (bearish reversal)
   // - SMA is above the current Bid price (indicating downward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   else if (sar_data[0] > high0 && sar_data[1] < low1 && signalTime != currTime0
      && sma_data[0] > Bid && PositionsTotal() == 0){
      Print("SELL SIGNAL @ ",TimeCurrent());  // Print sell signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Sell(0.01,_Symbol,Bid,Bid+150*_Point,Bid-100*_Point);  // Execute a sell order with a lot size of 0.01, stop loss and take profit
   }

Здесь мы реализуем торговую логику на продажу, которая будет выполняться при наличии определенных рыночных условий. Сначала проверяем, указывает ли индикатор Parabolic SAR (SAR) на медвежий тренд. Мы делаем это, сравнивая самые последние данные SAR с ценами бара. Значение SAR должно быть выше максимума бара для текущей свечи ("sar_data[0] > high0"), а предыдущее значение SAR было ниже минимальной цены предыдущего бара ("sar_data[1] < low1"), что указывает на возможный медвежий разворот. Индикатором тренда SAR может быть "безопасный для торговли" импульс. Затем проверяем индикатор SMA. Он должен быть выше цены спроса ("sma_data[0] > Bid"), что указывает на возможный "безопасный для торговли» нисходящий тренд.

Когда все вышеупомянутые условия выполнены, мы подаем сигнал на продажу и регистрируем его ("Print("SELL SIGNAL @ ", TimeCurrent())"). Переменная "signalTime" фиксирует время работы текущего бара; это важно, потому что мы должны быть уверены, что не подаем более одного сигнала на продажу на каждом баре. Для совершения сделки мы используем объектную функцию "obj_Trade.Sell". Мы систематически выходим на рынок в моменты, когда могут произойти потенциальные медвежьи развороты. Мы делаем это с оглядкой на риск, на который идем. И мы управляем этим риском с помощью ордеров стоп-лосс и тейк-профит. Настройка подтверждения продажи выглядит следующим образом:

SELL SETUP CONFIRMATION

На данный момент уже ясно, что мы корректно разработали алгоритм советника Rapid-Fire. Таким образом, полный исходный код OnTick, отвечающий за подтверждение сделок и открытие позиций, выглядит следующим образом:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   // OnTick is called whenever there is a new market tick (price update)

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);  // Get and normalize the Ask price
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);  // Get and normalize the Bid price

   // Retrieve the last 3 values of the SMA indicator into the sma_data array
   if (CopyBuffer(handleSMA,0,0,3,sma_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SMA FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SMA data
      return;  // Exit the function if not enough data is available
   }

   // Retrieve the last 3 values of the SAR indicator into the sar_data array
   if (CopyBuffer(handleSAR,0,0,3,sar_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SAR FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SAR data
      return;  // Exit the function if not enough data is available
   }
   
   //ArrayPrint(sma_data,_Digits," , ");
   //ArrayPrint(sar_data,_Digits," , ");
   
   // Get the low prices for the current and previous bars on the M1 timeframe
   double low0 = iLow(_Symbol,PERIOD_M1,0);  // Low of the current bar
   double low1 = iLow(_Symbol,PERIOD_M1,1);  // Low of the previous bar

   // Get the high prices for the current and previous bars on the M1 timeframe
   double high0 = iHigh(_Symbol,PERIOD_M1,0);  // High of the current bar
   double high1 = iHigh(_Symbol,PERIOD_M1,1);  // High of the previous bar
   
   //Print("High bar 0 = ",high0,", High bar 1 = ",high1);
   //Print("Low bar 0 = ",low0,", Low bar 1 = ",low1);
   
   // Define a static variable to track the last time a signal was generated
   static datetime signalTime = 0;
   datetime currTime0 = iTime(_Symbol,PERIOD_M1,0);  // Get the time of the current bar

   // Check for BUY signal conditions:
   // - Current SAR is below the current low (bullish)
   // - Previous SAR was above the previous high (bullish reversal)
   // - SMA is below the current Ask price (indicating upward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0
      && sma_data[0] < Ask && PositionsTotal() == 0){
      Print("BUY SIGNAL @ ",TimeCurrent());  // Print buy signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point);  // Execute a buy order with a lot size of 0.01, stop loss and take profit
   }

   // Check for SELL signal conditions:
   // - Current SAR is above the current high (bearish)
   // - Previous SAR was below the previous low (bearish reversal)
   // - SMA is above the current Bid price (indicating downward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   else if (sar_data[0] > high0 && sar_data[1] < low1 && signalTime != currTime0
      && sma_data[0] > Bid && PositionsTotal() == 0){
      Print("SELL SIGNAL @ ",TimeCurrent());  // Print sell signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Sell(0.01,_Symbol,Bid,Bid+150*_Point,Bid-100*_Point);  // Execute a sell order with a lot size of 0.01, stop loss and take profit
   }
}
//+------------------------------------------------------------------+

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


Тестирование и оптимизация

Настоящий раздел посвящен тестированию и оптимизации нашей торговой стратегии rapid-fire с использованием тестера стратегий MetaTrader 5. Тестирование важно, потому что мы должны убедиться, что наш советник (EA) работает должным образом и что он хорошо работает в различных рыночных условиях. Чтобы начать тестирование нашего советника, нам необходимо загрузить его в Тестер стратегий, выбрать желаемый торговый символ и таймфрейм (в нашем коде мы указали таймфрейм M1) и установить период тестирования. Протестировав наш советник на истории, мы можем получить историческую картину того, как бы работал наш советник при заданной стратегии и параметрах. Конечно, мы стараемся выявить любые потенциальные проблемы, которые могут привести к неправильной работе советника в реальных условиях торговли. Чтобы открыть тестер стратегий, нажмите "Просмотр" и выберите "Тестер стратегий".

OPEN STRATEGY TESTER

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

TESTER SETTINGS

После тестирования мы получили следующие результаты:

TESTER RESULTS

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

Чтобы найти наилучшую торговую стратегию, мы должны адаптировать ее к рынку. Для этого мы используем метод, называемый «оптимизация стратегии». Оптимизация стратегии - это не то, что мы делаем один раз и забываем об этом. Мы должны посмотреть, как система ведет себя в различных конфигурациях, а затем выбрать ту конфигурацию, которая дает нам наиболее согласованные результаты во всех проводимых нами тестах. Тестируя стратегию таким образом, мы эффективно находим оптимальные параметры и используем их в качестве ключей для разблокировки рассматриваемой нами торговой стратегии. После того, как найдем эти ключи, мы должны снова запустить стратегию и протестировать ее в течение длительного периода времени. В противном случае мы ничем не лучше тех, кто просто угадывает будущее направление развития рынка. Чтобы оптимизировать систему, нам понадобятся некоторые исходные данные, на основе которых могут выполняться циклы. Будет реализована следующая логика.

input int sl_points = 150; // Stoploss points
input int tp_points = 100; // Takeprofit points

После оптимизации мы получили следующие результаты:

OPTIMIZATION RESULTS

График оптимизированных результатов похож на приведенный ниже:

OPTIMIZATION GRAPH

Наконец, протокол испытаний выглядит так, как показано ниже:

FINAL TEST REPORT


Заключение

В настоящей статье мы обсудили разработку rapid-fire торгового советника (EA) на MQL5, который выполняет короткие, быстрые сделки. Стратегия основана на двух основных технических индикаторах: Parabolic SAR, который сигнализирует о развороте тренда, и простая скользящая средняя (SMA), который измеряет динамику рынка. Эти индикаторы формируют ядро торговой логики советника.

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

Внимание: Статья предназначена только для обучения. Она предназначена для демонстрации того, как создать советник стратегии Rapid-Fire, который основан на подходе ценового действия (Price Action) и, таким образом, должен быть использован в качестве основы для создания более совершенного советника с учетом большей оптимизации и извлечения данных. Представленная информация не гарантирует каких-либо торговых результатов.

Надеюсь, что статья оказалась для вас полезной, интересной и простой для понимания, и вы сможете использовать представленные в ней знания при разработке собственных советников. Технически это облегчает вам анализ рынка, основанный на подходе ценового действия (Price Action) и, в частности, на стратегии Rapid-Fire. Наслаждайтесь.


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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (6)
Allan Munene Mutiiria
Allan Munene Mutiiria | 29 авг. 2024 в 15:53
Qhumanani #:

Привет, Аллен,

Можно ли реализовать эту стратегию в MT4

@Qhumanani Здравствуйте. Да, но это потребует конвертации. Спасибо.

Manh Toan Do
Manh Toan Do | 3 сент. 2024 в 05:30
"При запуске бэктестов есть ордера на покупку и продажу, но при запуске на реальном счете ордеров нет, хотя используются настройки по умолчанию и он работает уже 2 дня".
Allan Munene Mutiiria
Allan Munene Mutiiria | 3 сент. 2024 в 09:59
toando185 ордера на покупку и продажу, но при работе на реальном счете ордеров нет, хотя используются настройки по умолчанию и он работает уже 2 дня".

Вам следует проверить возможные причины в журнале и подтвердить, что сигналы генерируются.

viper2k16
viper2k16 | 19 сент. 2024 в 14:51
Очень интересно, нашел эту стратегию после тестирования вручную!
Было бы интересно добавить возможность настройки риск-менеджмента, по фиксированному размеру лота или проценту от баланса, спасибо!
Allan Munene Mutiiria
Allan Munene Mutiiria | 22 сент. 2024 в 20:52
viper2k16 #:
Очень интересно, нашел эту стратегию после тестирования вручную!
Было бы интересно добавить возможность настройки риск-менеджмента, по фиксированному размеру лота или проценту от баланса, спасибо!

Мы рады слышать это. Мы рассмотрим эту идею. Спасибо.

Возможности Мастера MQL5, которые вам нужно знать (Часть 37): Регрессия гауссовских процессов с линейными ядрами и ядрами Матерна Возможности Мастера MQL5, которые вам нужно знать (Часть 37): Регрессия гауссовских процессов с линейными ядрами и ядрами Матерна
Линейные ядра — простейшая матрица, используемая в машинном обучении для линейной регрессии и опорных векторных машин. Ядро Матерна (Matérn) представляет собой более универсальную версию радиальной базисной функции (Radial Basis Function, RBF), которую мы рассматривали в одной из предыдущих статей, и оно отлично подходит для отображения функций, которые не настолько гладкие, как предполагает RBF. Создадим специальный класс сигналов, который использует оба ядра для прогнозирования условий на покупку и продажу.
Разработка системы репликации (Часть 72): Неожиданный способ оповещений (I) Разработка системы репликации (Часть 72): Неожиданный способ оповещений (I)
То, что мы создадим сегодня, будет сложным для понимания. Поэтому в данной статье я расскажу только о начальном этапе. Внимательно прочитайте содержание данной статьи, это важно для того, чтобы перейти к следующему шагу. Цель данного материала - исключительно дидактическая, только для изучения и освоения представленных концепций, без практического применения.
Нейросети в трейдинге: Адаптивное обнаружение рыночных аномалий (Окончание) Нейросети в трейдинге: Адаптивное обнаружение рыночных аномалий (Окончание)
Продолжаем построение алгоритмов, заложенные в основу фреймворка DADA — передового инструмента для обнаружения аномалий во временных рядах. Этот подход позволяет эффективно отличать случайные флуктуации от значимых отклонений. В отличие от классических методов, DADA динамически адаптируется к разным типам данных, выбирая оптимальный уровень сжатия в каждом конкретном случае.
Возможности Мастера MQL5, которые вам нужно знать (Часть 36): Q-обучение с цепями Маркова Возможности Мастера MQL5, которые вам нужно знать (Часть 36): Q-обучение с цепями Маркова
Обучение с подкреплением — один из трех основных принципов машинного обучения, наряду с обучением с учителем и без учителя. Поэтому возникает необходимость в оптимальном управлении или изучении наилучшей долгосрочной политики, которая наилучшим образом соответствует целевой функции. Именно на этом фоне мы исследуем его возможную роль в информировании процесса обучения MLP советника, собранного в Мастере.