
Переосмысливаем классические стратегии (Часть 13): Минимизация задержки при пересечении скользящих средних
В наших предыдущих обсуждениях мы рассмотрели различные точки зрения на то, как можно максимизировать нашу эффективность при использовании стратегий пересечения скользящих средних. Ранее мы провели исследование с более чем 200 различными символами и заметили, что компьютер, по-видимому, учится предсказывать будущие значения скользящей средней лучше, чем значения будущих уровней цен. Статью можно почитать здесь. Мы развили эту идею и смоделировали две скользящие средние с единственной целью — предсказывать пересечения раньше, чем это рядовые участники рынка, и соответствующим образом корректировать наши сделки. Вторую статью можно почитать здесь.
Здесь мы еще раз расширим исходную стратегию, чтобы минимизировать задержку, присущую торговой стратегии. Традиционные стратегии пересечения требуют наличия временного разрыва между двумя периодами скользящей средней. Однако мы отойдем от этой традиционной школы мышления и будем использовать один и тот же период для обеих скользящих средних.
На этом этапе некоторые читатели могут задаться вопросом, можно ли это все еще считать стратегией пересечения скользящих средних. Как скользящие средние будут пересекаться, если у них одинаковый период? Ответ на удивление прост: мы применим одну скользящую среднюю к цене открытия, а другую - к цене закрытия.
Когда начальная скользящая средняя выше закрывающей, уровни цен закрытия ниже цен открытия. Это указывает на снижение цен. И обратное верно - когда скользящая средняя закрытия выше средней открытия.
Общий период для обеих скользящих средних не является общепринятым, однако я выбрал его для нашего сегодняшнего упражнения, чтобы продемонстрировать читателю один из возможных способов, с помощью которого мы можем развеять любую критику относительно запаздывания, связанного с пересечениями скользящих средних.
Прежде чем мы углубимся в техническую часть нашего обсуждения, эту статью можно рассматривать как пример обобщенного класса торговых стратегий, который можно легко распространить на многие другие технические индикаторы. Например, индикатор относительной силы может применяться независимо к открытию, закрытию, максимуму или минимуму, и мы увидим, что RSI, отслеживающий цену открытия, будет иметь тенденцию подниматься выше RSI, отслеживающего цену закрытия, когда рынки находятся в нисходящем тренде.
Обзор тестирования на истории
Сначала установим эталонный показатель, полученный традиционной перекрестной стратегией. Затем мы сравним прежние результаты с тем, чего мы можем достичь, используя нашу переосмысленную версию стратегии.Для нашего сегодняшнего обсуждения я выбрал пару EURUSD. EURUSD — самая активно торгуемая валютная пара в мире. Она значительно более волатильна, чем большинство валютных пар, и, как правило, не является хорошим выбором для простых стратегий, основанных на пересечениях. Как мы уже обсуждали ранее, мы сосредоточимся на дневном таймфрейме. Мы проведем тестирование на истории нашей стратегии на основе исторических данных примерно за 4 года, с 1 января 2020 года по 24 декабря 2024 года. Период тестирования на истории показан ниже.
Рис. 1. 4-летний период тестирования EURUSD в MetaTrader 5 с использованием месячного таймфрейма
Хотя традиционные стратегии пересечения интуитивно понятны и подкреплены достаточно надежными фундаментальными принципами, для обеспечения эффективного использования эти стратегии часто требуют бесконечной оптимизации. Кроме того, "правильные" периоды для использования медленного и быстрого индикаторов не всегда очевидны и могут существенно меняться.
Напомним, что исходная стратегия основана на пересечениях, создаваемых двумя скользящими средними, обе из которых отслеживают цену закрытия одной и той же ценной бумаги, но с разными периодами. Когда скользящая средняя с более коротким периодом находится наверху, мы интерпретируем это как сигнал о том, что уровни цен находятся в восходящем тренде и, вероятно, продолжат расти. Обратное происходит, когда скользящая средняя с более длинным периодом находится сверху, мы интерпретируем это как медвежий сигнал, иллюстрированный пример представлен на рисунке 2 ниже.
Рис. 2. Пример традиционной стратегии пересечения скользящих средних в действии. Желтая линия — это быстро скользящая средняя, а белая — медленно
На рис. 2 выше мы случайным образом выбрали период времени, который показывает ограничения классической стратегии. Слева от вертикальной линии на графике вы увидите, что ценовое движение застряло в диапазоне примерно на 2 месяца. Такое вялое ценовое движение генерирует торговые сигналы, которые быстро меняются и, скорее всего, оказываются убыточными. Однако после этого периода удручающе низких показателей мы наконец увидели, как уровни цен начали меняться в сторону настоящего тренда справа от вертикальной линии. Традиционные стратегии пересечения лучше всего работают в трендовых рыночных условиях. Однако стратегия, предложенная в этой статье, элегантно решает эти проблемы.
Эталонное значение
Наше торговое приложение можно представить в виде четырех основных компонентов, которые будут работать вместе, помогая нам торговать:
Свойство | Описание |
---|---|
Системные константы | Помогают выделить улучшения, вызванные изменениями, которые мы вносим в торговую логику нашего приложения. |
Глобальные переменные | Отвечают за отслеживание значений индикаторов, текущих рыночных цен и возможной дополнительной информации, которая нам необходима. |
Обработчики событий | Выполняют различные задачи в подходящее время, чтобы достичь нашей цели — эффективной торговли на пересечениях скользящих средних. |
Настраиваемые функции | Каждой индивидуальной функции в нашей системе делегирована определенная задача, и все вместе эти функции помогают нам достичь нашей цели. |
Эталонная версия нашего приложения будет минималистичной по своей реализации. Наша первая задача — настроить системные константы, которые останутся неизменными в обоих тестах, которые мы выполним. Наши системные константы важны для проведения справедливых сравнений между различными торговыми стратегиями и не позволяют нам непреднамеренно изменять настройки, которые не должны меняться между тестами. Например, размер нашего стоп-лосса должен быть постоянным в обоих тестах, чтобы обеспечить справедливое сравнение.
//+------------------------------------------------------------------+ //| Channel And MA.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| System constants | //+------------------------------------------------------------------+ #define TF_1 PERIOD_D1 //--- Our main time frame #define TF_2 PERIOD_H1 //--- Our lower time frame #define ATR_PERIOD 14 //--- The period for our ATR #define ATR_MULTIPLE 3 //--- How wide should our stops be? #define VOL 0.01 //--- Trading volume
Мы также определим несколько важных глобальных переменных, которые будем использовать для извлечения значений индикаторов и получения текущих рыночных цен.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int trade; int ma_f_handler,ma_s_handler,atr_handler; double ma_f[],ma_s[],atr[]; double bid,ask; double o,h,l,c; double original_sl;
Для управления нашими позициями мы будем использовать торговую библиотеку.
//+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade;
В MQL5 советники строятся на основе обработчиков событий. В терминале MetaTrader 5 происходит множество различных типов событий. Эти события могут быть вызваны действиями пользователя или появлением новых цен. Каждое событие сопряжено с обработчиком событий, который вызывается каждый раз при возникновении события. Поэтому я разработал приложение таким образом, что каждый обработчик событий имеет собственную назначенную функцию, которую он будет вызывать по очереди для выполнения задач, необходимых для стратегии пересечения скользящих средних.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup setup(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release our indicators release(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update system variables update(); } //+------------------------------------------------------------------+
Функция настройки вызывается при запуске нашей системы. Обработчик OnInit будет вызван при первом применении торгового приложения на графике и передаст цепочку команд нашей настроенной функции, которая применит наши технические индикаторы.
//+------------------------------------------------------------------+ //| Custom functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE); ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE); }
Если мы больше не используем наше торговое приложение, вызывается обработчик событий OnDeinit, который вызывает функцию, освобождающую системные ресурсы, которые ранее потреблялись нашими техническими индикаторами.
//+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_f_handler); IndicatorRelease(ma_s_handler); }
Всякий раз, когда указываются новые рыночные цены, будет вызываться обработчик OnTick, который, в свою очередь, вызовет функцию обновления для сохранения новой доступной рыночной информации. После этого, если у нас нет открытых позиций, мы будем искать торговые настройки. В противном случае мы будем управлять имеющимися у нас открытыми позициями.
/+------------------------------------------------------------------+ //| Update system variables | //+------------------------------------------------------------------+ void update(void) { //--- Update the system static datetime time_stamp; datetime current_time = iTime(Symbol(),TF_2,0); if(current_time != time_stamp) { time_stamp = current_time; CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_s_handler,0,0,1,ma_s); CopyBuffer(ma_f_handler,0,0,1,ma_f); o = iOpen(Symbol(),TF_1,0); h = iHigh(Symbol(),TF_1,0); l = iLow(Symbol(),TF_1,0); c = iClose(Symbol(),TF_1,0); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); if(PositionsTotal() == 0) find_position(); if(PositionsTotal() > 0) manage_position(); } }
Наши правила входа в позиции просты и уже подробно объяснены выше. Мы входим в длинную позицию, если наша быстрая скользящая средняя выше медленной, в противном случае, мы открываем короткую позицию.
//+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_s[0] > ma_f[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_s[0] < ma_f[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } }
Наконец, наш стоп-лосс будет динамически корректироваться с использованием индикатора среднего истинного диапазона. Мы добавим фиксированный множитель значения ATR выше и ниже нашей цены входа, чтобы обозначить уровни стоп-лосса и тейк-профита. Кроме того, мы также добавим среднее значение ATR за последние 90 дней (1 деловой цикл). Это делается с целью учесть недавние уровни волатильности на рынке. Наконец, мы воспользуемся тернарным оператором для настройки уровней тейк-профита и стоп-лосса. Наше правило заключается в том, что стопы следует обновлять только в том случае, если новая позиция будет более прибыльной, чем старая. Тернарные операторы позволяют нам выразить эту логику в компактной форме. Кроме того, тернарные операторы также дают нам возможность легко настраивать тейк-профит и стоп-лосс независимо друг от друга.
//+------------------------------------------------------------------+ //| Manage our positions | //+------------------------------------------------------------------+ void manage_position(void) { //--- Select the position if(PositionSelect(Symbol())) { //--- Get ready to update the SL/TP double initial_sl = PositionGetDouble(POSITION_SL); double initial_tp = PositionGetDouble(POSITION_TP); //--- Calculate the average ATR move vector atr_mean; atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90); double buy_sl = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double sell_sl = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double buy_tp = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double sell_tp = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double new_sl = ((trade == 1) && (initial_sl < buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl); double new_tp = ((trade == 1) && (initial_tp < buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp); if(initial_sl == 0 && initial_tp == 0) { if(trade == 1) { original_sl = buy_sl; Trade.PositionModify(Symbol(),buy_sl,buy_tp); } if(trade == -1) { original_sl = sell_sl; Trade.PositionModify(Symbol(),sell_sl,sell_tp); } } //--- Update the position else if((initial_sl * initial_tp) != 0) { Trade.PositionModify(Symbol(),new_sl,new_tp); } } } //+------------------------------------------------------------------+
Наш текущий код выглядит следующим образом.
//+------------------------------------------------------------------+ //| Channel And MA.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| This version off the application is mean reverting | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| System constants | //+------------------------------------------------------------------+ #define TF_1 PERIOD_D1 //--- Our main time frame #define TF_2 PERIOD_H1 //--- Our lower time frame #define ATR_PERIOD 14 //--- The period for our ATR #define ATR_MULTIPLE 3 //--- How wide should our stops be? #define VOL 0.01 //--- Trading volume //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int trade; int ma_f_handler,ma_s_handler,atr_handler; double ma_f[],ma_s[],atr[]; double bid,ask; double o,h,l,c; double original_sl; //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup setup(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release our indicators release(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update system variables update(); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Custom functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE); ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE); } //+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_f_handler); IndicatorRelease(ma_s_handler); } //+------------------------------------------------------------------+ //| Update system variables | //+------------------------------------------------------------------+ void update(void) { //--- Update the system static datetime time_stamp; datetime current_time = iTime(Symbol(),TF_2,0); if(current_time != time_stamp) { time_stamp = current_time; CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_s_handler,0,0,1,ma_s); CopyBuffer(ma_f_handler,0,0,1,ma_f); o = iOpen(Symbol(),TF_1,0); h = iHigh(Symbol(),TF_1,0); l = iLow(Symbol(),TF_1,0); c = iClose(Symbol(),TF_1,0); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); if(PositionsTotal() == 0) find_position(); if(PositionsTotal() > 0) manage_position(); } } //+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_s[0] > ma_f[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_s[0] < ma_f[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } } //+------------------------------------------------------------------+ //| Manage our positions | //+------------------------------------------------------------------+ void manage_position(void) { //--- Select the position if(PositionSelect(Symbol())) { //--- Get ready to update the SL/TP double initial_sl = PositionGetDouble(POSITION_SL); double initial_tp = PositionGetDouble(POSITION_TP); //--- Calculate the average ATR move vector atr_mean; atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90); double buy_sl = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double sell_sl = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double buy_tp = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double sell_tp = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double new_sl = ((trade == 1) && (initial_sl < buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl); double new_tp = ((trade == 1) && (initial_tp < buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp); if(initial_sl == 0 && initial_tp == 0) { if(trade == 1) { original_sl = buy_sl; Trade.PositionModify(Symbol(),buy_sl,buy_tp); } if(trade == -1) { original_sl = sell_sl; Trade.PositionModify(Symbol(),sell_sl,sell_tp); } } //--- Update the position else if((initial_sl * initial_tp) != 0) { Trade.PositionModify(Symbol(),new_sl,new_tp); } } } //+------------------------------------------------------------------+
Поскольку наша текущая стратегия не использует ИИ или какие-либо методы подгонки кривой, мы можем провести простое тестирование на истории, не беспокоясь о подгонке под имеющиеся у нас данные. Кроме того, у нас нет входных параметров, которые необходимо корректировать. Поэтому мы установили для параметра Forward значение No, поскольку в данный момент нам не нужно использовать возможности форвардного тестирования терминала MetaTrader 5.
Рис. 3. Выбор дат для нашего тестирования на истории
Кроме того, хорошей практикой является тестирование ваших торговых приложений в максимально стрессовой среде, какую только можно смоделировать. Поэтому для тестирования на истории мы выбрали режим "Произвольная задержка", настроив моделирование на использование реальных тиков. При использовании реальных тиков время, необходимое для загрузки исторических данных, может быть больше, чем при использовании других режимов, таких как "Только цены открытия". Однако результаты, скорее всего, будут ближе к истинным результатам, полученным на реальных тиках.
Рис. 4. Наша вторая группа настроек для тестирования EURUSD на истории
При анализе результатов, полученных с помощью простой перекрестной стратегии, мы можем быстро увидеть потенциальные проблемы, с которыми столкнулись бы, если бы следовали стратегии в ее первоначальном виде. Обратите внимание, что с начала тестирования и до июля 2022 года наша стратегия работала изо всех сил только на то, чтобы выйти в ноль. Это период просадки, равный почти половине нашего тестирования на истории, - 2 года. Это нежелательно и не свойственно тому типу стратегии, которому мы можем доверять для автоматической торговли.
Рис. 5. Анализ нашей кривой прибылей и убытков, полученной с помощью стратегии пересечения скользящих средних
Наша стратегия в ее первоначальном виде едва ли прибыльна и приносит убытки примерно в 61% от всех совершаемых ею сделок. Это дает нам негативные ожидания относительно стратегии, и наш пессимизм дополнительно подтверждается тем фактом, что наш коэффициент Шарпа очень близок к 0. Но посмотрите, насколько радикально мы можем улучшить нашу стратегию, внеся несколько простых изменений в используемую торговую логику.
Рис. 6. Подробный обзор наших результатов с использованием оригинальной стратегии пересечения скользящих средних
Улучшение первоначальной стратегии
На рис. 7 ниже я представил иллюстрацию новой предлагаемой стратегии пересечения. Синяя и зеленая линии представляют собой 5-периодные скользящие средние, следующие за ценой закрытия (синяя) и открытия (зеленая). Обратите внимание, что когда синяя скользящая средняя находится выше зеленой, уровень цен растет. Кроме того, обратите внимание на то, насколько быстро наши пересечения реагируют на изменения уровня цен. Традиционным стратегиям кроссовера потребуется разное количество времени, чтобы отразить любые изменения в рыночном тренде. Однако, когда мы следим за уровнями цен, используя нашу новую стратегию, мы можем быстро заметить изменения в тренде и даже периоды консолидации, когда две скользящие средние продолжают пересекаться, но не демонстрируют реального прогресса.
Рис. 7. Визуализация нашей новой стратегии пересечения на дневном таймфрейме EURUSD
До сих пор наша система была способна торговать прибыльно, но мы можем добиться большего. Единственное изменение, которое мы собираемся внести в нашу систему, — это изменение условий, при которых срабатывают наши позиции:
Изменение | Описание |
---|---|
Торговые правила | Наши традиционные правила подразумевают покупку, когда быстрая скользящая средняя выше медленной. Вместо этого теперь мы будем покупать, когда наша скользящая средняя открытия выше скользящей средней закрытия. |
Чтобы реализовать желаемое улучшение, нам необходимо внести несколько изменений в изначальную форму нашей текущей торговой стратегии:
Изменение | Описание |
---|---|
Дополнительные системные переменные | Нам понадобится новая системная переменная, отвечающая за фиксацию периода скользящей средней открытия и закрытия. |
Новые глобальные переменные | Будут созданы новые глобальные переменные для отслеживания новой информации, на которую мы обращаем внимание. |
Изменение пользовательских функций | Некоторые из уже созданных нами индивидуальных функций нуждаются в расширении в свете нового системного дизайна, которому мы следуем. |
По большей части все остальные части нашей системы будут сохранены. Мы хотим выделить улучшения, вызванные изменением нашей точки зрения на пересечения скользящих средних. Поэтому, чтобы достичь нашей цели, мы начнем с создания новой системной константы, чтобы зафиксировать период наших скользящих средних.
//--- Omitted code that has not changed #define MA_PERIOD 2 //--- The period for our moving average following the close
Затем нам необходимо определить новые глобальные переменные для новой информации, которую мы отслеживаем. Сейчас мы создаем обработчики скользящих средних для каждой из 4 указанных цен (открытия, максимума, минимума и закрытия).
//--- Omitted code that have not changed int ma_h_handler,ma_l_handler,ma_c_handler,ma_o_handler; double ma_h[],ma_l[],ma_c[],ma_o[];
При запуске нашего торгового приложения нам потребуется загрузить несколько дополнительных индикаторов в дополнение к тем, с которыми мы уже знакомы.
//+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_h_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_HIGH); ma_l_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_LOW); ma_c_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_CLOSE); ma_o_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_OPEN); }
То же самое относится и к нашей настраиваемой функции, отвечающей за удаление нашего советника, она будет расширена для учета новых индикаторов, которые мы внедрили в систему.
//+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_h_handler); IndicatorRelease(ma_l_handler); IndicatorRelease(ma_c_handler); IndicatorRelease(ma_o_handler); }
Наконец, условия, которые мы будем использовать для открытия наших сделок, должны быть обновлены, чтобы они соответствовали нашей новой торговой логике.
//+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_o[0] > ma_c[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_o[0] < ma_c[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } }
Давайте теперь посмотрим, какое влияние это оказывает на наш конечный результат. Сначала мы настроим наше новое торговое приложение для торговли за тот же период времени, который мы использовали в нашем первом тесте.
Рис. 8. Настройка нашего нового и улучшенного торгового алгоритма для торговли за тот же период времени, который мы использовали в нашем первом тесте
Кроме того, нам нужно будет провести оба тестирования в идентичных условиях, чтобы убедиться в беспристрастности нашего теста. В противном случае, если одна стратегия получит несправедливое преимущество, то честность наших тестов будет поставлена под сомнение.
Рис. 9. Мы хотим убедиться, что условия тестирования в обоих тестах идентичны, чтобы провести справедливое сравнение
Мы уже видим значительное улучшение по сравнению с полученными нами первоначальными результатами. Первые два года нашего первоначального тестирования мы потратили на то, чтобы выйти на уровень безубыточности. Однако благодаря нашей новой торговой стратегии мы преодолели это ограничение и были прибыльными на протяжении всех 4 лет.
Рис. 10. Наша новая торговая стратегия преодолела убыточный период торговли, который было бы сложно преодолеть с помощью традиционных стратегий пересечения
В ходе нашего первоначального тестирования на истории наша система совершила в общей сложности 41 сделку, а в ходе последнего тестирования мы совершили в общей сложности 42 сделки. То есть наша новая система несет больше риска, чем старый способ торговли. Потому что если мы подождем еще немного, разрыв между ними может продолжать расти. Наша новая система, по-видимому, совершает больше сделок, чем старая. Общая прибыль от старой системы составляла USD 59,57, а от новой увеличилась более чем вдвое и достигла USD 125,36. Напомним, что на данный момент мы ограничили нашу торговую систему совершением одной сделки минимальным лотом. Кроме того, в нашей первой системе наш валовой убыток составил USD 410,02, а в новой стратегии снизился до USD 330,74.
При проектировании систем нам приходится идти на компромиссы. Наша новая система работает лучше. Однако следует также отметить, что размер нашей средней прибыли снизился с USD 29,35 до USD 22,81. Это происходит потому, что время от времени наша новая система будет упускать сделки, от которых наша старая система получала прибыль. Однако эти периодические пропуски могут быть оправданы, учитывая достигнутый нами прогресс в производительности.
Коэффициент Шарпа увеличился с 0,18 в нашем первом тесте до 0,5 в текущем. Это хороший знак, указывающий на то, что мы лучше используем наш капитал. Кроме того, доля убыточных сделок снизилась с 60,98% в первом тесте до нового минимума в 52,38%.
Рис. 11. Подробный обзор эффективности новой торговой стратегии
Заключение
Большинство членов нашего сообщества, как правило, являются независимыми разработчиками, работающими над своими проектами в одиночку. В этом случае более практичными решениями могут стать простые алгоритмы, такие как предложенный в этой статье. Его легче поддерживать, развивать и расширять. Управление сложной и большой базой кода в одиночку — непростая задача даже для опытных разработчиков. А если вы новичок в нашем сообществе алгоритмической торговли, то эта стратегия может оказаться для вас особенно полезной. Если вам понравилась эта статья, то обязательно присоединяйтесь к нам в следующем обсуждении, где мы постараемся превзойти результаты, достигнутые здесь.
Название файла | Описание |
---|---|
Open & Close MA Cross | Новая переосмысленная версия стратегии пересечения скользящих средних. |
Traditional MA Cross | Классическая реализация пересечений скользящих средних. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16758
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.




- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Спасибо за код и идеи, мне нравится, как вы структурировали свой код. Вы познакомили меня с типом данных Vector, я не использовал его раньше. Очень полезно для меня в других местах.