English 中文 Español Deutsch 日本語 Português
preview
Создаем простой мультивалютный советник с использованием MQL5 (Часть 7): Сигналы индикаторов ZigZag и Awesome Oscillator

Создаем простой мультивалютный советник с использованием MQL5 (Часть 7): Сигналы индикаторов ZigZag и Awesome Oscillator

MetaTrader 5Примеры | 25 июля 2024, 14:27
1 060 2
Roberto Jacobs
Roberto Jacobs

Введение

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

Как было показано в предыдущих статьях, мультивалютная торговля возможна, благодаря инструментам MQL5 как в торговом терминале, так и в тестере стратегий.
Чтобы удовлетворить потребности трейдеров, которым нужна эффективная автоматическая торговля, мы полагаемся на мощь и возможности, предоставляемые MQL5. Наша цель — создать простой мультивалютный советник, используя различные идеи и стратегии. В этой статье основное внимание будет уделено использованию индикаторов ZigZag и Awesome Oscillator, фильтрующих сигналы друг друга.


Особенности

1. Торговые пары.

Советник будет торговать на следующих парах:

Форекс:
EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCAD,USDCHF,USDJPY,EURGBP,EURAUD, EURNZD, EURCAD, EURCHF, EURJPY, GBPAUD, GBPNZD,
GBPCAD,GBPCHF,GBPJPY,AUDNZD,AUDCAD,AUDCHF,AUDJPY,NZDCAD,NZDCHF,NZDJPY, CADCHF, CADJPY, CHFJPY = 28 пар

Плюс 2 пары металлов: XAUUSD (золото) и XAGUSD (серебро)

Всего 30 пар.


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


1.1. Дополнительные особенности:

1.1.1. Одновалютный советник

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

За это отвечает настройка Select Trading Pairs Mode (Multi or Single) - режим выбора торговых пар (несколько или одна).

1. Single Pair (одна валюта)
2. Multi Pairs (мультивалютный режим)

trading-pair-option


Если выбрана опция Single Pair:
Советник ограничен торговлей только на той паре, на которой он размещен.

Если выбрана опция Multi Pairs:
Группа настроек Selected Pairs to Trade (выбранные пары для торговли) включает 10 пар.
Одна из возможных опций называется Trader's Wishes Pairs (трейдерские пары). При ее выборе торгуемые пары должны быть введены трейдером вручную в свойствах советника. 
Не забывайте, что имя введенной пары уже должно быть в списке из 30 пар.

Опцию Trader's Wishes Pairs можно использовать, чтобы советники торговали только отдельными валютами или работали автономно.
Советник будет торговать только на указанной паре.

Если трейдер введет название пары XAUUSD, советник будет торговать только на ней.
При этом положение советника на одной из 30 доступных пар значения иметь не будет.

Входные параметры советника надо настроить так, как показано на рисунке ниже.

stand-alone-twp


Итак, у советника в этой статье есть два способа торговать одной валютой или работать автономно.

1. Выбрать опции Multi Pairs и Trader's Desired Pairs. После этого необходимо ввести только имя одной пары.
Эта опция позволяет советнику торговать только на паре, указанной в Trader Wishes Pairs.

2. В разделе Select Trading Pairs Mode выбрать Single Pair.
Если советник применяется к паре EURUSD, он будет торговать исключительно на ней.


1.1.2. Торговля в определенное время

В группе Trade on Specific Time (торговля в определенное время) предусмотрены опции для трейдеров, которые хотят торговать в определенном часовом поясе.
Возможно, многие трейдеры хотят торговать в соответствии с часовым поясом, чтобы торгуемые пары соответствовали времени торговой сессии, поэтому в этом советнике мы по-прежнему используем опцию торговой сессии (часовой пояс).


2. Сигнальные индикаторы.

2.1. Индикатор ZigZag.

Индикатор ZigZag — это метод измерения движения цены без лишнего шума. Он работает путем определения расстояния между колебаниями цен (максимумами и минимумами). По сути, индикатор рассчитывает уровень отката от очередного максимума/минимума. Если откат превышает определенную ожидаемую величину, движение цены считается завершенным.

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

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


Технические аналитики отмечают, что:

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

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

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


Настройки параметров ZigZag:

Индикатор ZigZag располагает тремя входными параметрами:

1. Depth (глубина) со значением по умолчанию 12.
Настройка относится к тому, как далеко в ряду баров графика индикатор будет формироваться.
Достаточная глубина необходима для определения максимумов и минимумов.
Это минимальное количество баров без второго максимального или минимального отклонения бара (например, если у нас есть максимум на свече x, а глубина равна 12, то индикатор не сможет нарисовать следующий максимум до тех пор, пока не наступит хотя бы x +12 свечей).

2. Deviation (отклонение) со значением по умолчанию 5.
Отклонение показывает, какой процент изменения цены необходим, чтобы изменить линию тренда с положительной на отрицательную.

3. Backstep (количество баров на истории) со значением по умолчанию 3.
Параметр определяет минимальное количество баров между максимумами и минимумами колебания


Формула индикатора ZigZag:

ZigZag (HL, %change=X, retrace=FALSE, LastExtreme=TRUE)

 Если %change>=X, отобразить ZigZag 

где:

HL - серия цен High-Low или цен закрытия 

%change - минимальное движение цены в %

Retrace - является ли изменение откатом от предыдущего движения или абсолютным изменением от пика к впадине?

Last Extreme - если экстремальная цена одинакова в течение нескольких периодов, является ли экстремальная цена первым или последним наблюдением? 


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


2.2. Awesome Oscillator (AO).

Awesome Oscillator (AO) — это индикатор импульса, используемый трейдерами для определения рыночных тенденций и импульса. Он был разработан Биллом Уильямсом, известным техническим аналитиком, и стал популярным инструментом среди трейдеров благодаря своей простоте и надежности.

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

Awesome Oscillator рассчитывается путем вычитания 34-периодной простой скользящей средней (SMA) из 5-периодной SMA средней точки (H+L)/2 цены финансового инструмента.

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

Значения 34-периодной простой скользящей средней (SMA) и 5-периодной SMA постоянно задаются в коде индикатора и остаются неизменными в свойствах входных параметров индикатора.

Awesome Oscillator = 5-периодная SMA (средняя цена, 5-периодная) – 34-периодная SMA (средняя цена, 34-периодная)

Медианная цена равна: (Максимальная цена за сессию + минимальная цена за сессию) / 2.


Для индикатора АО можно использовать как минимум 3 сигнальных алгоритма.
Поэтому в этом советнике я ввел настройку, позволяющую трейдерам выбрать и опробовать три сигнала алгоритма индикатора AO.

На рисунке ниже показаны индикаторы ZigZag и Awesome Oscillator, фильтрующие сигналы друг друга.

Иллюстрации сигналов ниже представляют собой опцию 2 сигнала ZigZag и опцию 2 сигнала AO

ZZ_AO_Indi_Signal_illustration

ZZ_AO_Indi_Signal_illustration_variation


3. Управление сделками и ордерами

Есть несколько способов управления сделками с помощью этого мультивалютного советника:

3.1. Ордера стоп-лосс.

Варианты: Use Order Stop Loss (Yes) или (No) - использовать ордер стоп-лосс: да или нет

  • При выборе Use Order Stop Loss (No) все ордера будут открываться без стоп-лосса.

Открытие ордера без стоп-лосса будет безопасным, если параметр Close Trade By Opposite Signal (закрыть сделку противоположным сигналом) установлен на Yes. В противном случае существует очень высокий риск для капитала. Поэтому я добавил функцию, позволяющую проверять процентное соотношение между эквити и балансом. В этом случае, если процент эквити баланса меньше (100% - Percent Equity Risk per Trade), советник запустит функцию CheckLoss() (функция для запуска виртуального стоп-лосса) и закроет ордера, у которых есть убыток, превышающий установленное значение стоп-лосса.
            //--
            if(use_sl==No && CheckVSLTP==Yes)
              {
                if(!mc.CheckEquityBalance())
                  if(mc.CloseAllLoss())
                    mc.Do_Alerts(symbol,"Close order due stop in loss to secure equity.");
              }
            //--

bool MCEA::CheckEquityBalance(void)
  {
//---
   bool isgood=false;
   if((mc_account.Equity()/mc_account.Balance()*100) > (100.00-Risk)) isgood=true;
   //--
   return(isgood);
//---
  } //-end CheckEquityBalance()
//---------//

bool MCEA::CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0)
   {
//---
     Pips(symbol);
     bool inloss=false;
     double lossval=slc==0.0 ? (SLval*0.5) : slc;
     double posloss  = mc_symbol.NormalizePrice(slc*pip);
     int ttlorder=PositionsTotal(); // number of open positions
     //--
     for(int x=0; x<arrsymbx; x++)
       {
         string symbol=DIRI[x];
         //--
         for(int i=ttlorder-1; i>=0; i--)
            {
              string position_Symbol   = PositionGetSymbol(i);
              ENUM_POSITION_TYPE  type = mc_position.PositionType();
              if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
                {
                  double price    = mc_position.PriceCurrent();
                  double pos_open = mc_position.PriceOpen();
                  double posloss  = mc_symbol.NormalizePrice(lossval*pip);
                  double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open));
                  //---
                  if(type==intype && pricegab>posloss) inloss=true;
                }
            }
       }
     //--
     return(inloss);
//----
   } //-end CheckLoss()
//---------//

bool MCEA::CloseAllLoss(void)
   {
//----
    ResetLastError();
    //--
    bool orclose=false;
    string isloss="due stop in loss.";
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //--
    int ttlorder=PositionsTotal(); // number of open positions
    //--
    for(int x=0; x<arrsymbx; x++)
       {
         string symbol=DIRI[x];
         Pips(symbol);
         double posloss=mc_symbol.NormalizePrice(SLval*pip);
         orclose=false;
         //--
         for(int i=ttlorder-1; i>=0; i--)
            {
              string position_Symbol   = PositionGetSymbol(i);
              ENUM_POSITION_TYPE  type = mc_position.PositionType();
              if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
                {
                  double price    = mc_position.PriceCurrent();
                  double pos_open = mc_position.PriceOpen();
                  double posloss  = mc_symbol.NormalizePrice(SLval*pip);
                  double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open));
                  ulong  position_ticket = PositionGetTicket(i);
                  //---
                  if(type==POSITION_TYPE_BUY && pricegab>posloss)
                    {
                      RefreshTick(position_Symbol);
                      orclose = mc_trade.PositionClose(position_Symbol,slip);
                      //--- output information about the closure
                      PrintFormat("Close Buy %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss);
                    }
                  if(type==POSITION_TYPE_SELL && pricegab>posloss)
                    {
                      RefreshTick(position_Symbol);
                      orclose = mc_trade.PositionClose(position_Symbol,slip);
                      //--- output information about the closure
                      PrintFormat("Close Sell %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss);
                    }
                }
            }
       }
     //--
     return(orclose);
//----
   } //-end CloseAllLoss()
//---------//

  • При выборе Use Order Stop Loss (Yes) снова появляется выбор: Use Automatic Calculation Stop Loss (Yes) или (No) - использовать автоматически рассчитываемый стоп-лосс: да или нет
  • При выборе Automatic Calculation Stop Loss (Yes) стоп-лосс рассчитывается советником.
  • При выборе Automatic Calculation Stop Loss (No) трейдеру необходимо ввести значение стоп-лосса в пипсах.
  • При выборе Use Order Stop Loss (No) советник будет проверять выполнение условий сигнала. Если они выполняются, ордер сохраняется. Если сигнал ослаб, ордер необходимо закрыть для сохранения прибыли или состояние сигнала изменило направление, и ордер должен быть закрыт, а убыток зафиксирован.

Примечание: Перед закрытием сделки из-за слабого сигнала запрашивается подтверждение пользователя.

В части функции ExpertActionTrade():

                //--
                if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes)
                  {
                    mc.CheckOpenPMx(symbol);
                    if(mc.profitb[x]>mc.minprofit && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) 
                      {
                        mc.CloseBuyPositions(symbol); 
                        mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal.");
                      }
                    if(mc.profits[x]>mc.minprofit && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy)
                      {
                        mc.CloseSellPositions(symbol); 
                        mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal.");
                      }
                  }
                //--

Код закрытия сделки и сохранения прибыли из-за слабого сигнала следующий:

int MCEA::GetCloseInWeakSignal(const string symbol,int exis) // Signal Indicator Position Close in profit
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    //--
    int AOdir=AOColorSignal(symbol);
    int ZZDir=ZigZagSignal(symbol);
    //--
    if(exis==down && (AOdir==rise||ZZDir==rise)) ret=rise;
    if(exis==rise && (AOdir==down||ZZDir==down)) ret=down;
    //--
    return(ret);
//---
  } //-end GetCloseInWeakSignal()
//---------//
  • При No, даже несмотря на то, что сигнал ослаб, ордер все равно будет сохранен или не будет закрыт для сохранения прибыли.
  • При Yes условия для индикатора ZigZag и AO следующие:

Для закрытия ордеров на покупку: 
Когда индикатор АО меняет цвет с зеленого на красный (цвет по умолчанию) или когда индикатор ZigZag достигает крайне высокого положения и цена меняет направление, ордер на покупку будет закрыт.

Для закрытия ордеров на продажу: 
Когда индикатор АО меняет цвет с красного на зеленый (цвет по умолчанию) или когда индикатор ZigZag достигает крайне низкого положения и цена меняет направление, ордер на продажу будет закрыт.

Код для установки стоп-лосса следующий:

double MCEA::OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice)
  {
//---
    slv=0.0;
    int x=PairsIdxArray(xsymb);
    Pips(xsymb);
    RefreshTick(xsymb);
    //--
    switch(type) 
      { 
       case (ORDER_TYPE_BUY):
         {
           if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice-38*pip);
           else
           if(use_sl==Yes && autosl==No)  slv=mc_symbol.NormalizePrice(atprice-SLval*pip);
           else slv=0.0;
           //--
           break;
         }
       case (ORDER_TYPE_SELL):
         {
           if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice+38*pip);
           else
           if(use_sl==Yes && autosl==No)  slv=mc_symbol.NormalizePrice(atprice+SLval*pip);
           else slv=0.0;
         }
      }
    //---
    return(slv);
//---
  } //-end OrderSLSet()
//---------//


3.2. Ордера тейк-профит

Варианты: Use Order Take Profit (Yes) или (No) - использовать тейк-профит ордера: да или нет

  • При выборе Use Order Take Profit (No) все ордера будут открываться без тейк-профита.
  • При выборе Use Order Take Profit (Yes) снова появляется выбор: Use Automatic Calculation Order Take Profit (Yes) или (No) - использовать автоматически рассчитываемый тейк-профит: да или нет
  • При выборе Automatic Calculation Order Take Profit (Yes) тейк-профит рассчитывается советником.
  • При выборе Automatic Calculation Order Take Profit (No) трейдеру необходимо ввести значение тейк-профита в пипсах.

Код для установки тейк-профита:

double MCEA::OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice)
  {
//---
    tpv=0.0;
    int x=PairsIdxArray(xsymb);
    Pips(xsymb);
    RefreshTick(xsymb);
    //--
    switch(type) 
      { 
       case (ORDER_TYPE_BUY):
         {
           if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice+50*pip);
           else
           if(use_tp==Yes && autotp==No)  tpv=mc_symbol.NormalizePrice(atprice+TPval*pip);
           else tpv=0.0;
           //--
           break;
         }
       case (ORDER_TYPE_SELL):
         {
           if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice-50*pip);
           else
           if(use_tp==Yes && autotp==No)  tpv=mc_symbol.NormalizePrice(atprice-TPval*pip);
           else tpv=0.0;
         }
      }
    //---
    return(tpv);
//---
  } //-end OrderTPSet()
//---------//


3.3. Trailing Stop – трейлинг-стоп

Варианты: Use Trailing Stop Loss (Yes) или (No) - использовать стоп-лосс трейлинга: да или нет

  • При Use Trailing SL равном No советник не будет использовать трейлинг стоп-лосса и тейк-профита.
  • При Use Trailing SL равном Yes трейдеры могут выбирать между тремя вариантами:

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

2. Трейлинг индикатора
Трейлинг-стоп выполняется советником с использованием индикатора.
Согласно моим исследованиям и экспериментам, индикатор VIDYA несколько лучше подходит для трейлинг-стопов по сравнению с Parabolic SAR или несколькими вариантами скользящей средней.
По сравнению с индикатором Parabolic SAR индикатор VIDYA находится ближе к движению цены, а по сравнению с индикаторами AMA, DEMA и MA индикатор VIDYA находится еще дальше от движения цены.
Поэтому в этой статье я решил использовать индикатор VIDYA для функции трейлинг-стопа на основе индикатора.

3. Трейлинг-стоп на уровне предыдущего максимального (HIGH) или минимального (LOW) бара
Для ордеров на покупку трейлинг-стоп будет размещен по цене LOW предыдущего бара (LOW[1]).
Для ордеров на продажу трейлинг-стоп будет размещен по цене HIGH предыдущего бара (HIGH[1]).

Функция Trailing Stop Price:

double MCEA::TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type)
  {
//---
    int br=2;
    double pval=0.0;
    int x=PairsIdxArray(xsymb);
    Pips(xsymb);
    //--
    switch(TS_type)
      {
        case byprice:
          {
            RefreshTick(xsymb);
            if(ptype==POSITION_TYPE_BUY)  pval=mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip);
            if(ptype==POSITION_TYPE_SELL) pval=mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip);
            break;
          }
        case byindi:
          {
            double VIDyAv[];
            ArrayResize(VIDyAv,br,br);
            ArraySetAsSeries(VIDyAv,true);
            CopyBuffer(hVIDyAv[x],0,0,br,VIDyAv);
            RefreshPrice(xsymb,TFt,br);
            //--
            if(ptype==POSITION_TYPE_BUY  && (VIDyAv[0]<mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip)))
               pval=VIDyAv[0];
            if(ptype==POSITION_TYPE_SELL && (VIDyAv[0]>mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip)))
               pval=VIDyAv[0];
            break;
          }
        case byHiLo:
          {
            UpdatePrice(xsymb,TFt,2);
            //--
            if(ptype==POSITION_TYPE_BUY  && (HIGH[0]>HIGH[1]))
               pval=LOW[1];
            if(ptype==POSITION_TYPE_SELL && (LOW[0]<LOW[1]))
               pval=HIGH[1];
            break;
          }
      }
    //--
    return(pval);
//---
  } //-end TSPrice()
//---------//


bool MCEA::ModifyOrdersSL(const string symbx,int TS_type)
  {
//---
   ResetLastError();
   MqlTradeRequest req={};
   MqlTradeResult  res={};
   MqlTradeCheckResult check={};
   //--
   int TRSP=TS_type;
   bool modist=false;
   int x=PairsIdxArray(symbx);
   Pips(symbx);
   //--
   int total=PositionsTotal();
   //--        
   for(int i=total-1; i>=0; i--) 
     {
       string symbol=PositionGetSymbol(i);
       if(symbol==symbx && mc_position.Magic()==magicEA)
         {
           ENUM_POSITION_TYPE opstype = mc_position.PositionType();
           if(opstype==POSITION_TYPE_BUY) 
             {
               RefreshTick(symbol);
               double price = mc_position.PriceCurrent();
               double vtrsb = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP));
               double pos_open   = mc_position.PriceOpen();
               double pos_stop   = mc_position.StopLoss();
               double pos_tp     = mc_position.TakeProfit();
               double pos_profit = mc_position.Profit();
               double pos_swap   = mc_position.Swap();
               double pos_comm   = mc_position.Commission();
               double netp=pos_profit+pos_swap+pos_comm;
               double modstart=mc_symbol.NormalizePrice(pos_open+TSmin*pip);
               double modminsl=mc_symbol.NormalizePrice(vtrsb+((TSmin-1.0)*pip));
               double modbuysl=vtrsb;
               bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0||modbuysl>pos_stop));
               //--
               if(modbuy && netp>minprofit)
                 {
                   modist=mc_trade.PositionModify(symbol,modbuysl,pos_tp);
                 }  
             }
           if(opstype==POSITION_TYPE_SELL) 
             {
               RefreshTick(symbol);
               double price = mc_position.PriceCurrent();
               double vtrss = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP));
               double pos_open   = mc_position.PriceOpen();
               double pos_stop   = mc_position.StopLoss();
               double pos_tp     = mc_position.TakeProfit();
               double pos_profit = mc_position.Profit();
               double pos_swap   = mc_position.Swap();
               double pos_comm   = mc_position.Commission();
               double netp=pos_profit+pos_swap+pos_comm;
               double modstart=mc_symbol.NormalizePrice(pos_open-TSmin*pip);
               double modminsl=mc_symbol.NormalizePrice(vtrss-((TSmin+1.0)*pip));
               double modselsl=vtrss;
               bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==0.0||modselsl<pos_stop)); 
               //--
               if(modsel && netp>minprofit)
                 {
                   modist=mc_trade.PositionModify(symbol,modselsl,pos_tp);
                 }  
             }
         }
     }
    //--
    return(modist);
//---
  } //-end ModifyOrdersSL()
//---------//


3.4. Трейлинг тейк-профита

Варианты: Use Trailing Take Profit (Yes) или (No) - использовать трейлинг тейк-профита: да или нет

  • При Use Trailing TP равном No советник не будет выполнять трейлинг тейк-профита.
  • При Use Trailing TP равном Yes необходимо установить значение Input Trailing Profit Value in Pips - значение скользящей прибыли в пипсах (по умолчанию 25 пипсов). Советник выполняет трейлинг-профит на основе значения переменной TPmin (минимальное значение скользящей прибыли).

bool MCEA::ModifyOrdersTP(const string symbx)
  {
//---
   ResetLastError();
   MqlTradeRequest req={};
   MqlTradeResult  res={};
   MqlTradeCheckResult check={};
   //--
   bool modist=false;
   int x=PairsIdxArray(symbx);
   Pips(symbx);
   //--
   int total=PositionsTotal();
   //--        
   for(int i=total-1; i>=0; i--) 
     {
       string symbol=PositionGetSymbol(i);
       if(symbol==symbx && mc_position.Magic()==magicEA)
         {
           ENUM_POSITION_TYPE opstype = mc_position.PositionType();
           if(opstype==POSITION_TYPE_BUY) 
             {
               RefreshTick(symbol);
               double price    = mc_position.PriceCurrent();
               double pos_open = mc_position.PriceOpen();
               double pos_stop = mc_position.StopLoss();
               double pos_tp   = mc_position.TakeProfit();
               double modbuytp = pos_tp==0.0 ? mc_symbol.NormalizePrice(pos_open+TPmin*pip) : pos_tp;
               double modpostp = mc_symbol.NormalizePrice(price+TPmin*pip);
               bool modtpb = (price>pos_open && modbuytp-price<TPmin*pip && pos_tp<modpostp);
               //--
               if(modtpb)
                 {
                   modist=mc_trade.PositionModify(symbol,pos_stop,modpostp);
                 }  
             }
           if(opstype==POSITION_TYPE_SELL) 
             {
               RefreshTick(symbol);
               double price    = mc_position.PriceCurrent();
               double pos_open = mc_position.PriceOpen();
               double pos_stop = mc_position.StopLoss();
               double pos_tp   = mc_position.TakeProfit();
               double modseltp = pos_tp==0.0 ? mc_symbol.NormalizePrice(pos_open-TPmin*pip) : pos_tp;
               double modpostp = mc_symbol.NormalizePrice(price-TPmin*pip);
               bool modtps = (price<pos_open && price-modseltp<TPmin*pip && pos_tp>modpostp);
               //--
               if(modtps)
                 {
                   modist=mc_trade.PositionModify(symbol,pos_stop,modpostp);
                 }  
             }
         }
     }
    //--
    return(modist);
//---
  } //-end ModifyOrdersTP()
//---------//


3.5. Закрытие сделки по противоположному сигналу

Варианты: Close Trade By Opposite Signal (Yes) or (No) - закрыть сделку по противоположному сигналу: да или нет

  • При Close Trade By Opposite Signal равном Yes:

Итак, если ранее был открыт ордер на продажу, а затем сигнал индикатора развернулся, то ордер на продажу будет закрыт, а затем советник откроет ордер на покупку. И наоборот.
В части функции ExpertActionTrade():
if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol);
bool MCEA::CloseSellPositions(const string symbol)
  {
    //---
    ResetLastError();
    bool sellclose=false;
    int total=PositionsTotal(); // number of open positions
    ENUM_POSITION_TYPE closetype = POSITION_TYPE_SELL;
    ENUM_ORDER_TYPE     type_req = ORDER_TYPE_BUY;
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //--
    int x=PairsIdxArray(symbol);
    //--- iterate over all open positions
    for(int i=total-1; i>=0; i--)
      {
        if(mc_position.SelectByIndex(i))
          {
            //--- Parameters of the order
            string position_Symbol   = PositionGetSymbol(i);
            ulong  position_ticket   = PositionGetTicket(i);
            ENUM_POSITION_TYPE  type = mc_position.PositionType();
            //--- if the MagicNumber matches
            if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
              { 
                //--
                if(type==closetype)
                  {
                    RefreshTick(position_Symbol);
                    sellclose=mc_trade.PositionClose(position_Symbol,slip);
                    //--- output information about the closure
                    PrintFormat("Close Sell #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type));
                  }
              }
          }
      }
   //---
   return(sellclose);
//----
   } //-end CloseSellPositions()
//---------//

В части функции ExpertActionTrade():

if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol);

bool MCEA::CloseBuyPositions(const string symbol)
   {
 //---
    //--
    ResetLastError();
    bool buyclose=false;
    int total=PositionsTotal(); // number of open positions
    ENUM_POSITION_TYPE closetype = POSITION_TYPE_BUY;
    ENUM_ORDER_TYPE     type_req = ORDER_TYPE_SELL;
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //--
    int x=PairsIdxArray(symbol);
    //--- iterate over all open positions
    for(int i=total-1; i>=0; i--)
      {
        if(mc_position.SelectByIndex(i))
          {
            //--- Parameters of the order
            string position_Symbol   = PositionGetSymbol(i);
            ulong  position_ticket   = PositionGetTicket(i);
            ENUM_POSITION_TYPE  type = mc_position.PositionType();
            //--- if the MagicNumber matches
            if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
              { 
                //--
                if(type==closetype)
                  {
                    RefreshTick(position_Symbol);
                    buyclose=mc_trade.PositionClose(position_Symbol,slip);
                    //--- output information about the closure
                    PrintFormat("Close Buy #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type));
                  }
              }
          }
      }
   //---
   return(buyclose);
//----
   } //-end CloseBuyPositions()
//---------//


Проблемы возникнут, если Close Trade By Opposite Signal равен Нет:

1. Ордер будет в два раза превышать общую сумму каждой торгуемой пары.

2. В одной паре будут ордера, находящиеся в убытке и в прибыли.

3. Эквити будет падать из-за несбалансированных условий убытков и прибылей.

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

В части функции ExpertActionTrade():

            //--
            mc.CheckOpenPMx(symbol);
            if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1))
              {
                if(mc.CheckProfitLoss(symbol))
                   mc.Do_Alerts(symbol,"Close order due stop in loss.");
              }
            //--

bool MCEA::CheckProfitLoss(const string symbol)
   {
//----
     ResetLastError();
     //--
     bool closeinloss=false;
     string isloss="due stop in loss.";
     //--
     int xx=PairsIdxArray(symbol);
     //--
     bool BuyProfitSellLoss=(xob[xx]>0 && CheckProfit(symbol,POSITION_TYPE_BUY)) && (xos[xx]>0 && CheckLoss(symbol,POSITION_TYPE_SELL,0.0));
     bool SellProfitBuyLoss=(xos[xx]>0 && CheckProfit(symbol,POSITION_TYPE_SELL)) && (xob[xx]>0 && CheckLoss(symbol,POSITION_TYPE_BUY,0.0));
     //--
     if(BuyProfitSellLoss && !SellProfitBuyLoss)
       {
         if(CloseSellPositions(symbol))
           {
             PrintFormat("Close Sell %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss);
             closeinloss=true;
           }
       }
     if(SellProfitBuyLoss && !BuyProfitSellLoss)
       {
         if(CloseBuyPositions(symbol))
           {
             PrintFormat("Close Buy %s %s %s",symbol,EnumToString(POSITION_TYPE_SELL),isloss);
             closeinloss=true;
           }
       }
     //--
     return(closeinloss);
//----
   } //-end CheckProfitLoss()
//---------//


Функция CheckProfitLoss() вызовет еще две функции, которые будут сравнивать ордер в одной паре с условиями:
Buy Profit и Sell Loss или Buy Loss и Sell Profit.

  • При Buy Profit и Sell Loss будет закрыт ордер на продажу.
  • При Buy Loss и Sell Profit будет закрыт ордер на покупку.

bool MCEA::CheckProfit(const string symbol,ENUM_POSITION_TYPE intype)
   {
//---
     Pips(symbol);
     double posprofit=mc_symbol.NormalizePrice((TPval*0.5)*pip);
     bool inprofit=false;
     //--
     int ttlorder=PositionsTotal(); // number of open positions
     //--
     for(int x=0; x<arrsymbx; x++)
       {
         string symbol=DIRI[x];
         //--
         for(int i=ttlorder-1; i>=0; i--)
            {
              string position_Symbol   = PositionGetSymbol(i);
              ENUM_POSITION_TYPE  type = mc_position.PositionType();
              if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
                {
                  double price     = mc_position.PriceCurrent();
                  double pos_open  = mc_position.PriceOpen();
                  double posprofit = mc_symbol.NormalizePrice((TPval*0.5)*pip);
                  double pricegab  = mc_symbol.NormalizePrice(fabs(price-pos_open));
                  //---
                  if(type==intype && posprofit<pricegab) inprofit=true;
                }
            }
       }
     //--
     return(inprofit);
//----
   } //-end CheckProfit()
//---------//

bool MCEA::CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0)
   {
//---
     Pips(symbol);
     bool inloss=false;
     double lossval=slc==0.0 ? (SLval*0.5) : slc;
     double posloss  = mc_symbol.NormalizePrice(slc*pip);
     int ttlorder=PositionsTotal(); // number of open positions
     //--
     for(int x=0; x<arrsymbx; x++)
       {
         string symbol=DIRI[x];
         //--
         for(int i=ttlorder-1; i>=0; i--)
            {
              string position_Symbol   = PositionGetSymbol(i);
              ENUM_POSITION_TYPE  type = mc_position.PositionType();
              if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
                {
                  double price    = mc_position.PriceCurrent();
                  double pos_open = mc_position.PriceOpen();
                  double posloss  = mc_symbol.NormalizePrice(lossval*pip);
                  double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open));
                  //---
                  if(type==intype && pricegab>posloss) inloss=true;
                }
            }
       }
     //--
     return(inloss);
//----
   } //-end CheckLoss()
//---------//


4. Ручное управление ордерами.

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

4.1. Set SL / TP All Orders (установить стоп-лосс/тейк-профит для всех ордеров):
Эта кнопка полезна, если трейдер установил Use Order Stop Loss на No и/или Use Order Take Profit на No, а затем хочет использовать стоп-лгосс и тейк-профит для всех ордеров. Одним нажатием на кнопку Set SL/TP All Orders все ордера будут изменены и будет применен стоп-лосс и/или тейк-профит.

4.2. Close All Orders (закрыть все ордера):
Если трейдер хочет закрыть все ордера, он может сделать это одним щелчком по кнопке Close All Orders.

4.3. Close All Orders Profit (закрыть все прибыльные ордера):
Чтобы закрыть все прибыльные ордера, достаточно щелчка по кнопке Close All Orders Profit.


5. Управление ордерами и символами.

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


Реализация планирования в MQL5-программе

1. Заголовок программы и входные свойства.

Включение файла заголовка в MQL5

//+------------------------------------------------------------------+
//|                             Include                              |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\AccountInfo.mqh>
//--
CTrade              mc_trade;
CSymbolInfo         mc_symbol;
CPositionInfo       mc_position; 
CAccountInfo        mc_account;
//---


Перечисление для использования часового пояса

//--
enum tm_zone
 {
   Cus_Session,        // Trading on Custom Session
   New_Zealand,        // Trading on New Zealand Session
   Australia,          // Trading on Autralia Sydney Session
   Asia_Tokyo,         // Trading on Asia Tokyo Session
   Europe_London,      // Trading on Europe London Session
   US_New_York         // Trading on US New York Session
 };
//--


Перечисление для выбора часов

//--
enum swhour
  {
    hr_00=0,   // 00:00
    hr_01=1,   // 01:00
    hr_02=2,   // 02:00
    hr_03=3,   // 03:00
    hr_04=4,   // 04:00
    hr_05=5,   // 05:00
    hr_06=6,   // 06:00
    hr_07=7,   // 07:00
    hr_08=8,   // 08:00
    hr_09=9,   // 09:00
    hr_10=10,  // 10:00
    hr_11=11,  // 11:00
    hr_12=12,  // 12:00
    hr_13=13,  // 13:00
    hr_14=14,  // 14:00
    hr_15=15,  // 15:00
    hr_16=16,  // 16:00
    hr_17=17,  // 17:00
    hr_18=18,  // 18:00
    hr_19=19,  // 19:00
    hr_20=20,  // 20:00
    hr_21=21,  // 21:00
    hr_22=22,  // 22:00
    hr_23=23   // 23:00
  };
//--


Перечисление для выбора минут

//--
enum inmnt
  {
    mn_00=0,   // Minute 0
    mn_05=5,   // Minute 5
    mn_10=10,  // Minute 10
    mn_15=15,  // Minute 15
    mn_20=20,  // Minute 20
    mn_25=25,  // Minute 25
    mn_30=30,  // Minute 30
    mn_35=35,  // Minute 35
    mn_40=40,  // Minute 40
    mn_45=45,  // Minute 45
    mn_50=50,  // Minute 50
    mn_55=55   // Minute 55
  };
//--


Перечисление для выбора 10 пар для торговли

//--
enum PairsTrade
 {
   All30,  // All Forex 30 Pairs
   TrdWi,  // Trader Wishes Pairs 
   Usds,   // Forex USD Pairs
   Eurs,   // Forex EUR Pairs
   Gbps,   // Forex GBP Pairs
   Auds,   // Forex AUD Pairs
   Nzds,   // Forex NZD Pairs
   Cads,   // Forex CDD Pairs
   Chfs,   // Forex CHF Pairs
   Jpys    // Forex JPY Pairs
 };   
//--


Перечисление YN используется для опций (Yes) или (No) в параметре советника

//--
enum YN
  {
   No,
   Yes
  };
//--


Перечисление для использования размера лота в управлении капиталом

//--
enum mmt
  {
   FixedLot,   // Fixed Lot Size
   DynamLot    // Dynamic Lot Size
  };
//--


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

//--
enum TFUSE
  {
   TFM5,     // 00_PERIOD_M5
   TFM15,    // 01_PERIOD_M15
   TFM30,    // 02_PERIOD_M30
   TFH1,     // 03_PERIOD_H1
   TFH2,     // 04_PERIOD_H2
   TFH3,     // 05_PERIOD_H3
   TFH4,     // 06_PERIOD_H4
   TFH6,     // 07_PERIOD_H6
   TFH8,     // 08_PERIOD_H8
   TFH12,    // 09_PERIOD_H12
   TFD1      // 10_PERIOD_D1
  };
//--

При перечислении TFUSE мы ограничиваем использование расчетов временных рамок для советников только начиная с TF-M5 и заканчивая TF-D1.


Перечисление для выбора типа, который будет использоваться при расчете трейлинг-стопа.

//--
enum TrType
  {
    byprice, // Trailing Stop by Price
    byindi,  // Trailing Stop by Indicator
    byHiLo   // Trailing Stop in HIGH or LOW bar
  };
//--


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

//--
enum MS
 {
   SP, // Single Pair
   MP  // Multi Pairs
 };
//--


Перечисление для выбора алгоритма сигнала индикатора ZigZag

//--
enum SignalZZ
 {
   SZZ1,  // ZigZagSignal 1
   SZZ2,  // ZigZagSignal 2
   SZZ3,  // ZigZagSignal 3
   SZZ4   // ZigZagSignal 4
 };
//--


Перечисление для выбора алгоритма сигнала индикатора AO

//--
enum SignalAO
 {
   SAO1,  // AOSignal 1
   SAO2,  // AOSignal 2
   SAO3   // AOSignal 3
 };
//--


Входные параметры советника

//---
input group               "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter
input TFUSE               tfinuse = TFH4;             // Select Expert TimeFrame, default PERIOD_H4
//---
input group               "=== ZigZag Indicator Input Properties ===";  // ZigZag Indicator Input Properties
input int                 zzDepth = 12;               // Input ZigZag Depth, default 12
input int                 zzDevia = 5;                // Input ZigZag Deviation, default 5
input int                 zzBackS = 3;                // Input ZigZag Back Step, default 3
input SignalZZ              sigzz = SZZ2;             // Select ZigZag Signal to Use
input SignalAO              sigao = SAO2;             // Select AO Signal to Use
//---
input group               "=== Selected Pairs to Trade ===";  // Selected Pairs to trading
input MS                trademode = MP;              // Select Trading Pairs Mode (Multi or Single)
input PairsTrade         usepairs = All30;           // Select Pairs to Use
input string         traderwishes = "eg. eurusd,usdchf"; // If Use Trader Wishes Pairs, input pair name here, separate by comma
//--
input group               "=== Money Management Lot Size Parameter ==="; // Money Management Lot Size Parameter
input mmt                  mmlot = DynamLot;         // Money Management Type
input double                Risk = 10.0;             // Percent Equity Risk per Trade (Min=1.0% / Max=10.0%)
input double                Lots = 0.01;             // Input Manual Lot Size FixedLot
//--Trade on Specific Time
input group               "=== Trade on Specific Time ==="; // Trade on Specific Time
input YN           trd_time_zone = Yes;              // Select If You Like to Trade on Specific Time Zone
input tm_zone            session = Cus_Session;      // Select Trading Time Zone
input swhour            stsescuh = hr_00;            // Time Hour to Start Trading Custom Session (0-23)
input inmnt             stsescum = mn_15;            // Time Minute to Start Trading Custom Session (0-55)
input swhour            clsescuh = hr_23;            // Time Hour to Stop Trading Custom Session (0-23)
input inmnt             clsescum = mn_55;            // Time Minute to Stop Trading Custom Session (0-55)
//--Day Trading On/Off
input group               "=== Day Trading On/Off ==="; // Day Trading On/Off
input YN                    ttd0 = No;               // Select Trading on Sunday (Yes) or (No)
input YN                    ttd1 = Yes;              // Select Trading on Monday (Yes) or (No)
input YN                    ttd2 = Yes;              // Select Trading on Tuesday (Yes) or (No)
input YN                    ttd3 = Yes;              // Select Trading on Wednesday (Yes) or (No)
input YN                    ttd4 = Yes;              // Select Trading on Thursday (Yes) or (No)
input YN                    ttd5 = Yes;              // Select Trading on Friday (Yes) or (No)
input YN                    ttd6 = No;               // Select Trading on Saturday (Yes) or (No)
//--Trade & Order management Parameter
input group               "=== Trade & Order management Parameter ==="; // Trade & Order management Parameter
input YN                  use_sl = No;               // Use Order Stop Loss (Yes) or (No)
input YN                  autosl = Yes;              // Use Automatic Calculation Stop Loss (Yes) or (No)
input double               SLval = 30.0;             // If Not Use Automatic SL - Input SL value in Pips
input YN                  use_tp = Yes;              // Use Order Take Profit (Yes) or (No)
input YN                  autotp = Yes;              // Use Automatic Calculation Take Profit (Yes) or (No)
input double               TPval = 60.0;             // If Not Use Automatic TP - Input TP value in Pips
input YN              TrailingSL = Yes;              // Use Trailing Stop Loss (Yes) or (No)
input TrType               trlby = byHiLo;           // Select Trailing Stop Type
input double               TSval = 10.0;             // If Use Trailing Stop by Price Input value in Pips
input double               TSmin = 5.0;              // Minimum Pips to start Trailing Stop
input YN              TrailingTP = Yes;              // Use Trailing Take Profit (Yes) or (No)
input double               TPmin = 25.0;             // Input Trailing Profit Value in Pips
input YN           Close_by_Opps = Yes;              // Close Trade By Opposite Signal (Yes) or (No)
input YN               SaveOnRev = Yes;              // Close Trade and Save profit due to weak signal (Yes) or (No)
input YN              CheckVSLTP = Yes;              // Check Virtual SL/TP & Close Loss Trade (Yes) or (No)
//--Others Expert Advisor Parameter
input group               "=== Others Expert Advisor Parameter ==="; // Others EA Parameter
input YN                  alerts = Yes;              // Display Alerts / Messages (Yes) or (No)
input YN           UseEmailAlert = No;               // Email Alert (Yes) or (No)
input YN           UseSendnotify = No;               // Send Notification (Yes) or (No)
input YN      trade_info_display = Yes;              // Select Display Trading Info on Chart (Yes) or (No)
input ulong              magicEA = 20240218;         // Expert ID (Magic Number)
//---

Примечание: Если входной параметр Expert ID (Magic Number) оставить пустым, советник сможет управлять ордерами, открытыми вручную.


В группе входных свойств Global Strategy EA Parameters советника трейдерам предлагается выбрать таймфрейм для расчета сигналов индикатора. Значение по умолчанию - PERIOD_H4.

В группе свойств входных данных Select Pairs to Trade необходимо выбрать пару для торговли из 10 предоставленных вариантов. Значение по умолчанию - All Forex 30 Pairs (все 30 форекс-пар).

Чтобы настроить торгуемую пару, вызовем функцию HandlingSymbolArrays(). С помощью функции HandlingSymbolArrays() мы будем обрабатывать все торгуемые пары.

void MCEA::HandlingSymbolArrays(void)
  {
//---
    string All30[]={"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","USDJPY","EURGBP",
                    "EURAUD","EURNZD","EURCAD","EURCHF","EURJPY","GBPAUD","GBPNZD","GBPCAD",
                    "GBPCHF","GBPJPY","AUDNZD","AUDCAD","AUDCHF","AUDJPY","NZDCAD","NZDCHF",
                    "NZDJPY","CADCHF","CADJPY","CHFJPY","XAUUSD","XAGUSD"}; // 30 pairs
    string USDs[]={"USDCAD","USDCHF","USDJPY","AUDUSD","EURUSD","GBPUSD","NZDUSD","XAUUSD","XAGUSD"}; // USD pairs
    string EURs[]={"EURAUD","EURCAD","EURCHF","EURGBP","EURJPY","EURNZD","EURUSD"}; // EUR pairs
    string GBPs[]={"GBPAUD","GBPCAD","GBPCHF","EURGBP","GBPJPY","GBPNZD","GBPUSD"}; // GBP pairs
    string AUDs[]={"AUDCAD","AUDCHF","EURAUD","GBPAUD","AUDJPY","AUDNZD","AUDUSD"}; // AUD pairs
    string NZDs[]={"AUDNZD","NZDCAD","NZDCHF","EURNZD","GBPNZD","NZDJPY","NZDUSD"}; // NZD pairs
    string CADs[]={"AUDCAD","CADCHF","EURCAD","GBPCAD","CADJPY","NZDCAD","USDCAD"}; // CAD pairs
    string CHFs[]={"AUDCHF","CADCHF","EURCHF","GBPCHF","NZDCHF","CHFJPY","USDCHF"}; // CHF pairs
    string JPYs[]={"AUDJPY","CADJPY","CHFJPY","EURJPY","GBPJPY","NZDJPY","USDJPY"}; // JPY pairs
    //--
    sall=ArraySize(All30);
    arusd=ArraySize(USDs);
    areur=ArraySize(EURs);
    aretc=ArraySize(JPYs);
    ArrayResize(VSym,sall,sall);
    ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY);
    //--
    if(usepairs==TrdWi && StringFind(traderwishes,"eg.",0)<0)
      {
        string to_split=traderwishes; // A string to split into substrings pairs name
        string sep=",";               // A separator as a character 
        ushort u_sep;                 // The code of the separator character 
        //--- Get the separator code 
        u_sep=StringGetCharacter(sep,0);
        //--- Split the string to substrings 
        int p=StringSplit(to_split,u_sep,SPC); 
        if(p>0)
          {
            for(int i=0; i<p; i++) StringToUpper(SPC[i]);
            //--
            for(int i=0; i<p; i++)
              {
                if(ValidatePairs(SPC[i])<0) ArrayRemove(SPC,i,1);
              }
          }
        arspc=ArraySize(SPC);
      }
    //--
    SetSymbolNamePS();      // With this function we will detect whether the Symbol Name has a prefix and/or suffix
    //--
    if(inpre>0 || insuf>0)
      {
        if(usepairs==TrdWi && arspc>0)
          {
            for(int t=0; t<arspc; t++)
              {
                SPC[t]=pre+SPC[t]+suf;
              }
          }
        //--
        for(int t=0; t<sall; t++)
          {
            All30[t]=pre+All30[t]+suf;
          }
        for(int t=0; t<arusd; t++)
          {
            USDs[t]=pre+USDs[t]+suf;
          }
        for(int t=0; t<areur; t++)
          {
            EURs[t]=pre+EURs[t]+suf;
          }
        for(int t=0; t<aretc; t++)
          {
            GBPs[t]=pre+GBPs[t]+suf;
          }
        for(int t=0; t<aretc; t++)
          {
            AUDs[t]=pre+AUDs[t]+suf;
          }
        for(int t=0; t<aretc; t++)
          {
            NZDs[t]=pre+NZDs[t]+suf;
          }
        for(int t=0; t<aretc; t++)
          {
            CADs[t]=pre+CADs[t]+suf;
          }
        for(int t=0; t<aretc; t++)
          {
            CHFs[t]=pre+CHFs[t]+suf;
          }
        for(int t=0; t<aretc; t++)
          {
            JPYs[t]=pre+JPYs[t]+suf;
          }
      }
    //--
    ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY);
    ArrayResize(AS30,sall,sall);
    ArrayCopy(AS30,All30,0,0,WHOLE_ARRAY);
    for(int x=0; x<sall; x++) {SymbolSelect(AS30[x],true);}
    if(ValidatePairs(Symbol())>=0) symbfix=true;
    if(!symbfix) 
      {
        Alert("Expert Advisors will not trade on pairs "+Symbol());
        Alert("-- "+expname+" -- ",Symbol()," -- expert advisor will be Remove from the chart.");
        ExpertRemove();
      }
    //--
    switch(usepairs)
      {
        case 0: // All Forex & Metal 30 Pairs
          {
            ArrayResize(DIRI,sall,sall);
            arrsymbx=sall;
            ArraySymbolResize();
            ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY);
            pairs="Multi Currency "+string(sall)+" Pairs";
            //--
            break;
          }
        case 1: // Trader wishes pairs
          {
            ArrayResize(DIRI,arspc,arspc);
            arrsymbx=arspc;
            ArraySymbolResize();
            ArrayCopy(DIRI,SPC,0,0,WHOLE_ARRAY);
            pairs="("+string(arspc)+") Trader Wishes Pairs";
            //--
            break;
          }
        case 2: // USD pairs
          {
            ArrayResize(DIRI,arusd,arusd);
            arrsymbx=arusd;
            ArraySymbolResize();
            ArrayCopy(DIRI,USDs,0,0,WHOLE_ARRAY);
            pairs="("+string(arusd)+") Multi Currency USD Pairs";
            //--
            break;
          }
        case 3: // EUR pairs
          {
            ArrayResize(DIRI,areur,areur);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,EURs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex EUR Pairs";
            //--
            break;
          }
        case 4: // GBP pairs
          {
            ArrayResize(DIRI,aretc,aretc);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,GBPs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex GBP Pairs";
            //--
            break;
          }
        case 5: // AUD pairs
          {
            ArrayResize(DIRI,aretc,aretc);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,AUDs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex AUD Pairs";
            //--
            break;
          }
        case 6: // NZD pairs
          {
            ArrayResize(DIRI,aretc,aretc);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,NZDs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex NZD Pairs";
            //--
            break;
          }
        case 7: // CAD pairs
          {
            ArrayResize(DIRI,aretc,aretc);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,CADs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex CAD Pairs";
            //--
            break;
          }
        case 8: // CHF pairs
          {
            ArrayResize(DIRI,aretc,aretc);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,CHFs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex CHF Pairs";
            //--
            break;
          }
        case 9: // JPY pairs
          {
            ArrayResize(DIRI,aretc,aretc);
            arrsymbx=aretc;
            ArraySymbolResize();
            ArrayCopy(DIRI,JPYs,0,0,WHOLE_ARRAY);
            pairs="("+string(aretc)+") Forex JPY Pairs";
            //--
            break;
          }
      }
    //--
    return;
//---
  } //-end HandlingSymbolArrays()
//---------//


Внутри функции HandlingSymbolArrays() вызовем функцию SetSymbolNamePS(). Используя SetSymbolNamePS(), мы сможем обрабатывать имена символов с префиксом и/или суффиксом.

void MCEA::SetSymbolNamePS(void)
  {
//---
   int sym_Lenpre=0;
   int sym_Lensuf=0;
   string sym_pre="";
   string sym_suf="";
   SymbolSelect(Symbol(),true);
   string insymbol=Symbol();
   int inlen=StringLen(insymbol);
   int toseek=-1;
   string dep="";
   string bel="";
   string sym_use ="";
   int pairx=-1;
   string xcur[]={"EUR","GBP","AUD","NZD","USD","CAD","CHF"}; // 7 major currency
   int xcar=ArraySize(xcur);
   //--
   for(int x=0; x<xcar; x++)
     {
       toseek=StringFind(insymbol,xcur[x],0);
       if(toseek>=0)
         {
           pairx=x;
           break;
         }
     }
   if(pairx>=0)
     {
       int awl=toseek-3 <0 ? 0 : toseek-3;
       int sd=StringFind(insymbol,"SD",0);
       if(toseek==0 && sd<4)
         {
           dep=StringSubstr(insymbol,toseek,3);
           bel=StringSubstr(insymbol,toseek+3,3);
           sym_use=dep+bel;
         }
       else
       if(toseek>0)
         {
           dep=StringSubstr(insymbol,toseek,3);
           bel=StringSubstr(insymbol,toseek+3,3);
           sym_use=dep+bel;
         }
       else
         {
           dep=StringSubstr(insymbol,awl,3);
           bel=StringSubstr(insymbol,awl+3,3);
           sym_use=dep+bel;
         }
     }
   //--
   string sym_nmx=sym_use;
   int lensx=StringLen(sym_nmx);
   //--
   if(inlen>lensx && lensx==6)
     {
       sym_Lenpre=StringFind(insymbol,sym_nmx,0);
       sym_Lensuf=inlen-lensx-sym_Lenpre;
       //--
       if(sym_Lenpre>0)
         {
           sym_pre=StringSubstr(insymbol,0,sym_Lenpre);
           for(int i=0; i<xcar; i++)
             if(StringFind(sym_pre,xcur[i],0)>=0) sym_pre="";
         }
       if(sym_Lensuf>0)
         {
           sym_suf=StringSubstr(insymbol,sym_Lenpre+lensx,sym_Lensuf);
           for(int i=0; i<xcar; i++)
             if(StringFind(sym_suf,xcur[i],0)>=0) sym_suf="";
         }
     }
   //--
   pre=sym_pre;
   suf=sym_suf;
   inpre=StringLen(pre);
   insuf=StringLen(suf);
   posCur1=inpre;
   posCur2=posCur1+3;
   //--
   return;
//---
  } //-end SetSymbolNamePS()
//---------//

Примечание: Советник проверяет пары. Если трейдер допустил ошибку при вводе названия пары либо проверка пары не удалась, советник выдаст предупреждение и будет удален с графика.


В группе свойств входных данных Trade on Specific Time, трейдеру нужно выбрать Trade on Specific Time Zone (Yes) или (No) - торговать в определенном часовом поясе: да или нет.

При Trade on Specific Time Zone option равном No, советник будет торговать с 00:15 до 23:59.

При Yes необходимо выбрать параметры перечисления:

  • Trading on Custom Session (пользовательская сессия)
  • Trading on New Zealand Session (новозеландская сессия)
  • Trading on Australia Sydney Session (сиднейская сессия)
  • Trading on Asia Tokyo Session (токийская сессия)
  • Trading on Europe London Session (лондонская сессия)
  • Trading on America New York Session (нью-йоркская сессия)

По умолчанию Trading on Specific Time Zones равен Yes и установлен на Trading on Custom Sessions.

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

В остальных случаях время начала и окончания торговли выбирается советником.


2. Класс для работы советника

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

//+------------------------------------------------------------------+
//| Class for working Expert Advisor                                 |
//+------------------------------------------------------------------+
class MCEA
  {
//---
    private:
    //---- 
    int              x_year;       // Year 
    int              x_mon;        // Month 
    int              x_day;        // Day of the month 
    int              x_hour;       // Hour in a day 
    int              x_min;        // Minutes 
    int              x_sec;        // Seconds
    //--
    int              oBm,
                     oSm,
                     ldig;
    //--- Variables used in prefix and suffix symbols
    int              posCur1,
                     posCur2;
    int              inpre,
                     insuf;
    bool             symbfix;
    string           pre,suf;
    string           prefix,suffix;       
    //--- Variables are used in Trading Time Zone
    int              ishour,
                     onhour;
    int              tftrlst,
                     tfcinws;
    datetime         rem,
                     znop,
                     zncl,
                     zntm;
    datetime         SesCuOp,
                     SesCuCl,
                     Ses01Op,
                     Ses01Cl,
                     Ses02Op,
                     Ses02Cl,
                     Ses03Op,
                     Ses03Cl,
                     Ses04Op,
                     Ses04Cl,
                     Ses05Op,
                     Ses05Cl,
                     SesNoOp,
                     SesNoCl;
    //--
    string           tz_ses,
                     tz_opn,
                     tz_cls;
    //--
    string           tmopcu,
                     tmclcu,
                     tmop01,
                     tmcl01,
                     tmop02,
                     tmcl02,
                     tmop03,
                     tmcl03,
                     tmop04,
                     tmcl04,
                     tmop05,
                     tmcl05,
                     tmopno,
                     tmclno;      
    //----------------------
    //--
    double           LotPS;
    double           point;
    double           slv,
                     tpv,
                     pip,
                     xpip;
    double           SARstep,
                     SARmaxi;
    double           floatprofit,
                     fixclprofit;
    //--
    string           pairs,
                     hariini,
                     daytrade,
                     trade_mode;
    //--
    double           OPEN[],
                     HIGH[],
                     LOW[],
                     CLOSE[];
    datetime         TIME[];
    datetime         closetime;
    //--
    //------------
     
    //------------
    void             SetSymbolNamePS(void);
    void             HandlingSymbolArrays(void);
    void             Set_Time_Zone(void);
    void             Time_Zone(void);
    bool             Trade_session(void);
    string           PosTimeZone(void);
    int              ThisTime(const int reqmode);
    int              ReqTime(datetime reqtime,const int reqmode);
    //--
    int              DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf);
    int              GetIndiSignals(const string symbol);
    int              ZigZagSignal(const string symbol);
    int              AOSignal(const string symbol);
    int              AOColorSignal(const string symbol);
    int              PARSAR05(const string symbol);
    int              PARSAR15(const string symbol);
    int              LotDig(const string symbol);
    //--
    double           MLots(const string symbx);
    double           NonZeroDiv(double val1,double val2);
    double           OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice);
    double           OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice);
    double           SetOrderSL(const string xsymb,ENUM_POSITION_TYPE type,double atprice);
    double           SetOrderTP(const string xsymb,ENUM_POSITION_TYPE type,double atprice);
    double           TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type);
    //--
    string           ReqDate(int d,int h,int m);
    string           TF2Str(ENUM_TIMEFRAMES period);
    string           timehr(int hr,int mn);
    string           TradingDay(void);
    string           AccountMode();
    string           GetCommentForOrder(void)             { return(expname); }
    //------------

    public:
    //---
    
    //-- ZigZag_AO_MCEA Config --
    string           DIRI[],
                     AS30[],
                     VSym[];
    string           SPC[];
    string           USD[];
    string           EUR[];
    string           GBP[];
    string           AUD[];
    string           NZD[];
    string           CAD[];
    string           CHF[];
    string           JPY[];             
    //--                 
    string           expname;
    string           indiname1;
    //--
    //--- Indicators Handle
    int              hZigZag[],
                     hAO[];
    int              hVIDyAv[];
    int              hPar05[],
                     hPar15[];
    //---
    int              ALO,
                     dgts,
                     arrsar,
                     arrsymbx;
    int              sall,
                     arusd,
                     areur,
                     aretc,
                     arspc,
                     arper;
    ulong            slip;        
    //--
    double           profitb[],
                     profits[];
    double           minprofit;
    //--
    int              Buy,
                     Sell;
    int              ccur,
                     psec,
                     xtto,
                     checktml;
    int              OpOr[],xob[],xos[];         
    //--
    int              year,  // Year 
                     mon,   // Month 
                     day,   // Day 
                     hour,  // Hour 
                     min,   // Minutes 
                     sec,   // Seconds 
                     dow,   // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) 
                     doy;   // Day number of the year (January 1st is assigned the number value of zero)
    //--
    ENUM_TIMEFRAMES  TFt,
                     TFT05,
                     TFT15;
    //--
    bool             PanelExtra;
    //------------
                     MCEA(void);
                     ~MCEA(void);            
    //------------
    //--
    virtual void     ZigZag_AO_MCEA_Config(void);
    virtual void     ExpertActionTrade(void);
    //--
    void             ArraySymbolResize(void);
    void             CurrentSymbolSet(const string symbol);
    void             Pips(const string symbol);
    void             TradeInfo(void);
    void             Do_Alerts(const string symbx,string msgText);
    void             CheckOpenPMx(const string symbx);
    void             SetSLTPOrders(void);
    void             CloseAllOrders(void);
    void             CheckClose(const string symbx);
    void             TodayOrders(void);
    void             UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf);
    void             UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf,int bars);
    void             RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars);
    //--
    bool             CheckEquityBalance(void);
    bool             RefreshTick(const string symbx);  
    bool             TradingToday(void);
    bool             OpenBuy(const string symbol);
    bool             OpenSell(const string symbol);
    bool             ModifyOrderSLTP(double mStop,double ordtp);
    bool             ModifyOrdersSL(const string symbx,int TS_type);
    bool             ModifyOrdersTP(const string symbx);
    bool             CloseAllProfit(void);
    bool             CloseAllLoss(void);
    bool             ManualCloseAllProfit(void);
    bool             CheckProfitLoss(const string symbol);
    bool             CloseBuyPositions(const string symbol);
    bool             CloseSellPositions(const string symbol);
    bool             CheckProfit(const string symbol,ENUM_POSITION_TYPE intype);
    bool             CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0);
    //--
    int              PairsIdxArray(const string symbol);
    int              ValidatePairs(const string symbol);
    int              GetOpenPosition(const string symbol);
    int              GetCloseInWeakSignal(const string symbol,int exis);
    //--
    string           getUninitReasonText(int reasonCode);
    //--
    //------------
//---
  }; //-end class MCEA
//---------//
 
MCEA mc;

//---------//

Первая и наиболее важная функция в рабочем процессе мультивалютного советника, вызываемая из OnInit(), — это ZigZag_AO_MCEA_Config().

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//---
   mc.ZigZag_AO_MCEA_Config();
   //--
   return(INIT_SUCCEEDED);
//---
  } //-end OnInit()
//---------//


В функции ZigZag_AO_MCEA_Config() настраиваются все используемые символы, все таймфреймы, все используемые хэндлы индикаторов и некоторые важные функции заголовка включаемого файла.

Функция ZigZag_AO_MCEA_Config() описывает и реализует способы обработки таймфреймов и создания хэндлв индикаторов для всех индикаторов, используемых в рабочем процессе советника.

//+------------------------------------------------------------------+
//| Expert Configuration                                             |
//+------------------------------------------------------------------+
void MCEA::ZigZag_AO_MCEA_Config(void) 
  {
//---
    //--
    HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded
    //--
    TFT05=PERIOD_M5;
    TFT15=PERIOD_M15;
    ENUM_TIMEFRAMES TFs[]={PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,PERIOD_D1};
    int arTFs=ArraySize(TFs);
    //--
    for(int x=0; x<arTFs; x++) if(tfinuse==x) TFt=TFs[x]; // TF for calculation signal
    //--
    //-- Indicators handle for all symbol
    for(int x=0; x<arrsymbx; x++) 
      {
        hZigZag[x] = iCustom(DIRI[x],TFt,indiname1,zzDepth,zzDevia,zzBackS);   //-- Handle for the ZigZag indicator
        hAO[x]     = iAO(DIRI[x],TFt);                                         //-- Handle for the Awesome_Oscillator indicator
        hVIDyAv[x] = iVIDyA(DIRI[x],TFt,9,12,0,PRICE_WEIGHTED);                //-- Handle for the VIDYA indicator for Trailing Stop
        hPar05[x]  = iSAR(DIRI[x],TFT05,SARstep,SARmaxi);                      //-- Handle for the iSAR indicator for M5 Timeframe
        hPar15[x]  = iSAR(DIRI[x],TFT15,SARstep,SARmaxi);                      //-- Handle for the iSAR indicator for M15 Timeframe
        //--
      }
    //--
    TesterHideIndicators(true);
    minprofit=NormalizeDouble(TSmin/100.0,2);
    //--
    ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders();
    if(Close_by_Opps==No) 
      {
        if((int)mc_account.LimitOrders()>=(sall*2)) ALO=sall*2;
        else 
        ALO=(int)(mc_account.LimitOrders()/2);
      }
    //--
    LotPS=(double)ALO;
    //--
    mc_trade.SetExpertMagicNumber(magicEA);
    mc_trade.SetDeviationInPoints(slip);
    mc_trade.SetMarginMode();
    Set_Time_Zone();
    //--
    return;
//---
  } //-end ZigZag_AO_MCEA_Config()
//---------//


3. Функция Expert tick и необходимые действия

В функции Expert tick (OnTick()) мы будем вызывать одну из основных функций мультивалютного советника, а именно функцию ExpertActionTrade().

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(void)
  {
//---
    mc.ExpertActionTrade();
    //--
    return;
//---
  } //-end OnTick()
//---------//


В эту функцию включен весь процесс работы советника.

Это означает, что функция ExpertActionTrade() будет выполнять все действия и управлять автоматической торговлей, включая открытие/закрытие ордеров, трейлинг-стоп, трейлинг тейк-профита и другие дополнительные действия.

void MCEA::ExpertActionTrade(void)
  {
//---
    //--Check Trading Terminal
    ResetLastError();
    //--
    if(!MQLInfoInteger(MQL_TRADE_ALLOWED) && mc.checktml==0) //-- Check whether MT5 Algorithmic trading is Allow or Prohibit
      {
        mc.Do_Alerts(Symbol(),"Trading Expert at "+Symbol()+" are NOT Allowed by Setting.");
        mc.checktml=1;  //-- Variable checktml is given a value of 1, so that the alert is only done once.
        return;
      }
    //--
    if(!DisplayManualButton("M","C","R")) DisplayManualButton(); //-- Show the expert manual button panel
    //--
    if(trade_info_display==Yes) mc.TradeInfo(); //-- Displayed Trading Info on Chart
    //---
    //--
    int mcsec=mc.ThisTime(mc.sec); 
    //--
    if(fmod((double)mcsec,5.0)==0) mc.ccur=mcsec;
    //--
    if(mc.ccur!=mc.psec)
      {
        string symbol;
        //-- Here we start with the rotation of the name of all symbol or pairs to be traded
        for(int x=0; x<mc.arrsymbx && !IsStopped(); x++) 
          {
            //--
            switch(trademode)
              {
                case SP:
                  {
                    if(mc.DIRI[x]!=Symbol()) continue;
                    symbol=Symbol();
                    mc.pairs=mc.pairs+" ("+symbol+")";
                    break;
                  }
                case MP:
                  {
                    if(mc.DIRI[x]==Symbol()) symbol=Symbol();
                    else symbol=mc.DIRI[x];
                    break;
                  }
              }
            //--
            mc.CurrentSymbolSet(symbol);
            //--
            if(mc.TradingToday() && mc.Trade_session())
              {
                //--
                mc.OpOr[x]=mc.GetOpenPosition(symbol); //-- Get trading signals to open positions
                //--                                   //-- and store in the variable OpOr[x]
                if(mc.OpOr[x]==mc.Buy) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Buy" (value=1)
                  {
                    //--
                    mc.CheckOpenPMx(symbol);
                    //--
                    if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol);
                    //--
                    if(mc.xob[x]==0 && mc.xtto<mc.ALO) mc.OpenBuy(symbol);
                    else
                    if(mc.xtto>=mc.ALO)
                      {
                        //--
                        mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+
                                            "\n the limit = "+string(mc.ALO)+" Orders ");
                        //--
                        mc.CheckOpenPMx(symbol);
                        //--
                        if(mc.xos[x]>0 && mc.profits[x]<-1.02 && mc.xob[x]==0) {mc.CloseSellPositions(symbol); mc.OpenBuy(symbol);}
                        else
                          mc.CloseAllProfit();
                        //--
                        if(mc.xos[x]>0 && use_sl==No && CheckVSLTP==Yes)
                          {
                            if(mc.CheckLoss(symbol,POSITION_TYPE_SELL,SLval)) 
                              if(mc.CloseSellPositions(symbol))
                                mc.Do_Alerts(symbol,"Check Profit Trade and Close order due stop in loss.");
                          }
                      }
                  }
                if(mc.OpOr[x]==mc.Sell) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-1)
                  {
                    //--
                    mc.CheckOpenPMx(symbol);
                    //--
                    if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol);
                    //--
                    if(mc.xos[x]==0 && mc.xtto<mc.ALO) mc.OpenSell(symbol);
                    else
                    if(mc.xtto>=mc.ALO)
                      {
                        //--
                        mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+
                                            "\n the limit = "+string(mc.ALO)+" Orders ");
                        //--
                        mc.CheckOpenPMx(symbol);
                        //--
                        if(mc.xob[x]>0 && mc.profitb[x]<-1.02 && mc.xos[x]==0) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);}
                        else
                          mc.CloseAllProfit();
                        //--
                        if(mc.xob[x]>0 && use_sl==No && CheckVSLTP==Yes)
                          {
                            if(mc.CheckLoss(symbol,POSITION_TYPE_BUY,SLval)) 
                              if(mc.CloseBuyPositions(symbol))
                                mc.Do_Alerts(symbol,"Check Profit Trade and Close order due stop in loss.");
                          }
                      }
                  }
              }
            //--
            mc.CheckOpenPMx(symbol);
            //--
            if(mc.xtto>0)
              {
                //--
                if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes)
                  {
                    mc.CheckOpenPMx(symbol);
                    if(mc.profitb[x]>mc.minprofit && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) 
                      {
                        mc.CloseBuyPositions(symbol); 
                        mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal.");
                      }
                    if(mc.profits[x]>mc.minprofit && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy)
                      {
                        mc.CloseSellPositions(symbol); 
                        mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal.");
                      }
                  }
                //--
                if(TrailingSL==Yes) mc.ModifyOrdersSL(symbol,trlby); //-- Use Trailing Stop Loss (Yes)
                if(TrailingTP==Yes) mc.ModifyOrdersTP(symbol);       //-- Use Trailing Take Profit (Yes)
              }
            //--
            mc.CheckOpenPMx(symbol);
            if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1))
              {
                if(mc.CheckProfitLoss(symbol))
                   mc.Do_Alerts(symbol,"Close order due stop in loss.");
              }
            //--
            if(use_sl==No && CheckVSLTP==Yes)
              {
                if(!mc.CheckEquityBalance())
                  if(mc.CloseAllLoss())
                    mc.Do_Alerts(symbol,"Close order due stop in loss to secure equity.");
              }
            //--
            mc.CheckClose(symbol);
          }
        //--
        mc.psec=mc.ccur;
      }
    //--
    return;
//---
  } //-end ExpertActionTrade()
//---------//


Между тем, группа свойств Day Trading On/Off позволяет трейдерам торговать в определенные дни с воскресенья по субботу. Это позволяет трейдерам включать или отключать советников в определенный день, используя опции Yes или No.

//--Day Trading On/Off
input group               "=== Day Trading On/Off ==="; // Day Trading On/Off
input YN                    ttd0 = No;               // Select Trading on Sunday (Yes) or (No)
input YN                    ttd1 = Yes;              // Select Trading on Monday (Yes) or (No)
input YN                    ttd2 = Yes;              // Select Trading on Tuesday (Yes) or (No)
input YN                    ttd3 = Yes;              // Select Trading on Wednesday (Yes) or (No)
input YN                    ttd4 = Yes;              // Select Trading on Thursday (Yes) or (No)
input YN                    ttd5 = Yes;              // Select Trading on Friday (Yes) or (No)
input YN                    ttd6 = No;               // Select Trading on Saturday (Yes) or (No)

Порядок выполнения Day Trading On/Off следующий:

bool MCEA::TradingToday(void)
  {
//---
    bool tradetoday=false;
    int trdday=ThisTime(dow);
    hariini="No";
    //--
    int ttd[];
    ArrayResize(ttd,7);
    ttd[0]=ttd0;
    ttd[1]=ttd1;
    ttd[2]=ttd2;
    ttd[3]=ttd3;
    ttd[4]=ttd4;
    ttd[5]=ttd5;
    ttd[6]=ttd6;
    //--
    if(ttd[trdday]==Yes) {tradetoday=true; hariini="Yes";}
   //--
   return(tradetoday);
//---
  } //-end TradingToday()
//---------//

Примечание: Условия Day Trading On/Off будут отображаться в торговой информации на графике.

Это работа функции TradingDay():

string MCEA::TradingDay(void)
  {
//---
   int trdday=ThisTime(dow);
   switch(trdday)
     {
        case 0: daytrade="Sunday";    break;
        case 1: daytrade="Monday";    break;
        case 2: daytrade="Tuesday";   break;
        case 3: daytrade="Wednesday"; break;
        case 4: daytrade="Thursday";  break;
        case 5: daytrade="Friday";    break;
        case 6: daytrade="Saturday";  break;
     }
   return(daytrade);
//---
  } //-end TradingDay()  
//---------//

Трейдеры имеют возможность торговать по часовому поясу в группе свойств советника Trade at Specific Time (торговля в определенное время).

Примечание: Как уже упоминалось выше, в остальных случаях время начала и окончания торговли выбирается советником.
Таким образом, в свойствах Expert Entry трейдерам нужно только установить час и минуту начала торговли, а также час и минуту окончания торговли для пользовательской сессии.


Функция ExpertActionTrade() была расширена за счет вызова логической функции Trade_session() специально для торговли в часовых поясах.

При Trade_session() равном true, советник работает до завершения, а при false, советник будет выполнять только задачи Close Trade and Save profit due to weak signal (Yes) (закрыть сделку и сохранить прибыль из-за слабого сигнала (Да)) и Trailing stop (Yes) (трейлинг-стоп (да)).

bool MCEA::Trade_session(void)
  {
//---
   bool trd_ses=false;
   ishour=ThisTime(hour);
   if(ishour!=onhour) Set_Time_Zone();
   datetime tcurr=TimeCurrent(); // Server Time
   //--
   switch(session)
     {
       case Cus_Session:
         {
           if(tcurr>=SesCuOp && tcurr<=SesCuCl) trd_ses=true;
           break;
         }
       case New_Zealand:
         {
           if(tcurr>=Ses01Op && tcurr<=Ses01Cl) trd_ses=true;
           break;
         }
       case Australia:
         {
           if(tcurr>=Ses02Op && tcurr<=Ses02Cl) trd_ses=true;
           break;
         }
       case Asia_Tokyo:
         {
           if(tcurr>=Ses03Op && tcurr<=Ses03Cl) trd_ses=true;
           break;
         }
       case Europe_London:
         {
           if(tcurr>=Ses04Op && tcurr<=Ses04Cl) trd_ses=true;
           break;
         }
       case US_New_York:
         {
           if(tcurr>=Ses05Op && tcurr<=Ses05Cl) trd_ses=true;
           break;
         }
     }
   //--
   if(trd_time_zone==No) 
     {
      if(tcurr>=SesNoOp && tcurr<=SesNoCl) trd_ses=true;
     }
   //--
   onhour=ishour;
   //--
   return(trd_ses);
//---  
  } //-end Trade_session()
//---------//


Процесс торговли в определенное время выглядит следующим образом: сначала функция ExpertActionTrade() вызывает функцию Trade_session(), затем функция Trade_session() вызывает функцию Set_Time_Zone() и, наконец, функция Set_Time_Zone() вызывает функцию Time_Zone().

void MCEA::Set_Time_Zone(void)
  {
//---
    //-- Server Time==TimeCurrent()
    datetime TTS=TimeTradeServer();
    datetime GMT=TimeGMT();
    //--
    MqlDateTime svrtm,gmttm; 
    TimeToStruct(TTS,svrtm); 
    TimeToStruct(GMT,gmttm); 
    int svrhr=svrtm.hour;  // Server time hour
    int gmthr=gmttm.hour;  // GMT time hour
    int difhr=svrhr-gmthr; // Time difference Server time to GMT time
    //--
    int NZSGMT=12;  // New Zealand Session GMT/UTC+12
    int AUSGMT=10;  // Australia Sydney Session GMT/UTC+10
    int TOKGMT=9;   // Asia Tokyo Session GMT/UTC+9
    int EURGMT=0;   // Europe London Session GMT/UTC 0
    int USNGMT=-5;  // US New York Session GMT/UTC-5
    //--
    int NZSStm=8;   // New Zealand Session time start: 08:00 Local Time
    int NZSCtm=17;  // New Zealand Session time close: 17:00 Local Time 
    int AUSStm=7;   // Australia Sydney Session time start: 07:00 Local Time 
    int AUSCtm=17;  // Australia Sydney Session time close: 17:00 Local Time  
    int TOKStm=9;   // Asia Tokyo Session time start: 09:00 Local Time 
    int TOKCtm=18;  // Asia Tokyo Session time close: 18:00 Local Time  
    int EURStm=9;   // Europe London Session time start: 09:00 Local Time 
    int EURCtm=19;  // Europe London Session time close: 19:00 Local Time  
    int USNStm=8;   // US New York Session time start: 08:00 Local Time 
    int USNCtm=17;  // US New York Session time close: 17:00 Local Time  
    //--
    int nzo = (NZSStm+difhr-NZSGMT)<0 ? 24+(NZSStm+difhr-NZSGMT) : (NZSStm+difhr-NZSGMT);
    int nzc = (NZSCtm+difhr-NZSGMT)<0 ? 24+(NZSCtm+difhr-NZSGMT) : (NZSCtm+difhr-NZSGMT);
    //--
    int auo = (AUSStm+difhr-AUSGMT)<0 ? 24+(AUSStm+difhr-AUSGMT) : (AUSStm+difhr-AUSGMT);
    int auc = (AUSCtm+difhr-AUSGMT)<0 ? 24+(AUSCtm+difhr-AUSGMT) : (AUSCtm+difhr-AUSGMT);
    //--
    int tko = (TOKStm+difhr-TOKGMT)<0 ? 24+(TOKStm+difhr-TOKGMT) : (TOKStm+difhr-TOKGMT);
    int tkc = (TOKCtm+difhr-TOKGMT)<0 ? 24+(TOKCtm+difhr-TOKGMT) : (TOKCtm+difhr-TOKGMT);
    //--
    int euo = (EURStm+difhr-EURGMT)<0 ? 24+(EURStm+difhr-EURGMT) : (EURStm+difhr-EURGMT);
    int euc = (EURCtm+difhr-EURGMT)<0 ? 24+(EURCtm+difhr-EURGMT) : (EURCtm+difhr-EURGMT);
    //--
    int uso = (USNStm+difhr-USNGMT)<0 ? 24+(USNStm+difhr-USNGMT) : (USNStm+difhr-USNGMT);
    int usc = (USNCtm+difhr-USNGMT)<0 ? 24+(USNCtm+difhr-USNGMT) : (USNCtm+difhr-USNGMT);
    if(usc==0||usc==24) usc=23;
    //--
    //---Trading on Custom Session
    int _days00=ThisTime(day);
    int _days10=ThisTime(day);
    if(stsescuh>clsescuh) _days10=ThisTime(day)+1;
    tmopcu=ReqDate(_days00,stsescuh,stsescum); 
    tmclcu=ReqDate(_days10,clsescuh,clsescum); 
    //--
    //--Trading on New Zealand Session GMT/UTC+12
    int _days01=ThisTime(hour)<nzc ? ThisTime(day)-1 : ThisTime(day);
    int _days11=ThisTime(hour)<nzc ? ThisTime(day) : ThisTime(day)+1;
    tmop01=ReqDate(_days01,nzo,0);    // start: 08:00 Local Time == 20:00 GMT/UTC
    tmcl01=ReqDate(_days11,nzc-1,59); // close: 17:00 Local Time == 05:00 GMT/UTC
    //--
    //--Trading on Australia Sydney Session GMT/UTC+10
    int _days02=ThisTime(hour)<auc ? ThisTime(day)-1 : ThisTime(day);
    int _days12=ThisTime(hour)<auc ? ThisTime(day) : ThisTime(day)+1;
    tmop02=ReqDate(_days02,auo,0);    // start: 07:00 Local Time == 21:00 GMT/UTC
    tmcl02=ReqDate(_days12,auc-1,59); // close: 17:00 Local Time == 07:00 GMT/UTC
    //--
    //--Trading on Asia Tokyo Session GMT/UTC+9
    int _days03=ThisTime(hour)<tkc ? ThisTime(day) : ThisTime(day)+1;
    int _days13=ThisTime(hour)<tkc ? ThisTime(day) : ThisTime(day)+1;
    tmop03=ReqDate(_days03,tko,0);    // start: 09:00 Local Time == 00:00 GMT/UTC
    tmcl03=ReqDate(_days13,tkc-1,59); // close: 18:00 Local Time == 09:00 GMT/UTC
    //--
    //--Trading on Europe London Session GMT/UTC 00:00
    int _days04=ThisTime(hour)<euc ? ThisTime(day) : ThisTime(day)+1;
    int _days14=ThisTime(hour)<euc ? ThisTime(day) : ThisTime(day)+1;
    tmop04=ReqDate(_days04,euo,0);     // start: 09:00 Local Time == 09:00 GMT/UTC
    tmcl04=ReqDate(_days14,euc-1,59);  // close: 19:00 Local Time == 19:00 GMT/UTC
    //--
    //--Trading on US New York Session GMT/UTC-5
    int _days05=ThisTime(hour)<usc  ? ThisTime(day) : ThisTime(day)+1;
    int _days15=ThisTime(hour)<=usc ? ThisTime(day) : ThisTime(day)+1;
    tmop05=ReqDate(_days05,uso,0);  // start: 08:00 Local Time == 13:00 GMT/UTC
    tmcl05=ReqDate(_days15,usc,59); // close: 17:00 Local Time == 22:00 GMT/UTC
    //--
    //--Not Use Trading Time Zone
    if(trd_time_zone==No)
      {
        tmopno=ReqDate(ThisTime(day),0,15); 
        tmclno=ReqDate(ThisTime(day),23,59);
      }
    //--
    Time_Zone();
    //--
    return;
//---
  } //-end Set_Time_Zone()
//---------//
void MCEA::Time_Zone(void)
  {
//---
   //--
   tz_ses="";
   //--
   switch(session)
     {
       case Cus_Session:
         {
           SesCuOp=StringToTime(tmopcu);
           SesCuCl=StringToTime(tmclcu);
           zntm=SesCuOp;
           znop=SesCuOp;
           zncl=SesCuCl;
           tz_ses="Custom_Session";
           tz_opn=timehr(stsescuh,stsescum);
           tz_cls=timehr(clsescuh,clsescum);
           break;
         }
       case New_Zealand:
         {
           Ses01Op=StringToTime(tmop01);
           Ses01Cl=StringToTime(tmcl01);
           zntm=Ses01Op;
           znop=Ses01Op;
           zncl=Ses01Cl;
           tz_ses="New_Zealand/Oceania";
           tz_opn=timehr(ReqTime(Ses01Op,hour),ReqTime(Ses01Op,min));
           tz_cls=timehr(ReqTime(Ses01Cl,hour),ReqTime(Ses01Cl,min));
           break;
         }
       case Australia:
         {
           Ses02Op=StringToTime(tmop02);
           Ses02Cl=StringToTime(tmcl02);
           zntm=Ses02Op;
           znop=Ses02Op;
           zncl=Ses02Cl;
           tz_ses="Australia Sydney";
           tz_opn=timehr(ReqTime(Ses02Op,hour),ReqTime(Ses02Op,min));
           tz_cls=timehr(ReqTime(Ses02Cl,hour),ReqTime(Ses02Cl,min));
           break;
         }
       case Asia_Tokyo:
         {
           Ses03Op=StringToTime(tmop03);
           Ses03Cl=StringToTime(tmcl03);
           zntm=Ses03Op;
           znop=Ses03Op;
           zncl=Ses03Cl;
           tz_ses="Asia/Tokyo";
           tz_opn=timehr(ReqTime(Ses03Op,hour),ReqTime(Ses03Op,min));
           tz_cls=timehr(ReqTime(Ses03Cl,hour),ReqTime(Ses03Cl,min));
           break;
         }
       case Europe_London:
         {
           Ses04Op=StringToTime(tmop04);
           Ses04Cl=StringToTime(tmcl04);
           zntm=Ses04Op;
           znop=Ses04Op;
           zncl=Ses04Cl;
           tz_ses="Europe/London";
           tz_opn=timehr(ReqTime(Ses04Op,hour),ReqTime(Ses04Op,min));
           tz_cls=timehr(ReqTime(Ses04Cl,hour),ReqTime(Ses04Cl,min));
           break;
         }
       case US_New_York:
         {
           Ses05Op=StringToTime(tmop05);
           Ses05Cl=StringToTime(tmcl05);
           zntm=Ses05Op;
           znop=Ses05Op;
           zncl=Ses05Cl;
           tz_ses="US/New_York";
           tz_opn=timehr(ReqTime(Ses05Op,hour),ReqTime(Ses05Op,min));
           tz_cls=timehr(ReqTime(Ses05Cl,hour),ReqTime(Ses05Cl,min));
           break;
         }
     }
   //--
   if(trd_time_zone==No)
     {
       SesNoOp=StringToTime(tmopno);
       SesNoCl=StringToTime(tmclno);
       zntm=SesNoOp;
       znop=SesNoOp;
       zncl=SesNoCl;
       tz_ses="Not Use Time Zone";
       tz_opn=timehr(ReqTime(SesNoOp,hour),ReqTime(SesNoOp,min));
       tz_cls=timehr(ReqTime(SesNoCl,hour),ReqTime(SesNoCl,min));
     }
   //--
   return;
//---
  } //-end Time_Zone()
//---------//


4. Получение торговых сигналов для открытия позиций

Для получения сигнала на открытие позиции функция ExpertActionTrade() вызывает функцию GetOpenPosition().

int MCEA::GetOpenPosition(const string symbol) // Signal Open Position 
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    //--
    int ZZAOSignal=GetIndiSignals(symbol);
    int dirmove=DirectionMove(symbol,TFt);
    int psar15=PARSAR15(symbol);
    //--
    if(ZZAOSignal==rise && dirmove==rise && psar15==rise) ret=rise;
    if(ZZAOSignal==down && dirmove==down && psar15==down) ret=down;
    //--
    return(ret);
//---
  } //-end GetOpenPosition()
//---------//

Функция GetOpenPosition() вызовет три функции:

  1. GetIndiSignals(symbol)
  2. DirectionMove(symbol,TFt)
  3. PARSAR15(symbol)

4.1. Функция GetIndiSignals(symbol) вызовет две функции:

  1. ZigZagSignal(symbol)
  2. AOSignal(symbol)

4.1.1. ZigZag Signal

Внутри функции ZigZagSignal() мы используем и вызываем одну функцию - PairsIdxArray().

int xx=PairsIdxArray(symbol)
int MCEA::PairsIdxArray(const string symbol)
  {
//---
    int pidx=-1;
    //--
    for(int x=0; x<arrsymbx; x++)
      {
        if(DIRI[x]==symbol)
          {
            pidx=x;
            break;
          }
      } 
    //--
    return(pidx);
//---
  } //-end PairsIdxArray()
//---------//


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

    //-- Indicators handle for all symbol
    for(int x=0; x<arrsymbx; x++) 
      {
        hZigZag[x] = iCustom(DIRI[x],TFt,indiname1,zzDepth,zzDevia,zzBackS);   //-- Handle for the ZigZag indicator
        hAO[x]     = iAO(DIRI[x],TFt);                                         //-- Handle for the Awesome_Oscillator indicator
        hVIDyAv[x] = iVIDyA(DIRI[x],TFt,9,12,0,PRICE_WEIGHTED);                //-- Handle for the VIDYA indicator for Trailing Stop
        hPar05[x]  = iSAR(DIRI[x],TFT05,SARstep,SARmaxi);                      //-- Handle for the iSAR indicator for M5 Timeframe
        hPar15[x]  = iSAR(DIRI[x],TFT15,SARstep,SARmaxi);                      //-- Handle for the iSAR indicator for M15 Timeframe
        //--
      }
    //--


Итак, чтобы получить значение буфера индикатора ZigZag, мы скопируем каждый буфер из хэндла индикатора ZigZag.

Чтобы скопировать буфер ZigZag (буфер 0) из хэндла индикатора ZigZag в целевой массив:

CopyBuffer(hZigZag[x],0,0,barcalc,ZZBuffer);

Кроме того, вызовем функцию UpdatePrice(), чтобы получить значение максимальной и минимальной цен, которые будут использоваться для получения позиций максимального и минимального баров буфера ZigZag.

void MCEA::UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf)
  {
//---
    //--
    ArrayFree(OPEN);
    ArrayFree(HIGH);
    ArrayFree(LOW);
    ArrayFree(CLOSE);
    ArrayFree(TIME);
    //--
    ArrayResize(OPEN,arper,arper);
    ArrayResize(HIGH,arper,arper);
    ArrayResize(LOW,arper,arper);
    ArrayResize(CLOSE,arper,arper);
    ArrayResize(TIME,arper,arper);
    //--
    ArraySetAsSeries(OPEN,true);
    ArraySetAsSeries(HIGH,true);
    ArraySetAsSeries(LOW,true);
    ArraySetAsSeries(CLOSE,true);
    ArraySetAsSeries(TIME,true);
    //--
    ArrayInitialize(OPEN,0.0);
    ArrayInitialize(HIGH,0.0);
    ArrayInitialize(LOW,0.0);
    ArrayInitialize(CLOSE,0.0);
    ArrayInitialize(TIME,0);    
    //--
    RefreshPrice(symbol,xtf,arper);
    //--
    int co=CopyOpen(symbol,xtf,0,arper,OPEN);
    int ch=CopyHigh(symbol,xtf,0,arper,HIGH);
    int cl=CopyLow(symbol,xtf,0,arper,LOW);
    int cc=CopyClose(symbol,xtf,0,arper,CLOSE);
    int ct=CopyTime(symbol,xtf,0,arper,TIME);
   //--
   return;
//---
  } //-end UpdatePrice()
//---------//

И чтобы получить максимальную и минимальную позиции баров ZigZag, перебираем и сравниваем максимальные и минимальные цены.

    //--
    for(int i=barcalc-1; i>=0; i--)
      {
        if(ZZBuffer[i]==HIGH[i]) ZH=i;
        if(ZZBuffer[i]==LOW[i])  ZL=i;
      }
    //--

После получения максимальной (ZH) и минимальной (ZL) позиции баров ZigZag дальнейшие действия зависят от настройки входных параметров сигнального индикатора ZigZag.

Полная функция ZigZagSignal() выглядит так:

int MCEA::ZigZagSignal(const string symbol) // ZigZag Signal for Open Position
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    int ZH=-1,
        ZL=-1;
    int barcalc=100;
    bool ZZrise=false;
    bool ZZdown=false;
    //--
    double ZZBuffer[];
    ArrayResize(ZZBuffer,barcalc,barcalc);
    ArraySetAsSeries(ZZBuffer,true);
    //--  
    int x=PairsIdxArray(symbol);
    UpdatePrice(symbol,TFt);
    //--    
    CopyBuffer(hZigZag[x],0,0,barcalc,ZZBuffer);
    //--
    for(int i=barcalc-1; i>=0; i--)
      {
        if(ZZBuffer[i]==HIGH[i]) ZH=i;
        if(ZZBuffer[i]==LOW[i])  ZL=i;
      }
    //--
    switch(sigzz)
      {
        case SZZ1:
          {
            ZZrise=((ZH==0 && HIGH[0]>HIGH[1])||(ZL<ZH && ZL>1));
            ZZdown=((ZL==0 && LOW[0]<LOW[1])||(ZH<ZL && ZH>1));
            //--
            break;
          }
        case SZZ2:
          {
            ZZrise=(ZL<ZH && ZL>1);
            ZZdown=(ZH<ZL && ZH>1);
            //--
            break;
          }
        case SZZ3:
          {
            ZZrise=((ZH==0 && HIGH[0]>HIGH[1])||(ZL<ZH && ZL>0));
            ZZdown=((ZL==0 && LOW[0]<LOW[1])||(ZH<ZL && ZH>0));
            //--
            break;
          }
        case SZZ4:
          {
            ZZrise=(ZL<ZH && ZL>0);
            ZZdown=(ZH<ZL && ZH>0);
            //--
            break;
          }
      };
    //--
    if(ZZrise) ret=rise;
    if(ZZdown) ret=down;
    //--
    return(ret);
//---
  } //-end ZigZagSignal()
//---------//


4.1.2. AO Signal

Как и в функции ZigZagSignal(), в функции AOSignal() нам также необходимо использовать функцию PairsIdxArray(), чтобы получить значение буфера из индикатора AO.

Итак, чтобы получить значение буфера индикатора AO, мы скопируем каждый буфер из хэндла индикатора AO.

Чтобы скопировать буфер AO (буфер 0) из хэндла индикатора AO в целевой массив:

CopyBuffer(hAO[x],0,0,barcalc,AOValue);

Дальнейшие действия зависят от настройки входных параметров сигнального индикатора AO.

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

Для этой цели мы создали функцию для получения значения цвета индикатора AO с именем функции AOColorSignal().

В этой функции мы скопируем буфер 1 (буфер индекса цвета индикатора) из индикатора AO.

CopyBuffer(hAO[x],1,0,barcalc,AOColor);
int MCEA::AOColorSignal(const string symbol)
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    int barcalc=9;
    //--
    double AOColor[];
    ArrayResize(AOColor,barcalc,barcalc);
    ArraySetAsSeries(AOColor,true);
    //--
    int x=PairsIdxArray(symbol);
    UpdatePrice(symbol,TFt,barcalc);
    //--
    CopyBuffer(hAO[x],1,0,barcalc,AOColor);
    //--
    bool AORise=((AOColor[1]==1.0 && AOColor[0]==0.0)||(AOColor[1]==0.0 && AOColor[0]==0.0));
    bool AODown=((AOColor[1]==0.0 && AOColor[0]==1.0)||(AOColor[1]==1.0 && AOColor[0]==1.0));
    //--
    if(AORise) ret=rise;
    if(AODown) ret=down;
    //--
    return(ret);
//---
  } //-end AOColorSignal()
//---------//

Полная функция AOSignal() выглядит так:

int MCEA::AOSignal(const string symbol)
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    int barcalc=9;
    bool AORValue=false;
    bool AODValue=false;
    //--
    double AOValue[];
    ArrayResize(AOValue,barcalc,barcalc);
    ArraySetAsSeries(AOValue,true);
    //--
    int x=PairsIdxArray(symbol);
    UpdatePrice(symbol,TFt,barcalc);
    //--
    CopyBuffer(hAO[x],0,0,barcalc,AOValue);
    //--
    switch(sigao)
      {
        case SAO1:
          {
            AORValue=(AOValue[2]<=0.0 && AOValue[1]>0.0 && AOValue[0]>AOValue[1])||(AOValue[1]>AOValue[2] && AOValue[0]>AOValue[1]);
            AODValue=(AOValue[2]>=0.0 && AOValue[1]<0.0 && AOValue[0]<AOValue[1])||(AOValue[1]<AOValue[2] && AOValue[0]<AOValue[1]);
            //--
            break;
          }
        case SAO2:
          {
            AORValue=(AOValue[1]<=0.0 && AOValue[0]>0.0)||(AOValue[0]>0.0 && AOValue[0]>AOValue[1]);
            AODValue=(AOValue[1]>=0.0 && AOValue[0]<0.0)||(AOValue[0]<0.0 && AOValue[0]<AOValue[1]);
            //--
            break;
          }
        case SAO3:
          {
            AORValue=(AOValue[1]<=0.0 && AOValue[0]>0.0)||(AOValue[0]>AOValue[1]);
            AODValue=(AOValue[1]>=0.0 && AOValue[0]<0.0)||(AOValue[0]<AOValue[1]);
            //--
            break;
          }
      };
    //--
    bool AORise=(AOColorSignal(symbol)==rise);
    bool AODown=(AOColorSignal(symbol)==down);
    //--
    if(AORValue && AORise) ret=rise;
    if(AODValue && AODown) ret=down;
    //--
    return(ret);
//---
  } //-end AOSignal()
//---------//


Функция GetIndiSignals() суммирует возвращаемые значения функций ZigZagSignal() и AOSignal().

int MCEA::GetIndiSignals(const string symbol) // Get Signal for Open Position 
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    int sigrise=2;
    int sigdown=-2;
    //--
    int ZZSignal=ZigZagSignal(symbol);
    int AwSignal=AOSignal(symbol);
    //Print(symbol+" = ZZ="+string(ZZSignal)+" AO="+string(AwSignal)+" Signal="+string(ZZSignal+AwSignal));
    //--
    if(ZZSignal+AwSignal==sigrise) ret=rise;
    if(ZZSignal+AwSignal==sigdown) ret=down;
    //--
    return(ret);
//---
  } //-end GetIndiSignals()
//---------//        


Функция GetIndiSignals() суммирует возвращаемые значения функций ZigZagSignal() и AOSignal().

  • Если результат равен 2, это сигнал на покупку.
  • Если результат -2, это сигнал на продажу.

4.2. Функция DirectionMove.

Функция DirectionMove() полезна для получения цены закрытия на текущем баре, будь то выше цены открытия (вверх) или ниже цены открытия (вниз).

int MCEA::DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf) // Bar Price Direction 
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    //--
    Pips(symbol);
    double difud=mc_symbol.NormalizePrice(1.5*pip);
    UpdatePrice(symbol,stf,2);
    //--
    if(CLOSE[0]>OPEN[0]+difud) ret=rise;
    if(CLOSE[0]<OPEN[0]-difud) ret=down;
    //--
    return(ret);
//---
  } //-end DirectionMove()
//---------//


4.3. Функция PARSAR15().

Функция PARSAR15() полезна для согласования движения индикаторов ZigZag и АО с индикаторами Parabolic Stop и Reverse (PSAR/iSAR) на таймфрейме M15.

int MCEA::PARSAR15(const string symbol) // formula Parabolic SAR M15
  {
//---
   int ret=0;
   int rise=1,
       down=-1;
   int br=2;
//--
   double PSAR[];
   ArrayResize(PSAR,br,br);
   ArraySetAsSeries(PSAR,true);
   int xx=PairsIdxArray(symbol);
   CopyBuffer(hPar15[xx],0,0,br,PSAR);
   //--
   UpdatePrice(symbol,TFT15,br);
   //--
   if(PSAR[0]<LOW[0])
      ret=rise;
   if(PSAR[0]>HIGH[0])
      ret=down;
//--
   return(ret);
//---
  } //-end PARSAR15()
//---------//


После выполнения трех основных сигнальных функций и нескольких вспомогательных функций функция GetOpenPosition() предоставит следующие значения:

  • 0 - сигнал неизвестен.
  • 1 - сигнал на покупку;
  • -1 - сигнал на продажу.

Когда функция GetOpenPosition() возвращает значение 1, советник вызывает функцию OpenBuy() для открытия ордера на покупку.

bool MCEA::OpenBuy(const string symbol) 
  {
//---
    ResetLastError();
    //--
    bool buyopen      = false;
    string ldComm     = GetCommentForOrder()+"_Buy";
    double ldLot      = MLots(symbol);
    ENUM_ORDER_TYPE type_req = ORDER_TYPE_BUY;
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //-- structure is set to zero
    ZeroMemory(req);
    ZeroMemory(res);
    ZeroMemory(check);
    //--
    CurrentSymbolSet(symbol);
    double SL=OrderSLSet(symbol,type_req,mc_symbol.Bid());
    double TP=OrderTPSet(symbol,type_req,mc_symbol.Ask());
    //--
    if(RefreshTick(symbol))
       buyopen=mc_trade.Buy(ldLot,symbol,mc_symbol.Ask(),SL,TP,ldComm);
    //--
    int error=GetLastError();
    if(buyopen||error==0)
      {
        string bsopen="Open BUY Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!";
        Do_Alerts(symbol,bsopen);
      }
    else
      {
        mc_trade.CheckResult(check);
        Do_Alerts(Symbol(),"Open BUY order for "+symbol+" FAILED!!. Return code= "+
                 (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]");
        return(false);   
      }
    //--
    return(buyopen);
    //--
//---
  } //-end OpenBuy
//---------//


При этом, если функция GetOpenPosition() возвращает значение -1, советник вызовет функцию OpenSell() для открытия ордера на продажу.

bool MCEA::OpenSell(const string symbol) 
  {
//---
    ResetLastError();
    //--
    bool selopen      = false;
    string sdComm     = GetCommentForOrder()+"_Sell";
    double sdLot      = MLots(symbol);
    ENUM_ORDER_TYPE type_req = ORDER_TYPE_SELL;
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //-- structure is set to zero
    ZeroMemory(req);
    ZeroMemory(res);
    ZeroMemory(check);
    //--
    CurrentSymbolSet(symbol);
    double SL=OrderSLSet(symbol,type_req,mc_symbol.Ask());
    double TP=OrderTPSet(symbol,type_req,mc_symbol.Bid());
    //--
    if(RefreshTick(symbol))
       selopen=mc_trade.Sell(sdLot,symbol,mc_symbol.Bid(),SL,TP,sdComm);
    //--
    int error=GetLastError();
    if(selopen||error==0)
      {
        string bsopen="Open SELL Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!";
        Do_Alerts(symbol,bsopen);
      }
    else
      {
        mc_trade.CheckResult(check);
        Do_Alerts(Symbol(),"Open SELL order for "+symbol+" FAILED!!. Return code= "+
                 (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]");
        return(false);   
      }
    //--
    return(selopen);
    //--
//---
  } //-end OpenSell
//---------//


5. Функция ChartEvent

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

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
//--- handling CHARTEVENT_CLICK event ("Clicking the chart")
   ResetLastError();
   //--
   ENUM_TIMEFRAMES CCS=mc.TFt;
   //--
   if(id==CHARTEVENT_OBJECT_CLICK) 
     {
       int lensymbol=StringLen(Symbol());
       int lensparam=StringLen(sparam);
       //--
       //--- if "Set SL All Orders" button is click
       if(sparam=="Set SL/TP All Orders") 
         { 
           mc.SetSLTPOrders();
           Alert("-- "+mc.expname+" -- ",Symbol()," -- Set SL/TP All Orders");
           //--- unpress the button 
           ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_STATE,false);
           ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_ZORDER,0);
           CreateManualPanel();
         }
       //--- if "Close All Order" button is click
       if(sparam=="Close All Order") 
         { 
           mc.CloseAllOrders();
           Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Orders");
           //--- unpress the button 
           ObjectSetInteger(0,"Close All Order",OBJPROP_STATE,false);
           ObjectSetInteger(0,"Close All Order",OBJPROP_ZORDER,0);
           CreateManualPanel();
         }
       //--- if "Close All Profit" button is click
       if(sparam=="Close All Profit") 
         { 
           mc.ManualCloseAllProfit();
           Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Profit");
           //--- unpress the button 
           ObjectSetInteger(0,"Close All Profit",OBJPROP_STATE,false);
           ObjectSetInteger(0,"Close All Profit",OBJPROP_ZORDER,0);
           CreateManualPanel();
         }
       //--- if "X" button is click
       if(sparam=="X") 
         { 
           ObjectsDeleteAll(0,0,OBJ_BUTTON);
           ObjectsDeleteAll(0,0,OBJ_LABEL);
           ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL);
           //--- unpress the button 
           ObjectSetInteger(0,"X",OBJPROP_STATE,false);
           ObjectSetInteger(0,"X",OBJPROP_ZORDER,0);
           //--
           DeleteButtonX();
           mc.PanelExtra=false;
           DisplayManualButton();
         }
       //--- if "M" button is click
       if(sparam=="M") 
         { 
           //--- unpress the button 
           ObjectSetInteger(0,"M",OBJPROP_STATE,false);
           ObjectSetInteger(0,"M",OBJPROP_ZORDER,0);
           mc.PanelExtra=true;
           CreateManualPanel();
         }
       //--- if "C" button is click
       if(sparam=="C") 
         { 
           //--- unpress the button 
           ObjectSetInteger(0,"C",OBJPROP_STATE,false);
           ObjectSetInteger(0,"C",OBJPROP_ZORDER,0);
           mc.PanelExtra=true;
           CreateSymbolPanel();
         }
       //--- if "R" button is click
       if(sparam=="R") 
         { 
           Alert("-- "+mc.expname+" -- ",Symbol()," -- expert advisor will be Remove from the chart.");
           ExpertRemove();
           //--- unpress the button 
           ObjectSetInteger(0,"R",OBJPROP_STATE,false);
           ObjectSetInteger(0,"R",OBJPROP_ZORDER,0);
           if(!ChartSetSymbolPeriod(0,Symbol(),Period()))
             ChartSetSymbolPeriod(0,Symbol(),Period());
           DeletePanelButton();
           ChartRedraw(0);
         }
       //--- if Symbol button is click
       if(lensparam==lensymbol)
         {
           int sx=mc.ValidatePairs(sparam);
           ChangeChartSymbol(mc.AS30[sx],CCS);
           mc.PanelExtra=false;
         }
       //--
     }
    //--
    return;
//---
  } //-end OnChartEvent()
//---------//

В группе входных свойств Other (другие) трейдеру будет предоставлена возможность выбрать, отображать ли торговую информацию на графике (Yes) или нет (No).

При Yes торговая информация будет отображаться на графике, на котором размещен советник, путем вызова функции TradeInfo().

Мы также добавили функцию для описания времени в соответствии с условиями торгового часового пояса как часть функции TradeInfo().

string MCEA::PosTimeZone(void)
  {
//---
    string tzpos="";
    //--
    if(ReqTime(zntm,day)>ThisTime(day))
     {
       tzpos=tz_opn+ " Next day to " +tz_cls + " Next day";
     }
    else
    if(TimeCurrent()<znop)
      {
        if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)==ReqTime(zncl,day))
          tzpos=tz_opn+" to " +tz_cls+ " Today";
        //else
        if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day))
          tzpos=tz_opn+ " Today to " +tz_cls+ " Next day";
      }
    else
    if(TimeCurrent()>=znop && TimeCurrent()<zncl)
      {
        if(ThisTime(day)<ReqTime(zncl,day))
          tzpos=tz_opn+ " Today to " +tz_cls+ " Next day";
        else
        if(ThisTime(day)==ReqTime(zncl,day))
          tzpos=tz_opn+" to " +tz_cls+ " Today";
      }
    else
    if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day))
      {
        tzpos=tz_opn+" Today to " +tz_cls+ " Next day";
      }
    //--
    return(tzpos);
//----
  } //-end PosTimeZone()
//---------//
void MCEA::TradeInfo(void) // function: write comments on the chart
  {
//----
   Pips(Symbol());
   double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/xpip;
   rem=zntm-TimeCurrent();
   string postime=PosTimeZone();
   string eawait=" - Waiting for active time..!";
   //--
   string comm="";
   TodayOrders();
   //--
   comm="\n     :: Server Date Time : "+string(ThisTime(year))+"."+string(ThisTime(mon))+"."+string(ThisTime(day))+ "   "+TimeToString(TimeCurrent(),TIME_SECONDS)+
        "\n     ------------------------------------------------------------"+
        "\n      :: Broker               :  "+ TerminalInfoString(TERMINAL_COMPANY)+
        "\n      :: Expert Name      :  "+ expname+
        "\n      :: Acc. Name         :  "+ mc_account.Name()+
        "\n      :: Acc. Number      :  "+ (string)mc_account.Login()+
        "\n      :: Acc. TradeMode :  "+ AccountMode()+
        "\n      :: Acc. Leverage    :  1 : "+ (string)mc_account.Leverage()+
        "\n      :: Acc. Equity       :  "+ DoubleToString(mc_account.Equity(),2)+
        "\n      :: Margin Mode     :  "+ (string)mc_account.MarginModeDescription()+
        "\n      :: Magic Number   :  "+ string(magicEA)+
        "\n      :: Trade on TF      :  "+ EnumToString(TFt)+
        "\n      :: Today Trading   :  "+ TradingDay()+" : "+hariini+
        "\n      :: Trading Session :  "+ tz_ses+
        "\n      :: Trading Time    :  "+ postime;
        if(TimeCurrent()<zntm)
          {
            comm=comm+
            "\n      :: Time Remaining :  "+(string)ReqTime(rem,hour)+":"+(string)ReqTime(rem,min)+":"+(string)ReqTime(rem,sec) + eawait;
          }
        comm=comm+
        "\n     ------------------------------------------------------------"+
        "\n      :: Trading Pairs     :  "+pairs+
        "\n      :: BUY Market      :  "+string(oBm)+
        "\n      :: SELL Market     :  "+string(oSm)+
        "\n      :: Total Order       :  "+string(oBm+oSm)+
        "\n      :: Order Profit      :  "+DoubleToString(floatprofit,2)+
        "\n      :: Fixed Profit       :  "+DoubleToString(fixclprofit,2)+
        "\n      :: Float Money     :  "+DoubleToString(floatprofit,2)+
        "\n      :: Nett Profit        :  "+DoubleToString(floatprofit+fixclprofit,2);
   //--
   Comment(comm);
   ChartRedraw(0);
   return;
//----
  } //-end TradeInfo()  
//---------//


Интерфейс мультивалютного советника ZigZag_AO_MCEA выглядит следующим образом.

ZZ_AO_MCEA_Look


Как видим, под именем советника ZigZag_AO_MCEA расположены кнопки M, C и R.

При нажатии на кнопку М отобразится панель кнопок для ручного нажатия, как показано на рисунке ниже.

Expert_manual_button_01

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

5.1. Set SL/TP All Orders (установить стоп-лосс/тейк-профит для всех ордеров)

Как объяснялось выше, если Use Order Stop Loss равен No и/или Use Order Take Profit равен No, но затем трейдер намеревается использовать стоп-лосс или тейк-профит для всех ордеров, один клик по кнопке Set SL/TP All Orders изменит все ордера и применит стоп-лосс и/или тейк-профит.

void MCEA::SetSLTPOrders(void) 
  {
//---
   ResetLastError();
   MqlTradeRequest req={};
   MqlTradeResult  res={};
   MqlTradeCheckResult check={};
   //--
   double modbuysl=0;
   double modselsl=0;
   double modbuytp=0;
   double modseltp=0;
   string position_symbol;
   int totalorder=PositionsTotal();
   //--    
   for(int i=totalorder-1; i>=0; i--) 
     {
       string symbol=PositionGetSymbol(i);
       position_symbol=symbol;
       if(mc_position.Magic()==magicEA)
         {
           ENUM_POSITION_TYPE opstype = mc_position.PositionType();
           if(opstype==POSITION_TYPE_BUY) 
             {
               Pips(symbol);
               RefreshTick(symbol);
               double price    = mc_position.PriceCurrent();
               double pos_open = mc_position.PriceOpen();
               double pos_stop = mc_position.StopLoss();
               double pos_take = mc_position.TakeProfit();
               modbuysl=SetOrderSL(symbol,opstype,pos_open);
               if(price<modbuysl) modbuysl=mc_symbol.NormalizePrice(price-slip*pip);
               modbuytp=SetOrderTP(symbol,opstype,pos_open);
               if(price>modbuytp) modbuytp=mc_symbol.NormalizePrice(price+slip*pip);
               //--
               if(pos_stop==0.0 || pos_take==0.0)
                 {
                   if(!mc_trade.PositionModify(position_symbol,modbuysl,modbuytp))
                     {
                       mc_trade.CheckResult(check);
                       Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+
                                (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]");
                     }
                 }
             }
           if(opstype==POSITION_TYPE_SELL) 
             {
               Pips(symbol);
               RefreshTick(symbol);
               double price    = mc_position.PriceCurrent();
               double pos_open = mc_position.PriceOpen();
               double pos_stop = mc_position.StopLoss();
               double pos_take = mc_position.TakeProfit();
               modselsl=SetOrderSL(symbol,opstype,pos_open);
               if(price>modselsl) modselsl=mc_symbol.NormalizePrice(price+slip*pip);
               modseltp=SetOrderTP(symbol,opstype,pos_open);
               if(price<modseltp) modseltp=mc_symbol.NormalizePrice(price-slip*pip);
               //--
               if(pos_stop==0.0 || pos_take==0.0)
                 {
                   if(!mc_trade.PositionModify(position_symbol,modselsl,modseltp))
                     {
                       mc_trade.CheckResult(check);
                       Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+
                                (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]");
                     }
                 }
             }
         }
     }
    //--
    return;
//---
  } //-end SetSLTPOrders
//---------//


5.2. Close All Orders (закрыть все ордера)

Если трейдер хочет закрыть все ордера, он может сделать это одним щелчком по кнопке Close All Orders.

void MCEA::CloseAllOrders(void) //-- function: close all order
   {
//----
    ResetLastError();
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //--
    int total=PositionsTotal(); // number of open positions
    //--- iterate over all open positions
    for(int i=total-1; i>=0; i--)
      {
        //--- if the MagicNumber matches
        if(mc_position.Magic()==magicEA)
          { 
            //--
            string position_Symbol   = PositionGetSymbol(i);  // symbol of the position
            ulong  position_ticket   = PositionGetTicket(i);  // ticket of the the opposite position
            ENUM_POSITION_TYPE  type = mc_position.PositionType();
            RefreshTick(position_Symbol);
            bool closepos = mc_trade.PositionClose(position_Symbol,slip);
            //--- output information about the closure
            PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type));
            //---
          }
      }
   //---
   return;
//----
   } //-end CloseAllOrders()
//---------//


5.3. Close All Profits (закрыть все прибыльные ордера)

Чтобы закрыть все прибыльные ордера, достаточно щелчка по кнопке Close All Profits.

bool MCEA::ManualCloseAllProfit(void)
   {
//----
    ResetLastError();
    //--
    bool orclose=false;
    //--
    MqlTradeRequest req={};
    MqlTradeResult  res={};
    MqlTradeCheckResult check={};
    //--
    int ttlorder=PositionsTotal(); // number of open positions
    //--
    for(int x=0; x<arrsymbx; x++)
       {
         string symbol=DIRI[x];
         orclose=false;
         //--
         for(int i=ttlorder-1; i>=0; i--)
            {
              string position_Symbol   = PositionGetSymbol(i);
              ENUM_POSITION_TYPE  type = mc_position.PositionType();
              if((position_Symbol==symbol) && (mc_position.Magic()==magicEA))
                {
                  double pos_profit = mc_position.Profit();
                  double pos_swap   = mc_position.Swap();
                  double pos_comm   = mc_position.Commission();
                  double cur_profit = NormalizeDouble(pos_profit+pos_swap+pos_comm,2);
                  ulong  position_ticket = PositionGetTicket(i);
                  //---
                  if(type==POSITION_TYPE_BUY && cur_profit>0.02)
                    {
                      RefreshTick(position_Symbol);
                      orclose = mc_trade.PositionClose(position_Symbol,slip);
                      //--- output information about the closure
                      PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type));
                    }
                  if(type==POSITION_TYPE_SELL && cur_profit>0.02)
                    {
                      RefreshTick(position_Symbol);
                      orclose = mc_trade.PositionClose(position_Symbol,slip);
                      //--- output information about the closure
                      PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type));
                    }
                }
            }
       }
     //--
     return(orclose);
//----
   } //-end ManualCloseAllProfit()
//---------//


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

Expert_manual_button_02

void CreateSymbolPanel()
  {
//---    
    //--
    ResetLastError();
    DeletePanelButton();
    int sydis=83;
    int tsatu=int(mc.sall/2);
    //--
    CreateButtonTemplate(0,"Template",180,367,STYLE_SOLID,5,BORDER_RAISED,clrYellow,clrBurlyWood,clrWhite,CORNER_RIGHT_UPPER,187,45,true);
    CreateButtonTemplate(0,"TempCCS",167,25,STYLE_SOLID,5,BORDER_RAISED,clrYellow,clrBlue,clrWhite,CORNER_RIGHT_UPPER,181,50,true);
    CreateButtonClick(0,"X",14,14,"Arial Black",10,BORDER_FLAT,"X",clrWhite,clrWhite,clrRed,ANCHOR_CENTER,CORNER_RIGHT_UPPER,22,48,true,"Close Symbol Panel");
    //--
    string chsym="Change SYMBOL";
    int cspos=int(181/2)+int(StringLen(chsym)/2);
    CreateButtontLable(0,"CCS","Bodoni MT Black",chsym,11,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,cspos,62,true,"Change Chart Symbol");
    //--
    for(int i=0; i<tsatu; i++)
      CreateButtonClick(0,mc.AS30[i],80,17,"Bodoni MT Black",8,BORDER_RAISED,mc.AS30[i],clrYellow,clrBlue,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,180,sydis+(i*22),true,"Change to "+mc.AS30[i]);
    //--
    for(int i=tsatu; i<mc.sall; i++)
      CreateButtonClick(0,mc.AS30[i],80,17,"Bodoni MT Black",8,BORDER_RAISED,mc.AS30[i],clrYellow,clrBlue,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,94,sydis+((i-tsatu)*22),true,"Change to "+mc.AS30[i]);
    //--
    ChartRedraw(0);
    //--
    return;
//---
   } //-end CreateSymbolPanel()
//---------//

В этом случае функция OnChartEvent() вызывается функцией ChangeChartSymbol() при нажатии на одно из названий символов.
   if(id==CHARTEVENT_OBJECT_CLICK) 
     {
       int lensymbol=StringLen(Symbol());
       int lensparam=StringLen(sparam);

       //--- if Symbol button is click
       if(lensparam==lensymbol)
         {
           int sx=mc.ValidatePairs(sparam);
           ChangeChartSymbol(mc.AS30[sx],CCS);
           mc.PanelExtra=false;
         }
       //--
     }
void ChangeChartSymbol(string c_symbol,ENUM_TIMEFRAMES cstf)
  {
//---
   //--- unpress the button 
   ObjectSetInteger(0,c_symbol,OBJPROP_STATE,false);
   ObjectSetInteger(0,c_symbol,OBJPROP_ZORDER,0);
   ObjectsDeleteAll(0,0,OBJ_BUTTON);
   ObjectsDeleteAll(0,0,OBJ_LABEL);
   ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL);
   //--
   ChartSetSymbolPeriod(0,c_symbol,cstf);
   //--
   ChartRedraw(0);
   //--
   return;
//---
  } //-end ChangeChartSymbol()
//---------//


Наконец, нажатие кнопки R удалит с графика мультивалютный советник ZigZag_AO_MCEA, поэтому трейдерам не придется удалять советники вручную.

   if(id==CHARTEVENT_OBJECT_CLICK) 
     {
       //--
       //--- if "R" button is click
       if(sparam=="R") 
         { 
           Alert("-- "+mc.expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart.");
           ExpertRemove();
           //--- unpress the button 
           ObjectSetInteger(0,"R",OBJPROP_STATE,false);
           ObjectSetInteger(0,"R",OBJPROP_ZORDER,0);
           if(!ChartSetSymbolPeriod(0,Symbol(),Period()))
             ChartSetSymbolPeriod(0,Symbol(),Period());
           DeletePanelButton();
           ChartRedraw(0);
         }
       //---
     }


Тестер стратегий

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

В тестере стратегий MetaTrader 5 мы протестируем мультивалютный советник ZigZag_AO_MCEA.

Установим ZigZag_AO_MCEA на пару XAUUSD H4 с пользовательским периодом 2023.10.01 - 2024.02.17.

ZZ_AO_ST_input

 Результаты представлены ниже.

ZZ_AO_ST_result


ZZ_AO_ST_result_graph_01

ZZ_AO_ST_result_graph_02

ZZ_AO_ST_result_graph_03

ZZ_AO_ST_result_graph_04


Заключение

  1. Создание мультивалютного советника на MQL5 мало чем отличается от разработки одновалютного. Мультивалютный советник также может использоваться как одновалютный.
  2. Создание мультивалютного советника повысит эффективность и результативность трейдеров, поскольку им не нужно будет открывать много графиков.
  3. Правильная торговая стратегия увеличивает вероятность получения прибыли по сравнению с использованием одновалютного советника. Убытки по одной паре будут перекрываться прибылью в других парах.
  4. Мультивалютный советник ZigZag_AO_MCEA является всего лишь примером для изучения и развития собственных идей. Результаты тестирования в тестере стратегий по-прежнему неудовлетворительны. Экспериментируя и тестируя на разных таймфреймах, рассчитывая разные периоды индикаторов и выбирая разные сигналы, можно получить более прибыльные результаты.
  5. Сочетание индикаторов ZigZag и AO следует дополнительно изучить, экспериментируя с таймфреймами и значениями дифференциации входных параметров индикатора ZigZag. Возможно, имеет смысл добавить другие сигналы алгоритма к индикаторам ZigZag и AO.
  6. Удовлетворительные результаты получены только на таймфреймах H1 и выше. На более мелких таймфреймах открывается больше сделок, заканчивающихся убытками.

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

Спасибо за внимание!

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

Прикрепленные файлы |
ZigZag_AO_MCEA.mq5 (123.74 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Soshianth Azar
Soshianth Azar | 1 апр. 2024 в 12:33

Уважаемый Роберто Якобс

Приветствую вас и всех остальных.

Большое спасибо за вашу замечательную статью.

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

С уважением и наилучшими пожеланиями


Roberto Jacobs
Roberto Jacobs | 1 апр. 2024 в 14:06
Soshianth Azar #:

Уважаемый Роберто Джейкобс

Привет вам и всем

Большое спасибо за вашу замечательную статью.

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

С уважением и наилучшими пожеланиями


Пожалуйста, спросите у вашего брокера о AccountInfoInteger(ACCOUNT_LIMIT_ORDERS) вашего счета, или о том, является ли ваш счет в настоящее время ограниченным.

Нейросети в трейдинге: "Легкие" модели прогнозирования временных рядов Нейросети в трейдинге: "Легкие" модели прогнозирования временных рядов
Легковесные модели прогнозирования временных рядов обеспечивают высокую производительность, используя минимальное количество параметров. Что, в свою очередь, снижает расход вычислительных ресурсов и ускоряет принятие решений. При этом они достигают качества прогнозов, сопоставимого с более сложными моделями.
Введение в MQL5 (Часть 5): Функции для работы с массивами для начинающих Введение в MQL5 (Часть 5): Функции для работы с массивами для начинающих
В пятой статье из нашей серии мы познакомимся с миром массивов в MQL5. Статья предназначена для начинающих. В статье попытаемся упрощенно рассмотреть сложные концепции программирования, чтобы материал был понятен всем. Давайте вместе будем изучать основные концепции, обсуждать вопросы и делиться знаниями!
Фильтр сезонности и временные периоды в моделях глубокого обучения с ONNX и Python в советнике Фильтр сезонности и временные периоды в моделях глубокого обучения с ONNX и Python в советнике
Можем ли мы извлечь выгоду из сезонности при создании моделей для глубокого обучения с помощью Python? Помогает ли фильтрация данных в моделях ONNX получить лучшие результаты? Какой период времени использовать? Обо всем этом расскажем в этой статье.
Мониторинг торговли с помощью Push-уведомлений — пример сервиса в MetaTrader 5 Мониторинг торговли с помощью Push-уведомлений — пример сервиса в MetaTrader 5
В статье рассмотрим создание программы сервиса для отправки уведомлений на смартфон о результатах торговли. В рамках статьи научимся работать со списками объектов Стандартной Библиотеки для организации выборки объектов по требуемым свойствам.