English Русский Español Deutsch 日本語 Português
preview
构建和测试 Aroon 交易系统

构建和测试 Aroon 交易系统

MetaTrader 5交易 | 21 八月 2024, 16:22
360 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

概述

在交易和技术分析领域,我们可以使用很多工具,但肯定不会全部使用,而是在测试和优化后选择一种或多种盈利工具组合。本文的目的是尝试提供一种在这种情况下有用的方法,或者让你深入了解一种不同的观点或想法,你可以应用它,看看哪种自动化交易系统适合你。

这种类型的文章对您很有帮助,因为您只需花很少的时间就能找到一个好的交易系统设置,但在找到您感兴趣的方式后,您仍需继续努力,以进一步研究和优化,直到达到最终结果,为您的交易带来利润。我们只是尝试在此介绍一些交易工具,这些工具可以单独使用,也可以与其他工具结合使用,以节省您的时间,找到您需要关注的重点,并开发出对您更有利的工具。它还提供了如何在策略测试器中对这些系统进行测试的代码,这意味着您可以通过自动测试流程而不是手动测试来节省大量时间。

在本文中,我们将使用 Aroon 技术指标,并根据其概念测试多个策略,查看其结果,更有价值的是,我们将学习如何通过 MQL5 对指标进行编码,并基于策略将其用于我们的交易系统中。所有这一切都将在我们了解了 Aroon 指标是什么以及我们如何计算和使用它之后发生。

本文将介绍以下主题:

在前面的主题之后,我们将能够更多地了解 Aroon 指标,如何使用它,构建交易系统需要什么,Aroon 交易策略,基于这些策略构建交易系统并对其进行测试,看看它们在交易中有多有用。

这里需要提到的是,如果你想在现有的交易系统中添加任何工具,你需要从不同的角度进行测试,以获得适合你的交易风格或交易系统的最佳和更好的结果,因为我无法在这里获得或提供所有的角度,因为每个人都是不同的,没有一种策略或交易系统适合所有交易者,但我可以贡献其中的一部分,这可以帮助你节省一些工作时间。

免责声明:所有信息 "按原样 "提供,仅用于教育目的,不用于交易目的或提供建议。本信息不保证任何结果。如果您选择在您的任何交易账户上使用这些材料,您将自行承担风险,并承担唯一责任。


Aroon 指标定义

在本部分中,我们将研究 Aroon 技术指标,以便了解其主要概念。我们将通过了解该指标的计算方法来理解它,这种方法有助于我们理解该指标的主要概念,而不仅仅是使用所提及的内容,还能帮助我们制定适当使用该指标的方法。

Aroon 由 Tushar S. Chande 于 1995 年创建和设计。该指标的主要理念是检测趋势的变化,并衡量趋势的强度。该指标可以通过测量在一段时间内从高点到低点之间经过了多少时间来实现这一点。通过衡量这一点,我们可以说,强劲的上升趋势是,我们可以看到它经常创下新高,但强劲的下降趋势是,它常常创下新低,指标为我们提供了这方面的信号。

Aroon 指标由两条线组成,一条是 Aroon 上行线(Aroon Up),用于衡量上升趋势的强度,另一条是 Aroon 下行线(Aroon Down),用于衡量下降趋势的强度。在这种情况下,我们可以说,当 Aroon 上行线高于 Aroon 下行线时,就表示看涨信号。当 Aroon 下行线高于 Aroon 上行线时,这是一个看跌信号。Aroon 指标也在 0 和 100 之间移动或振荡。

下面介绍如何计算 Aroon 技术指标:

Aroon Up = 100 * ((n-H)/n)

Aroon Down = 100 * ((n-L)/n)

其中:

  • Aroon Up:以 n 期总数的百分比表示,代表自上一个 n 期高点以来的周期数。
  • Aroon Down: 以 n 期总数的百分比表示,代表自上一个 n 期低点以来的周期数。
  • H:自上一个 n 期高点以来,在 n 期的给定时间内的周期数。
  • L:自上一个 n 期低点以来,在 n 期的给定时间内的周期数。
  • n:时期数。


Aroon 策略

在本部分中,我们将根据 Aroon 指标的概念,介绍两种简单的使用策略。这些策略也将由 MQL5 进行编码,由策略测试器进行测试,查看每个策略的结果并进行比较,这与我们在测试 Aroon 交易系统主题中看到的相同。

我们将使用以下两种主要策略:

  • Aroon 交叉策略
  • Aroon 水平策略

Aroon 交叉战略:

根据这一策略,我们需要在 Aroon 指标的上升线和下降线相互交叉时下单。因此,当我们看到上行交叉线高于下行交叉线时,我们需要下达买入订单;当我们看到下行交叉线高于上行交叉线时,我们需要下达卖出订单。

上行线 > 下行线 ==> 买入

下行线 > 上行线 ==> 卖出

Aroon 水平策略:

根据这一策略,我们需要在 Aroon 指标的下行线与 Aroon 指标的 10 和 50 水平线交叉时下单。因此,当我们看到下行交叉线低于 10 水平时,我们需要下达买入订单;当我们看到下行交叉线高于 Aroon 指标的 50 水平时,我们需要下达卖出订单。

下行线 < 10 ==> 买入

下行线 > 50 ==> 卖出


Aroon 交易系统

在这个部分中,我们将学习如何在 MQL5 中对上述 Aroon 策略进行编码,以便使用测试器进行测试并评估其结果。首先,我们需要编写 Aroon 自定义指标的代码,以便在交易策略中使用它,如下所示:

在 MQL5 的全局范围内,使用属性预处理器设置指标属性

//properties of the indicator
#property indicator_separate_window // the place of the indicator
#property indicator_buffers 2 // number of buffers
#property indicator_plots 2 // number of plots

//up line
#property indicator_type1  DRAW_LINE      // type of the up values to be drawn is a line
#property indicator_color1 clrGreen       // up line color
#property indicator_style1 STYLE_DASH     // up line style
#property indicator_width1 2              // up line width
#property indicator_label1 "Up"           // up line label

// down line
#property indicator_type2  DRAW_LINE      // type of the down values to be drawn is a line
#property indicator_color2 clrRed         // down line color
#property indicator_style2 STYLE_DASH     // down line style
#property indicator_width2 2              // down line width
#property indicator_label2 "Down"         // down line label

// drawing some levels to be used later 10 and 50
#property indicator_level1 10.0
#property indicator_level2 50.0
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT

使用 input 关键字为指标创建两个整数型输入参数,即周期数和水平位移

//inputs
input int                      periodInp = 25; // Period
input int                      shiftInp  = 0;  // horizontal shift

为指标的上行线和下行线值创建两个双精度型数组

//buffers of the indicator
double                         upBuffer[];
double                         downBuffer[];

在 OnInit() 中,使用 SetIndexBuffer 函数将指示器缓冲区与双精度型数组连接起来,参数如下:

  • index:指定缓冲区索引,upBuffer 用 0,downBuffer 用 1。
  • buffer[]:用于指定数组,我们将使用 upBuffer 和 downBuffer 数组。
  • data_type:指定我们需要存储的数据类型,可以是 ENUM_INDEXBUFFR_TYPE 中的一种,我们将使用 INDICATOR_DATA 来表示上行线和下行线。
   SetIndexBuffer(0, upBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, downBuffer, INDICATOR_DATA);

使用 PlotIndexSetInteger 函数,通过调用变量指示属性的标识符和参数,设置相应属性的值和相应的上下行指标线,如下所示:

  • plot_index:是绘图样式索引的整数值,0 表示上行线,1 表示下行线。
  • prop_id::是属性标识符的整数值,可以是 ENUM_PLOT_PROPERTY_INTEGER 中的一个。我们将使用 PLOT_SHIFT 来处理上下行线数值。我们将使用 PLOT_DRAW_BEGIN 来表示上行线和下行线。
  • prop_value:是要设置的整数值,上行和下行线均为 shiftInp,上行和下行线均为 periodInp。
   PlotIndexSetInteger(0, PLOT_SHIFT, shiftInp);
   PlotIndexSetInteger(1, PLOT_SHIFT, shiftInp);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, periodInp);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, periodInp);

为上下行线缓冲区设置 AS_SERIES 标志

   ArraySetAsSeries(upBuffer, true);
   ArraySetAsSeries(downBuffer, true);

声明 indicatorName 字符串变量,并使用 StringFormat 函数设置我们需要在指标窗口中看到的格式,然后设置指标的字符串名称和整数值

   string indicatorName = StringFormat("Aroon Indicator (%i,%i) - ", periodInp, shiftInp);
   IndicatorSetString(INDICATOR_SHORTNAME, indicatorName);
   IndicatorSetInteger(INDICATOR_DIGITS, 0);

作为 OnInit() 事件的一部分,返回 INIT_SUCCEEDED。

return INIT_SUCCEEDED;

OnCalculate 事件,

int OnCalculate(const int       rates_total,
                const int       prev_calculated,
                const datetime &time[],
                const double   &open[],
                const double   &high[],
                const double   &low[],
                const double   &close[],
                const long     &tick_volume[],
                const long     &volume[],
                const int      &spread[])

在事件处理的主体中,我们将计算指标,如果没有足够的数据,则返回 0

   if(rates_total < periodInp - 1)
      return (0);

创建一个整数 count 变量,并将其计算为 rate_total 和 prev_calculated 相减的结果

int count = rates_total - prev_calculated;

如果 prev_calculated 大于 0(这意味着我们有了新数据),那么我们将更新 count,加 1

   if(prev_calculated > 0)
      count++;

创建另一个条件来更新 count 值

   if(count > (rates_total - periodInp + 1))
      count = (rates_total - periodInp + 1);

创建一个 for 循环,在计算出最高值和最低值后,计算并更新指标的上行线值和下行线值

   for(int i = count - 1; i >= 0; i--)
     {
      int highestVal   = iHighest(Symbol(), Period(), MODE_HIGH, periodInp, i);
      int lowestVal    = iLowest(Symbol(), Period(), MODE_LOW, periodInp, i);
      upBuffer[i]   = (periodInp - (highestVal - i)) * 100 / periodInp;
      downBuffer[i] = (periodInp - (lowestVal - i)) * 100 / periodInp;
     }

作为 OnCalculate 事件的一部分返回 rate_total

return (rates_total);

以下是在一个代码块中创建 Aroon 自定义指标的完整代码

//+------------------------------------------------------------------+
//|                                                        Aroon.mq5 |
//+------------------------------------------------------------------+
#property indicator_separate_window // the place of the indicator
#property indicator_buffers 2 // number of buffers
#property indicator_plots 2 // number of plots
#property indicator_type1  DRAW_LINE      // type of the up values to be drawn is a line
#property indicator_color1 clrGreen       // up line color
#property indicator_style1 STYLE_DASH     // up line style
#property indicator_width1 2              // up line width
#property indicator_label1 "Up"           // up line label
#property indicator_type2  DRAW_LINE      // type of the down values to be drawn is a line
#property indicator_color2 clrRed         // down line color
#property indicator_style2 STYLE_DASH     // down line style
#property indicator_width2 2              // down line width
#property indicator_label2 "Down"         // down line label
#property indicator_level1 10.0
#property indicator_level2 50.0
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT
input int periodInp = 25; // Period
input int shiftInp  = 0;  // horizontal shift
double    upBuffer[];
double    downBuffer[];
int OnInit()
  {
   SetIndexBuffer(0, upBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, downBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_SHIFT, shiftInp);
   PlotIndexSetInteger(1, PLOT_SHIFT, shiftInp);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, periodInp);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, periodInp);
   ArraySetAsSeries(upBuffer, true);
   ArraySetAsSeries(downBuffer, true);
   string indicatorName = StringFormat("Aroon Indicator (%i,%i) - ", periodInp, shiftInp);
   IndicatorSetString(INDICATOR_SHORTNAME, indicatorName);
   IndicatorSetInteger(INDICATOR_DIGITS, 0);
   return INIT_SUCCEEDED;
  }
int OnCalculate(const int       rates_total,
                const int       prev_calculated,
                const datetime &time[],
                const double   &open[],
                const double   &high[],
                const double   &low[],
                const double   &close[],
                const long     &tick_volume[],
                const long     &volume[],
                const int      &spread[])
  {
   if(rates_total < periodInp - 1)
      return (0);
   int count = rates_total - prev_calculated;
   if(prev_calculated > 0)
      count++;
   if(count > (rates_total - periodInp + 1))
      count = (rates_total - periodInp + 1);
   for(int i = count - 1; i >= 0; i--)
     {
      int highestVal   = iHighest(Symbol(), Period(), MODE_HIGH, periodInp, i);
      int lowestVal    = iLowest(Symbol(), Period(), MODE_LOW, periodInp, i);
      upBuffer[i]   = (periodInp - (highestVal - i)) * 100 / periodInp;
      downBuffer[i] = (periodInp - (lowestVal - i)) * 100 / periodInp;
     }
   return (rates_total);
  }
//+------------------------------------------------------------------+

编译完这段代码后,我们会发现自己的自定义指标在插入图表时如同下图

Aroon 指标

创建自定义指标后,我们就可以构建交易策略了。因此,我们将从第一个策略开始,即 Aroon 交叉策略,然后是 Aroon 水平策略,但在此之前,我们将创建一个能够上下显示 Aroon 指标值的简单程序,下面是实现该功能的方法:

首先,在全局范围内,我们将使用 input 关键字创建两个用户输入参数,分别是周期数和移位

input int         periodInp = 25; // Period
input int         shiftInp  = 0; // Shift

为 Aroon 声明一个整数变量,以便稍后将指标句柄分配给它

int aroon;

在 OnInit() 中,我们将使用 iCustom 函数,以将创建的 Aroon 自定义指标的句柄附加或返回到 EA,参数如下:

  • Symbol:指定交易品种名称,我们将使用 _Symbol 返回当前交易品种。
  • period: 指定周期数,我们还将使用 _Period 返回当前周期数。
  • name:指定自定义指标的准确名称及其在 Indicators 文件夹中的准确路径目录。
  • ...然后指定自定义指标的输入参数列表。我们将只使用创建的两个输入参数(period 和 shift)。
aroon = iCustom(_Symbol,PERIOD_CURRENT,"Aroon",periodInp,shiftInp);

然后,在 EA 成功初始化后返回 (INIT_SUCCEEDED)。

return(INIT_SUCCEEDED);

在 OnDeinit 中,当 Deinit 事件发生时,我们将使用 Print 函数打印 "EA is removed"(EA 已被删除),以取消正在运行的 MQL5 程序的初始化。

void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }

在 OnTick() 事件中,我们将声明 upBuffer 和 downBuffer 两个双精度数组

double upBuffer[], downBuffer[];

使用 CopyBuffer 函数获取已创建的 Aroon 指标缓冲区数据,该函数的变体是通过第一个位置和所需元素的数量进行调用,其参数为:

  • indicator_handle: 用于指定 Aroon 自定义指标的句柄。
  • buffer_num:用于指定指标的缓冲区编号。
  • start_pos:用于指定开始计数的位置。
  • count:用于指定从 start_pos 开始的计数量。
  • buffer[]: 用于指定目标数组。
   CopyBuffer(aroon,0,0,3,upBuffer);
   CopyBuffer(aroon,1,0,3,downBuffer);

使用 ArraySetAsSeries 将 AS_SERIES 标志设置为指定的标志,该标志为 true 时,数组的索引顺序将相反。

   ArraySetAsSeries(upBuffer,true);
   ArraySetAsSeries(downBuffer,true);

声明 upValue 和 downValue 两个双精度变量,通过索引 [0] 从数组中分配 Aroon 指标的当前值

   double upValue = upBuffer[0];
   double downValue = downBuffer[0];

使用 Comment 函数在图表上输出注释,并显示 Aroon 指标的上下波动值

Comment("upValue: ",upValue,"\ndownValue: ",downValue);

下面是实现这些的完整代码:

//+------------------------------------------------------------------+
//|                                                AroonValuesEA.mq5 |
//+------------------------------------------------------------------+
input int         periodInp = 25; // Period
input int         shiftInp  = 0; // Shift
int aroon;
int OnInit()
  {
   aroon = iCustom(_Symbol,PERIOD_CURRENT,"Aroon",periodInp,shiftInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   double upBuffer[], downBuffer[];
   CopyBuffer(aroon,0,0,3,upBuffer);
   CopyBuffer(aroon,1,0,3,downBuffer);
   ArraySetAsSeries(upBuffer,true);
   ArraySetAsSeries(downBuffer,true);
   double upValue = upBuffer[0];
   double downValue = downBuffer[0];
   Comment("upValue: ",upValue,"\ndownValue: ",downValue);
  }
//+------------------------------------------------------------------+

在无错误编译代码并在图表上执行 EA 后,我们可以发现其输出就如同下面的示例:

AroonValues

正如我们在上一张图表中看到的那样,我们可以找到注释中的上行线和下行线值,我们还可以找到插入到图表中的已创建自定义指标,以确保 EA 返回相同的指标值(96,48)。

Aroon 交叉策略:

现在,是时候对我们提到的交易策略进行编码了,我们将从 Aroon 交叉策略开始。

在全局范围内,我们将使用 #include 预处理器为我们的 EA 添加交易功能,以便根据我们的策略自动下单

#include <trade/trade.mqh>

创建 period、horizontal shift、lotSize、slLvl 和 tpLvl 五个输入参数,并为每个输入参数分配默认值

input int         periodInp = 25; // Period
input int         shiftInp  = 0; // Shift
input double      lotSize=1;
input double      slLvl=200;
input double      tpLvl=600;

创建以下变量:

  • 稍后用于指标定义的整数(Aroon)变量。
  • 用于限制每个柱形开仓订单的整数(barstotal)变量。
  • 用于下订单的 CTrade 交易对象。
int aroon;
int barsTotal;
CTrade trade;

在 OnInit() 事件中,我们将使用 iBars 函数来定义已声明的 (barsTotal) 变量,这些函数会返回历史中交易品种和周期的可用柱形数量。

barsTotal=iBars(_Symbol,PERIOD_CURRENT);

使用 iCustom 定义 Aroon 变量,包含我们创建的 Aroon 自定义指标

aroon = iCustom(_Symbol,PERIOD_CURRENT,"Aroon",periodInp,shiftInp);

在 OnDeinit() 事件中,我们将打印移除 EA 的相关内容

Print("EA is removed");

在 OnTick() 事件中,我们将声明一个整数 bars 变量,用于存储每个分时报价的 bars 编号

int bars=iBars(_Symbol,PERIOD_CURRENT);

检查 barsTotal 是否不等于 bars

if(barsTotal != bars)

然后,我们将使用柱形缓冲区更新 barsTotal

barsTotal=bars;

声明两个上行线和下行线的双精度型数组,获取指标缓冲区的数据,为所选数组设置 AS_SERIES 标志,声明并定义四个双精度型变量,分别代表之前和当前的上行线和下行线值

      double upBuffer[], downBuffer[];
      CopyBuffer(aroon,0,0,3,upBuffer);
      CopyBuffer(aroon,1,0,3,downBuffer);
      ArraySetAsSeries(upBuffer,true);
      ArraySetAsSeries(downBuffer,true);
      double prevUpValue = upBuffer[1];
      double prevDownValue = downBuffer[1];
      double upValue = upBuffer[0];
      double downValue = downBuffer[0];

然后设置买入订单的条件,即 prevUpValue 小于 prevDownValue,同时 upValue 大于 downValue。

if(prevUpValue<prevDownValue && upValue>downValue)

当满足该条件时,声明一个双精度型 ask 变量,并将其定义为当前交易品种的当前卖出价,声明并定义双精度型 slVal 和 tpVal,并在当前交易品种上以当前卖出价按用户预先定义的 lotSize 建立买入仓位,止损将与预先定义的 slVal 相同,止盈将与预先定义的 tpVal 相同。

         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);

然后设置卖出指令的条件,即 prevUpValue 大于 prevDownValue,同时 upValue 小于 downValue。

if(prevUpValue>prevDownValue && upValue<downValue)
当满足该条件时,声明一个双精度型 bid 变量并将其定义为当前交易品种的当前买入价,声明并定义双精度型 slVal 和 tpVal,并在当前交易品种上以当前买入价建立一个用户预定义 lotSize 的卖出仓位,止损将与 slVal 的预定义相同,止盈将与 tpVal 的预定义相同。
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);

就这样,下面是 Aroon 交叉策略的完整代码:

//+------------------------------------------------------------------+
//|                                             AroonCrossoverEA.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int         periodInp = 25; // Period
input int         shiftInp  = 0; // Shift
input double      lotSize=1;
input double      slLvl=200;
input double      tpLvl=600;
int aroon;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   aroon = iCustom(_Symbol,PERIOD_CURRENT,"Aroon",periodInp,shiftInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double upBuffer[], downBuffer[];
      CopyBuffer(aroon,0,0,3,upBuffer);
      CopyBuffer(aroon,1,0,3,downBuffer);
      ArraySetAsSeries(upBuffer,true);
      ArraySetAsSeries(downBuffer,true);
      double prevUpValue = upBuffer[1];
      double prevDownValue = downBuffer[1];
      double upValue = upBuffer[0];
      double downValue = downBuffer[0];
      if(prevUpValue<prevDownValue && upValue>downValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevUpValue>prevDownValue && upValue<downValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

在无任何错误地编译此代码后,我们可以找到与以下策略相同的下单示例:

买入订单示例:

买入交易

正如我们在上一个示例中看到的,在上下两条线交叉后,我们有一个买入订单。

卖出订单示例:

卖出交易

正如我们在上一个例子中看到的,在下行线与上行线交叉后,我们有一个卖出订单。

Aroon 水平策略:

在本部分中,我们将对 Aroon 策略中提到的 Aroon 水平策略进行编码,该策略将让 EA 根据 Aroon 指标本身的下行线与 10 和 50 水平线之间的交叉情况开立订单。下面是我们如何在 MQL5 中进行编码。它与我们在 Aroon 交叉策略中的代码大部分相同,但也有一些不同之处,因此我们将提供完整的代码,仅提及该代码与之前 Aroon 交叉策略代码的不同之处。

以下是在一个代码块中编码 Aroon 水平策略的完整代码:

//+------------------------------------------------------------------+
//|                                                AroonLevelsEA.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int         periodInp = 25; // Period
input int         shiftInp  = 0; // Shift
input double      lotSize=1;
input double      slLvl=200;
input double      tpLvl=600;
int aroon;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   aroon = iCustom(_Symbol,PERIOD_CURRENT,"Aroon",periodInp,shiftInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      double upBuffer[], downBuffer[];
      CopyBuffer(aroon,0,0,3,upBuffer);
      CopyBuffer(aroon,1,0,3,downBuffer);
      ArraySetAsSeries(upBuffer,true);
      ArraySetAsSeries(downBuffer,true);
      double prevDownValue = downBuffer[1];
      double downValue = downBuffer[0];
      if(prevDownValue> 10 && downValue<10)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevDownValue < 50 && downValue>50)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }

该代码的不同之处如下:

我们只需定义前值和当前值

      double prevDownValue = downBuffer[1];
      double downValue = downBuffer[0];

如果 prevDownValue 大于 10,同时当前 downValue 小于 10,则为策略条件。我们需要 EA 在定义卖出价、止损价和止盈价后下达买入订单

      if(prevDownValue> 10 && downValue<10)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

如果 prevDownValue 小于 50,同时当前 downValue 大于 50。我们需要 EA 在定义当前出价、止损和止盈之后下达卖出订单

      if(prevDownValue < 50 && downValue>50)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

在无任何错误的情况下编译该代码后,我们可以发现 EA 可以下达与以下示例相同的订单:

买入订单示例:

买入交易

卖出订单示例:

卖出交易


测试 Aroon 交易系统

在这一部分,我们将测试每种策略的效果,我无法足够确认,所以您可能需要对这些策略进行更多优化,才能获得更好的效果,因此您需要做足功课,看看哪些策略适合你的方式,哪些策略更有价值。 

我们将重点关注以下关键衡量指标,对两者进行比较:

  • 净利润:计算方法是从毛利润中减去毛损失,最高值就是最好的。
  • 相对余额回撤:  这是账户在交易过程中的最大亏损,最低的就是最好的。
  • 利润因子:  这是毛利润与毛损失的比率,最高值为最佳值
  • 预期回报:  也就是一笔交易的平均利润或亏损,最高值就是最好的。
  • 恢复因子:  它衡量的是经过测试的策略在遭受损失后的恢复能力, 最高的就是最好的。
  • 夏普比率: 它通过比较收益率和无风险收益率来确定测试交易系统的风险和稳定性。 夏普比率最高的就是最好的。

在测试两种策略时,我们还将测试相同的时间段,即从 2023 年 1 月 1 日至 2023 年 12 月 31 日的一年时间段,并测试两个时间框架,即 15 分钟和一小时。

Aroon 交叉策略:

现在,我们开始看看 15 分钟和 1 小时两种时间框架下的 Aroon 交叉策略的结果,看看哪种策略更好。

在 15 分钟时间框架内测试该策略:

下图为结果图:

测试结果 15分钟

测试结果3- 15分钟

测试结果2- 15分钟

根据之前的结果,我们得出以下测试数据:

  • 净利润:14791
  • 相对余额回撤:6.78%
  • 利润因子:1.17
  • 预期回报:24.53
  • 恢复因子:1.91
  • 夏普比率:2.23

在 1 小时时间框架内测试该策略:

下图为结果图:

测试结果-1小时时段

测试结果3-1小时时段

测试结果2- 1小时时段

根据之前的结果,我们得出以下测试数据:

  • 净利润: 6242.20
  • 相对余额回撤: 1.80%
  • 利润因子:1.39
  • 预期回报: 53.81
  • 恢复因子: 2.43
  • 夏普比率: 3.23

Aroon 水平策略:

在本部分中,我们将测试基于相同概念的 Aroon 水平线策略,我们将在 15 分钟和 1 小时两个时间框架内测试相同的策略,以比较两个时间框架内的相同数据。

在 15 分钟时间框架内测试该策略:

下图为测试结果:

测试结果 15分钟

测试结果3- 15分钟

测试结果2- 15分钟

根据之前的结果,我们得出以下测试数据:

  • 净利润: 42417.30
  • 相对余额回撤: 12.91%
  • 利润因子:1.21
  • 预期回报: 29.62
  • 恢复因子: 2.27
  • 夏普比率: 1.88

在 1 小时时间框架内测试该策略:

下图为测试结果:

测试结果 1小时

测试结果3- 1小时

测试结果2- 1小时

根据之前的结果,我们得出以下测试数据:

  • 净利润: 16001.10
  • 相对余额回撤: 5.11%
  • 利润因子:1.30
  • 预期回报: 41.89
  • 恢复因子: 2.68
  • 夏普比率:2.61

下图将所有结果集中在一处,以便更好地进行比较:

数字

根据上述情况,我们可以找到与所测试的策略和时间框架相同的最佳数字:

  • 净利润:  在 15 分钟时间框架内进行测试时,Aroon 水平策略显示了最佳更高数字(42417.30 美元)。
  • 相对余额回撤: 在 1 小时时间框架内进行测试时,Aroon 交叉策略显示了最佳较低数字(1.80%)。
  • 利润因子: 在 1 小时时间框架内进行测试时,Aroon 交叉策略显示了最佳较高数字(1.39)。
  • 预期回报: 在 1 小时时间框架内进行测试时,Aroon 交叉策略显示了更高的数字(53.81)。
  • 恢复因子: 在 1 小时时间框架内进行测试时,Aroon 水平策略显示的数字较高(2.68)。
  • 夏普比率: 更高的数字(3.23)显示的是在 1 小时时间框架内测试的 Aroon 交叉策略。

利用前面的数据,我们可以根据自己的交易目标以及哪些数据可以实现这些目标来选择合适的策略。


结论

建立和测试交易系统对于任何认真从事交易的交易者来说都是一项至关重要的任务。在本文中,我们介绍了 Aroon 指标,该指标可用于任何交易系统,既可单独使用,也可与其他工具结合使用。这可能对您的交易有帮助,或为您建立良好的交易系统提供启发。

我们详细介绍了 Aroon 指标,如何根据其主要概念使用该指标,以及如何计算该指标。我们已经确定了两种可以使用的简单策略:

  • Aroon 交叉策略:当 Aroon 上升线高于 Aroon 下降线时,该策略可以自动建立买入头寸;当 Aroon 下降线高于 Aroon 上升线时,该策略可以自动建立卖出头寸。
  • Aroon 水平策略:如果 Aroon 下行线数值低于指定的 10 水平,该策略就会自动建立买入头寸;如果 Aroon 下行线数值高于指定的 50 水平,该策略就会自动建立卖出头寸。

在使用 MQL5 创建 Aroon 自定义指标并编写了一个简单的程序,将该指标插入图表,即可在图表上生成 Aroon 上行线和 Aroon 下行线之后,我们对这些策略进行了测试,并根据每个策略在 15 分钟和 1 小时两个时间框架内的测试结果确定了重要数据。我们可以根据自己的交易目标和每种策略的结果来使用它们。

我们还需要明白,对于这些提到的策略,我们可能需要更多的优化和更多的努力才能找到更好的结果。本文的主要目的是通过分享不同交易系统的一些思路,让我们开放思想,建立或开发出更好的交易系统。

我希望这篇文章对你的交易和发展之路有所帮助,让你获得更好、更有效的结果。如果你觉得这篇文章很有趣,并且需要阅读更多关于根据不同策略和不同技术指标构建交易系统的文章,你可以通过查看我的出版物页面阅读我以前的文章,在这方面可以找到许多关于最流行技术指标的文章,我希望这些文章对你有用。

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

附加的文件 |
Aroon.mq5 (3.04 KB)
AroonValuesEA.mq5 (0.91 KB)
AroonLevelsEA.mq5 (1.63 KB)
开发回放系统(第 38 部分):铺路(II) 开发回放系统(第 38 部分):铺路(II)
许多认为自己是 MQL5 程序员的人,其实并不具备我在本文中将要概述的基础知识。许多人认为 MQL5 是一个有限的工具,但实际原因是他们尚未具备所需的知识。所以,如果您有啥不知道,不要为此感到羞愧。最好是因为不去请教而感到羞愧。简单地强制 MetaTrader 5 禁用指标重叠,并不能确保指标和智能系统之间的双向通信。我们离这个目标还很远,但指标在图表上没有重叠的事实给了我们一些信心。
种群优化算法:改变概率分布的形状和位移,并基于智能头足类生物(SC)进行测试 种群优化算法:改变概率分布的形状和位移,并基于智能头足类生物(SC)进行测试
本文研究了改变概率分布形状对优化算法性能的影响。我们将进行的实验,会用到智能头足类生物(SC)测试算法,从而评估优化问题背景下各种概率分布的效能。
开发回放系统(第 41 部分):启动第二阶段(二) 开发回放系统(第 41 部分):启动第二阶段(二)
如果到目前为止,你觉得一切都很好,那就说明你在开始开发应用程序时,并没有真正考虑到长远的问题。随着时间的推移,你将不再需要为新的应用程序编程,只需让它们协同工作即可。让我们看看如何完成鼠标指标的组装。
开发具有 RestAPI 集成的 MQL5 强化学习代理(第 4 部分):在 MQL5 中组织类中的函数 开发具有 RestAPI 集成的 MQL5 强化学习代理(第 4 部分):在 MQL5 中组织类中的函数
本文讨论 MQL5 中从面向过程编码向面向对象编程 (OOP) 的过渡,重点是与 REST API 的集成。今天,我们将讨论如何将 HTTP 请求函数(GET 和 POST)组织到类中。我们将仔细研究代码重构,并展示如何用类方法替换孤立的函数。本文包含实用的示例和测试。