English Русский Español Deutsch 日本語 Português
preview
测试不同的移动平均类型以了解它们的洞察力

测试不同的移动平均类型以了解它们的洞察力

MetaTrader 5测试者 | 23 二月 2024, 10:37
647 1
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

概述

在我之前的文章中,我们探讨了最流行的移动平均线类型(简单、加权、指数):了解如何设计不同的移动平均系统。我们将在本文中继续这个主题,并使用简单类型将其结果与其他类型的移动平均线进行比较。许多交易者根据自己的偏好使用不同类型的移动平均线,因此,在本文中,我们将详细探讨它们的不同类型,测试它们的性能,并比较结果,以确定哪种类型的性能更好。

出于本文的目的,我们将创建一个简单的应用程序,以便在内置的MetaTrader 5 Strategy Tester中执行平均指标的回测。要了解我们在研究测试结果时需要关注的内容,请查看我之前的文章理解并有效地使用 MQL5 策略测试器;它提供了很多有用的信息。

为了探讨不同类型的移动平均线并了解它们的表现,我们将研究以下主题:

请注意,尽管我会尝试优化每个移动平均线的设置以获得最佳结果,但您需要自己做更多的检查和测试,因为这里的目的只是教学。因此,您应该测试、优化并找到更好的设置,以便获得更好的结果。这种情况可能会发生,因此这些类型中的任何一种或全部都不适合您的交易风格。但我希望你能找到有助于改善交易的见解。一般来说,无论你遇到什么理想情况,在实际账户中使用任何交易策略之前,衡量其效果都很重要。

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

简单移动平均(SMA)系统测试

在这一部分中,我将分享简单系统的结果,该系统仅基于简单的一次移动平均策略。它寻找价格和简单移动平均线之间的交叉点,这将产生买入和卖出信号。我们将创建一个能够自动执行这些信号的系统,

我们将使用以下信号进行交易操作:


买入信号:

收盘价高于简单移动平均值

而且,之前的收盘价低于之前的简单移动平均值

卖出信号:

收盘价低于简单移动平均值

而且,之前的收盘价高于之前的简单移动平均值

如果你想了解简单均线和其他流行的均线类型,我建议你阅读我之前的文章了解如何设计不同的均线系统,这将有助于你很好地了解它们,也有助于很好地理解这篇文章。

以下是创建这种类型的交易系统的步骤,该系统能够根据上述信号自动执行买卖订单。

在全局范围内,我们将把Trade包含文件包括在软件中,该软件允许使用预处理器#include根据我们的信号执行订单。如果您想了解更多关于预处理器的信息,可以阅读文章有关MQL5程序结构的所有信息了解更多详细信息。

#include <Trade\Trade.mqh>

创建三个用户输入参数,分别为 double 型变量 lotSize、ENUM_TIMEFRAMES 变量 timeFrame、和整数变量 MAPeriod,根据用户需求进行更改,并启动它们的默认值:

input double lotSize = 1;
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;
input int MAPeriod= 50;

创建两个整数变量 simpleMA 和 barsTotal 而不进行赋值,因为我们稍后将在OnInit()部分中定义它们。

int simpleMA;
int barsTotal;

创建 CTrade 类的 trade 对象,以便轻松访问交易函数。

CTrade trade;

在OnInit()部分,我们将使用iMA函数来定义simpleMA,以返回移动平均指标的句柄,其参数为:

  • symbol:用于指定交易品种名称,我们将使用 _Symbol 作为当前交易品种
  • period:用于指定时间框架,我们将使用默认值为1小时的用户输入,但用户可以进行调整
  • ma_period:用于指定简单的移动平均周期,我们将使用默认值为50的用户输入,但用户也可以调整它
  • ma_shift:用于指定水平偏移,我们将使用0 
  • applied_price:用于指定价格类型,我们将使用收盘价进行简单的移动平均线计算。
simpleMA = iMA(_Symbol, timeFrame, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);

barsTotal 通过使用 iBars 函数返回柱数,其参数为:

  • symbol:用于指定交易品种,我们将使用(_Symbol)应用于当前交易品种
  • timeframe:用于指定时间框架,我们将使用创建的默认值为1小时的用户输入时间框架,用户可以对其进行调整
   barsTotal=iBars(_Symbol,timeFrame);

在OnTick()部分中,我们将创建两个数组,一个用于使用MqlRates存储价格、数量和价差信息的价格,另一个用于简单移动平均值:

   MqlRates priceArray[];
   double mySMAArray[];

使用 ArraySetAsSeries 函数为这两个创建的数组设置AS_SERIES标志,其参数为:

  • array[]: 使用引用方式指定数组
  • flag: 用于指定数组的索引方向
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(mySMAArray,true);

在创建两个 double 型变量后定义卖家出价和买家出价

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

使用 CopyRates 函数获取 MqlRates 的历史数据,其参数为:

  • symbol_name: 用于确定交易品种名称
  • timeframe: 用于确定时间框架
  • start_pos: 用于指定起始位置
  • count: 用于指定要复制的数据量
  • rates_array[]: 用于指定复制的目标数组
int Data=CopyRates(_Symbol,_Period,0,3,priceArray);

使用 CopyBuffer 函数获取指标缓冲区的数据,其参数为:

  • indicator_handle: 用于指定指标句柄,此处为 simpleMA
  • buffer_num: 用于指定指标缓冲区编号,此处为0
  • start_pos: 用于确定起始位置,0表示当前烛形
  • count: 用于指定我们需要复制的数据量,此处为3
  • buffer[]: 用于指定我们需要复制的目标数组,此处为 mySMAArray
CopyBuffer(simpleMA,0,0,3,mySMAArray);

创建两个 double 型变量,用于定义同一烛形的最后收盘价和简单移动平均值

   double lastClose=(priceArray[1].close);
   double SMAVal = NormalizeDouble(mySMAArray[1],_Digits);

创建另外两个 double 型变量,用于定义上一个收盘价和同一蜡烛的简单移动平均值

   double prevClose=(priceArray[2].close);
   double prevSMAVal = NormalizeDouble(mySMAArray[2],_Digits);

创建一个整型 bars 变量,用于和创建的 barsTotal 作比较

int bars=iBars(_Symbol,timeFrame);

通过检查 bars 和 barsTotal 是否相等来检查是否有新柱生成

if(barsTotal != bars)

如果 barsTotal 不等于 bars,我们需要用 bars 的值来更新 barsTotal

barsTotal=bars;

如果 barsTotal 不等于 bars,我们还需要检查我们的策略条件,如果最后一次收盘的前一次低于同一烛形的简单移动平均值,并且同时最后一次的收盘价高于同一个烛形的简单运动平均值。我们需要该程序来关闭当前打开的头寸并打开买入头寸

      if(prevClose<prevSMAVal && lastClose>SMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Buy(lotSize,_Symbol,Ask,0,0,NULL);
        }

如果最后一次收盘的前一个收盘价高于同一烛形的简单移动平均值,同时最后一次的收盘价低于同一个烛形的简单运动平均值。我们需要该程序来关闭当前打开的头寸并打开卖出头寸

      if(prevClose>prevSMAVal && lastClose<SMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Sell(lotSize,_Symbol,Bid,0,0,NULL);
        }

然后我们将编译代码,我们会发现它编译后没有任何错误或警告。

以下是一个代码块中的完整代码:

//+------------------------------------------------------------------+
//|                                                   SMA_System.mq5 |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
input double lotSize = 1;
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;
input int MAPeriod= 50;
int simpleMA;
int barsTotal;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   simpleMA = iMA(_Symbol, timeFrame, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   barsTotal=iBars(_Symbol,timeFrame);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   MqlRates priceArray[];
   double mySMAArray[];
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(mySMAArray,true);
   int Data=CopyRates(_Symbol,_Period,0,3,priceArray);
   CopyBuffer(simpleMA,0,0,3,mySMAArray);
   double lastClose=(priceArray[1].close);
   double SMAVal = NormalizeDouble(mySMAArray[1],_Digits);
   double prevClose=(priceArray[2].close);
   double prevSMAVal = NormalizeDouble(mySMAArray[2],_Digits);
   int bars=iBars(_Symbol,timeFrame);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      if(prevClose<prevSMAVal && lastClose>SMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Buy(lotSize,_Symbol,Ask,0,0,NULL);
        }
      if(prevClose>prevSMAVal && lastClose<SMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Sell(lotSize,_Symbol,Bid,0,0,NULL);
        }
     }
  }
//+------------------------------------------------------------------+

我们编译EA并在EURUSD数据上运行它,以对策略进行回溯测试。测试期为2022年1月1日至6月30日,与我们在所有提到的移动平均值类型中所做的相同。SMA的设置如下:

  • 手数大小: 1
  • 时间框架: 1小时
  • MA 周期数: 50

测试结果:

SMA 结果

正如我们所看到的,在测试SMA指标后,我们需要研究以下数字:

  • 净利润: 2700.30 (27%)
  • 余额相对回撤: 37.07%
  • 净值相对回撤: 41.76%
  • 利润因子: 1.10
  • 期望收益: 12.68
  • 恢复因子: 0.45
  • 夏普比率: 0.57

自适应移动平均(iAMA)

在这一部分中,我们将了解另一种类型的移动平均线,即AMA(自适应移动平均线,Adaptive Moving Average)。它是由Perry J.Kaufman开发的,也被称为(KAMA),主要概念是减少价格波动中的噪音。它跟随价格运动,无论这种价格运动的波动性如何。该指标是一个趋势跟随指标,因此,它可以用于识别趋势和转折点。

该指标分几个步骤计算。

第一步:计算 AMA 或 KAMA

第一步

第二步:计算平滑常数(smoothing constant, SC)

第二步

第三步:计算效率比率(Efficiency Ratio, ER)

 第三步

现在,我们学习了如何手动计算AMA指标,但我们不需要这样做,因为我们可以通过从可用的现成指标中选择它,将其自动插入MetaTrader 5。现在是时候创建一个交易系统了,该系统可以根据收盘价和AMA指标之间的交叉执行买卖订单。因此,我们将使用以下信号作为我们的策略。

买入信号:

收盘价高于自适应移动平均值

而且,之前的收盘价低于之前的自适应移动平均值

卖出信号:

收盘价低于自适应移动平均值

而且,之前的收盘价高于之前的自适应移动平均值

以下是创建此类交易系统的完整代码:

//+------------------------------------------------------------------+
//|                                                  iAMA_System.mq5 |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
input double lotSize = 1;
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;
input int MAPeriod= 50;
input int fastMAPeriod= 5;
input int slowMAPeriod= 100;
int adaptiveMA;
int barsTotal;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   adaptiveMA = iAMA(_Symbol, timeFrame, MAPeriod,fastMAPeriod,slowMAPeriod, 0, PRICE_CLOSE);
   barsTotal=iBars(_Symbol,timeFrame);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   MqlRates priceArray[];
   double myAMAArray[];
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(myAMAArray,true);
   int Data=CopyRates(_Symbol,timeFrame,0,3,priceArray);
   CopyBuffer(adaptiveMA,0,0,3,myAMAArray);
   double lastClose=(priceArray[1].close);
   double AMAVal = NormalizeDouble(myAMAArray[1],_Digits);
   double prevClose=(priceArray[2].close);
   double prevAMAVal = NormalizeDouble(myAMAArray[2],_Digits);
   int bars=iBars(_Symbol,timeFrame);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      if(lastClose>AMAVal && prevClose<prevAMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Buy(lotSize,_Symbol,Ask,0,0,NULL);
        }
      if(lastClose<AMAVal && prevClose>prevAMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Sell(lotSize,_Symbol,Bid,0,0,NULL);
        }
     }
  }
//+------------------------------------------------------------------+

此代码的区别在于使用iAMA函数,该函数返回自适应移动平均指标的句柄,其参数为:

  • symbol:用于指定交易品种名称,我们将使用 _Symbol 作为当前交易品种
  • period: 用于指定时间框架,用户输入的默认值为1小时
  • ama_period: 用于指定自适应移动平均的周期数
  • fast_ma_period: 用于指定快速 MA 的周期数
  • slow_ma_period: 用于指定慢速 MA 的周期数
  • ama_shift: 用于指定水平偏移,我们将使用0
  • applied_price: 用于指定价格类型,我们将使用收盘价

我们需要对同期的AMA指标进行回溯测试,以查看其结果:

  • 手数大小 = 1
  • 时间框架 = 1小时
  • MA 周期数 = 50
  • 快速 MA = 5
  • 慢速 MA = 100

结果:

AMA 结果

我们可以看到,在测试AMA指标后,我们需要研究以下数字:

  • 净利润: 3638.20 (36.39%)
  • 余额相对回撤: 22.48%
  • 净值相对回撤: 35.53%
  • 利润因子: 1.31
  • 期望收益: 35.67
  • 恢复因子: 0.65
  • 夏普比率: 0.86

双重指数移动平均(iDEMA)

在这一部分中,我们将确定另一种移动平均线类型,即双重指数移动平均(DEMA)技术指标。这是一个趋势跟随指标,由Patrick Mulloy开发。该指标的主要目标是减少均线的滞后性,但使其对市场走势更有反应,正如我们将在该指标的计算中看到的那样。

该指标可用于通过检测价格相对于该移动平均线的位置来识别趋势或趋势中的转折点。指标计算步骤如下:

1. 第一个指数移动平均线(EMA)的计算

EMA one = EMA of n period of price

2. 计算第一个EMA的EMA

EMA two = EMA of EMA one

3. 计算DEMA

DEMA = (2 * EMA one) - EMA two

正如我们所知,我们不需要进行这种手动计算,这是为了更好地理解指标,与MetaTrader 5中的许多技术指标相同,我们已经有了这个指标。现在,我们需要创建与之前创建的交易系统相同的交易系统,但这次我们将使用DEMA来测试与其他类型相比的结果。该交易系统将执行相同的订单,这些订单也基于交叉点进行买卖,但这次将是价格和DEMA指标之间的交叉点。信号将是:

买入信号:

收盘价高于双重指数移动平均值(DEMA)

而且,之前的收盘价低于之前的DEMA值

卖出信号:

收盘价低于DEMA值

而且,之前的收盘价高于之前的DEMA值

以下是该交易系统的完整代码,与使用iDEMA函数返回双指数移动平均线指标的句柄略有不同,其参数为:

  • symbol: 用于指定交易品种名称,对于当前交易品种我们将使用 (_Symbol)
  • period: 用于指定时间框架,我们将在用户输入部分默认使用1小时
  • ma_period: 用于指定移动平均周期数,我们将在用户输入部分默认使用50
  • ma_shift: 用于指定水平偏移,我们将使用0,因为我们在图表上不需要偏移
  • applied_price: 用于指定价格类型,我们将使用收盘价来计算移动平均

它将与下面的一个代码块相同:

//+------------------------------------------------------------------+
//|                                                 iDEMA_System.mq5 |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
input double lotSize = 1;
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;
input int MAPeriod= 50;
int DEMA;
int barsTotal;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   DEMA = iDEMA(_Symbol, timeFrame, MAPeriod,0, PRICE_CLOSE);
   barsTotal=iBars(_Symbol,timeFrame);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   MqlRates priceArray[];
   double myDEMAArray[];
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(myDEMAArray,true);
   int Data=CopyRates(_Symbol,timeFrame,0,3,priceArray);
   CopyBuffer(DEMA,0,0,3,myDEMAArray);
   double lastClose=(priceArray[1].close);
   double DEMAVal = NormalizeDouble(myDEMAArray[1],_Digits);
   double prevClose=(priceArray[2].close);
   double prevDEMAVal = NormalizeDouble(myDEMAArray[2],_Digits);
   int bars=iBars(_Symbol,timeFrame);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      if(lastClose>DEMAVal && prevClose<prevDEMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Buy(lotSize,_Symbol,Ask,0,0,NULL);
        }
      if(lastClose<DEMAVal && prevClose>prevDEMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Sell(lotSize,_Symbol,Bid,0,0,NULL);
        }
     }
  }
//+------------------------------------------------------------------+

编译完这段代码后,我们可以对之前其他类型使用的相同周期进行回溯测试,该指标的设置或输入如下:

  • 手数大小 = 1
  • 时间框架 = 1小时
  • MA 周期数 = 50

运行测试后,我们得到以下结果:

DEMA 结果

我们可以看到,在测试iDEMA后,我们需要研究以下数字:

  • 净利润: - 961.60 (- 9.62%)
  • 余额相对回撤: 39.62%
  • 净值相对回撤: 41.15%
  • 利润因子: 0.97
  • 期望收益: - 3.12
  • 恢复因子: - 0.18
  • 夏普比率: - 0.21

三重指数移动平均(iTEMA)

现在,我们将确定另一种移动平均线类型,即三重指数移动平均线(TEMA),它是由Patrick Mulloy开发的,旨在为指标增加更多的响应能力,以适合短期交易。在这个指标中,我们使用了三次平滑的指数平均线、单次和两次平滑指数均线。因此,这一指标将更接近价格,与我们提到的一样,反应更灵敏。与我们使用双重指数移动平均线一样,我们可以以同样的方式计算TEMA,除了它对价格的反应更快之外,它还可以用于识别趋势和趋势的转折点或变化。

以下是计算该TEMA指标的步骤:

1. 计算第一个指数移动平均线(EMA)

EMA one = EMA of n period of price

2. 计算EMA1的EMA

EMA two = EMA of EMA one

3. 计算EMA2的EMA

EMA three = EMA of EMA two

4. 计算TEMA

DEMA = (3 * EMA one) - (3 * EMA two) + (EMA3)

我们可以从MetaTrader 5中现有的现成技术指标中插入该指标,而无需手动计算,然后我们需要使用该TEMA指标创建我们的交易系统来测试它,并将其结果与其他类型进行比较。因此,以下是创建该交易系统的完整代码,与我们之前所做的相同,不同之处在于使用iTEMA函数返回三指数移动平均指数指标的句柄,其参数与我们在DEMA和SMA中提到的相同。

//+------------------------------------------------------------------+
//|                                                 iTEMA_System.mq5 |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
input double lotSize = 1;
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;
input int MAPeriod= 50;
int TEMA;
int barsTotal;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   TEMA = iTEMA(_Symbol, timeFrame, MAPeriod,0, PRICE_CLOSE);
   barsTotal=iBars(_Symbol,timeFrame);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   MqlRates priceArray[];
   double myTEMAArray[];
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(myTEMAArray,true);
   int Data=CopyRates(_Symbol,timeFrame,0,3,priceArray);
   CopyBuffer(TEMA,0,0,3,myTEMAArray);
   double lastClose=(priceArray[1].close);
   double TEMAVal = NormalizeDouble(myTEMAArray[1],_Digits);
   double prevClose=(priceArray[2].close);
   double prevTEMAVal = NormalizeDouble(myTEMAArray[2],_Digits);
   int bars=iBars(_Symbol,timeFrame);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      if(lastClose>TEMAVal && prevClose<prevTEMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Buy(lotSize,_Symbol,Ask,0,0,NULL);
        }
      if(lastClose<TEMAVal && prevClose>prevTEMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Sell(lotSize,_Symbol,Bid,0,0,NULL);
        }
     }
  }
//+------------------------------------------------------------------+

在编译和执行该软件后,我们可以对同一时期进行回溯测试,将其结果与其他移动平均线类型进行比较,以下是将用于尝试在该测试过程中固定所有参数的设置或输入:

  • 手数大小 = 1
  • 时间框架 = 1小时
  • MA 周期数 = 50

运行并完成此测试后,我们可以找到与以下相同的结果:

TEMA 结果

正如我们所看到的,我们需要研究以下数字:

  • 净利润: - 3973.10 (- 39.74%)
  • 余额相对回撤: 63.98%
  • 净值相对回撤: 66.06%
  • 利润因子: 0.90
  • 期望收益: - 10.59
  • 恢复因子: - 0.52
  • 夏普比率: - 0.83

分形自适应移动平均(iFrAMA)

我们还有最后一种类型要在本文中确定的移动平均线,它是由John Ehlers开发的分形自适应移动平均(FrAMA)指标。这是一个趋势跟踪指标,假设市场价格是分形的。它还可以用于检测趋势和转折点。以下是我们如何计算它的步骤:

 FrAMA

我们可以从MetaTrader 5中现有的现成技术指标中插入该指标,现在我们需要用相同的策略创建相同的交易系统,但我们将在这里使用FrAMA指标来比较测试后与其他类型的结果。该策略是价格和FrAMA指标之间的交叉,当价格超过FrAMA时,这将是买入信号,当价格低于FrAMA则是卖出信号。

买入信号:

收盘价高于分形自适应移动平均值(FrAMA)

而且,之前的收盘价低于之前的FrAMA值

卖出信号:

收盘价低于FrAMA值

而且,之前的收盘价高于之前的FrAMA值

以下是创建该交易系统的完整代码,它将与我们之前在其他类型中提到的相同,但我们将使用(iFrAMA)函数返回分形自适应移动平均线的句柄,其参数为:

  • symbol:用于指定交易品种名称,我们将使用 _Symbol 作为当前交易品种
  • period: 用于指定时间框架,用户输入的默认值为1小时
  • ma_period: 用于指定移动平均周期数,我们将在用户输入部分默认使用50
  • ma_shift:用于指定水平偏移,我们将使用0 
  • applied_price: to specify the price type, we will use closing price
//+------------------------------------------------------------------+
//|                                                iFrAMA_System.mq5 |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
input double lotSize = 1;
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;
input int MAPeriod= 50;
int FrAMA;
int barsTotal;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   FrAMA = iFrAMA(_Symbol, timeFrame, MAPeriod,0, PRICE_CLOSE);
   barsTotal=iBars(_Symbol,timeFrame);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   MqlRates priceArray[];
   double myFrAMAArray[];
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(myFrAMAArray,true);
   int Data=CopyRates(_Symbol,timeFrame,0,3,priceArray);
   CopyBuffer(FrAMA,0,0,3,myFrAMAArray);
   double lastClose=(priceArray[1].close);
   double FrAMAVal = NormalizeDouble(myFrAMAArray[1],_Digits);
   double prevClose=(priceArray[2].close);
   double prevFrAMAVal = NormalizeDouble(myFrAMAArray[2],_Digits);
   int bars=iBars(_Symbol,timeFrame);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      if(lastClose>FrAMAVal && prevClose<prevFrAMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Buy(lotSize,_Symbol,Ask,0,0,NULL);
        }
      if(lastClose<FrAMAVal && prevClose>prevFrAMAVal)
        {
         trade.PositionClose(_Symbol);
         trade.Sell(lotSize,_Symbol,Bid,0,0,NULL);
        }
     }
  }
//+------------------------------------------------------------------+

在编译和执行本EA后,使用FrAMA指示器以以下相同设置或输入测试该策略:

  • 手数大小: 1
  • 时间框架: 1小时
  • MA 周期数: 50

我们在与所有其他提到的移动平均线相同的时期测试这个EA,并得到以下结果:

FrAMA 结果

正如我们所看到的,我们需要研究的最重要的数字如下:

  • 净利润: - 2993.70 (- 29.94%)
  • 余额相对回撤: 73.28%
  • 净值相对回撤: 74.81%
  • 利润因子: 0.93
  • 期望收益: - 6.45
  • 恢复因子: - 0.33
  • 夏普比率: - 0.46

将结果与简单移动平均(SMA)结果进行比较

在该部分中,我将提供上述每种移动平均线类型的结果的比较,您需要知道,我将尽可能提供最佳结果,但如果我们对每个移动平均线进行优化并更改周期或测试周期,每个移动平均值都会给出不同的结果,因为我们知道移动平均线可以根据指标设置和市场条件给出不同的效果。

这里的主要目标是尽可能提供相同的条件,看看是否有一种类型比其他类型更好,我们可以从我们的角度选择最好的设置。但为了让您了解我们在寻找什么,我们需要了解其最高性能的最重要指标:

  • 净利润:值最高即为最佳
  • 回撤(DD):最低的最好
  • 利润因子:最高的最好
  • 期望收益:最高的最好
  • 恢复因子:最高的最好
  • 夏普比率:最高的夏普比率就是最好的

现在,我们将通过下表将先前的简单移动平均值结果与基于先前上下文的其他移动平均值类型进行比较:

衡量标准 SMA AMA DEMA TEMA FrAMA
净利润 2700.30 (27%) 3638.20 (36.39%) - 961.60 (- 9.62%) - 3973.10 (- 39.74%) - 2993.70 (- 29.94%)
余额相对回撤 37.07% 22.48% 39.62% 63.98% 73.28%
净值相对回撤  41.76% 35.53% 41.15% 66.06% 74.81%
利润因子 1.10 1.31 0.97 0.90 0.93
期望收益 12.68 35.67 - 3.12 - 10.59 - 6.45
恢复因子 0.45 0.65 - 0.18 - 0.52 - 0.33
夏普比率 0.57 0.86 - 0.21 - 0.83 - 0.46

根据我们所有测试的结果,根据设置和测试周期,我们有两种类型的性能最好。这些是简单移动平均和自适应移动平均。最好的是自适应移动平均。它拥有:

  • 最高的净利润
  • 最低的余额相对回撤
  • 最低的净值相对回撤
  • 最佳利润因子
  • 最高的期望收益
  • 最高的恢复因子
  • 最高的夏普比率

正如我们之前提到的,通过对设置和策略进行更多优化,我们可能会找到更好的结果,但这里的目标是,我们需要了解并将测试过程应用于真实的示例,然后我们可以将相同的过程用于任何测试目的。

结论

在本文中,我们探讨了以下移动平均线类型的性能结果:

  • 自适应移动平均(AMA)
  • 双重指数移动平均(DEMA)
  • 三重指数移动平均线(TEMA)
  • 分形自适应移动平均(FrAMA)

我们为每种类型创建了交易系统,并将其结果与最流行的移动平均线类型简单移动平均线进行比较。根据测试结果,我们发现最佳结果是由简单移动平均和自适应移动平均产生的,其中最重要的是自适应移动平均。我们分析并比较了以下指标,以确定获胜者:

  • 净利润
  • 回撤(DD)
  • 利润因子
  • 期望收益
  • 恢复因子
  • 夏普比率

测试是交易中一个非常重要的话题。所以,我鼓励你对更多的策略进行更多的测试。除了评估策略本身,随着每次测试加深你的理解,它还可以为你带来一些意想不到的见解。

感谢您花时间阅读这篇文章。我希望你发现这篇文章对你有用,并为你的知识增加价值。如果你想阅读更多关于基于流行技术指标创建交易系统的文章,如RSI、MACD、随机振荡、布林带和其他主题,你可以通过出版物查看我的文章,我希望你也会发现它们很有用。

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

附加的文件 |
SMA_System.mq5 (2.07 KB)
iAMA_System.mq5 (2.15 KB)
iDEMA_System.mq5 (2.06 KB)
iTEMA_System.mq5 (2.06 KB)
iFrAMA_System.mq5 (2.08 KB)
最近评论 | 前往讨论 (1)
go123
go123 | 26 2月 2024 在 03:27
单纯使用一根自适应均线做1小时周期的外汇,能不能盈利?为什么我在回测外汇品的时候大部分品种都是亏损的?
软件开发和 MQL5 中的设计范式(第一部分):创建范式 软件开发和 MQL5 中的设计范式(第一部分):创建范式
有一些方法可以用来解决许多重复性的问题。一旦明白如何运用这些方法,就可助您有效地创建软件,并贯彻 DRY(不要重复自己)的概念。在这种境况下,设计范式的主题就非常好用,因为它们为恰当描述过,且重复的问题提供了解决方案。
神经网络变得轻松(第四十八部分):降低 Q-函数高估的方法 神经网络变得轻松(第四十八部分):降低 Q-函数高估的方法
在上一篇文章中,我们概述了 DDPG 方法,它允许在连续动作空间中训练模型。然而,与其它 Q-学习方法一样,DDPG 容易高估 Q-函数的数值。这个问题往往会造成训练代理者时选择次优策略。在本文中,我们将研究一些克服上述问题的方式。
MetaTrader 5中的蒙特卡罗置换测试 MetaTrader 5中的蒙特卡罗置换测试
在本文中,我们将了解如何仅使用 Metatrader 5在任何 EA 交易上基于修改的分时数据进行置换测试。
交易事务. 请求和响应结构、描述和记录 交易事务. 请求和响应结构、描述和记录
本文探讨了处理交易请求结构,即创建请求、将其发送到服务器之前的初步验证、服务器对交易请求的响应以及交易交易的结构。我们将创建简单方便的函数,将交易订单发送到服务器,并根据所讨论的内容,创建EA来通知交易事务。