Обсуждение статьи "Стратегии торговли прорыва: разбор ключевых методов"

 

Опубликована статья Стратегии торговли прорыва: разбор ключевых методов:

Стратегии прорыва диапазона открытия (Opening Range Breakout, ORB) основаны на идее о том, что начальный торговый диапазон, установленный вскоре после открытия рынка, отражает значимые уровни цен, когда покупатели и продавцы договариваются о стоимости. Выявляя прорывы определенного диапазона вверх или вниз, трейдеры могут извлекать выгоду из моментума, который часто возникает, когда направление рынка становится более отчетливым. В этой статье рассмотрим три стратегии ORB, адаптированные из материалов компании Concretum Group.

Стратегии прорыва диапазона открытия (Opening Range Breakout, ORB) основаны на идее о том, что начальный торговый диапазон, установленный вскоре после открытия рынка, отражает значимые уровни цен, когда покупатели и продавцы договариваются о стоимости. Выявляя прорывы определенного диапазона вверх или вниз, трейдеры могут извлекать выгоду из моментума, который часто возникает, когда направление рынка становится более отчетливым. 

В этой статье рассмотрим три стратегии ORB, адаптированные из статей компании Concretum Group. Сначала рассмотрим предпосылки для проведения данного исследования, включая основные концепции и используемую методологию. Затем объясним принцип работы каждой стратегии, перечислим правила использования в ней сигналов и выполним статистический анализ ее эффективности. Наконец, изучим их с точки зрения использования портфеля, уделив особое внимание теме диверсификации.  

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


Автор: Zhuo Kai Chen

 
Отличная статья

Стоит прочитать любому трейдеру, преследующему свои цели.

Говорю из собственного опыта
 
Timmy T #:
Отличная статья

Стоит прочитать любому трейдеру, преследующему свои цели

Говорю из собственного опыта

Спасибо, Тимми.

 

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

Есть простой вопрос: "В исследованиях, которые мы адаптируем, они сосредоточены на стратегиях, которые торгуют между открытием и закрытием рынка (с 9:30 утра до 4:00 вечера по восточному времени).Поскольку наш брокер использует UTC+2/3, это означает 18:30-24:00 по серверному времени - не забудьте сделать поправку на часовой пояс вашего брокера при тестировании".

Можете ли вы объяснить мне ход ваших мыслей по поводу конвертации времени? Я явно что-то упускаю...

Используя общий конвертер времени - мое преобразование 09:30 по восточному времени заканчивается 16:30 для mt5 в GMT+2 и 17:30 для mt5 в GMT+3. 18:30 кажется, что это 1-2 часа после открытия рынка.

Спасибо за помощь и еще раз спасибо.

Файлы:
NY_GMT.png  12 kb
 
Sunjoo по серверному времени - не забудьте сделать поправку на часовой пояс вашего брокера при тестировании ".

Можете ли вы объяснить мне ход ваших мыслей по поводу перевода времени? Я явно что-то упускаю...

Используя общий конвертер времени, я перевел 09:30 по восточному времени в 16:30 для mt5 в GMT+2 и 17:30 для mt5 в GMT+3. 18:30 кажется, что это 1-2 часа после открытия рынка.

Спасибо за помощь и еще раз спасибо.

Вы правы, я неправильно перевел время сервера в статье для времени открытия Нью-Йоркского фондового рынка. Оно должно быть 17:30, а не 18:30. Учитывая это, вы можете предположить, что все мои правила стратегии в статье должны торговать через 1 час после открытия рынка. Спасибо, что указали на это, и извините за путаницу.

 

Спасибо за публикацию, очень интересно!

Вопрос по коду:

В вашей функции MarketOpened вы используете:

"if (currentHour >= startHour && currentMinute>=startMinute)return true;" <- Это похоже на то, что она будет торговать только часть часа, если рынок открыт, так как она вернет false, если вы находитесь в начале каждого часа, даже если рынок открыт. Она работает только в том случае, если минута равна 0, что не является началом рыночной сессии.

 
Digitus #:

Спасибо за публикацию, очень интересно!

Вопрос по поводу кода:

В вашей функции MarketOpened вы используете:

"if (currentHour >= startHour && currentMinute>=startMinute)return true;" <- Это выглядит так, как будто он будет торговать только часть часа, если рынок открыт, поскольку он вернет false, если вы находитесь в начале каждого часа, даже если рынок открыт. Это работает только в том случае, если минута равна 0, что не является началом рыночной сессии.

ОМГ, не могу поверить, что я это пропустил. Так и должно быть:

    if ((currentHour >= startHour &&currentMinute>=startMinute)||currentHour>startHour)return true;

Я искренне сожалею о небрежной ошибке. Спасибо, что так внимательно прочитали и указали на нее.

 

Ps. Для ORB3 я жестко закодировал время открытия рынка на 9:30. Вы можете изменить его в этих функциях, чтобы соответствовать серверному времени открытия рынка в Нью-Йорке.

//+------------------------------------------------------------------+
//| Получите значение верхней полосы бетона.|
//+------------------------------------------------------------------+
double getUpperBand(int target_hour = 17, int target_min = 30) {
    // Получение времени текущего бара
    datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
    MqlDateTime current_dt;
    TimeToStruct(current_time, current_dt);
    int current_hour = current_dt.hour;
    int current_min = current_dt.min;
    
    // Найдите сегодняшнюю цену открытия в целевое время (например, 17:30 по серверному времени)
    datetime today_start = iTime(_Symbol, PERIOD_D1, 0);
    int bar_at_target_today = getBarShiftForTime(today_start, target_hour, target_min);
    if (bar_at_target_today < 0) return 0; // Возвращает 0, если целевой бар не существует
    double open_target_today = iOpen(_Symbol, PERIOD_M1, bar_at_target_today);
    if (open_target_today == 0) return 0; // Нет действующей цены
    
    // Рассчитайте сигму на основе последних 14 дней
    double sum_moves = 0;
    int valid_days = 0;
    for (int i = 1; i <= 14; i++) {
        datetime day_start = iTime(_Symbol, PERIOD_D1, i);
        int bar_at_target = getBarShiftForTime(day_start, target_hour, target_min);
        int bar_at_HHMM = getBarShiftForTime(day_start, current_hour, current_min);
        if (bar_at_target < 0 || bar_at_HHMM < 0) continue; // Пропустить, если бары не существуют
        double open_target = iOpen(_Symbol, PERIOD_M1, bar_at_target);
        double close_HHMM = iClose(_Symbol, PERIOD_M1, bar_at_HHMM);
        if (open_target == 0) continue; // Пропустить, если нет действительной цены открытия
        double move = MathAbs(close_HHMM / open_target - 1);
        sum_moves += move;
        valid_days++;
    }
    if (valid_days == 0) return 0; // Возвращаем 0, если нет достоверных данных
    double sigma = sum_moves / valid_days;
    
    // Рассчитайте верхнюю полосу
    double upper_band = open_target_today * (1 + sigma);
    
    // Поставьте синюю точку на уровне верхней полосы
    string obj_name = "UpperBand_" + TimeToString(current_time, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
    ObjectCreate(0, obj_name, OBJ_ARROW, 0, current_time, upper_band);
    ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 159); // Символ точки
    ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clrBlue);
    ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 2);
    
    return upper_band;
}

//+------------------------------------------------------------------+
//| Получите значение нижней полосы Concretum.|
//+------------------------------------------------------------------+
double getLowerBand(int target_hour = 17, int target_min = 30) {
    // Получение времени текущего бара
    datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
    MqlDateTime current_dt;
    TimeToStruct(current_time, current_dt);
    int current_hour = current_dt.hour;
    int current_min = current_dt.min;
    
    // Найдите сегодняшнюю цену открытия в целевое время (например, 17:30 по серверному времени)
    datetime today_start = iTime(_Symbol, PERIOD_D1, 0);
    int bar_at_target_today = getBarShiftForTime(today_start, target_hour, target_min);
    if (bar_at_target_today < 0) return 0; // Возвращает 0, если целевой бар не существует
    double open_target_today = iOpen(_Symbol, PERIOD_M1, bar_at_target_today);
    if (open_target_today == 0) return 0; // Нет действующей цены
    
    // Рассчитайте сигму на основе последних 14 дней
    double sum_moves = 0;
    int valid_days = 0;
    for (int i = 1; i <= 14; i++) {
        datetime day_start = iTime(_Symbol, PERIOD_D1, i);
        int bar_at_target = getBarShiftForTime(day_start, target_hour, target_min);
        int bar_at_HHMM = getBarShiftForTime(day_start, current_hour, current_min);
        if (bar_at_target < 0 || bar_at_HHMM < 0) continue; // Пропустить, если бары не существуют
        double open_target = iOpen(_Symbol, PERIOD_M1, bar_at_target);
        double close_HHMM = iClose(_Symbol, PERIOD_M1, bar_at_HHMM);
        if (open_target == 0) continue; // Пропустить, если нет действительной цены открытия
        double move = MathAbs(close_HHMM / open_target - 1);
        sum_moves += move;
        valid_days++;
    }
    if (valid_days == 0) return 0; // Возвращаем 0, если нет достоверных данных
    double sigma = sum_moves / valid_days;
    
    // Рассчитайте нижнюю полосу
    double lower_band = open_target_today * (1 - sigma);
    
    // Поставьте красную точку на уровне нижнего диапазона
    string obj_name = "LowerBand_" + TimeToString(current_time, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
    ObjectCreate(0, obj_name, OBJ_ARROW, 0, current_time, lower_band);
    ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 159); // Символ точки
    ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clrRed);
    ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 2);
    
    return lower_band;
}

Изменение времени расчета может быть способом дальнейшей оптимизации стратегии :)

 
Zhuo Kai Chen #:

ОМГ, не могу поверить, что пропустил это. Так и должно быть:

Я искренне сожалею о небрежной ошибке. Спасибо, что так внимательно читали и указали на нее.

Не беспокойтесь, я уже объединил открытый и закрытый рынок в одну функцию.

bool MarketState()
{
   MqlDateTime structTime;
   TimeCurrent(structTime);
   structTime.sec = 0;
   structTime.hour = startHour;
   structTime.min = startMinute; 
   datetime timeStart = StructToTime(structTime);
   structTime.hour = endHour;
   structTime.min = endMinute;   
   datetime timeEnd = StructToTime(structTime);
   if(TimeCurrent() >= timeStart && TimeCurrent() < timeEnd)return true;
   else return false;
}


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

Еще раз спасибо за ваши усилия!

 
Digitus #:

Не беспокойтесь, я уже объединил открытый и закрытый рынок в одну функцию.


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

Еще раз спасибо за ваши усилия!

Отличная работа над модификацией! Я обновил весь код на своем Github.

К вашему сведению, торговая логика происходит на каждом новом баре и не предполагает тикового движения. Кроме того, среднее время удержания составляет около нескольких часов, что, я думаю, не сделает проскальзывание существенной проблемой. Я бы сказал, что очень немногие брокеры предоставляют реальные тиковые данные за более чем 5 лет, и 1 мин OHLC вполне достаточно.

 
Отличная статья