English Русский Español Deutsch 日本語 Português
preview
在MetaTrader 5中实现基于EMA交叉的级联订单交易策略

在MetaTrader 5中实现基于EMA交叉的级联订单交易策略

MetaTrader 5交易 | 13 二月 2025, 13:14
551 0
Kikkih25
Kikkih25

概述 

在本文中,我们将展示用于MetaTrader 5(MT5)外汇交易的级联订单交易策略EA,该策略使用MetaQuotes Language 5(MQL5)编写。本文介绍了基于移动平均线交叉的交易策略,基于MQL5的EA以及在MetaTrader 5平台上执行自动化交易。本文包含了仓位初始化、调整和监控的基本功能,并使用了Trade.mqh库进行有效的订单管理。

首先,该技术使用两条具有预设周期的指数移动平均线(EMA)作为策略的一部分。根据移动平均线的交叉方向,当这些移动平均线交叉时,会产生买入或卖出信号。订单设有指定的止盈和止损,并会随着市场进展而动态调整。

此外,该脚本具有识别新K线的功能,这对于确保交易决策基于已完成的柱形形态至关重要。此外,还提供了一项功能,用于在达成盈利目标时调整当前持仓。

总体而言,这一专家建议展示了如何使用MQL5来实现系统化交易策略,通过自动化执行和技术指标,按照预定规则执行交易。

在本文中,我们将探讨以下主题:

  1. 级联订单交易策略说明
  2. MQL5中EA的实现
  3. 结论


级联订单交易策略说明 

级联订单交易策略描述了一种技术,根据早期订单的结果或条件确定后续订单的设置。为了根据市场波动和预定规则管理并优化入场、出场和头寸规模,该策略在交易中经常被采用。以下是关于如何运作级联订单交易策略的详细描述。

级联订单交易策略的核心要素:

  1. 连续订单配置:级联订单策略涉及根据预定事件或条件连续发起交易。例如,交易者可以根据特定的技术指标信号配置初始订单。
  2. 条件订单:后续订单是基于早期交易的结果或市场状态来配置的。如果市场走势对您有利,这样可能需要配置更多订单以逐步增加头寸。
  3. 逐步建仓和平仓:为了控制风险和优化盈利潜力,级联策略通常涉及逐步进入头寸。另一方面,逐步平仓意味着在达到盈利目标或市场走势不利时减少头寸规模。
  4. 风险管理:在级联技术中,有效的风险管理对于减少损失至关重要。这包括为每个订单设定止损阈值,或随着头寸的变化动态调整它们。

获利了结:当满足某些要求时,需要采取行动以确保收益。为每个交易阶段的每个订单中都设定了盈利目标。这确保了交易者的利润,同时如果市场条件允许,还可能有额外的上涨空间。

以下是级联订单交易策略的总结图表:

级联订单交易策略


MQL5中EA的实现

首先,MQL5的Trade.mqh库是一个强大且实用的库,它简化了交易活动。该库提供了一个高级接口,用于开设、修改和删除仓位和订单。当包含Trade.mqh时,它会让我们能够访问CTrade类,这个类简化和封装了许多交易活动的复杂细节,从而提高了代码的可读性和可维护性。

#include <Trade/Trade.mqh>
CTrade obj_Trade;

在包含Trade.mqh之后,我们可以访问CTrade类,该类通常会被实例化为obj_Trade对象。我们已经注意到,许多交易功能都被封装在CTrade类中。以下是CTrade类提供的一些主要技术:

  1.  下单
  2.  修改订单
  3.  订单终止

接下来,我们深入了解全局变量,这些变量对交易策略的运行至关重要,并在EA中发挥着多种功能。让我们来探讨每个全局变量及其含义:

  •  整型变量

int handleMAFast;
int handleMASlow;

该变量中存储了由OnInit函数计算得出的慢速和快速移动平均指标的处理程序(或ID)。要检索这些指标的当前值,需要使用这些处理程序来访问它们的缓冲区。

  • 用于移动平均值的双精度数组

double maSlow[],maFast[];

这些数组保存了从指标缓冲区中提取的快速和慢速移动平均值。在分析和交易决策中,它们被用来存储指标的当前值和历史值。

  • 双重止盈和止损变量

double takeProfit = 0;
double stopLoss = 0;

该变量中存储了当前交易操作的止盈(TP)和止损(SL)水平。它们被用于提交或修改交易订单,并且会根据市场条件进行更新。

  •  系统状态已记录变量

bool isBuySystemInitiated = false;
bool isSellSystemInitiated = false;

这些已记录的标识监控买入和卖出交易系统的初始状态。它们有助于避免不必要的或重复的订单,并确保只有当满足某些条件(如移动平均线交叉)时才下单。

  • 输入参数

input int slPts = 300;
input int tpPts = 300;
input double lot = 0.01;
input int slPts_Min = 100;
input int fastPeriods = 10;
input int slowPeriods = 20;

这些变量是输入参数,它们无需更改代码本身即可实现对EA行为的外部配置。交易者可以通过MetaTrader界面的EA设置来修改这些参数。它们管理包括止盈、止损、手数大小和快速移动周期在内的变量。

在解释了变量的意义之后,我们意识到需要将MQL5中的多个ticks和函数之间访问的数据存储在全局变量中。这是因为它们存储了关键数据,包括订单参数、交易状态、指标值和用户定义的设置。它们被用于执行交易策略、维持仓位和响应市场条件,不同的EA函数会访问和修改这些变量。

当然!让我们来分解代码的初始化部分。OnInit函数负责设置EA的初始状态。首先,使用iMA函数为快速和慢速移动平均线创建句柄。检查这些句柄是否有效。如果有任何句柄无效,则初始化失败。然后,使用ArraySetAsSeries函数将maFast和maSlow数组设置为时间序列数组。最后,返回成功。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   
   handleMAFast = iMA(_Symbol,_Period,fastPeriods,0,MODE_EMA,PRICE_CLOSE);
   if (handleMAFast == INVALID_HANDLE){
      Print("UNABLE TO LOAD FAST MA, REVERTING NOW");
      return (INIT_FAILED);
   }
   
   handleMASlow = iMA(_Symbol,_Period,slowPeriods,0,MODE_EMA,PRICE_CLOSE);
   if (handleMASlow == INVALID_HANDLE){
      Print("UNABLE TO LOAD SLOW MA, REVERTING NOW");
      return (INIT_FAILED);
   }
   
   ArraySetAsSeries(maFast,true);
   ArraySetAsSeries(maSlow,true);
   
   return(INIT_SUCCEEDED);
}

然后,我们来看看反初始化函数。在MQL5的EA系统中,当EA被关闭或者从图表上被移除时,就会调用这个函数。这是进行任务清理之处。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }

在本例中,该函数为空,这意味着在EA反初始化时不会执行任何特定操作。 

该函数接受一个整数参数reason,用于说明反初始化的原因。这可能是因为交易平台终端被关闭,从图表上移除EA,或其他原因。

我们现在转到OnTick函数,这个函数会在每次收到新的行情数据(价格变动)时被触发。交易逻辑就是在这个函数中实现的。在提供的代码中,OnTick函数实现了基于移动平均线交叉的交易策略。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   if (CopyBuffer(handleMAFast,0,1,3,maFast) < 3){
      Print("NO ENOUGH DATA FROM FAST MA FOR ANALYSIS, REVERTING NOW");
      return;
   }
   if (CopyBuffer(handleMASlow,0,1,3,maSlow) < 3){
      Print("NO ENOUGH DATA FROM SLOW MA FOR ANALYSIS, REVERTING NOW");
      return;
   }
   
   //if (IsNewBar()){Print("FAST MA DATA:");ArrayPrint(maFast,6);}
   
   if (PositionsTotal()==0){
      isBuySystemInitiated=false;isSellSystemInitiated=false;
   }
   
   if (PositionsTotal()==0 && IsNewBar()){
      if (maFast[0] > maSlow[0] && maFast[1] < maSlow[1]){
         Print("BUY SIGNAL");
         takeProfit = Ask+tpPts*_Point;
         stopLoss = Ask-slPts*_Point;
         obj_Trade.Buy(lot,_Symbol,Ask,stopLoss,0);
         isBuySystemInitiated = true;
      }
      else if (maFast[0] < maSlow[0] && maFast[1] > maSlow[1]){
         Print("SELL SIGNAL");
         takeProfit = Bid-tpPts*_Point;
         stopLoss = Bid+slPts*_Point;
         obj_Trade.Sell(lot,_Symbol,Bid,stopLoss,0);
         isSellSystemInitiated = true;
      }
   }
   
   else {
      if (isBuySystemInitiated && Ask >= takeProfit){
         takeProfit = takeProfit+tpPts*_Point;
         stopLoss = Ask-slPts_Min*_Point;
         obj_Trade.Buy(lot,_Symbol,Ask,0);
         ModifyTrades(POSITION_TYPE_BUY,stopLoss);
      }
      else if (isSellSystemInitiated && Bid <= takeProfit){
         takeProfit = takeProfit-tpPts*_Point;
         stopLoss = Bid+slPts_Min*_Point;
         obj_Trade.Sell(lot,_Symbol,Bid,0);
         ModifyTrades(POSITION_TYPE_SELL,stopLoss);
      }
   }
}

以下是对OnTick函数的详细解析,以便于理解:

获取卖出价(Ask)和买入价(Bid):该函数获取当前的卖价和买价,并将它们四舍五入到适当的小数位数。在级联订单交易策略中,获取并规范化当前价格对于做出在不同价格点下单的明智决策至关重要。在实施级联订单交易策略时,密切关注当前市场价格是必要的。这包括获取您在MetaTrader中交易品种的卖出价和买入价。通过函数调用获取指定交易品种的当前卖出价。卖出价(Ask)价是指您可以购买该资产的价格。通过该函数调用获取该交易品种的当前买入价。买入价(Bid)是指您可以卖出该资产的价格。

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

归一化确保了所获取的价格符合交易品种对准确性的要求。避免浮点运算问题也依赖于归一化。此函数调用会将数值四舍五入到指定的小数位数(_Digits)。_Digits表示交易品种支持的小数位数,通常对于特定交易品种是预定义的。

复制移动平均缓冲区:该函数获取慢线和快线移动平均的最新值。如果数据量不足,该函数将终止并打印错误消息。在级联顺序交易策略中,移动平均线用于发现交易机会并精确确定下单时间。为了使该策略可靠,在进行研究之前,确保有足够的移动平均数据至关重要。

if (CopyBuffer(handleMAFast,0,1,3,maFast) < 3){
      Print("NO ENOUGH DATA FROM FAST MA FOR ANALYSIS, REVERTING NOW");
      return;
   }
   if (CopyBuffer(handleMASlow,0,1,3,maSlow) < 3){
      Print("NO ENOUGH DATA FROM SLOW MA FOR ANALYSIS, REVERTING NOW");
      return;
   }

该函数从快速移动平均指标缓冲区向maFast数组传输信息。快速移动平均指标句柄、缓冲区编号(对于指标的主线,通常为0)、数据应该开始复制的起始点、要复制的项的数量,以及数据副本将存储的数组,是此函数的输入参数。函数返回成功复制的元素数量。在验证数据可行性时,它确定是否向maFast复制的元素少于三个。如果复制的元素少于三个,则表明来自快速移动平均的数据不足以支持可靠地分析。此时,函数会打印一条消息并提前结束执行。对于具有句柄MASlow和maSlow的慢速移动平均,遵循相同的逻辑。

以下是移动平均线与价格数据对比的图示。该图示包括价格序列、快速移动平均线(10日指数移动平均线)和慢速移动平均线(20日指数移动平均线)。

MOVING AVERAGES CROSSOVER

在我们做出任何交易决定之前,该策略会确保存在来自移动平均线最小量的历史数据。这有助于我们做出更可靠、更有依据的选择。该策略通过验证是否有足够的数据,来避免信息不足的情况,这种情况可能会导致错误信号,甚至可能带来交易损失。此段文字是交易前的一个验证步骤。如果不满足条件,该策略将撤销,并且对于该时间刻度将不执行任何交易或分析。

在级联订单交易策略中,移动平均线用于确定趋势的方向和强度。例如,如果快速移动平均线交叉至慢速移动平均线上方或下方,您可以在不同的价位下单。

检查开仓情况:如果没有开仓,则重置买入和卖出系统启动标识。在使用级联订单交易策略时,检查开仓情况至关重要,以防止交易重叠并有效管理交易系统的状态。我们在该功能中实现的一种机制是,如果没有开仓,则重置特定标识。

//if (IsNewBar()){Print("FAST MA DATA:");ArrayPrint(maFast,6);}
   
   if (PositionsTotal()==0){
      isBuySystemInitiated=false;isSellSystemInitiated=false;
   }

该函数返回当前交易账户的总开仓数量。了解是否有任何活跃交易是很有帮助的。当我们假设PositionasTotal() = 0时,这个条件用于确定是否总共有0个开仓,这意味着当前没有交易处于开仓状态。当没有开仓时,isBuySystemInitiated(买入系统已启动)和isSellSystemInitiated(卖出系统已启动)这两个标识被设置为false。重置表示买入系统是否已启动的标识(_isBuySystemInitiated = false;)。重置表示卖出系统是否已启动的标识(_isSellSystemInitiated = false;)。

该方法通过定期检查开仓情况并重置启动标识,确保只有在没有开仓的情况下才开始新的交易。这样做可以避免重叠交易,因为重叠交易可能会导致过度暴露于有风险的或不一致的交易行为中。我们通过启动标识isBuySystemInitiated和isSellSystemInitiated来管理交易系统。这些标识确保在当前仓位关闭之前,系统不会在特定趋势中多次启动相同的买入或卖出订单。在检查过程中,我们还能够保持策略逻辑流的完整性。该方法可以确保在适当的时候(即没有开仓时)配置新订单,从而准确地对市场条件和趋势做出反应。我们通过防止同时开启多个仓位来实现更好的风险管理。通过这种方式,该策略可以限制市场暴露并防止可能的过度杠杆化。

 新K线检测:基于移动平均线交叉,如果形成了新的K线(柱形图)且没有开仓,则寻找买入或卖出信号。为了防止交易策略多次执行,检测新K线的出现是很重要的。与可能不稳定的柱内价格波动相比,使用已完成的柱形数据作为交易选择的基础可以提高交易的准确性。

if (PositionsTotal()==0 && IsNewBar()){
      if (maFast[0] > maSlow[0] && maFast[1] < maSlow[1]){
         Print("BUY SIGNAL");
         takeProfit = Ask+tpPts*_Point;
         stopLoss = Ask-slPts*_Point;
         obj_Trade.Buy(lot,_Symbol,Ask,stopLoss,0);
         isBuySystemInitiated = true;
      }
      else if (maFast[0] < maSlow[0] && maFast[1] > maSlow[1]){
         Print("SELL SIGNAL");
         takeProfit = Bid-tpPts*_Point;
         stopLoss = Bid+slPts*_Point;
         obj_Trade.Sell(lot,_Symbol,Bid,stopLoss,0);
         isSellSystemInitiated = true;
      }
   }

当PositionsTotal等于0时,这个条件确保if代码块的功能仅在没有任何持仓时运行。这样做可以防止交易重叠,确保只有在旧交易关闭后才会开启新交易。IsNewBar()函数用于确定自上次运行以来是否形成了新的K线。基本上,如果之前已经记录了开盘时间,并且自那时起形成了新的K线,则此方法应返回true。

在买入信号部分,我们确定最近的K线是否出现了看涨交叉,即快速移动平均线(MA)上穿慢速移动平均线。如果出现这种情况,它会计算盈利目标和止损水平,并发布“买入信号”消息。之后,会将isBuySystemInitiated标识设置为true,并使用obj_Trade.Buy()提交买入订单。以下是级联订单交易策略中的指数移动平均线(EMA)交叉买入信号:

买入信号


在卖出信号部分,我们确定最近的K线是否代表了一个看跌交叉,即快速移动平均线(MA)已经下穿慢速移动平均线。如果出现这种情况,它会计算盈利目标和止损水平,并输出一个“卖出信号”消息。之后,使用obj_Trade发送卖出订单。Sell()函数会将isSellSystemInitiated标识设置为true。以下是级联订单交易策略中的指数移动平均线(EMA)交叉卖出信号:

卖出信号

该策略通过识别新K线的出现,确保每根K线只运行一次交易逻辑。这样做可以避免在同一根K线内配置多个订单的问题,从而防止过度交易和产生更高的交易费用。我们使用已完成K线的数据来做出交易决策,这样可以确保决策是基于可靠的信息。K线内部的价格变动可能是杂乱的,并可能产生错误的指示。通过将无持仓检查与新K线检测相结合,交易系统的状态得到了有效的管理。这确保了只有在必要时才会进行新的交易,从而保持了策略的逻辑连贯性。 

执行买入或卖出订单:- 如果快速移动平均线上穿慢速移动平均线,则配置买入订单。如果快速移动平均线下穿慢速移动平均线,则启动卖出订单。它还会修改启动标识,并设置盈利目标和止损(SL)点。这种订单的“级联”方式有助于随着价格朝向第一笔交易有利的方向发展,从而逐渐建立仓位。

else {
      if (isBuySystemInitiated && Ask >= takeProfit){
         takeProfit = takeProfit+tpPts*_Point;
         stopLoss = Ask-slPts_Min*_Point;
         obj_Trade.Buy(lot,_Symbol,Ask,0);
         ModifyTrades(POSITION_TYPE_BUY,stopLoss);
      }

在买入订单级联中,策略会判断盈利目标是否已经达到,以及当前卖出价是否大于或等于该盈利目标。这种情况意味着,由于价格已经朝向当前买入仓位有利的方向移动,触发了额外的买入订单。盈利目标将按照预定点数提高,为级联中的下一次增量设置一个新的全局目标。修改止损确保了新买入订单的风险能够得到控制,并将止损水平更改为一个低于当前卖出价的新水平。根据给定的手数,在当前卖出价有效时配置第二个买入订单作为附加买入订单。修改当前交易管理整体仓位的风险,将当前买入仓位的止损更改为新的止损水平。

在按序卖出订单中,策略会判断当前买入价是否小于或等于盈利目标,以及卖出系统是否已经启动。这种情况意味着,由于价格已经朝向当前卖出仓位有利的方向移动,已经触发了附加的卖出订单。盈利目标将参照预定点数降低,为级联中的下一次增量设置一个新的目标。更新止损确保了新卖出订单的风险得到控制,并将止损水平更改为一个高于当前买入价的新水平。以当前买入价执行指定手数的附加卖出订单。修改当前交易全面控制风险,将当前卖出仓位的止损更改为新的止损水平。

调整当前仓位:如果有开仓,且价格达到盈利阈值,则会配置更多订单,并适当地更新止损金额。这样有助于逐步最大化利润和管理风险。

else if (isSellSystemInitiated && Bid <= takeProfit){
         takeProfit = takeProfit-tpPts*_Point;
         stopLoss = Bid+slPts_Min*_Point;
         obj_Trade.Sell(lot,_Symbol,Bid,0);
         ModifyTrades(POSITION_TYPE_SELL,stopLoss);
      }
   }
}

有一个名为isBuySystemInitiated的布尔类型标识,它表示买入系统是否已经被启动。另一个名为isSellSystemInitiated的布尔标识,它表示卖出系统是否已经被启动。当前的卖出价、当前的买入价、盈利目标的金额、修改盈利目标所需的点数、止损水平、止损调整的最小点数、交易手数、配置买入订单的能力、提交卖出订单的能力,以及允许更改现有持仓止损的功能。

现在,我们来验证UT系统是否已经被启动,并且当前的卖出价是否已经达到或超过了盈利目标。将盈利目标提高到一个预定的点数。通过设置一个新的、高于现有水平的盈利目标,使策略能够捕获更多的潜在收益。将止损设置在一个新的、低于卖出价的价格水平。锁定部分之前的收益有助于风险管理。以指定的手数,按照卖出价执行第二个买入订单,完成附加的买入订单。随着价格朝向初始交易有利的方向移动,而逐渐增加持仓。更新所有当前买入持仓的止损金额,以反映新的止损金额。这有助于控制买入持仓的总风险。

接下来,我们验证卖出机制是否已经被启动,并且当前的出价是否已经上涨到或下跌到盈利目标。将盈利目标减少到一个固定的点数。因此,新的盈利目标低于现有水平。在更新止损后,这将把止损移动到高于买入价的新水平。锁定部分之前的收益有助于风险管理。以指定的手数,按照当前的买入价完成附加的卖出订单。随着价格朝向初始交易有利的方向移动,而逐渐增加持仓。更新所有当前卖出持仓的止损金额,以反映新的止损金额。这有助于控制卖出持仓的总风险。

随着市场朝向初始交易有利的方向移动,策略逐渐增加交易持仓。因此,交易者可以在最初不承担过多风险的情况下增加持仓,并从显著的趋势中获利。该策略通过为新的订单和开仓调整止损水平来有效地管理风险。如果市场转向不利,这样有助于保留收益并减少损失。每当市场朝着级联策略偏好的方向发展时,都会逐步设定新的盈利目标,以最大化利润。这样允许未来有增长空间,同时确保逐步锁定收益。

现在,让我们看一下MQL5 Expert Advisor(EA)代码中的两个实用函数:IsNewBar和ModifyTrades。这些例程提供必要的辅助操作,以支持在OnTick函数中提供的核心交易逻辑。

我们从IsNewBar函数开始。该函数检查图表,以确定是否形成了新的K线到来。这是很有必要的,以确保特定操作(如开立新交易)仅在每个K线形成时执行。

//+------------------------------------------------------------------+

bool IsNewBar(){
   static int prevBars = 0;
   int currBars = iBars(_Symbol,_Period);
   if (prevBars==currBars) return (false);
   prevBars = currBars;
   return (true);
}

前序K线数量被存储在静态变量 prevBars 中。由于 static 关键字的作用,该变量的值在函数调用时会被保存。**当前柱状图计算**:currBars获取图表中的当前柱状图数量。**比较**:如果没有产生新的柱状图,即prevBars和currBars相等,则函数返回false。如果它们不相等,则表示出现了新的柱状图。在将prevBars更新为currBars后,函数返回true。 

现在让我们转向ModifyTrades函数。ModifyTrades函数根据指定的仓位类型,调整活跃仓位的止损(SL)水平。

void ModifyTrades(ENUM_POSITION_TYPE posType, double sl){
   for (int i=0; i<=PositionsTotal(); i++){
      ulong ticket = PositionGetTicket(i);
      if (ticket > 0){
         if (PositionSelectByTicket(ticket)){
            ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
            if (type==posType){
               obj_Trade.PositionModify(ticket,sl,0);
            }
         }
      }
   }
}

在该函数中:

  1. 遍历仓位:使用positionsTotal()函数遍历所有的开仓仓位。
  2. 获取仓位编号:返回索引{i}处的仓位编号。
  3. 验证仓位编号:验证仓位编号是否真实(大于0)。
  4. 选择仓位:使用PositionSelectByTicket函数选择仓位。
  5.  **检查仓位类型**:验证指定的posType是否与仓位类型匹配。
  6. ** 修改仓位**:如果仓位类型匹配,则使用PositionModify函数修改仓位。

以下是级联订单交易策略的完整代码:

//|                                                     CASCADE ORDERING.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade obj_Trade;

int handleMAFast;
int handleMASlow;
double maSlow[], maFast[];

double takeProfit = 0;
double stopLoss = 0;
bool isBuySystemInitiated = false;
bool isSellSystemInitiated = false;

input int slPts = 300;
input int tpPts = 300;
input double lot = 0.01;
input int slPts_Min = 100;
input int fastPeriods = 10;
input int slowPeriods = 20;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
    handleMAFast = iMA(_Symbol, _Period, fastPeriods, 0, MODE_EMA, PRICE_CLOSE);
    if (handleMAFast == INVALID_HANDLE) {
        Print("UNABLE TO LOAD FAST MA, REVERTING NOW");
        return (INIT_FAILED);
    }

    handleMASlow = iMA(_Symbol, _Period, slowPeriods, 0, MODE_EMA, PRICE_CLOSE);
    if (handleMASlow == INVALID_HANDLE) {
        Print("UNABLE TO LOAD SLOW MA, REVERTING NOW");
        return (INIT_FAILED);
    }

    ArraySetAsSeries(maFast, true);
    ArraySetAsSeries(maSlow, true);

    return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
    // Cleanup code if necessary
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
    double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
    double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);

    if (CopyBuffer(handleMAFast, 0, 1, 3, maFast) < 3) {
        Print("NO ENOUGH DATA FROM FAST MA FOR ANALYSIS, REVERTING NOW");
        return;
    }
    if (CopyBuffer(handleMASlow, 0, 1, 3, maSlow) < 3) {
        Print("NO ENOUGH DATA FROM SLOW MA FOR ANALYSIS, REVERTING NOW");
        return;
    }

    if (PositionsTotal() == 0) {
        isBuySystemInitiated = false;
        isSellSystemInitiated = false;
    }

    if (PositionsTotal() == 0 && IsNewBar()) {
        if (maFast[0] > maSlow[0] && maFast[1] < maSlow[1]) {
            Print("BUY SIGNAL");
            takeProfit = Ask + tpPts * _Point;
            stopLoss = Ask - slPts * _Point;
            obj_Trade.Buy(lot, _Symbol, Ask, stopLoss, 0);
            isBuySystemInitiated = true;
        } else if (maFast[0] < maSlow[0] && maFast[1] > maSlow[1]) {
            Print("SELL SIGNAL");
            takeProfit = Bid - tpPts * _Point;
            stopLoss = Bid + slPts * _Point;
            obj_Trade.Sell(lot, _Symbol, Bid, stopLoss, 0);
            isSellSystemInitiated = true;
        }
    } else {
        if (isBuySystemInitiated && Ask >= takeProfit) {
            takeProfit = takeProfit + tpPts * _Point;
            stopLoss = Ask - slPts_Min * _Point;
            obj_Trade.Buy(lot, _Symbol, Ask, 0);
            ModifyTrades(POSITION_TYPE_BUY, stopLoss);
        } else if (isSellSystemInitiated && Bid <= takeProfit) {
            takeProfit = takeProfit - tpPts * _Point;
            stopLoss = Bid + slPts_Min * _Point;
            obj_Trade.Sell(lot, _Symbol, Bid, 0);
            ModifyTrades(POSITION_TYPE_SELL, stopLoss);
        }
    }
}

    static int prevBars = 0;
    int currBars = iBars(_Symbol, _Period);
    if (prevBars == currBars) return (false);
    prevBars = currBars;
    return (true);
}

//+------------------------------------------------------------------+
//| ModifyTrades Function                                            |
//+------------------------------------------------------------------+

void ModifyTrades(ENUM_POSITION_TYPE posType, double sl) {
    for (int i = 0; i <= PositionsTotal(); i++) {
        ulong ticket = PositionGetTicket(i);
        if (ticket > 0) {
            if (PositionSelectByTicket(ticket)) {
                ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE) PositionGetInteger(POSITION_TYPE);
                if (type == posType) {
                    obj_Trade.PositionModify(ticket, sl, 0);
                }
            }
        }
    }
}


结论

最后,这个MQL5中的EA是移动平均线交叉交易策略成功执行的一个绝佳范例。本文通过利用Trade.mqh库来简化订单管理,并包含动态止盈和止损水平,从而有效地实现了交易决策自动化。

这个EA的显著特点包括:

  • 初始化和管理:基于交叉点的买入和卖出信号得到了系统的管理,有效初始化移动平均线。
  • 风险管理:通过采用止盈和止损程序来降低风险,确保在用户可配置的范围内获得收益。
  • 模块化和灵活性:通过使用输入参数和全局变量,可以根据不同的市场情况和交易偏好进行定制和调整。 

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

MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库 MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库
了解如何在 MQL5 代码或项目中导入和使用 EX5 库。在这篇续文中,我们将通过向现有库中添加更多仓位管理功能并创建两个 EA 交易系统来扩展 EX5 库。第一个例子将使用可变指数动态平均(Variable Index Dynamic Average,VIDYA)技术指标来开发追踪止损交易策略 EA 交易,而第二个例子将利用交易面板来监控、开仓、平仓和修改仓位。这两个例子将演示如何使用和实现升级后的 EX5 仓位管理库。
神经网络变得简单(第 91 部分):频域预测(FreDF) 神经网络变得简单(第 91 部分):频域预测(FreDF)
我们继续探索时间序列在频域中的分析和预测。在本文中,我们将领略一种在频域中预测数据的新方法,它可被加到我们之前研究过的众多算法当中。
用Python和MQL5进行投资组合优化 用Python和MQL5进行投资组合优化
本文探讨了使用Python和MQL5结合MetaTrader 5进行高级投资组合优化的技术。文章展示了如何开发用于数据分析、资产配置和交易信号生成的算法,强调了在现代金融管理和风险缓解中数据驱动决策的重要性。
交易中的混沌理论(第一部分):简介、在金融市场中的应用和李亚普诺夫指数 交易中的混沌理论(第一部分):简介、在金融市场中的应用和李亚普诺夫指数
混沌理论可以应用于金融市场吗?在这篇文章中,我们将探讨传统混沌理论和混沌系统与比尔·威廉姆斯提出的概念有何不同。