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

Создаем простой мультивалютный советник с использованием MQL5 (Часть 1): Сигналы на основе ADX в сочетании с Parabolic SAR

MetaTrader 5Примеры | 4 декабря 2023, 11:42
1 465 5
Roberto Jacobs
Roberto Jacobs

Введение

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

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

Таким образом, цель состоит в том, чтобы удовлетворить основные потребности трейдеров, которым нужны эффективные и действенные торговые роботы. Полагаясь на сильные стороны и возможности MQL5, мы можем создать простой мультивалютный советник, который в этой статье использует среднее направленное движение в сочетании с индикатором Parabolic SAR.


Особенности

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 пар.

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


2. Сигналы.

Мультивалютный советник будет использовать 2 сигнала индикатора: 1. Индикатор среднего направленного движения (ADX) с периодом 7 в качестве основного сигнала и 2. индикатор Parabolic SAR в качестве подтверждения сигнала.

Два основных индикатора будут использовать один и тот же таймфрейм, указанный в свойстве советника. Помимо определения силы или слабости тренда, индикатор Parabolic SAR также используется с таймфреймами M15 и M5.

Формула стратегии состояния сигналов:  iADX

UP   = (+DI[2] <= -DI[2]) && (+DI[1] > -DI[1]+difference) && (+DI[0] > +DI[1]) && ((+DI[0]/-DI[0]) > (+DI[1]/-DI[1]))

DOWN = (+DI[2] >= -DI[2]) && (+DI[1] < -DI[1]-difference) && (+DI[0] < +DI[1]) && ((+DI[0]/-DI[0]) < (+DI[1]/-DI[1]))

где разность = 0.34:

(+DI[1] > -DI[1]+0.34) = сигнал на покупку верен;   

(+DI[1] < -DI[1]-0.34) = сигнал на продажу верен;     

Процент текущего бара PLUSDI_LINE, разделенный на текущий бар MINUSDI_LINE. 

по сравнению с   

Процентом предыдущего бара PLUSDI_LINE, разделенного на предыдущий бар MINUSDI_LINE.

(+DI[0]/-DI[0]) = V0 = (текущий бар PLUSDI_LINE / текущий бар MINUSDI_LINE x 100) - 100;

(+DI[1]/-DI[1]) = V1 = (предыдущий бар PLUSDI_LINE / предыдущий бар MINUSDI_LINE x 100) - 100;

   Тогда:

ЕСЛИ V0 > V1 = процентное значение условия ADX = Рост

ЕСЛИ V0 < V1 = процентное значение условия ADX = Падение


Стратегия состояния сигналов Parabolic SAR: Parabolic Stop And Reverse System (iSAR) - параболическая система остановки и разворота

iSAR UP      = PRICE_LOW[0] > iSAR[0]

iSAR DOWN = PRICE_HIGH[0] < iSAR[0]


На рисунке 1 показан сигнал iADX в сочетании с iSAR.

Рис. 1. Сигнал индикаторов


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

Управление торговлей в этом мультивалютном советнике имеет несколько вариантов:

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

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

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

                При выборе 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) советник будет проверять выполнение условий сигнала. Если они выполняются,

                ордер сохраняется. Если сигнал ослаб, ордер необходимо закрыть для сохранения прибыли

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

               

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) трейдеру необходимо ввести значение тейк-профита в пипсах.


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

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

                При Use Trailing SL/TP option (No) советник не будет использовать стоп-лосс и тейк-профит трейлинга.

                При Use Trailing SL/TP (Yes) снова появляется выбор: Use Automatic Trailing (Yes) или (No) - использовать автоматический трейлинг: да или нет

                При Use Automatic Trailing (Yes) трейлинг-стоп выполняется советником с использованием значения Parabolic SAR.

                При Use Automatic Trailing (No) трейлинг-стоп выполняется советником с использованием значения входного параметра.

                Примечание: Советник осуществляет трейлинг тейк-профита одновременно с трейлинг-стопом.


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

Для повышения эффективности будет добавлено несколько кнопок.

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

       Если трейдер установит Use Order Stop Loss (No) и/или Use Order Take Profit (No),

       но затем захочет использовать стоп-лосс или тейк-профит для всех ордеров, ему необходимо лишь нажать на кнопку "Set SL / TP All Orders" для

       модификации всех ордеров и применения стоп-лосса и/или тейк-профита.

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

   3. 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;
//---


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

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


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

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


Перечисление таймфреймов для расчета сигнала советника

enum TFX
  {
   TFH1,     // PERIOD_H1
   TFH2,     // PERIOD_H2
   TFH3,     // PERIOD_H3
   TFH4,     // PERIOD_H4
   TFH6,     // PERIOD_H6
   TFH8,     // PERIOD_H8
   TFH12,    // PERIOD_H12
   TFD1      // PERIOD_D1
  };
//--


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

//---
input group               "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter
input TFX             TimeFrames = TFH4;             // Select Expert TimeFrame, default PERIOD_H4
input int              ADXPeriod = 7;                // Input ADX Period
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
//--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;               // If Not Use Automatic SL - Input SL value in Pips
input YN                  use_tp = No;               // Use Order Take Profit (Yes) or (No)
input YN                  autotp = Yes;              // Use Automatic Calculation Take Profit (Yes) or (No)
input double               TPval = 50;               // If Not Use Automatic TP - Input TP value in Pips
input YN            TrailingSLTP = Yes;              // Use Trailing SL/TP (Yes) or (No)
input YN                 autotrl = No;               // Use Automatic Trailing (Yes) or (No)
input double               TSval = 5;                // If Not Use Automatic Trailing Input Trailing value in Pips
input double               TSmin = 5;                // Minimum Pips to start Trailing Stop
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)
//--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 = 202307;          // Expert ID (Magic Number)
//---


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

//+------------------------------------------------------------------+
//| 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;
   int               posCur1,
                     posCur2;
   //--
   double            LotPS,
                     difDi;
   double            slv,
                     tpv,
                     pip,
                     xpip;
   double            floatprofit,
                     fixclprofit;
   double            ADXDIp[];
   double            ADXDIm[];
   //--
   string            pairs,
                     hariini,
                     daytrade,
                     trade_mode;
   //--
   double            OPEN[],
                     HIGH[],
                     LOW[],
                     CLOSE[];
   datetime          TIME[];
   datetime          closetime;
   //--
   //------------

   //------------
   int               iADXCross(const string symbol);
   int               iADXpct(const string symbol,const int index);
   int               PARSAR05(const string symbol);
   int               PARSAR15(const string symbol);
   int               PARSAROp(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:
   //---

   //-- ADXPSAR_MCEA Config --
   string            DIRI[],
                     AS30[];
   string            expname;
   int               handADX[];
   int               hParOp[],
                     hPar15[],
                     hPar05[];
   int               ALO,
                     dgts,
                     arrsymbx;
   int               sall,
                     arper;
   ulong             slip;
   ENUM_TIMEFRAMES   TFt,
                     TFT15,
                     TFT05;
   //--
   double            SARstep,
                     SARmaxi;
   double            profitb[],
                     profits[];
   //--
   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)
   //------------
                     MCEA(void);
                    ~MCEA(void);
   //------------
   //--
   virtual void      ADXPSAR_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              CloseBuyPositions(const string symbol);
   void              CloseSellPositions(const string symbol);
   void              CloseAllOrders(void);
   void              CheckClose(const string symbx);
   void              TodayOrders(void);
   void              UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf);
   void              RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars);
   //--
   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              ModifySLTP(const string symbx,int TS_type);
   bool              CloseAllProfit(void);
   bool              ManualCloseAllProfit(void);
   //--
   int               PairsIdxArray(const string symbol);
   int               GetOpenPosition(const string symbol);
   int               DirectionMove(const string symbol);
   int               GetCloseInWeakSignal(const string symbol,int exis);
   int               CheckToCloseInWeakSignal(const string symbol,int exis);
   int               ThisTime(const int reqmode);
   //--
   string            getUninitReasonText(int reasonCode);
   //--
   //------------
   //---
  }; //-end class MCEA
//---------//


Самая первая и главная функция в работе мультивалютного советника, вызываемая из OnInit(), - это ADXPSAR_MCEA_Config().

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


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

//+------------------------------------------------------------------+
//| Expert Configuration                                             |
//+------------------------------------------------------------------+
void MCEA::ADXPSAR_MCEA_Config(void)
  {
//---
//-- Here we will register all the symbols or pairs that will be used on the Multi-Currency Expert Advisor
//--
   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
//--
   sall=ArraySize(All30);
   ArrayResize(AS30,sall,sall);
//-- These AS30[] arrays will be used in the symbol list panel and for the buttons to change symbols and charts
   ArrayCopy(AS30,All30,0,0,WHOLE_ARRAY);
//--
   arrsymbx=sall;
   ArraySymbolResize();
   ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY); //-- The "DIRI[]" array containing the symbol or pair name will be used
//-- in all trading activities of the multi-currency expert
//--
//-- This function is for Select all symbol in the Market Watch window
   for(int x=0; x<arrsymbx; x++)
     {
      SymbolSelect(DIRI[x],true);
     }
   pairs="Multi Currency 30 Pairs";
//---
//-- Here we will provide a Period Timeframe value which will be used for signal calculations according
//-- to the Timeframe option on the expert input property.
   ENUM_TIMEFRAMES TFs[]= {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(x==TimeFrames)
        {
         TFt=TFs[x];
         break;
        }
     }
//--
//-- Indicators handle for all symbol
   for(int x=0; x<arrsymbx; x++)
     {
      handADX[x]=iADX(DIRI[x],TFt,ADXPeriod);         //-- Handle for the iADX indicator according to the selected Timeframe
      hParOp[x]=iSAR(DIRI[x],TFt,SARstep,SARmaxi);    //-- Handle for the iSAR indicator according to the selected Timeframe
      hPar15[x]=iSAR(DIRI[x],TFT15,SARstep,SARmaxi);  //-- Handle for the iSAR indicator for M15 Timeframe
      hPar05[x]=iSAR(DIRI[x],TFT05,SARstep,SARmaxi);  //-- Handle for the iSAR indicator for M5 Timeframe
     }
//--
//-- Since this expert advisor is a multi-currency expert, we must check the maximum number
//-- of account limit orders allowed by the broker.
//-- This needs to be checked, so that when the expert opens an order there will be
//-- no return codes of the trade server error 10040 = TRADE_RETCODE_LIMIT_POSITIONS
   ALO=(int)mc_account.LimitOrders()>arrsymbx ? arrsymbx : (int)mc_account.LimitOrders();
//--
//-- The LotPS variable will later be used for the proportional distribution of Lot sizes for each symbol
   LotPS=(double)ALO;
//--
   mc_trade.SetExpertMagicNumber(magicEA); //-- Set Magic Number as expert ID
   mc_trade.SetDeviationInPoints(slip);    //-- Set expert deviation with slip variable value
   mc_trade.SetMarginMode();               //-- Set the Margin Mode expert to the value of Account Margin Mode
//--
   return;
//---
  } //-end ADXPSAR_MCEA_Config()
//---------//


2. Функция 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
    //--
    //-- The functions below will be displayed on the expert chart according to
    //-- the Select Display Trading Info on Chart (Yes) or (No) option on expert property.
    //--
    if(trade_info_display==Yes)
       mc.TradeInfo(); //-- Displayed Trading Info on Chart
//--
    //--
    if(trade_info_display==Yes) mc.TradeInfo(); //-- Displayed Trading Info on Chart
    //---
    //-- Because the current prices of a specified symbol (SymbolInfoTick) will occur differently
    //-- for each symbol, we reduce the tick update frequency to only every 5 seconds.
    //-- So, looping to check the signal for all trading activity of all symbols will only be done every 5 seconds.
    //--
    int mcsec=mc.ThisTime(mc.sec);
    //-- With the function ThisTime(mc.sec), we will retrieve the current seconds value to mcsec variable.
    //--
    //-- MathMod is a formula that gives the (modulus) real remainder after the division of two numbers.
    //-- By dividing the value of seconds with the value of 5.0, if the result is 0, it means that 5 seconds
    //-- have been reached from the previous psec variable seconds value.
    //--
    if(fmod((double)mcsec,5.0)==0)
       mc.ccur=mcsec;
    //--
    if(mc.ccur!=mc.psec) //-- So, if the seconds value in the ccur variable is not the same as the psec variable value
        {
         //-- (then the psec variable value already 5 seconds before)
         string symbol;
         //-- Here we start with the rotation of the name of all symbol or pairs to be traded
         //-- This is the basic framework for the automated trading workflow of this Multi-Currency Expert Advisor
         //-- Here we start with the rotation of the name of all symbol or pairs to be traded
         //-- This is the basic framework for the automated trading workflow of this Multi-Currency Expert Advisor
         for(int x=0; x<mc.arrsymbx && !IsStopped(); x++) 
           {
             //--
             if(mc.DIRI[x]==Symbol())
                symbol=Symbol();
             else
                symbol=mc.DIRI[x];
             //-- After the symbol or pair name is set, we declare or notify the symbol to MarketWatch
             //-- and the trade server by calling the CurrentSymbolSet(symbol) function.
             mc.CurrentSymbolSet(symbol);
             //--
             if(mc.TradingToday()) //-- The TradingToday() function checks whether today is allowed for trading
               {                   //-- If today is not allowed for trading, then the Expert will only perform management
                                   //-- orders such as trailing stops or trailing profits and closing orders.
                 //-- according to the expert input property Day Trading On/Off group
                 //-- If TradingToday() == Yes, then the next process is to call the function ThisTime(mc.sec)
                 //--
                 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 it turns out that the "Sell Order" has been opened,
                     //-- and Close Trade By Opposite Signal according to the input property is (Yes),
                     //-- then call the function CloseSellPositions(symbol) to close the sell order on that 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); //-- Open BUY order for this symbol
                     else
                     //-- OR
                     //-- If Close Trade and Save profit due to weak signal according to the input property is (Yes)
                     //-- then call the CloseAllProfit() function to close all orders
                     //-- who are already in profit.
                     //--
                     if(mc.xtto>=mc.ALO)
                       {
                         //-- If the total number of orders is greater than or equal
                         //-- to the account limit orders allowed by the broker, then turn on alerts
                         //--
                         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); //-- Call the CheckOpenPMx(symbol) function
                         //--
                         //-- If it turns out that the "Sell Order" has been opened,
                         //-- and the condition of the Sell order has lost more than 1.02 USD,
                         //-- and the "Buy Order" has not been opened, then call CloseSellPositions(symbol)
                         //-- to close "Sell order" and open "Buy order".
                         if(mc.xos[x]>0 && mc.profits[x]<-1.02 && mc.xob[x]==0)
                           {
                            mc.CloseSellPositions(symbol);
                            mc.OpenBuy(symbol);
                           }
                         else
                         //-- OR
                         //-- If Close Trade and Save profit due to weak signal according to the input property is (Yes)
                         //-- then call the CloseAllProfit() function to close all orders
                         //-- who are already in profit.
                         //--
                         if(SaveOnRev==Yes)
                            mc.CloseAllProfit();
                       }
                   }
                 if(mc.OpOr[x]==mc.Sell) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-1)
                   {
                     //--
                     //-- Call the CheckOpenPMx(symbol) function to check whether there are
                     //-- already open "Buy" or "Sell" orders or no open orders.
                     //--
                     mc.CheckOpenPMx(symbol);
                     //--
                     //-- If it turns out that the "Buy Order" has been opened,
                     //-- and Close Trade By Opposite Signal according to the input property is (Yes),
                     //-- then call the function CloseBuyPositions(symbol) to close the buy order on that symbol.
                     //--
                     if(Close_by_Opps==Yes && mc.xob[x]>0)
                        mc.CloseBuyPositions(symbol);
                     //--
                     //-- The algorithm below means that the expert will only open 1 order per symbol,
                     //-- provided that the total number of orders is still less than
                     //-- the account limit orders allowed by the broker.
                     //--
                     if(mc.xos[x]==0 && mc.xtto<mc.ALO) mc.OpenSell(symbol);  //-- Open SELL order for this symbol
                     else
                     if(mc.xtto>=mc.ALO)
                       {
                         //-- If the total number of orders is greater than or equal
                         //-- to the account limit orders allowed by the broker, then turn on alerts
                         //--
                         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); //-- Call the CheckOpenPMx(symbol) function
                         //--
                         //-- If it turns out that the "Buy Order" has been opened,
                         //-- and the condition of the Buy order has lost more than 1.02 USD,
                         //-- and the "Sell Order" has not been opened, then call CloseBuyPositions(symbol)
                         //-- to close "Buy order" and open "Sell order".
                         if(mc.xob[x]>0 && mc.profitb[x]<-1.02 && mc.xos[x]==0)
                           {
                             mc.CloseBuyPositions(symbol);
                             mc.OpenSell(symbol);
                           }
                         else
                         //-- OR
                         //-- If Close Trade and Save profit due to weak signal according to the input property is (Yes)
                         //-- then call the CloseAllProfit() function to close all orders
                         //-- who are already in profit.
                         //--
                         if(SaveOnRev==Yes)
                            mc.CloseAllProfit();
                       }
                   }
                 //--
                 mc.CheckOpenPMx(symbol);
                 //-- The algorithm block below will check whether there is a weakening of the signal on Buy or Sell positions.
                 //-- If it is true that there is a weakening signal and the iSAR indicator has reversed direction,
                 //-- then close the losing order and immediately opened an order in the opposite direction.
                 //--
                 if(mc.xob[x]>0 && mc.CheckToCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);}
                 if(mc.xos[x]>0 && mc.CheckToCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) {mc.CloseSellPositions(symbol); mc.OpenBuy(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]>0.02 && 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]>0.02 && 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(mc.xob[x]>0 && mc.CheckToCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) mc.CloseBuyPositions(symbol);
                     if(mc.xos[x]>0 && mc.CheckToCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) mc.CloseSellPositions(symbol);
                   }
                 //--
                 if(TrailingSLTP==Yes) //-- Use Trailing SL/TP (Yes)
                   {
                     if(autotrl==Yes) mc.ModifySLTP(symbol,1); //-- If Use Automatic Trailing (Yes)
                     if(autotrl==No)  mc.ModifySLTP(symbol,0); //-- Use Automatic Trailing (No)
                   }
               }
             //--
             //-- Check if there are orders that were closed in the last 6 seconds.
             //-- If there are give alerts.
             mc.CheckClose(symbol);
           }
        //-- replace the value of the psec variable with the value of the ccur variable.
        mc.psec=mc.ccur;
      }
    //--
    return;
//---
  } //-end ExpertActionTrade()
//---------//


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

Чтобы получить сигнал индикатора, вызовем функцию GetOpenPosition(symbol) для получения сигнала на открытие

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int MCEA::GetOpenPosition(const string symbol) // Signal Open Position
  {
//---
   int ret=0;
   int rise=1,
       down=-1;
//--
   int dirmov=DirectionMove(symbol);
   int pars15=PARSAR15(symbol);
   int parsOp=PARSAROp(symbol);
   int sigADX=iADXCross(symbol);
//--
   if(sigADX==rise && parsOp==rise && dirmov==rise && pars15==rise)
      ret=rise;
   if(sigADX==down && parsOp==down && dirmov==down && pars15==down)
      ret=down;
//--
   return(ret);
//---
  } //-end GetOpenPosition()
//---------//


Функция GetOpenPosition() вызовет четыре сигнальные функции и сохранит их в переменной OpOr[].

1. DirectionMove(symbol); //-- Функция проверки наличия цены на свечном баре на таймфрейме советника

2. PARSAR15(symbol);        //-- Функция проверки роста или падения индикатора iSAR на таймфрейме M15

3. PARSAROp(symbol);       //-- Функция проверки роста или падения индикатора iSAR на таймфрейме советника

4. iADXCross(symbol);       //-- Функция проверки роста или падения индикатора iADX на таймфрейме советника


Затем в функции iADXCross(symbol) будет вызвана функция iADXpct() для проверки процентного движения между +DI и -DI, как описано в разделе "Сигналы".

Чтобы получить состояние индикатора, мы должны получить порядковый номер каждого требуемого хэндла индикатора в четырех функциях PARSAR15(symbol), PARSAROP(symbol), iADXCross(symbol) и iADXpct().

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

int x=PairsIdxArray(symbol);

Значение x представляет собой массивы порядкового номера хэндла индикатора рассматриваемого символа.

На примере функции PARSAR15() мы можем увидеть, как вызвать хэндл индикатора iSAR для рассматриваемого символа.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
//--
   RefreshPrice(symbol,TFT15,br);
   double HIG0=iHigh(symbol,TFT15,0);
   double LOW0=iLow(symbol,TFT15,0);
//--
   if(PSAR[0]<LOW0)
      ret=rise;
   if(PSAR[0]>HIG0)
      ret=down;
//--
   return(ret);
//---
  } //-end PARSAR15()
//---------//


4. Функция 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/TP 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); //-- Button state (depressed button)
         ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         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); //-- Button state (depressed button)
         ObjectSetInteger(0,"Close All Order",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         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); //-- Button state (depressed button)
         ObjectSetInteger(0,"Close All Profit",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         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); //-- Button state (depressed button)
         ObjectSetInteger(0,"X",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         //--
         DeleteButtonX();
         mc.PanelExtra=false;
         DisplayManualButton();
        }
      //--- if "M" button is click
      if(sparam=="M")
        {
         //--- unpress the button
         ObjectSetInteger(0,"M",OBJPROP_STATE,false); //-- Button state (depressed button)
         ObjectSetInteger(0,"M",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         mc.PanelExtra=true;
         CreateManualPanel();
        }
      //--- if "C" button is click
      if(sparam=="C")
        {
         //--- unpress the button
         ObjectSetInteger(0,"C",OBJPROP_STATE,false); //-- Button state (depressed button)
         ObjectSetInteger(0,"C",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         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); //-- Button state (depressed button)
         ObjectSetInteger(0,"R",OBJPROP_ZORDER,0);    //-- Priority of a graphical object for receiving events
         if(!ChartSetSymbolPeriod(0,Symbol(),Period()))
            ChartSetSymbolPeriod(0,Symbol(),Period());
         DeletePanelButton();
         ChartRedraw(0);
        }
      //--- if Symbol name button is click
      if(lensparam==lensymbol)
        {
         int sx=mc.PairsIdxArray(sparam);
         ChangeChartSymbol(mc.AS30[sx],CCS);
        }
      //--
     }
//--
   return;
//---
  } //-end OnChartEvent()
//---------//


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

ADXPSAR_MCEA


Кнопки

Кнопки

При нажатии на M отобразится панель ручного управления

Expert_manual_button_01

Далее трейдер может управлять ордерами:

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

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

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


При нажатии на C отобразится панель кнопок с 30 парами

Expert_manual_button_02

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

При нажатии на R мультивалютный советник ADXPSAR_MCEA удаляется с графика.



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

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

Тестирование торговых стратегий Мультивалютное тестирование

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

Результаты тестера стратегий

Кривая баланса


Входы в рынок и прибыль


Корреляция


Минимальная позиция


Заключение

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

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

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


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

Прикрепленные файлы |
ADXPSAR_MCEA.mq5 (83.84 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (5)
Daniil Shchukin
Daniil Shchukin | 4 дек. 2023 в 14:53
Добрый вечер! 

Для тех, кто не умеет кодить: можете прикрепить файл советника? 
И можно узнать на каких настройках и на каком таймфрейме использовался советник ? Хочется потестировать его на большом диапазоне дат.
MrBrooklin
MrBrooklin | 4 дек. 2023 в 15:02
summertop #:
можете прикрепить файл советника? 

Файл с кодом советника расположен в самом конце статьи.


С уважением, Владимир.

Sergey Pavlov
Sergey Pavlov | 4 дек. 2023 в 17:29
... любопытно, но не понятно: на каком участке истории тестирование было, ибо сотня трейдов на 30 валютных парах вводит в ступор.
Alexey Viktorov
Alexey Viktorov | 4 дек. 2023 в 17:37
Sergey Pavlov #:
... любопытно, но не понятно: на каком участке истории тестирование было, ибо сотня трейдов на 30 валютных парах вводит в ступор.

Это писалось не для втюхивания лохам. Тут рыбы нет…

Maxim Kuznetsov
Maxim Kuznetsov | 4 дек. 2023 в 18:37

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

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

Теория категорий в MQL5 (Часть 16): Функторы с многослойными перцептронами Теория категорий в MQL5 (Часть 16): Функторы с многослойными перцептронами
Мы продолжаем рассматривать функторы и то, как их можно реализовать с помощью искусственных нейронных сетей. Мы временно оставим подход, который включал в себя прогнозирование волатильности, и попытаемся реализовать собственный класс сигналов для установки сигналов входа и выхода из позиции.
Популяционные алгоритмы оптимизации: Метод Нелдера-Мида, или метод симплексного поиска (Nelder–Mead method, NM) Популяционные алгоритмы оптимизации: Метод Нелдера-Мида, или метод симплексного поиска (Nelder–Mead method, NM)
Статья представляет полное исследование метода Нелдера-Мида объясняя, как симплекс — пространство параметров функции — изменяется и перестраивается на каждой итерации для достижения оптимального решения, а также описывает способ улучшения этого метода.
Тестируем информативность разных типов скользящих средних Тестируем информативность разных типов скользящих средних
Мы все знаем важность скользящей средней для многих трейдеров. Существуют разные типы скользящих средних, которые могут быть полезны в торговле. Мы рассмотрим их и проведем простое сравнение, чтобы увидеть, какой из них может показать лучшие результаты.
Нейросети — это просто (Часть 66): Проблематика исследования в офлайн обучении Нейросети — это просто (Часть 66): Проблематика исследования в офлайн обучении
Обучение моделей в офлайн режиме осуществляется на данных ранее подготовленной обучающей выборки. Это дает нам ряд преимуществ, но при этом информация об окружающей среде сильно сжимается до размеров обучающей выборки. Что, в свою очередь, ограничивает возможности исследования. В данной статье хочу предложить познакомиться с методом, позволяющем наполнить обучающую выборку максимально разнообразными данными.