English Русский Español Deutsch 日本語 Português
preview
在MQL5中实现基于抛物线转向指标(Parabolic SAR)和简单移动平均线(SMA)的快速交易策略算法

在MQL5中实现基于抛物线转向指标(Parabolic SAR)和简单移动平均线(SMA)的快速交易策略算法

MetaTrader 5交易系统 | 14 四月 2025, 07:56
425 0
Allan Munene Mutiiria
Allan Munene Mutiiria

概述

本文将带您深入了解测试和优化我们交易算法的关键流程。我们将使用MQL5的策略测试器,用历史数据对我们的EA进行回测。用通俗的话来说,我们将检验算法在过去的表现,与实际市场条件进行对比。在讨论我们EA的表现时,我们还会涵盖如何解读各种绩效指标(例如,盈利因子、回撤、胜率等),以及这些指标如何反映我们算法的可靠性和盈利能力。到本文结束时,我们将更清楚地了解如何实施快速交易策略,以及确保其成功所需的条件。以下主题将贯穿整篇文章:

  1. 快速交易策略简介
  2. 了解指标:抛物线SAR和SMA
  3. 在MQL5中的实现
  4. 测试和优化
  5. 结论


快速交易策略专注于通过在短时间内执行多次交易,从快速且频繁的市场波动中获利,通常持仓时间不超过一小时。这种策略与传统的长期趋势交易策略形成对比,其目标是利用证券中快速变动的小幅价格变化。这种策略的成功依赖于几乎即时处理和响应市场数据的能力,因此像EA这样的自动化系统对于高效执行交易至关重要。

这种策略的有效性关键在于使用技术指标,例如抛物线SAR和简单移动平均线,这些指标有助于识别趋势反转并平滑价格数据。这些指标被集成到高频交易算法中,使策略能够快速适应市场变化,在信号生成和交易执行中保持准确性。如果实施得当,快速交易可以快速获利,尽管它需要谨慎管理成本和风险。


了解指标:抛物线SAR和SMA

有效的快速交易策略需要理解那些指导交易决策的重要技术指标。在这方面有用的两个指标分别是抛物线 SAR(Stop and Reverse,止损和反转)和简单移动平均线(SMA)。SMA是最古老且最常用的趋势跟踪指标之一。相比之下,抛物线SAR是相对较新的工具,但肯定不是鲜为人知的工具;这两个指标在确定市场条件和发出潜在交易机会信号方面都非常有用。

抛物线SAR指标旨在跟随趋势,并寻找和识别潜在的价格方向反转。它通过在价格上方或下方绘制一系列点,根据这些点相对于价格与 SAR(止损和反转)的位置而实施交易行为。如果我们把抛物线SAR纳入我们的交易策略,我们可以通过它相对于这一系列点来决定在市场上买入、卖出或做空。如果价格在SAR点上方,我们处于看涨趋势;如果价格在SAR点下方,我们处于看跌趋势。使用默认的指标设置。如下图所示:

抛物线SAR设置

抛物线SAR指标设置:

抛物线SAR安装

我们交易策略的另一个重要部分是简单移动平均线(SMA)。SMA在指定期间内取价格数据并将其“平滑”处理。它令我们更容易读懂整体趋势。我们也可以用 SMA 来更简单地判断趋势。如果SMA呈上升趋势,那么我们可以简单地说市场处于上升趋势。如果SMA呈下降趋势,我们可以说市场处于下降趋势。SMA是一种趋势过滤器。它告诉我们应该寻找多头(上升趋势)、空头(下降趋势)还是不进行交易(当价格在 SMA 的水平线附近徘徊时)。使用周期为60的SMA。如下图所示:

SMA设置

SMA指标设置:

SMA安装

当抛物线SAR和SMA一起使用时,它们可以提供市场条件的完整画面。SAR可以解读为当前趋势可能正在发生的变化的即时信号。它通常会在趋势反转发生之前就发出信号。SMA在更长的时间内查看更多的数据,因此能够更确定地确认趋势方向。将它们结合使用时,如下所示:

抛物线SAR&SMA组合安装

在提供了策略概述之后,让我们在MQL5中编写该策略的EA。


在MQL5中的实现

在学习了快速交易策略的所有理论之后,让我们将这些理论自动化,用MQL5语言为MetaTrader 5(MT5)编写一个EA。

要创建一个EA,在您的MetaTrader 5终端上,点击工具(Tools)选项卡并检查MetaQuotes语言编辑器,或者直接按键盘上的F4键。另外,您也可以点击工具栏上的IDE(集成开发环境)图标。这样就会打开MetaQuotes语言编辑器环境,该环境允许用户编写自动交易、技术指标、脚本和函数库。

打开MetaEditor

打开MetaEditor后,在工具栏上,点击“文件”选项卡,然后勾选“新建文件”,或者直接按CTRL + N键,以创建一个新文档。或者,您也可以点击工具栏选项卡上的“新建”图标。这将弹出一个MQL向导窗口。

新建EA

在弹出的向导中,选中EA(模板),然后单击下一步。

MQL 向导

在EA的一般属性中,在名称部分,提供您的文件名称。请注意,如果要指定或创建一个不存在的文件夹,您需要在EA名称前使用反斜杠。例如,这里我们默认有“Experts\”。这意味着我们的EA将被创建在Experts文件夹中,我们可以去那里找。其他部分相对直观,但您可以按照向导底部的链接了解如何精准地执行该过程。

NEW EA NAME

在输入您希望的EA文件名后,依次点击“下一步”、再“下一步”,然后点击“完成”。在完成上述所有操作后,我们现在可以开始编写和实现我们的策略了。

首先,我们从定义一些关于EA的基础数据开始。包括EA的名称、版权信息以及指向MetaQuotes网站的链接。我们还指定了EA的版本号,设置为“1.00”。

//+------------------------------------------------------------------+
//|                                               #1. RAPID FIRE.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+

#property copyright "Copyright 2024, MetaQuotes Ltd."   // Set the copyright property for the Expert Advisor
#property link      "https://www.mql5.com"              // Set the link property for the Expert Advisor
#property version   "1.00"                              // Set the version of the Expert Advisor

加载程序时,会显示如下信息。

EA METADATA

首先,我们在源代码的开头使用#include指令来包含一个交易实例。获得了CTrade类的访问权限后,我们将使用该类来创建一个交易对象。这一步至关重要,因为我们需要使用它开立交易。

#include <Trade/Trade.mqh>   // Include the MQL5 standard library for trading operations
CTrade obj_Trade;            // Create an instance of the CTrade class to handle trade operations

预处理器会将#include<Trade/Trade.mqh>这一行替换为Trade.mqh文件的内容。尖括号表示Trade.mqh文件将从标准目录中获取(通常路径为terminal_installation_directory\MQL5\Include)。当前目录不会被包含在搜索范围内。这条指令可以放在程序的任何地方,但通常所有的包含指令都放在源代码的开头,这样可以使代码结构更清晰,也便于引用。声明CTrade类的对象obj_Trade,可以让我们轻松地访问该类中包含的方法,这要归功于MQL5开发者的努力。

CTrade类

我们需要创建指标句柄,以便将必要的指标包含在策略中。

int handleSMA = INVALID_HANDLE, handleSAR = INVALID_HANDLE;  // Initialize handles for SMA and SAR indicators

这里,我们首先声明并初始化两个整数变量,“handleSMA”和“handleSAR”。这两个变量将作为我们计划在EA中使用的两个技术指标的句柄。在MQL5中,句柄是我们在使用特定的MQL5函数创建指标时分配给该指标的唯一标识符。我们将使用这些标识符在程序的任何地方引用非绘制的指标。我们还将这两个标识符都分配给了一个INVALID_HANDLE常量,这确保了如果我们偷懒,在程序编译后忘记了检查程序的流程逻辑,程序也不会引用一个无效的句柄。如果我们正确地处理这些句柄,并且在引用之前先检查句柄,避免引用无效的句柄或执行任何不当的操作,那么这两个指标就可以顺利地完成它们的工作。

接下来,我们需要创建数组,用于存储我们从指标中检索到的值或数据。

double sma_data[], sar_data[];  // Arrays to store SMA and SAR indicator data

在此,我们定义了两个动态数组,“sma_data”和“sar_data”,我们将把SMA和抛物线SAR指标生成的数据点分配给这些数组。我们将每个指标计算出的最新值分配给这些数组,让它们可以自由地引用和分析数据,以便在生成交易信号时使用。交易信号可以通过信号线交叉来生成。为了生成与当前趋势一致的信号或当前趋势反转的信号,我们可以利用这些数组中保存的过去值。通过利用这些过去值,我们应该能够更准确地解释指标以及正在交易的资产的价格走势。

接下来,我们需要OnInit事件处理程序。这个处理程序至关重要,因为当EA在图表上初始化时,它会被自动被调用。该函数负责设置EA,包括创建必要的指标句柄、初始化变量以及准备资源。换句话说,OnInit是一个内置函数,它确保在EA开始处理市场数据之前,一切都已正确配置。具体代码如下:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   // OnInit is called when the EA is initialized on the chart
//...
}

OnInit事件处理程序中,我们需要初始化指标句柄,以便将数据值分配给它们。

   handleSMA = iMA(_Symbol,PERIOD_M1,60,0,MODE_SMA,PRICE_CLOSE);  // Create an SMA (Simple Moving Average) indicator handle for the M1 timeframe

这里,我们使用iMA函数为简单移动平均线(SMA)指标创建一个句柄。这个句柄将为我们提供指定交易品种和时间框架的SMA值。在此,我们将其设置为当前交易品种(_Symbol)在1分钟图表(PERIOD_M1)上,周期为60根K线。参数“0”表示SMA计算中没有偏移,而计算模式的需求则通过常量MODE_SMA指定;因此,我们将计算一个SMA。最后,我们指定价格类型PRICE_CLOSE,这意味着该指标将基于每根K线的收盘价。我们将生成的句柄存储在变量“handleSMA”中。接下来,我们为抛物线SAR指标创建句柄。

   handleSAR = iSAR(_Symbol,PERIOD_M1,0.02,0.2);                  // Create a SAR (Parabolic SAR) indicator handle for the M1 timeframe

同样地,我们使用iSAR函数为抛物线SAR(SAR)指标创建一个句柄。iSAR函数返回特定交易品种和时间框架的抛物线SAR值。在此情况下,我们将其用于1分钟图表(PERIOD_M1)上的当前交易品种(_Symbol)。参数0.02和0.2定义了SAR的步长和最大值,这意味着它们控制了指标对价格变动的敏感度。生成的句柄将使我们能够访问SAR数据,这些数据将是我们第一个交易策略的核心,该策略依赖于市场趋势和价格反转。

默认情况下,指标句柄从10开始,并以1作为间隔递增。因此,如果我们打印指标值,我们应该得到10和11,因为我们有两个句柄。为了可视化它们的值,需要做记录。

   Print("SMA Handle = ",handleSMA);
   Print("SAR Handle = ",handleSAR);

我们得到了如下值:

句柄值

现在我们正确地记录了这些值。接下来,让我们确保这些指标句柄确实不为空。

   // Check if the handles for either the SMA or SAR are invalid (indicating failure)
   if (handleSMA == INVALID_HANDLE || handleSAR == INVALID_HANDLE){
      Print("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW");  // Print error message in case of failure
      return (INIT_FAILED);  // Return failure code, stopping the EA from running
   }

在这一部分,我们设置了一个机制,用于捕获在创建SMA和SAR指标句柄期间发生的严重错误。如果这些句柄没有成功创建,EA将无法正常工作。因此,我们检查“handleSMA”或“handleSAR”是否仍然等于INVALID_HANDLE,如果等于表明这两个指标中的任何一个初始化失败。如果我们发现这两个句柄中的任何一个无效,我们打印一条错误消息(“ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW.”)。并通过初始化函数返回INIT_FAILED值。这些操作共同构成了一个完善的错误处理程序,确保EA不会做出不安全的交易决策。然后,我们需要将存储数组设置为时间序列。可以通过以下代码段来实现:

   // Configure the SMA and SAR data arrays to work as series, with the newest data at index 0
   ArraySetAsSeries(sma_data,true);
   ArraySetAsSeries(sar_data,true);

我们将存储数组设置为时间序列数据,最新的信息位于索引0处。我们通过使用ArraySetAsSeries函数以及两个数组(“sma_data”和“sar_data”)作为第一个参数,并将第二个参数设置为true来实现这一点,以确保这些数组被视为序列。这是在交易逻辑中正常决策时间访问最新指标值的必要步骤,因为这些指标所表示的数据会在交易决策过程中被输出到交易决策程序中。逻辑的下一部分引用了这两个指标的当前值和过去值序列。最后,如果我们已经完成了这些,这意味着一切初始化正确,因此返回一个成功的初始化实例。

   return(INIT_SUCCEEDED);  // Return success code to indicate successful initialization

到目前为止,初始化部分的所有内容都正常工作。负责程序初始化的完整源代码如下:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   // OnInit is called when the EA is initialized on the chart

   handleSMA = iMA(_Symbol,PERIOD_M1,60,0,MODE_SMA,PRICE_CLOSE);  // Create an SMA (Simple Moving Average) indicator handle for the M1 timeframe
   handleSAR = iSAR(_Symbol,PERIOD_M1,0.02,0.2);                  // Create a SAR (Parabolic SAR) indicator handle for the M1 timeframe
   
   Print("SMA Handle = ",handleSMA);
   Print("SAR Handle = ",handleSAR);
   
   // Check if the handles for either the SMA or SAR are invalid (indicating failure)
   if (handleSMA == INVALID_HANDLE || handleSAR == INVALID_HANDLE){
      Print("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW");  // Print error message in case of failure
      return (INIT_FAILED);  // Return failure code, stopping the EA from running
   }

   // Configure the SMA and SAR data arrays to work as series, with the newest data at index 0
   ArraySetAsSeries(sma_data,true);
   ArraySetAsSeries(sar_data,true);

   return(INIT_SUCCEEDED);  // Return success code to indicate successful initialization
}

接下来,我们转向OnDeinit事件处理程序,这是一个在程序反初始化时被调用的函数。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   // OnDeinit is called when the EA is removed from the chart or terminated
//...
}

当EA从图表中移除或终端关闭时,OnDeinit函数会被调用。我们必须使用这个事件处理程序来确保正确的维护和管理资源。当EA终止时,我们必须释放在初始化阶段创建的所有指标句柄。如果我们不这么做,我们可能会占用曾经使用过的内存空间,这会导致低效;我们当然不想冒险留下任何不需要的资源。这就是为什么OnDeinit很重要,以及为什么在任何编程环境中清理步骤都是至关重要的。

   // Release the indicator handles to free up resources
   IndicatorRelease(handleSMA);
   IndicatorRelease(handleSAR);

我们清理了曾经分配使用过的每一个资源。为了执行这一清理操作,我们使用IndicatorRelease函数,我们分别为分配并初始化的每一个句柄调用这个函数,就像我们在一开始先将它们存储起来再使用它们一样。这样确保我们以与分配它们时相反的顺序释放资源。在这里,我们特别关注SMA和SAR指标。清理对于维护平台的性能至关重要,尤其是如果您正在使用多个EA或者长时间运行该平台。因此,用于释放资源的完整源代码如下:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   // OnDeinit is called when the EA is removed from the chart or terminated

   // Release the indicator handles to free up resources
   IndicatorRelease(handleSMA);
   IndicatorRelease(handleSAR);
}

接下来,我们需要在价格更新时检查交易机会。这是通过OnTick事件处理器来实现的。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   // OnTick is called whenever there is a new market tick (price update)

//...

}

事件处理函数OnTick在每次出现新的tick或市场条件变化时执行并处理最新的价格信息。它是我们EA运行的一个重要部分,因为这是运行交易逻辑的地方,我们希望这个交易逻辑能够产生盈利的交易。当市场数据发生变化时,我们评估市场的当前状态,并决定是否开仓或平仓。该函数的执行频率与市场条件的变化频率相同,确保我们的策略实时运行,并对当前价格以及我们市场指标的变化做出反应。

为了紧跟当前市场状况,我们需要获取当前价格报价的值。

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);  // Get and normalize the Ask price
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);  // Get and normalize the Bid price

这里,我们获取交易品种的最新卖出价(Ask)和买入价(Bid)。为了获取这些价格,我们使用SymbolInfoDouble函数。对于卖出价(Ask),我们指定SYMBOL_ASK,对于买入价(Bid),我们指定SYMBOL_BID。在获取价格后,我们使用NormalizeDouble函数将价格四舍五入到由_Digits定义的小数位数。这一步至关重要,因为它确保我们的交易操作以标准化且准确的价格执行。如果我们不四舍五入价格,浮点数的不准确性可能会在操作价格计算中产生误导性的结果。接下来,我们复制指标值以用于分析和交易操作。

   // Retrieve the last 3 values of the SMA indicator into the sma_data array
   if (CopyBuffer(handleSMA,0,0,3,sma_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SMA FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SMA data
      return;  // Exit the function if not enough data is available
   }

我们使用CopyBuffer函数来获取SMA指标的三个最新值。CopyBuffer函数接收输入参数,包括SMA指标的句柄、SMA数据缓冲区索引、起始位置(在此情况下表示最新值)、要获取的值的数量(三个),以及用于存储数据的数组(sma_data)。在将SMA值复制到数组后,我们检查它们是否正确复制。如果此时发生错误,我们将错误信息打印到日志中,并退出函数,以避免基于可能有缺陷或不完整的数据继续交易逻辑。

同样地,我们使用CopyBuffer函数来获取SAR指标的数据。使用的代码段如下:

   // Retrieve the last 3 values of the SAR indicator into the sar_data array
   if (CopyBuffer(handleSAR,0,0,3,sar_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SAR FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SAR data
      return;  // Exit the function if not enough data is available
   }

您现在需要知道的只是用于存储数据的起始K线索引和缓冲区索引。让我们如下图所示进行可视化:

K线和缓冲区索引

让我们将数据打印到日志中,以便查看数据是否获取正确。

   ArrayPrint(sma_data,_Digits," , ");
   ArrayPrint(sar_data,_Digits," , ");

我们使用ArrayPrint函数来显示“sma_data”和“sar_data”数组的内容。ArrayPrint是一个基本函数,它将数组的内容打印到终端。它适用于将正在处理的数据进行简单的可视化。要使用该函数,您需要向其传递三部分信息:

  • 您要打印的数组(“sma_data”或“sar_data”)。
  • 您希望显示值的精度(a0>_Digits ),决定了将显示多少个小数位。
  • 在输出时,打印值之间使用的分隔符是(", ")。

通过打印SMA和SAR数组的内容,我们可以跟踪算法实际使用的数字,这样有助于调试。我们还可以在回测中检查这些数据,以确保我们确实获得了所需的数据。

数据确认

从图中我们可以看到数据被正确检索并存储。红色的数据代表移动平均线数据,而蓝色的数据代表SAR数据。通过十字准线,我们正在引用第二根K线和索引1。现在很清楚,我们检索到了3个数据值,正如所请求的那样。我们可以继续获取必要的K线值。

   // Get the low prices for the current and previous bars on the M1 timeframe
   double low0 = iLow(_Symbol,PERIOD_M1,0);  // Low of the current bar
   double low1 = iLow(_Symbol,PERIOD_M1,1);  // Low of the previous bar

这里,我们使用iLow函数从1分钟时间框架的当前K线和前一根K线中提取最低价。第一个参数_Symbol会自动调整为EA正在操作的交易工具(例如,EUR/USD)。第二个参数PERIOD_M1告诉系统我们正在1分钟图表上工作;这一点非常重要,因为快速交易策略是基于快速价格波动进行短期交易的。在iLow(_Symbol, PERIOD_M1, 0)中,第三个参数0表示我们想要正在形成的当前K线的最低价。同样地,在iLow(_Symbol, PERIOD_M1, 1)中,1表示我们想要前一根已完成K线的最低价。我们将检索到的值分别存储在double数据类型变量low0和low1中。

同样地,我们采用类似的逻辑获取当前K线和前一根K线的最高价。

   // Get the high prices for the current and previous bars on the M1 timeframe
   double high0 = iHigh(_Symbol,PERIOD_M1,0);  // High of the current bar
   double high1 = iHigh(_Symbol,PERIOD_M1,1);  // High of the previous bar

这里,唯一改变的是使用的函数iHigh,它检索指定K线索引和时间框架的最高价数据。为了确认我们获取的数据是正确的,让我们再次使用Print函数将其打印出来。

   Print("High bar 0 = ",high0,", High bar 1 = ",high1);
   Print("Low bar 0 = ",low0,", Low bar 1 = ",low1);

数据确认如下:

最高价和最低价数据确认

非常成功。我们现在可以继续构建交易逻辑。然而,我们不需要在每个tick上执行交易逻辑。因此,让我们构建一个机制,以确保我们每根K线只交易一次。

   // Define a static variable to track the last time a signal was generated
   static datetime signalTime = 0;
   datetime currTime0 = iTime(_Symbol,PERIOD_M1,0);  // Get the time of the current bar

在这一部分,我们创建了一个变量“signalTime”,用于跟踪最后一条交易信号的生成时间。我们将这个变量定义为static,以确保它在OnTick函数的调用之间保持其值。如果“signalTime”没有静态存储期,那么每次调用OnTick时都会为其分配内存,而在OnTick执行完毕后,它的值就会丢失。我们希望信号时间每根K线只设置一次,并且希望它保持其值直到下一根K线开始。因此,我们在开始时将其设置为0,以便在第一条信号生成后它才有值。为了防止在相同的K线期间生成第二个(或多个)信号,我们将当前K线的时间与变量中存储的值进行比较。之后,我们现在可以定义交易逻辑。

   // Check for BUY signal conditions:
   // - Current SAR is below the current low (bullish)
   // - Previous SAR was above the previous high (bullish reversal)
   // - SMA is below the current Ask price (indicating upward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0
      && sma_data[0] < Ask && PositionsTotal() == 0){
      Print("BUY SIGNAL @ ",TimeCurrent());  // Print buy signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
   }

在此,我们根据抛物线SAR和SMA指标同一时间的表现,以及一些其他约束条件,来验证发出买入信号的条件。要满足抛物线SAR的看涨信号条件,当前的SAR值(“sar_data[0]”)必须低于当前的最低价(“low0”)。同时,之前的SAR值(“sar_data[1]”)必须高于之前的最高价(“high1”)。要满足SMA的看涨信号条件,SMA必须低于当前的卖出价(“sma_data[0] < Ask”)。

此外,我们还验证没有其他未平仓头寸(PositionsTotal == 0)。我们不希望同时开启多个交易。然后,我们通过比较“signalTime”和“currTime0”来确保当前K线尚未生成信号。如果所有这些检查都通过,我们打印一条消息,宣布生成买入信号。我们还将信号时间变量更新为当前时间。这个巧妙的小技巧使我们能够将信号生成限制为每根K线仅一次。最终,我们所做的是确保EA只对特定的市场条件做出反应,而不做任何不必要的工作。我们得到的结果如下:

买入信号

既然我们已经得到了信号,那么让我们继续开启买入交易。

      obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point);  // Execute a buy order with a lot size of 0.01, stop loss and take profit

在此,我们使用CTrade类的“Buy”方法(由我们创建的“obj_Trade”对象表示)来执行买入订单。以下是参数的详细说明:

  • 0.01:表示交易的手数。这里,我们指定一个较小的仓位规模,即0.01手。
  • _Symbol:表示EA所附加的交易品种(货币对、股票等)。通过使用内置的_Symbol变量,交易品种可以被自动检测。
  • Ask:这是买入订单将被执行的价格,即市场中的当前卖出价(Ask)。卖出价通常用于买入交易。
  • Ask - 150 * _Point:表示止损水平。我们将止损设置在卖出价下方150个点,以限制市场反向波动时可能出现的潜在损失。
  • Ask + 100 * _Point:表示获利水平。我们将获利设置在卖出价上方100个点,这意味着当市场达到这个获利水平时,交易将自动平仓。

通过下达此订单,我们指示EA在当前卖出价开启买入订单,并设置预定义的止损和获利水平来管理风险和回报。确认买入信号并开启买入订单的最终代码段如下:

   // Check for BUY signal conditions:
   // - Current SAR is below the current low (bullish)
   // - Previous SAR was above the previous high (bullish reversal)
   // - SMA is below the current Ask price (indicating upward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0
      && sma_data[0] < Ask && PositionsTotal() == 0){
      Print("BUY SIGNAL @ ",TimeCurrent());  // Print buy signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point);  // Execute a buy order with a lot size of 0.01, stop loss and take profit
   }

确认卖出信号并开启卖出订单,同样适用类似的逻辑。

   // Check for SELL signal conditions:
   // - Current SAR is above the current high (bearish)
   // - Previous SAR was below the previous low (bearish reversal)
   // - SMA is above the current Bid price (indicating downward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   else if (sar_data[0] > high0 && sar_data[1] < low1 && signalTime != currTime0
      && sma_data[0] > Bid && PositionsTotal() == 0){
      Print("SELL SIGNAL @ ",TimeCurrent());  // Print sell signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Sell(0.01,_Symbol,Bid,Bid+150*_Point,Bid-100*_Point);  // Execute a sell order with a lot size of 0.01, stop loss and take profit
   }

这里,我们实现了卖出交易逻辑,当特定的市场条件出现时,该逻辑将被执行。我们首先检查抛物线SAR(SAR)指标是否暗示看跌趋势。我们通过将最新的SAR数据与K线价格进行比较来实现这一点。SAR应该高于当前K线的最高价(“sar_data[0] > high0”),并且之前的SAR值低于前一根K线的最低价(“sar_data[1] < low1”),这表明可能发生了看跌反转。SAR的趋势指示可以作为“安全交易”的契机。然后我们检查SMA。它应该高于买入价(“sma_data[0] > Bid”),这表明可能的下降趋势是“安全交易”。

当所有上述条件都满足时,我们发出卖出信号并做记录(“Print(“SELL SIGNAL @ ”, TimeCurrent())”)。“signalTime”变量记录当前K线的时间;这一点很重要,因为我们必须确保每根K线不会发出多个卖出信号。为了执行交易,我们使用对象函数“obj_Trade.Sell”。我们有条不紊地在可能发生潜在看跌反转的时间点进入市场。这样做是因为考虑到我们所承担的风险。并且我们使用止损和获利订单来管理这种风险。卖出确认设置如下:

卖出设置确认

到目前为止,可以清楚地看到,我们已经正确地构建了快速交易EA算法。因此,负责确认交易和开启仓位的完整OnTick源代码如下:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   // OnTick is called whenever there is a new market tick (price update)

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);  // Get and normalize the Ask price
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);  // Get and normalize the Bid price

   // Retrieve the last 3 values of the SMA indicator into the sma_data array
   if (CopyBuffer(handleSMA,0,0,3,sma_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SMA FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SMA data
      return;  // Exit the function if not enough data is available
   }

   // Retrieve the last 3 values of the SAR indicator into the sar_data array
   if (CopyBuffer(handleSAR,0,0,3,sar_data) < 3){
      Print("ERROR: NOT ENOUGH DATA FROM SAR FOR FURTHER ANALYSIS. REVERTING");  // Print error if insufficient SAR data
      return;  // Exit the function if not enough data is available
   }
   
   //ArrayPrint(sma_data,_Digits," , ");
   //ArrayPrint(sar_data,_Digits," , ");
   
   // Get the low prices for the current and previous bars on the M1 timeframe
   double low0 = iLow(_Symbol,PERIOD_M1,0);  // Low of the current bar
   double low1 = iLow(_Symbol,PERIOD_M1,1);  // Low of the previous bar

   // Get the high prices for the current and previous bars on the M1 timeframe
   double high0 = iHigh(_Symbol,PERIOD_M1,0);  // High of the current bar
   double high1 = iHigh(_Symbol,PERIOD_M1,1);  // High of the previous bar
   
   //Print("High bar 0 = ",high0,", High bar 1 = ",high1);
   //Print("Low bar 0 = ",low0,", Low bar 1 = ",low1);
   
   // Define a static variable to track the last time a signal was generated
   static datetime signalTime = 0;
   datetime currTime0 = iTime(_Symbol,PERIOD_M1,0);  // Get the time of the current bar

   // Check for BUY signal conditions:
   // - Current SAR is below the current low (bullish)
   // - Previous SAR was above the previous high (bullish reversal)
   // - SMA is below the current Ask price (indicating upward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0
      && sma_data[0] < Ask && PositionsTotal() == 0){
      Print("BUY SIGNAL @ ",TimeCurrent());  // Print buy signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point);  // Execute a buy order with a lot size of 0.01, stop loss and take profit
   }

   // Check for SELL signal conditions:
   // - Current SAR is above the current high (bearish)
   // - Previous SAR was below the previous low (bearish reversal)
   // - SMA is above the current Bid price (indicating downward momentum)
   // - No other positions are currently open (PositionsTotal() == 0)
   // - The signal hasn't already been generated on the current bar
   else if (sar_data[0] > high0 && sar_data[1] < low1 && signalTime != currTime0
      && sma_data[0] > Bid && PositionsTotal() == 0){
      Print("SELL SIGNAL @ ",TimeCurrent());  // Print sell signal with timestamp
      signalTime = currTime0;  // Update the signal time to the current bar time
      obj_Trade.Sell(0.01,_Symbol,Bid,Bid+150*_Point,Bid-100*_Point);  // Execute a sell order with a lot size of 0.01, stop loss and take profit
   }
}
//+------------------------------------------------------------------+

为我们成功创建了这个程序鼓掌。接下来,我们需要对EA进行测试并对其进行优化,以实现最大收益。这将在下一部分中完成。


测试和优化

本章节专注于使用MetaTrader 5策略测试器对我们的快速交易策略进行测试和优化。测试至关重要,因为我们需要确保EA能够正常运行,并且在各种市场条件下表现出色。要开始测试我们的EA,需要将其加载到策略测试器中,选择所需的交易品种和时间框架(我们在代码中指定了M1时间框架),并设置测试期间。通过回测EA,我们可以得到一个历史模拟,展示EA在指定策略和参数下本应如何表现。当然,我们试图找出可能导致EA在实际交易场景中出现故障的任何潜在问题。需要打开策略测试器,点击“查看”,然后选择“策略测试器”。

打开策略测试器

打开后,找到“概览”选项卡,选择“可视化”,切换到“设置”选项卡,加载程序,并完成所需的设置。在我们的实例中,我们将测试期间仅设置为本月,以便处理少量数据。

测试器设置

经测试,我们得到了以下结果:

测试器结果

我们需要仔细查看测试阶段的结果,重点关注诸如净收益总额、最大回撤和获胜交易的百分比等关键绩效指标。这些数字将告诉我们策略表现得好(或不好)的程度,并让我们对其整体稳健性有所了解。我们还将需要查看策略测试器生成的交易报告。这些报告将使我们能够看到EA正在做(或不做)什么样的交易,并让我们有机会了解它在遇到各种市场条件时的“推理”过程。如果我们对结果不满意,我们将不得不重新调整策略,寻找SMA和SAR指标表现更好的参数组合。或者,我们将不得不深入研究策略的整体逻辑,以找出为什么它的表现没有预期好。

为了找到最佳交易策略,我们需要将其调整到适合市场的状态。为此,我们使用一种称为“策略优化”的方法。策略优化不是一次性的任务,不能做完就结束。我们需要观察系统在不同配置下的表现,然后选择在所有测试中给出最具一致结果的配置。以这种方式测试策略时,我们实际上是在寻找最优参数,并将它们作为打开我们正在检查的交易策略的钥匙。找到这些钥匙后,我们需要再次运行该策略,并在向前推进的期间对其进行测试。否则,我们不会比那些仅仅猜测市场未来方向的人更优秀。为了优化该系统,我们需要有一些输入,以便进行迭代。实现以下逻辑。

input int sl_points = 150; // Stoploss points
input int tp_points = 100; // Takeprofit points

经过优化后,我们得到了以下结果:

优化结果

优化后的结果图如下所示:

优化图解

最后,测试报告如下所示:

最终测试报告


结论

在本文中,我们讨论了在MQL5中开发一个快速交易EA,负责执行短暂且快速的交易。该策略依赖于两个主要的技术指标:抛物线SAR,它发出趋势反转的信号,以及简单移动平均线(SMA),它用来衡量市场动量。这些指标构成了EA交易逻辑的核心。

我们涵盖了实现细节,包括配置指标、设置数据句柄以及管理交易,以确保EA能够有效地与市场互动。最后,我们强调了使用MetaTrader 5的策略测试器对EA进行测试和优化的重要性,以确保它符合我们的交易目标。尽管这是一个自动化系统,但开发过程与手动交易策略的开发过程非常相似。

免责声明:本文中的信息仅用于教学目的。它仅旨在展示如何基于价格行为(Price Action)方法创建一个快速交易策略EA,可以被用作创建一个更好的EA的基础,同时考虑到更多的优化和数据提取。所展示的信息并不能保证任何交易结果。

我们真心希望您觉得这篇文章不仅对您有帮助,并且易于理解,这样您就能在未来的EA开发中利用文章中提供的内容。从技术上讲,这有助于您基于价格行为(Price Action)方法,特别是快速交易(Rapid-Fire)策略来分析市场。请自行感受。


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

附加的文件 |
数据科学和机器学习(第 28 部分):使用 AI 预测 EURUSD 的多个期货 数据科学和机器学习(第 28 部分):使用 AI 预测 EURUSD 的多个期货
众多人工智能模型的惯常做法是预测单一未来值。不过,在本文中,我们将钻研运用机器学习模型的更强大技术,即预测多个未来值。这种方式被称为多步预测,它令我们不仅能够预测明天的收盘价,还可以预测后天、及更久的收盘价。通过掌握多步骤预测,交易者和数据科学家能够获得更深入的见解,并制定更明智的决策,从而显著增强他们的预测能力和策略计划。
纳什博弈论与隐马尔可夫滤模型在交易中的应用 纳什博弈论与隐马尔可夫滤模型在交易中的应用
这篇文章深入探讨了约翰·纳什的博弈论,特别是纳什均衡,在交易中的应用。文章讨论了交易者如何利用Python脚本和MetaTrader 5,依据纳什的原则来识别并利用市场的无效性。文章还提供了实施这些策略的逐步指南,包括使用隐马尔可夫模型(HMM)和统计分析,以提升交易表现。
将您自己的 LLM 集成到 EA 中(第 4 部分):使用 GPU 训练自己的 LLM 将您自己的 LLM 集成到 EA 中(第 4 部分):使用 GPU 训练自己的 LLM
随着当今人工智能的快速发展,语言模型(LLMs)是人工智能的重要组成部分,因此我们应该考虑如何将强大的 LLMs 整合到我们的算法交易中。对于大多数人来说,很难根据他们的需求微调这些强大的模型,在本地部署它们,然后将它们应用于算法交易。本系列文章将采取循序渐进的方法来实现这一目标。
交易中的神经网络:使用语言模型进行时间序列预测 交易中的神经网络:使用语言模型进行时间序列预测
我们继续研究时间序列预测模型。在本文中,我们领略一种建立在预训练语言模型基础上的复杂算法。
OSZAR »