English Русский Español Deutsch 日本語 Português
preview
如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR

如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR

MetaTrader 5示例 | 15 四月 2024, 14:13
765 0
Roberto Jacobs
Roberto Jacobs

概述

本文中多币种智能交易系统的定义是交易机器人,它可从单一品种的图表中交易(开单、平单和管理订单)超过一个品种对。

当前对自动化交易系统、或多币种交易机器人系统的需求和兴趣非常高涨,但我们看到,在 MQL5 自动交易机器人上实现多币种系统程序尚未广泛发布,或也许仍被许多程序员保密。

因此,我们旨在满足那些寻求高效及有效的交易机器人的交易者的基本需求,故此,通过依据高度可靠的 MQL5 提供的优势、能力和设施,我们可以创建一个简单的多币种智能交易系统,在本文中它采用的指标信号:平均方向走势(ADM)结合抛物线 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 个指标信号:1. 周期为 7 的平均方向走势(ADX)指标作为主信号;和 2. 抛物线 SAR 指标,作为趋势信号确认。

这两个主要指标将采用智能系统属性中指定的相同时间帧。除了检测趋势的强弱外,抛物线 SAR 指标还用到 M15 和 M5 时间帧。

ADX 信号条件策略公式:iADX

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

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

其中 difference = 0.34 :

(+DI[1] > -DI[1]+0.34) = 买入信号 true;   

(+DI[1] < -DI[1]-0.34) = 卖出信号 true;     

当前柱线 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 条件百分值 = 上行

IF V0 < V1 = ADX 条件百分值 = 下行


抛物线 SAR 信号条件策略:抛物线破位和逆转系统(iSAR)

iSAR 上行      = PRICE_LOW[0] > iSAR[0]

iSAR 下行 = PRICE_HIGH[0] < iSAR[0]


iADX 信号结合 iSAR 的图示如图例 1 所示。

图例 1. 指标信号


3. 交易和订单管理

该多币种智能交易系统的交易管理有多种选项:

1. 止损单

  • 选项:使用订单止损(是)或(否)

                如果选中使用订单止损(否)选项,则所有订单在开单时都不带止损。

                如果选项使用订单止损(是):再次给出选项:使用自动计算止损(是)或(否)

                如果选项是自动计算止损(是),则将由智能系统自动执行止损计算。

                如果选项是自动计算止损(否),则交易者必须以点为单位输入止损值。

                如果选项是使用订单止损(否):则智能系统将检查每笔开单,信号条件是否仍然良好,且订单

                仍维持在盈利状态,或者信号减弱,需要平仓来保护盈利

                或信号条件方向逆转,订单必须在亏损位置平仓。

               

2. 止盈订单

   选项:使用订单止盈(是)或(否)

                如果选中使用订单止盈(否)选项,则所有订单在开单时都不带止盈。

                如果选项是使用订单止盈(是):再次给出选项:使用自动计算订单止盈(是)或(否)

                如果选项是自动计算订单止盈(是),则将由智能系统自动执行止盈计算。

                如果选项是自动计算订单止盈(否),则交易者必须以点数输入订单止盈值。


3. 尾随止损和尾随止盈

   选项:使用尾随止损/止盈(是)或(否)

                如果使用尾随止损/止盈选项为(否),则智能系统不会执行尾随止损和尾随止盈。

                如果选项是使用尾随 SL/TP(是):再次给出选项:使用自动尾随(是)或(否)

                如果选项是使用自动尾随(是),则尾随止损由智能系统按抛物线 SAR 值执行。

                如果选项是使用自动尾随(No),则智能系统将采用输入属性中的值执行尾随止损。

                注意:智能系统将同时执行尾随止盈和尾随止损。


4. 手动订单管理。

为了提高该多币种智能交易系统的效率,将添加若干个手动单击按钮

   1. 为所有订单设置止损/止盈

       当交易者在输入参数设置使用订单止损(否)和/或使用订单止盈(否)时

       但随后交易者打算为所有订单使用止损或止盈,那么只需单击 “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() 函数中,配置了所有要用到的品种、用到的所有指标句柄、以及智能交易系统工作流包含文件头里的一些重要函数。

//+------------------------------------------------------------------+
//| 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. 智能系统跳价函数

在智能系统跳价函数(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() 函数将调用 4 个信号函数,并存储在变量 OpOr[] 当中。

1. DirectionMove(symbol); //-- 函数检查烛台柱线价格是否在智能系统周期内

2. PARSAR15(symbol);        //-- 函数检查 iSAR 指标的状况在周期 M15 中是上升还是下降

3. PARSAROp(symbol);       //-- 函数检查 iSAR 指标在智能系统周期内是上升还是下降

4. iADXCross(symbol);       //-- 函数检查 iADX 指标在智能系统周期内的状况是上升还是下降


然后在 iADXCross(symbol) 函数中,将调用 iADXpct() 函数来检查 +DI 和 -DI 之间的百分比走势,如信号指标部分所述。

若要获取指标的条件,在 4 个函数 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. 为所有订单设置止损/止盈

2. 所有订单平仓

3. 所有盈利单平仓


如果单击 C 按钮,将显示一个包含 30 个品种(对)名称的面板按钮,如下所示

Expert_manual_button_02

如果单击其中一个货币对或品种名称,则图表品种将立即替换为所单击名称的品种。

如果单击 R 按钮,则多币种智能交易系统 ADXPSAR_MCEA 将从图表中删除。



策略测试器

众所周知,MetaTrader 5 终端的策略测试器已经支持并允许我们执行策略测试、交易多品种、或测试所有可用品种的自动交易。

测试交易策略多币种测试

故此,在这种场合下,我们将在 MetaTrader 5 平台策略测试器上测试 ADXPSAR_MCEA 多币种智能交易系统。

策略测试器结果

余额净值


整体盈利


调整


最小持仓


结束语

正如我上面所写,之后我得出的结论是,使用 MQL5 创建多币种智能交易系统:

  • 事实摆明,在 MQL5 中创建多币种智能交易系统非常简单,与单币种智能交易系统没有太大区别。
  • 创建多币种智能交易系统将提高交易者的效率和有效性,因为交易者无需打开更多品种图表进行交易。
  • 通过应用正确的交易策略,并计算更佳指标信号,比之单货币智能交易系统,其获利可能性会提升。因为一个品种(对)发生的亏损会被其它品种(对)的盈利所补偿。
  • 这个 ADXPSAR_MCEA 多币种智能交易系统只是一个思路学习和开发的示例。而且策略测试器的测试结果仍然不佳。因此,如果实现更好的策略和更准确的信号计算,我相信结果会比这个更好。

希望本文和 MQL5 多币种智能交易系统程序对交易者思路学习和开发有所帮助。

感谢您的阅读。


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13008

附加的文件 |
ADXPSAR_MCEA.mq5 (84.17 KB)
MQL5 中的范畴论 (第 16 部分):多层感知器函子 MQL5 中的范畴论 (第 16 部分):多层感知器函子
本文是我们系列文章的第 16 篇,继续考察函子以及如何使用人工神经网络实现它们。我们偏离了迄今为止在该系列中所采用的方式,这涉及预测波动率,并尝试实现自定义信号类来设置入仓和出仓信号。
神经网络变得轻松(第五十三部分):奖励分解 神经网络变得轻松(第五十三部分):奖励分解
我们已经不止一次地讨论过正确选择奖励函数的重要性,我们通过为单独动作添加奖励或惩罚来刺激代理者的预期行为。但是关于由代理者解密我们的信号的问题仍旧悬而未决。在本文中,我们将探讨将单独信号传输至已训练代理者时的奖励分解。
MQL5中的范畴论(第18部分):自然性四边形 MQL5中的范畴论(第18部分):自然性四边形
本文通过介绍自然变换这一主题中的一个关键支柱,继续我们的范畴理论系列。我们研究看似复杂的定义,然后深入研究本系列“面包和黄油”的示例和应用程序;波动性预测。
开发回放系统 — 市场模拟(第 23 部分):外汇(IV) 开发回放系统 — 市场模拟(第 23 部分):外汇(IV)
现在,创建发生在我们将跳价转换为柱线的同一点。以这种方式,如果在转换过程中出现问题,我们就能立即注意到错误。这是因为在快进期间,在图表上放置 1-分钟柱线的代码,也同样在正常表现期间用于定位系统放置柱线。换言之,负责此任务的代码不会在其它任何地方重复。如此这般,我们获得的系统就能更好的维护和改进。