English Deutsch 日本語
preview
MQL5自动化交易策略(第十九部分):包络线趋势反弹剥头皮交易——交易执行与风险管理(下篇)

MQL5自动化交易策略(第十九部分):包络线趋势反弹剥头皮交易——交易执行与风险管理(下篇)

MetaTrader 5交易 |
50 0
Allan Munene Mutiiria
Allan Munene Mutiiria

概述

在我们前一篇文章(第十八部分)中,我们为 MetaQuotes Language 5(MQL5)环境下的包络线趋势反弹剥头皮策略奠定了基础,完成了智能交易系统(EA)的核心架构搭建与交易信号生成逻辑。如今在第十九部分中,我们将通过实现交易执行模块与风险管理体系,推动该策略向全自动化交易系统升级。我们将涵盖以下主题:

  1. 策略规划与架构
  2. 在MQL5中的实现
  3. 回测与优化
  4. 结论

最终,您将获得一套针对趋势反弹剥头皮交易的完整MQL5交易系统,其性能经过深度优化——让我们即刻启程!


策略规划与架构

在第十八部分中,我们为包络线趋势反弹剥头皮策略奠定了基础,构建了一套系统,该系统利用价格与包络线指标的交互来检测交易信号,并通过移动平均线相对强弱指数(RSI)等趋势过滤器进行确认。当时我们专注于搭建监控市场条件、识别潜在交易机会的基础架构,但尚未实现自动交易执行与风险控制功能。第十九部分的规划重心将转向激活交易执行模块,并实施风险管理体系,确保策略能安全高效地基于信号采取行动。

我们的架构设计采用模块化方法,为信号到交易的转化提供清晰路径,同时嵌入多重保护机制。我们旨在实现以下功能:基于验证后的信号生成买入/卖出订单,并建立完整的风险控制体系——设置止损止盈、根据账户余额动态调整仓位、限制总体亏损以保全本金。该设计将构建一个高度协同的自动化剥头皮系统。我们将通过定义更多交易处理类,并将所有逻辑整合至tick处理函数中,使系统真正运转起来。简言之,这就是我们想要实现的目标。

规划架构


在MQL5中的实现

要创建MQL5程序,请打开MetaEditor,进入导航器,定位至Experts文件夹,点击“新建”选项卡,并按照提示创建文件。文件创建完成后,在编码环境中,我们需要声明一些接口和类,以增强信号生成、逻辑组织及交易管理能力。首先,我们从基础信号表达式接口的设计开始。

//--- Define interface for strategy signal expressions
interface IAdvisorStrategyExpression {
   bool Evaluate();                                       //--- Evaluate signal
   bool GetFireOnlyWhenReset();                           //--- Retrieve fire-only-when-reset flag
   void SetFireOnlyWhenReset(bool value);                 //--- Set fire-only-when-reset flag
   void ResetSignalValue();                               //--- Reset signal value
};

//--- Define base class for advisor signals
class ASSignal : public IAdvisorStrategyExpression {
private:
   bool _fireOnlyWhenReset;                               //--- Store fire-only-when-reset flag
   int _previousSignalValue;                              //--- Store previous signal value
   int _signalValue;                                      //--- Store current signal value

protected:
   //--- Declare pure virtual method for signal evaluation
   bool virtual EvaluateSignal() = 0;                      //--- Require derived classes to implement

public:
   //--- Initialize signal
   void ASSignal() {
      _fireOnlyWhenReset = false;                         //--- Set fire-only-when-reset to false
      _previousSignalValue = -1;                          //--- Set previous value to -1
      _signalValue = -1;                                  //--- Set current value to -1
   }

   //--- Evaluate signal
   bool Evaluate() {
      if (_signalValue == -1) {                           //--- Check if signal uncomputed
         _signalValue = EvaluateSignal();                 //--- Compute signal
      }
      if (_fireOnlyWhenReset) {                           //--- Check fire-only-when-reset
         return (_previousSignalValue <= 0 && _signalValue == 1); //--- Return true if signal transitions to 1
      } else {
         return _signalValue == 1;                        //--- Return true if signal is 1
      }
   }

   //--- Retrieve fire-only-when-reset flag
   bool GetFireOnlyWhenReset() {
      return _fireOnlyWhenReset;                          //--- Return flag
   }

   //--- Set fire-only-when-reset flag
   void SetFireOnlyWhenReset(bool value) {
      _fireOnlyWhenReset = value;                         //--- Set flag
   }

   //--- Reset signal value
   void ResetSignalValue() {
      _previousSignalValue = _signalValue;                //--- Store current as previous
      _signalValue = -1;                                  //--- Reset current value
   }
};

为了构建高效的交易信号处理框架,我们聚焦于模块化且可靠的信号评估系统。首先定义"IAdvisorStrategyExpression"接口,为信号操作提供统一的设计规范。该接口包含四大核心函数:"Evaluate"函数判断信号是否激活;"GetFireOnlyWhenReset"检查控制信号触发的标识位;"SetFireOnlyWhenReset"函数修改该标识位的状态;"ResetSignalValue"清除信号状态以备后续评估。

随后我们开发"ASSignal",该类实现"IAdvisorStrategyExpression"接口,作为策略中具体信号类型的基础。在"ASSignal"类中,我们定义三个私有变量:"_fireOnlyWhenReset"用于控制信号是否仅在重置后激活;"_previousSignalValue"用于记录上一次信号状态;"_signalValue"用于存储当前的信号状态。我们在"ASSignal"构造函数中初始化这些变量,将"_fireOnlyWhenReset"设置为false,同时将"_previousSignalValue"和"_signalValue"设置为-1(表示未计算状态)。"Evaluate"函数的逻辑如下:如果"_signalValue"为-1,则调用纯虚函数"EvaluateSignal"(需在子类中实现)计算信号值;如果"_signalValue"为1,或信号从非正数的"_previousSignalValue"转变为1,函数将根据"_fireOnlyWhenReset"标识返回true。

为管理信号触发行为,我们实现用于获取"_fireOnlyWhenReset"标识位的当前值的"GetFireOnlyWhenReset"函数,以及用于更新该标识位,实现信号触发时机动态配置的"SetFireOnlyWhenReset"函数。我们同时提供"ResetSignalValue"函数,用于将当前信号值"_signalValue"保存至历史值"_previousSignalValue"中,并且将"_signalValue"重置为-1,为下一个评估周期做准备。通过将"EvaluateSignal"声明为纯虚函数,我们强制要求所有派生类必须实现具体的信号计算逻辑,从而确保系统的可扩展性。在此基础上,我们可以进一步定义更多信号管理类。

//--- Define class for managing trade signals
class TradeSignalCollection {
private:
   IAdvisorStrategyExpression* _tradeSignals[];           //--- Store array of signal pointers
   int _pointer;                                          //--- Track current iteration index
   int _size;                                             //--- Track number of signals

public:
   //--- Initialize empty signal collection
   void TradeSignalCollection() {
      _pointer = -1;                                      //--- Set initial pointer to -1
      _size = 0;                                          //--- Set initial size to 0
   }

   //--- Destructor to clean up signals
   void ~TradeSignalCollection() {
      for (int i = 0; i < ArraySize(_tradeSignals); i++) { //--- Iterate signals
         delete(_tradeSignals[i]);                        //--- Delete each signal
      }
   }

   //--- Add signal to collection
   void Add(IAdvisorStrategyExpression* item) {
      _size = _size + 1;                                  //--- Increment size
      ArrayResize(_tradeSignals, _size, 8);               //--- Resize array with reserve
      _tradeSignals[(_size - 1)] = item;                  //--- Store signal at last index
   }

   //--- Remove signal at index
   IAdvisorStrategyExpression* Remove(int index) {
      IAdvisorStrategyExpression* removed = NULL;         //--- Initialize removed signal as null
      if (index >= 0 && index < _size) {                  //--- Check valid index
         removed = _tradeSignals[index];                  //--- Store signal to remove
         for (int i = index; i < (_size - 1); i++) {      //--- Shift signals left
            _tradeSignals[i] = _tradeSignals[i + 1];      //--- Move signal
         }
         ArrayResize(_tradeSignals, ArraySize(_tradeSignals) - 1, 8); //--- Reduce array size
         _size = _size - 1;                               //--- Decrement size
      }
      return removed;                                     //--- Return removed signal or null
   }

   //--- Retrieve signal at index
   IAdvisorStrategyExpression* Get(int index) {
      if (index >= 0 && index < _size) {                  //--- Check valid index
         return _tradeSignals[index];                     //--- Return signal
      }
      return NULL;                                        //--- Return null for invalid index
   }

   //--- Retrieve number of signals
   int Count() {
      return _size;                                       //--- Return current size
   }

   //--- Reset iterator to start
   void Rewind() {
      _pointer = -1;                                      //--- Set pointer to -1
   }

   //--- Move to next signal
   IAdvisorStrategyExpression* Next() {
      _pointer++;                                         //--- Increment pointer
      if (_pointer == _size) {                            //--- Check if at end
         Rewind();                                        //--- Reset pointer
         return NULL;                                     //--- Return null
      }
      return Current();                                   //--- Return current signal
   }

   //--- Move to previous signal
   IAdvisorStrategyExpression* Prev() {
      _pointer--;                                         //--- Decrement pointer
      if (_pointer == -1) {                               //--- Check if before start
         return NULL;                                     //--- Return null
      }
      return Current();                                   //--- Return current signal
   }

   //--- Check if more signals exist
   bool HasNext() {
      return (_pointer < (_size - 1));                    //--- Return true if pointer is before end
   }

   //--- Retrieve current signal
   IAdvisorStrategyExpression* Current() {
      return _tradeSignals[_pointer];                     //--- Return signal at pointer
   }

   //--- Retrieve current iterator index
   int Key() {
      return _pointer;                                    //--- Return current pointer
   }
};

//--- Define class for managing trading signals
class AdvisorStrategy {
private:
   TradeSignalCollection* _openBuySignals;                //--- Store open Buy signals
   TradeSignalCollection* _openSellSignals;               //--- Store open Sell signals
   TradeSignalCollection* _closeBuySignals;               //--- Store close Buy signals
   TradeSignalCollection* _closeSellSignals;              //--- Store close Sell signals

   //--- Evaluate signal at specified level
   bool EvaluateASLevel(TradeSignalCollection* signals, int level) {
      if (level > 0 && level <= signals.Count()) {        //--- Check valid level
         return signals.Get(level - 1).Evaluate();        //--- Evaluate signal
      }
      return false;                                       //--- Return false for invalid level
   }

public:
   //--- Initialize strategy
   void AdvisorStrategy() {
      _openBuySignals = new TradeSignalCollection();      //--- Create open Buy signals collection
      _openSellSignals = new TradeSignalCollection();     //--- Create open Sell signals collection
      _closeBuySignals = new TradeSignalCollection();     //--- Create close Buy signals collection
      _closeSellSignals = new TradeSignalCollection();    //--- Create close Sell signals collection
   }

   //--- Destructor to clean up signals
   void ~AdvisorStrategy() {
      delete(_openBuySignals);                            //--- Delete open Buy signals
      delete(_openSellSignals);                           //--- Delete open Sell signals
      delete(_closeBuySignals);                           //--- Delete close Buy signals
      delete(_closeSellSignals);                          //--- Delete close Sell signals
   }

   //--- Retrieve trading advice
   bool GetAdvice(TradeAction tradeAction, int level) {
      if (tradeAction == OpenBuyAction) {                  //--- Check open Buy action
         return EvaluateASLevel(_openBuySignals, level);   //--- Evaluate Buy signal
      } else if (tradeAction == OpenSellAction) {          //--- Check open Sell action
         return EvaluateASLevel(_openSellSignals, level);  //--- Evaluate Sell signal
      } else if (tradeAction == CloseBuyAction) {          //--- Check close Buy action
         return EvaluateASLevel(_closeBuySignals, level);  //--- Evaluate close Buy signal
      } else if (tradeAction == CloseSellAction) {         //--- Check close Sell action
         return EvaluateASLevel(_closeSellSignals, level); //--- Evaluate close Sell signal
      } else {
         Alert("Unsupported TradeAction in Advisor Strategy. TradeAction: " + DoubleToStr(tradeAction)); //--- Log unsupported action
      }
      return false;                                       //--- Return false for invalid action
   }

   //--- Register open Buy signal
   void RegisterOpenBuy(IAdvisorStrategyExpression* openBuySignal, int level) {
      if (level <= _openBuySignals.Count()) {             //--- Check if level already set
         Alert("Register Open Buy failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _openBuySignals.Add(openBuySignal);                 //--- Add signal
   }

   //--- Register open Sell signal
   void RegisterOpenSell(IAdvisorStrategyExpression* openSellSignal, int level) {
      if (level <= _openSellSignals.Count()) {            //--- Check if level already set
         Alert("Register Open Sell failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _openSellSignals.Add(openSellSignal);               //--- Add signal
   }

   //--- Register close Buy signal
   void RegisterCloseBuy(IAdvisorStrategyExpression* closeBuySignal, int level) {
      if (level <= _closeBuySignals.Count()) {            //--- Check if level already set
         Alert("Register Close Buy failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _closeBuySignals.Add(closeBuySignal);               //--- Add signal
   }

   //--- Register close Sell signal
   void RegisterCloseSell(IAdvisorStrategyExpression* closeSellSignal, int level) {
      if (level <= _closeSellSignals.Count()) {           //--- Check if level already set
         Alert("Register Close Sell failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _closeSellSignals.Add(closeSellSignal);             //--- Add signal
   }

   //--- Retrieve number of signals for action
   int GetNumberOfExpressions(TradeAction tradeAction) {
      if (tradeAction == OpenBuyAction) {                 //--- Check open Buy action
         return _openBuySignals.Count();                  //--- Return Buy signal count
      } else if (tradeAction == OpenSellAction) {         //--- Check open Sell action
         return _openSellSignals.Count();                 //--- Return Sell signal count
      } else if (tradeAction == CloseBuyAction) {         //--- Check close Buy action
         return _closeBuySignals.Count();                 //--- Return close Buy signal count
      } else if (tradeAction == CloseSellAction) {        //--- Check close Sell action
         return _closeSellSignals.Count();                //--- Return close Sell signal count
      }
      return 0;                                           //--- Return 0 for invalid action
   }

   //--- Set fire-only-when-reset for all signals
   void SetFireOnlyWhenReset(bool value) {
      _openBuySignals.Rewind();                           //--- Reset Buy signals iterator
      while (_openBuySignals.Next() != NULL) {            //--- Iterate Buy signals
         _openBuySignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
      _openSellSignals.Rewind();                          //--- Reset Sell signals iterator
      while (_openSellSignals.Next() != NULL) {           //--- Iterate Sell signals
         _openSellSignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
      _closeBuySignals.Rewind();                          //--- Reset close Buy signals iterator
      while (_closeBuySignals.Next() != NULL) {           //--- Iterate close Buy signals
         _closeBuySignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
      _closeSellSignals.Rewind();                         //--- Reset close Sell signals iterator
      while (_closeSellSignals.Next() != NULL) {          //--- Iterate close Sell signals
         _closeSellSignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
   }

   //--- Handle tick event for signals
   void HandleTick() {
      _openBuySignals.Rewind();                           //--- Reset Buy signals iterator
      while (_openBuySignals.Next() != NULL) {            //--- Iterate Buy signals
         _openBuySignals.Current().ResetSignalValue();    //--- Reset signal value
         if (_openBuySignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _openBuySignals.Current().Evaluate();         //--- Evaluate signal
         }
      }
      _openSellSignals.Rewind();                          //--- Reset Sell signals iterator
      while (_openSellSignals.Next() != NULL) {           //--- Iterate Sell signals
         _openSellSignals.Current().ResetSignalValue();   //--- Reset signal value
         if (_openSellSignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _openSellSignals.Current().Evaluate();        //--- Evaluate signal
         }
      }
      _closeBuySignals.Rewind();                          //--- Reset close Buy signals iterator
      while (_closeBuySignals.Next() != NULL) {           //--- Iterate close Buy signals
         _closeBuySignals.Current().ResetSignalValue();   //--- Reset signal value
         if (_closeBuySignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _closeBuySignals.Current().Evaluate();        //--- Evaluate signal
         }
      }
      _closeSellSignals.Rewind();                         //--- Reset close Sell signals iterator
      while (_closeSellSignals.Next() != NULL) {          //--- Iterate close Sell signals
         _closeSellSignals.Current().ResetSignalValue();  //--- Reset signal value
         if (_closeSellSignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _closeSellSignals.Current().Evaluate();       //--- Evaluate signal
         }
      }
   }
};

在此阶段,我们将实现更多用于管理交易信号的类。由于在前文部分已经对类的语法进行过说明,此处仅展示核心实现部分。我们创建"TradeSignalCollection"类,其中,使用"_tradeSignals"存储"IAdvisorStrategyExpression"指针,"_pointer"为迭代器指针,"_size"为信号计数器,这些成员在"TradeSignalCollection"构造函数中完成初始化。我们通过"Add"函数添加信号,使用"Remove"从集合中移除信号,并使用“Next”、“Prev”、“Current”、“Rewind”和“HasNext”函数进行导航,通过析构函数实现内存清理。

在"AdvisorStrategy"中,我们定义了 "_openBuySignals"、"_openSellSignals"、"_closeBuySignals"和"_closeSellSignals"作为"TradeSignalCollection"对象,这些都在"AdvisorStrategy"构造函数中设置。我们使用"GetAdvice"和"EvaluateASLevel"函数评估信号,通过"RegisterOpenBuy"、"RegisterOpenSell"、"RegisterCloseBuy"和"RegisterCloseSell"注册信号,并使用"GetNumberOfExpressions"管理当前注册的信号总数。我们使用"SetFireOnlyWhenReset"函数设置重置标识,并通过"HandleTick"调用"ResetSignalValue"和"Evaluate"来重置并评估信号,确保策略中的信号处理高效可靠。现在我们可以为给定级别的卖出和买入信号定义类。让我们先从卖出类开始。

//--- Define class for open Sell signal at level 1
class ASOpenSellLevel1 : public ASSignal {
protected:
   //--- Evaluate Sell signal
   bool EvaluateSignal() {
      Order* openOrder = _ea.GetWallet().GetMostRecentOpenOrder(); //--- Retrieve recent open order
      if (openOrder != NULL && openOrder.Type == ORDER_TYPE_BUY) { //--- Check if Buy order
         openOrder = NULL;                                //--- Clear if Buy to avoid conflict
      }
      if (((((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) == EMPTY_VALUE) //--- Check no recent order
            && ((BidFunc.GetValue(0) < fn_iMA_SMA_4(Symbol(), 0)) //--- Check Bid below 4-period SMA
                && ((BidFunc.GetValue(0) > fn_iMA_SMA8(Symbol(), 0)) //--- Check Bid above 8-period SMA
                    && ((BidFunc.GetValue(0) < fn_iEnvelopes_ENV_UPPER(Symbol(), 0, 0)) //--- Check Bid below upper Envelope
                        && ((fn_iRSI_RSI(Symbol(), 1) < OpenSell_Const_0) //--- Check previous RSI below threshold
                            && (fn_iRSI_RSI(Symbol(), 0) >= OpenSell_Const_0) //--- Check current RSI above threshold
                           )
                       )
                   )
               )
           )
            || (((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) != EMPTY_VALUE) //--- Check existing order
                && (BidFunc.GetValue(0) > ((openOrder != NULL ? openOrder.OpenPrice : EMPTY_VALUE) + (PipPoint * OpenSell_Const_1))) //--- Check Bid above open price plus offset
               )
          )) {
         return true;                                     //--- Return true for Sell signal
      }
      return false;                                       //--- Return false if no signal
   }

public:
   //--- Initialize Sell signal
   void ASOpenSellLevel1() {}                             //--- Empty constructor
};

我们定义了"ASOpenSellLevel1"类(继承自"ASSignal"类)来处理卖出信号逻辑。在受保护的"EvaluateSignal"函数中,我们通过"_ea"的"Wallet"中的"GetMostRecentOpenOrder"函数检查最新订单,如果为买入单则清除。当满足以下任一条件时,则触发卖出信号:无近期订单;通过"BidFunc"的"GetValue"获取的买入价低于4周期SMA("fn_iMA_SMA_4");高于8周期SMA("fn_iMA_SMA8");低于包络线上轨("fn_iEnvelopes_ENV_UPPER");前一周期的RSI值("fn_iRSI_RSI")低于"OpenSell_Const_0";当前周期的RSI值大于或等于"OpenSell_Const_0",如果当前存在未平仓订单,且最新买入价超过“开仓价” + "PipPoint" × "OpenSell_Const_1"(即达到预设的止盈或止损点差)。

触发有效信号时返回true,否则返回false。当"ASOpenSellLevel1"构造函数为空时,则直接调用父类"ASSignal"的初始化逻辑。买入信号采用与卖出信号相同的逻辑。

//--- Define class for open Buy signal at level 1
class ASOpenBuyLevel1 : public ASSignal {
protected:
   //--- Evaluate Buy signal
   bool EvaluateSignal() {
      Order* openOrder = _ea.GetWallet().GetMostRecentOpenOrder();  //--- Retrieve most recent open order
      if (openOrder != NULL && openOrder.Type == ORDER_TYPE_SELL) { //--- Check if Sell order
         openOrder = NULL;                                          //--- Clear if Sell to avoid conflict
      }
      if (((((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) == EMPTY_VALUE) //--- Check no recent order
            && ((AskFunc.GetValue(0) > fn_iMA_SMA_4(Symbol(), 0))                                   //--- Check Ask above 4-period SMA
                && ((AskFunc.GetValue(0) < fn_iMA_SMA8(Symbol(), 0))                                //--- Check Ask below 8-period SMA
                    && ((AskFunc.GetValue(0) > fn_iEnvelopes_ENV_LOW(Symbol(), 1, 0))               //--- Check Ask above lower Envelope
                        && ((fn_iRSI_RSI(Symbol(), 1) > OpenBuy_Const_0)                            //--- Check previous RSI above threshold
                            && (fn_iRSI_RSI(Symbol(), 0) <= OpenBuy_Const_0)                        //--- Check current RSI below threshold
                           )
                       )
                   )
               )
           )
            || (((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) != EMPTY_VALUE) //--- Check existing order
                && (AskFunc.GetValue(0) < ((openOrder != NULL ? openOrder.OpenPrice : EMPTY_VALUE) - (PipPoint * OpenBuy_Const_1))) //--- Check Ask below open price minus offset
               )
          )) {
         return true;                                     //--- Return true for Buy signal
      }
      return false;                                       //--- Return false if no signal
   }

public:
   //--- Initialize Buy signal
   void ASOpenBuyLevel1() {}                              //--- Empty constructor
};

在定义买入逻辑时,我们只需采用与定义卖出逻辑时相同的思路。接下来,我们可以添加一些输入参数来控制交易信号逻辑。

//--- Define input group for trade and risk module settings
input string trademodule = "------TRADE/RISK MODULE------";//--- Label trade/risk module inputs
//--- Define input group for open Buy constants
input double LotSizePercentage = 1;                        //--- Set lot size as percentage of account balance (default: 1%)
input double OpenBuy_Const_0 = 11;                         //--- Set RSI threshold for Buy signal (default: 11)
input double OpenBuy_Const_1 = 10;                         //--- Set pip offset for additional Buy orders (default: 10 pips)
input double OpenSell_Const_0 = 89;                        //--- Set RSI threshold for Sell signal (default: 89)
input double OpenSell_Const_1 = 10;                        //--- Set pip offset for additional Sell orders (default: 10 pips)

在定义好信号生成的输入变量之后,我们便可以着手定义信号执行后的风险管理和交易管理逻辑。要实现这一点,我们需要另一个接口和类。

//--- Define interface for money management
interface IMoneyManager {
   double GetLotSize();                                   //--- Retrieve lot size
   int GetNextLevel(Wallet* wallet);                      //--- Retrieve next trading level
};

//--- Define class for money management
class MoneyManager : public IMoneyManager {
public:
   //--- Initialize money manager
   void MoneyManager(Wallet* wallet) {
      _minLot = MarketInfo_LibFunc(Symbol(), MODE_MINLOT);   //--- Set minimum lot size
      _maxLot = MarketInfo_LibFunc(Symbol(), MODE_MAXLOT);   //--- Set maximum lot size
      _lotStep = MarketInfo_LibFunc(Symbol(), MODE_LOTSTEP); //--- Set lot step
   }

   //--- Retrieve calculated lot size
   double GetLotSize() {
      double lotSize = NormalizeLots(NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE) * 0.0001 * LotSizePercentage / 100.0, 2)); //--- Calculate lot size
      return lotSize;                                     //--- Return normalized lot size
   }

   //--- Retrieve next trading level
   int GetNextLevel(Wallet* wallet) {
      return wallet.GetOpenOrders().Count() + 1;          //--- Return next level based on open orders
   }

private:
   double _minLot;                                        //--- Store minimum lot size
   double _maxLot;                                        //--- Store maximum lot size
   double _lotStep;                                       //--- Store lot step

   //--- Normalize lot size to broker specifications
   double NormalizeLots(double lots) {
      lots = MathRound(lots / _lotStep) * _lotStep;       //--- Round to lot step
      if (lots < _minLot) lots = _minLot;                 //--- Enforce minimum lot
      else if (lots > _maxLot) lots = _maxLot;            //--- Enforce maximum lot
      return lots;                                        //--- Return normalized lot size
   }
};

为该策略设置资金管理机制时,我们定义了“IMoneyManager”接口 ,包含“GetLotSize”和“GetNextLevel”函数,用于规范交易手数及级别递增规则。在“MoneyManager”类中,我们实现“IMoneyManager”接口,并在"MoneyManager"类的构造函数中,利用“MarketInfo_LibFunc”函数初始化“_minLot”、“_maxLot”和“_lotStep”变量。“GetLotSize”函数通过AccountInfoDouble经由账户余额计算手数,并根据“LotSizePercentage”进行调整,再使用“NormalizeLots”函数进行规范化处理,以符合经纪商规则。

将“GetOpenOrders”中未平仓的订单数量加1,作为“GetNextLevel”函数的返回值。“NormalizeLots”函数通过将手数四舍五入至“_lotStep”,并且遵守“_minLot”和“_maxLot”的限制,确保手数有效。从而构建一个简洁的交易量及级别管理系统。现在,我们可以进入交易管理阶段,为此,我们需要另一个接口。

//--- Define enumeration for trading module demands
enum TradingModuleDemand {
   NoneDemand = 0,                                        //--- Represent no demand
   NoBuyDemand = 1,                                       //--- Prevent Buy orders
   NoSellDemand = 2,                                      //--- Prevent Sell orders
   NoOpenDemand = 4,                                      //--- Prevent all open orders
   OpenBuySellDemand = 8,                                 //--- Demand both Buy and Sell opens
   OpenBuyDemand = 16,                                    //--- Demand Buy open
   OpenSellDemand = 32,                                   //--- Demand Sell open
   CloseBuyDemand = 64,                                   //--- Demand Buy close
   CloseSellDemand = 128,                                 //--- Demand Sell close
   CloseBuySellDemand = 256                               //--- Demand both Buy and Sell closes
};

//--- Define interface for trading module signals
interface ITradingModuleSignal {
   string GetName();                                      //--- Retrieve signal name
   bool Evaluate(Order* openOrder = NULL);                //--- Evaluate signal
};

//--- Define interface for trading module values
interface ITradingModuleValue {
   string GetName();                                      //--- Retrieve value name
   double Evaluate(Order* openOrder = NULL);              //--- Evaluate value
};

//--- Define interface for trade strategy modules
interface ITradeStrategyModule {
   TradingModuleDemand Evaluate(Wallet* wallet, TradingModuleDemand demand, int level = 1); //--- Evaluate module
   void RegisterTradeSignal(ITradingModuleSignal* tradeSignal); //--- Register signal
};

//--- Define interface for open trade strategy modules
interface ITradeStrategyOpenModule : public ITradeStrategyModule {
   TradingModuleDemand EvaluateOpenSignals(Wallet* wallet, TradingModuleDemand demand, int requestedEvaluationLevel = 0); //--- Evaluate open signals
   TradingModuleDemand EvaluateCloseSignals(Wallet* wallet, TradingModuleDemand demand); //--- Evaluate close signals
};

//--- Define interface for close trade strategy modules
interface ITradeStrategyCloseModule : public ITradeStrategyModule {
   ORDER_GROUP_TYPE GetOrderGroupingType();                  //--- Retrieve grouping type
   void RegisterTradeValue(ITradingModuleValue* tradeValue); //--- Register value
};

在此阶段,我们通过定义一组结构化的枚举类型和接口,为交易决策管理奠定基础。我们首先创建“TradingModuleDemand”枚举类型,用于对交易行为和限制进行分类,例如:用“NoneDemand”表示无操作,用“NoBuyDemand”和“NoSellDemand”分别禁止特定类型的订单(如禁止买入或禁止卖出),用“OpenBuyDemand”和“OpenSellDemand”分别发起买入或卖出交易,用“CloseBuyDemand”和“CloseSellDemand”分别平掉多头或空头头寸,等等。该枚举类型将提供清晰的方式以便为策略传达明确的交易执行意图。

随后,我们定义“ITradingModuleSignal”接口,旨在将交易信号操作标准化,该接口包含“GetName”函数(获取信号的标识符)和“Evaluate”函数(评估信号是否处于激活状态,并可以选择性地考虑已存在的未成交订单)。同样地,我们引入“ITradingModuleValue”接口,该接口也具备“GetName”和“Evaluate”函数,用于管理如止盈目标等影响交易决策的数值。对于策略模块,我们创建了“ITradeStrategyModule”接口,该接口包含“Evaluate”函数(根据“钱包”对象处理交易需求)和“RegisterTradeSignal”函数(纳入交易信号)。

我们在此基础上进行了扩展,引入“ITradeStrategyOpenModule”接口,该接口新增“EvaluateOpenSignals”和“EvaluateCloseSignals”函数,分别用于处理开仓和平仓信号;同时还引入“ITradeStrategyCloseModule”接口,该接口包含“GetOrderGroupingType”函数(订单分组)和“RegisterTradeValue”函数(数值注册)。这些组件共同构成了一个灵活的框架,用于在我们的策略中协调交易操作。现在,我们可以基于这些模块来定义管理

//--- Define class for managing trade strategy
class TradeStrategy {
public:
   ITradeStrategyCloseModule* CloseModules[];             //--- Store close modules

private:
   ITradeStrategyModule* _preventOpenModules[];           //--- Store prevent-open modules
   ITradeStrategyOpenModule* _openModule;                 //--- Store open module

   //--- Evaluate prevent-open modules
   TradingModuleDemand EvaluatePreventOpenModules(Wallet* wallet, TradingModuleDemand preventOpenDemand, int evaluationLevel = 1) {
      TradingModuleDemand preventOpenDemands[];                           //--- Declare prevent-open demands array
      ArrayResize(preventOpenDemands, ArraySize(_preventOpenModules), 8); //--- Resize array
      for (int i = 0; i < ArraySize(_preventOpenModules); i++) {          //--- Iterate modules
         preventOpenDemands[i] = _preventOpenModules[i].Evaluate(wallet, NoneDemand, evaluationLevel); //--- Evaluate module
      }
      return PreventOpenModuleBase::GetCombinedPreventOpenDemand(preventOpenDemands); //--- Return combined demand
   }

   //--- Evaluate close modules
   TradingModuleDemand EvaluateCloseModules(Wallet* wallet, TradingModuleDemand closeDemand, int evaluationLevel = 1) {
      TradingModuleDemand closeDemands[];                    //--- Declare close demands array
      ArrayResize(closeDemands, ArraySize(CloseModules), 8); //--- Resize array
      for (int i = 0; i < ArraySize(CloseModules); i++) {    //--- Iterate modules
         closeDemands[i] = CloseModules[i].Evaluate(wallet, NoneDemand, evaluationLevel); //--- Evaluate module
      }
      return CloseModuleBase::GetCombinedCloseDemand(closeDemands); //--- Return combined demand
   }

   //--- Evaluate close conditions for TP/SL
   void EvaluateCloseConditions(Wallet* wallet, TradingModuleDemand signalDemand) {
      OrderCollection* openOrders = wallet.GetOpenOrders(); //--- Retrieve open orders
      if (openOrders.Count() == 0) {                        //--- Check if no open orders
         return;                                            //--- Exit
      }
      double bid = Bid_LibFunc();                           //--- Retrieve Bid price
      double ask = Ask_LibFunc();                           //--- Retrieve Ask price
      for (int i = openOrders.Count() - 1; i >= 0; i--) {   //--- Iterate open orders
         Order* order = openOrders.Get(i);                  //--- Get order
         bool closeSignal = (order.Type == OP_BUY && signalDemand == CloseBuyDemand) ||   //--- Check Buy close signal
                            (order.Type == OP_SELL && signalDemand == CloseSellDemand) || //--- Check Sell close signal
                            signalDemand == CloseBuySellDemand;                           //--- Check Buy/Sell close signal
         bool closeManualSLTP = AllowManualTPSLChanges && ((order.StopLossManual != 0 && order.Type == OP_BUY && bid <= order.StopLossManual) ||    //--- Check manual Buy SL
                                                          (order.StopLossManual != 0 && order.Type == OP_SELL && ask >= order.StopLossManual) ||    //--- Check manual Sell SL
                                                          (order.TakeProfitManual != 0 && order.Type == OP_BUY && bid >= order.TakeProfitManual) || //--- Check manual Buy TP
                                                          (order.TakeProfitManual != 0 && order.Type == OP_SELL && ask <= order.TakeProfitManual)); //--- Check manual Sell TP
         bool fullOrderClose = closeSignal || closeManualSLTP; //--- Determine full close
         OrderCloseInfo* activePartialCloseCloseInfo = NULL;   //--- Initialize partial close info
         if (!fullOrderClose) {                                //--- Check if not full close
            if (!AllowManualTPSLChanges || order.StopLossManual == 0) { //--- Check manual SL
               for (int cli = 0; cli < ArraySize(order.CloseInfosSL); cli++) { //--- Iterate SL info
                  if (order.CloseInfosSL[cli].IsOld) continue; //--- Skip old info
                  if (order.CloseInfosSL[cli].IsClosePriceSLHit(order.Type, ask, bid)) { //--- Check SL hit
                     if (activePartialCloseCloseInfo == NULL || order.CloseInfosSL[cli].Percentage > activePartialCloseCloseInfo.Percentage) { //--- Check higher percentage
                        activePartialCloseCloseInfo = order.CloseInfosSL[cli]; //--- Set active info
                     }
                  }
               }
            }
            if (!AllowManualTPSLChanges || order.TakeProfitManual == 0) {      //--- Check manual TP
               for (int cli = 0; cli < ArraySize(order.CloseInfosTP); cli++) { //--- Iterate TP info
                  if (order.CloseInfosTP[cli].IsOld) continue;                 //--- Skip old info
                  if (order.CloseInfosTP[cli].IsClosePriceTPHit(order.Type, ask, bid)) { //--- Check TP hit
                     if (activePartialCloseCloseInfo == NULL || order.CloseInfosTP[cli].Percentage > activePartialCloseCloseInfo.Percentage) { //--- Check higher percentage
                        activePartialCloseCloseInfo = order.CloseInfosTP[cli]; //--- Set active info
                     }
                  }
               }
            }
            fullOrderClose = activePartialCloseCloseInfo != NULL && activePartialCloseCloseInfo.Percentage == 100; //--- Check if full close
         }
         if (fullOrderClose) {                            //--- Handle full close
            TradingModuleDemand finalPreventOpenAdvice = EvaluatePreventOpenModules(wallet, NoneDemand, 0);      //--- Evaluate prevent-open
            TradingModuleDemand openDemand = _openModule.EvaluateOpenSignals(wallet, finalPreventOpenAdvice, 1); //--- Evaluate open signals
            int orderTypeOfOpeningOrder = wallet.GetOpenOrders().Get(0).Type;                                    //--- Get first order type
            if ((orderTypeOfOpeningOrder == ORDER_TYPE_BUY && openDemand == OpenBuyDemand) ||                    //--- Check Buy re-entry
                (orderTypeOfOpeningOrder == ORDER_TYPE_SELL && openDemand == OpenSellDemand) ||                  //--- Check Sell re-entry
                (openDemand == OpenBuySellDemand)) {      //--- Check Buy/Sell re-entry
               return;                                    //--- Block close to prevent re-entry
            }
            wallet.SetOpenOrderToPendingClose(order);      //--- Move order to pending close
         } else if (activePartialCloseCloseInfo != NULL) { //--- Handle partial close
            Order* partialCloseOrder = order.SplitOrder(activePartialCloseCloseInfo.Percentage); //--- Split order
            if (partialCloseOrder.Lots < 1e-13) {          //--- Check if last piece
               delete(partialCloseOrder);                  //--- Delete split order
               wallet.SetOpenOrderToPendingClose(order);   //--- Move to pending close
            } else {
               partialCloseOrder.ParentOrder = order;      //--- Link to parent order
               if (wallet.AddPendingCloseOrder(partialCloseOrder)) { //--- Add to pending close
                  activePartialCloseCloseInfo.IsOld = true; //--- Mark info as old
               }
            }
         }
      }
   }

public:
   //--- Initialize trade strategy
   void TradeStrategy(ITradeStrategyOpenModule* openModule) {
      _openModule = openModule;                           //--- Set open module
   }

   //--- Destructor to clean up strategy
   void ~TradeStrategy() {
      for (int i = ArraySize(_preventOpenModules) - 1; i >= 0; i--) { //--- Iterate prevent-open modules
         delete(_preventOpenModules[i]);                  //--- Delete module
      }
      delete(_openModule);                                //--- Delete open module
      for (int i = ArraySize(CloseModules) - 1; i >= 0; i--) { //--- Iterate close modules
         delete(CloseModules[i]);                         //--- Delete module
      }
   }

   //--- Evaluate trading strategy
   void Evaluate(Wallet* wallet) {
      int orderCount = wallet.GetOpenOrders().Count();    //--- Retrieve open order count
      TradingModuleDemand finalPreventOpenAdvice = EvaluatePreventOpenModules(wallet, NoneDemand, orderCount + 1); //--- Evaluate prevent-open
      if (orderCount > 0) {                               //--- Check if orders exist
         EvaluateCloseModules(wallet, NoneDemand);        //--- Evaluate close modules
         TradingModuleDemand signalDemand = _openModule.EvaluateCloseSignals(wallet, finalPreventOpenAdvice); //--- Evaluate close signals
         EvaluateCloseConditions(wallet, signalDemand);   //--- Evaluate close conditions
      }
      _openModule.Evaluate(wallet, finalPreventOpenAdvice, 0); //--- Evaluate open module
   }

   //--- Register prevent-open module
   void RegisterPreventOpenModule(ITradeStrategyModule* preventOpenModule) {
      int size = ArraySize(_preventOpenModules);          //--- Get current array size
      ArrayResize(_preventOpenModules, size + 1, 8);      //--- Resize array
      _preventOpenModules[size] = preventOpenModule;      //--- Add module
   }

   //--- Register close module
   void RegisterCloseModule(ITradeStrategyCloseModule* closeModule) {
      int size = ArraySize(CloseModules);                 //--- Get current array size
      ArrayResize(CloseModules, size + 1, 8);             //--- Resize array
      CloseModules[size] = closeModule;                   //--- Add module
   }
};

//--- Define base class for module calculations
class ModuleCalculationsBase {
public:
   //--- Calculate profit for order collection
   static double CalculateOrderCollectionProfit(OrderCollection &orders, ORDER_PROFIT_CALCULATION_TYPE calculationType) {
      double collectionProfit = 0;                        //--- Initialize profit
      for (int i = 0; i < orders.Count(); i++) {          //--- Iterate orders
         Order* order = orders.Get(i);                    //--- Get order
         collectionProfit += CalculateOrderProfit(order, calculationType); //--- Add order profit
      }
      return collectionProfit;                            //--- Return total profit
   }

   //--- Calculate profit for single order
   static double CalculateOrderProfit(Order* order, ORDER_PROFIT_CALCULATION_TYPE calculationType) {
      if (calculationType == Pips) {                      //--- Check pips calculation
         return order.CalculateProfitPips();              //--- Return profit in pips
      } else if (calculationType == Money) {              //--- Check money calculation
         return order.CalculateProfitCurrency();          //--- Return profit in currency
      } else if (calculationType == EquityPercentage) {   //--- Check equity percentage
         return order.CalculateProfitEquityPercentage();  //--- Return profit as percentage
      } else {
         Alert("Can't execute CalculateOrderCollectionProfit. Unknown calculationType: " + IntegerToString(calculationType)); //--- Log error
         return 0;                                        //--- Return 0 for invalid type
      }
   }
};

//--- Define base class for open trade modules
class OpenModuleBase : public ITradeStrategyOpenModule {
protected:
   AdvisorStrategy* _advisorStrategy;                     //--- Store advisor strategy
   IMoneyManager* _moneyManager;                          //--- Store money manager

   //--- Create new order
   Order* OpenOrder(ENUM_ORDER_TYPE orderType, bool mustBeVisibleOnChart) {
      Order* order = new Order(mustBeVisibleOnChart);     //--- Create new order
      order.SymbolCode = Symbol();                        //--- Set symbol
      order.Type = orderType;                             //--- Set order type
      order.MagicNumber = MagicNumber;                    //--- Set magic number
      order.Lots = _moneyManager.GetLotSize();            //--- Set lot size
      if (order.Type == ORDER_TYPE_BUY) {                 //--- Check Buy order
         order.OpenPrice = Ask_LibFunc();                 //--- Set open price to Ask
         order.StopLoss = -DBL_MAX;                       //--- Set initial SL to minimum
         order.TakeProfit = DBL_MAX;                      //--- Set initial TP to maximum
      } else if (order.Type == ORDER_TYPE_SELL) {         //--- Check Sell order
         order.OpenPrice = Bid_LibFunc();                 //--- Set open price to Bid
         order.StopLoss = DBL_MAX;                        //--- Set initial SL to maximum
         order.TakeProfit = -DBL_MAX;                     //--- Set initial TP to minimum
      }
      order.LowestProfitPips = DBL_MAX;                   //--- Set initial lowest profit
      order.HighestProfitPips = -DBL_MAX;                 //--- Set initial highest profit
      order.Comment = OrderComment;                       //--- Set order comment
      OrderRepository::CalculateAndSetCommision(order);   //--- Calculate and set commission
      return order;                                       //--- Return order
   }

public:
   //--- Initialize open module
   void OpenModuleBase(AdvisorStrategy* advisorStrategy, IMoneyManager* moneyManager) {
      _advisorStrategy = advisorStrategy;                 //--- Set advisor strategy
      _moneyManager = moneyManager;                       //--- Set money manager
   }

   //--- Retrieve trade actions
   void GetTradeActions(Wallet* wallet, TradingModuleDemand preventOpenDemand, TradeAction& result[]) {
      TradeAction tempresult[];                             //--- Declare temporary actions array
      if (wallet.GetOpenOrders().Count() > 0) {             //--- Check if open orders exist
         Order* firstOrder = wallet.GetOpenOrders().Get(0); //--- Get first open order
         if (firstOrder.Type == ORDER_TYPE_BUY) {           //--- Check if Buy order
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[0] = OpenBuyAction;                  //--- Add open Buy action
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[1] = CloseBuyAction;                 //--- Add close Buy action
         } else if (firstOrder.Type == ORDER_TYPE_SELL) {   //--- Check if Sell order
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[0] = OpenSellAction;                 //--- Add open Sell action
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[1] = CloseSellAction;                //--- Add close Sell action
         } else {
            Alert("Unsupported ordertype. Ordertype: " + DoubleToStr(firstOrder.Type)); //--- Log error
         }
      } else {
         ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
         tempresult[0] = OpenBuyAction;                   //--- Add open Buy action
         ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
         tempresult[1] = OpenSellAction;                  //--- Add open Sell action
      }
      for (int i = 0; i < ArraySize(tempresult); i++) {   //--- Iterate actions
         if ((preventOpenDemand == NoOpenDemand && (tempresult[i] == OpenBuyAction || tempresult[i] == OpenSellAction)) || //--- Check no open demand
             (preventOpenDemand == NoBuyDemand && tempresult[i] == OpenBuyAction) || //--- Check no Buy demand
             (preventOpenDemand == NoSellDemand && tempresult[i] == OpenSellAction)) { //--- Check no Sell demand
            continue;                                     //--- Skip action
         }
         ArrayResize(result, ArraySize(result) + 1, 8);   //--- Resize result array
         result[ArraySize(result) - 1] = tempresult[i];   //--- Add action
      }
   }

   //--- Register trade signal (empty implementation)
   virtual void RegisterTradeSignal(ITradingModuleSignal* tradeSignal) {} //--- Do nothing

   //--- Combine open demands
   static TradingModuleDemand GetCombinedOpenDemand(TradingModuleDemand &openDemands[]) {
      TradingModuleDemand result = NoneDemand;            //--- Initialize result
      for (int i = 0; i < ArraySize(openDemands); i++) {  //--- Iterate demands
         if (result == OpenBuySellDemand) {               //--- Check if Buy/Sell demand
            return OpenBuySellDemand;                     //--- Return Buy/Sell demand
         }
         if (openDemands[i] == OpenBuySellDemand) {       //--- Check if demand is Buy/Sell
            result = OpenBuySellDemand;                   //--- Set Buy/Sell demand
         } else if (result == NoneDemand && openDemands[i] == OpenBuyDemand) { //--- Check Buy demand
            result = OpenBuyDemand;                       //--- Set Buy demand
         } else if (result == NoneDemand && openDemands[i] == OpenSellDemand) { //--- Check Sell demand
            result = OpenSellDemand;                      //--- Set Sell demand
         } else if (result == OpenBuyDemand && openDemands[i] == OpenSellDemand) { //--- Check mixed demands
            result = OpenBuySellDemand;                   //--- Set Buy/Sell demand
         } else if (result == OpenSellDemand && openDemands[i] == OpenBuyDemand) { //--- Check mixed demands
            result = OpenBuySellDemand;                   //--- Set Buy/Sell demand
         }
      }
      return result;                                      //--- Return combined demand
   }

   //--- Combine close demands
   static TradingModuleDemand GetCombinedCloseDemand(TradingModuleDemand &closeDemands[]) {
      TradingModuleDemand result = NoneDemand;            //--- Initialize result
      for (int i = 0; i < ArraySize(closeDemands); i++) { //--- Iterate demands
         if (result == CloseBuySellDemand) {              //--- Check if Buy/Sell demand
            return CloseBuySellDemand;                    //--- Return Buy/Sell demand
         }
         if (closeDemands[i] == CloseBuySellDemand) {     //--- Check if demand is Buy/Sell
            result = CloseBuySellDemand;                  //--- Set Buy/Sell demand
         } else if (result == NoneDemand && closeDemands[i] == CloseBuyDemand) { //--- Check Buy demand
            result = CloseBuyDemand;                      //--- Set Buy demand
         } else if (result == NoneDemand && closeDemands[i] == CloseSellDemand) { //--- Check Sell demand
            result = CloseSellDemand;                     //--- Set Sell demand
         } else if (result == CloseBuyDemand && closeDemands[i] == CloseSellDemand) { //--- Check mixed demands
            result = CloseBuySellDemand;                  //--- Set Buy/Sell demand
         } else if (result == CloseSellDemand && closeDemands[i] == CloseBuyDemand) { //--- Check mixed demands
            result = CloseBuySellDemand;                  //--- Set Buy/Sell demand
         }
      }
      return result;                                      //--- Return combined demand
   }

   //--- Retrieve number of open orders
   int GetNumberOfOpenOrders(Wallet* wallet) {
      return wallet.GetOpenOrders().Count();              //--- Return open order count
   }
};

我们利用"TradeStrategy"类构建交易策略框架,分别将”ITradeStrategyCloseModule”对象存入"CloseModules”中, 将"ITradeStrategyModule”对象存入”_preventOpenModules"中,并将唯一的"ITradeStrategyOpenModule"存入"_openModule”中。我们在“TradeStrategy”类的构造函数中初始化“_openModule”,并在析构函数中清理模块。我们的“Evaluate”函数通过“GetOpenOrders”评估未成交订单,使用“EvaluatePreventOpenModules”和“GetCombinedPreventOpenDemand”评估“_preventOpenModules”,并使用“EvaluateCloseModules”和“GetCombinedCloseDemand”处理“CloseModules”。

我们的“Evaluate”函数通过“GetOpenOrders”评估未成交订单,使用“EvaluatePreventOpenModules”和“GetCombinedPreventOpenDemand”评估“_preventOpenModules”,并使用“EvaluateCloseModules”和“GetCombinedCloseDemand”处理“CloseModules”。我们使用“RegisterPreventOpenModule”和“RegisterCloseModule”注册模块。“ModuleCalculationsBase”类通过“CalculateOrderCollectionProfit”和“CalculateOrderProfit”计算利润,而“OpenModuleBase”类通过“OpenOrder”创建订单,并使用“GetTradeActions”确定操作,通过“GetCombinedOpenDemand”和“GetCombinedCloseDemand”合并需求,以构建一个连贯的系统。

我们现在已准备好进行交易,为防止过度交易,我们可以通过设置外部变量来控制每个实例的最大持仓数量。

//--- Define input for maximum open orders
input int MaxNumberOfOpenOrders1 = 1;                     //--- Set maximum number of open orders (default: 1)

在设置好交易数量限制变量后,我们现在可以定义一个来同时开启多个模块。

//--- Define class for multiple open module
class MultipleOpenModule_1 : public OpenModuleBase {
protected:
   TradingModuleDemand previousSignalDemand;              //--- Store previous signal demand

public:
   //--- Initialize multiple open module
   void MultipleOpenModule_1(AdvisorStrategy* advisorStrategy, MoneyManager* moneyManager)
      : OpenModuleBase(advisorStrategy, moneyManager) {
      _advisorStrategy.SetFireOnlyWhenReset(true);         //--- Configure signals to fire only when reset
   }

   //--- Evaluate and act on signals
   TradingModuleDemand Evaluate(Wallet* wallet, TradingModuleDemand preventOpenDemand, int level) {
      TradingModuleDemand newSignalsDemand = EvaluateSignals(wallet, preventOpenDemand, level); //--- Evaluate signals
      if (newSignalsDemand != NoneDemand) {                //--- Check if demand exists
         EvaluateOpenConditions(wallet, newSignalsDemand); //--- Evaluate open conditions
      }
      return newSignalsDemand;                             //--- Return new signal demand
   }

   //--- Evaluate open signals without acting
   TradingModuleDemand EvaluateOpenSignals(Wallet* wallet, TradingModuleDemand preventOpenDemand, int requestedEvaluationLevel) {
      TradingModuleDemand openDemands[];                   //--- Declare open demands array
      TradeAction tradeActionsToEvaluate[];                //--- Declare actions to evaluate
      GetTradeActions(wallet, preventOpenDemand, tradeActionsToEvaluate); //--- Retrieve actions
      AddPreviousDemandTradeActionIfMissing(tradeActionsToEvaluate); //--- Add previous demand actions
      int level;                                           //--- Declare level
      if (requestedEvaluationLevel == 0) {                 //--- Check if level unspecified
         level = _moneyManager.GetNextLevel(wallet);       //--- Set level based on orders
      } else {
         level = requestedEvaluationLevel;                 //--- Use specified level
      }
      for (int i = 0; i < ArraySize(tradeActionsToEvaluate); i++) { //--- Iterate actions
         if (tradeActionsToEvaluate[i] == CloseBuyAction || tradeActionsToEvaluate[i] == CloseSellAction) { //--- Skip close actions
            continue;                                      //--- Move to next
         }
         if (requestedEvaluationLevel == 0) {              //--- Check if level unspecified
            level = GetTopLevel(tradeActionsToEvaluate[i], level); //--- Cap level
            if (wallet.GetOpenOrders().Count() >= MaxNumberOfOpenOrders1) { //--- Check order limit
               level += 1;                                 //--- Increment level
            }
         }
         if (_advisorStrategy.GetAdvice(tradeActionsToEvaluate[i], level)) { //--- Check if action advised
            if (tradeActionsToEvaluate[i] == OpenBuyAction) { //--- Check Buy action
               int size = ArraySize(openDemands);             //--- Get current size
               int newSize = size + 1;                        //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);          //--- Resize array
               openDemands[newSize - 1] = OpenBuyDemand;      //--- Add Buy demand
            } else if (tradeActionsToEvaluate[i] == OpenSellAction) { //--- Check Sell action
               int size = ArraySize(openDemands);             //--- Get current size
               int newSize = size + 1;                        //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);          //--- Resize array
               openDemands[newSize - 1] = OpenSellDemand;     //--- Add Sell demand
            }
         }
      }
      TradingModuleDemand combinedOpenSignalDemand = OpenModuleBase::GetCombinedOpenDemand(openDemands); //--- Combine open demands
      TradingModuleDemand multiOrderOpenSignal = GetOpenDemandBasedOnPreviousOpenDemand(combinedOpenSignalDemand, level - 1); //--- Adjust for previous demand
      multiOrderOpenSignal = FilterPreventOpenDemand(multiOrderOpenSignal, preventOpenDemand); //--- Filter prevent-open demands
      return multiOrderOpenSignal;                        //--- Return final open signal
   }

   //--- Retrieve trade actions (custom for multiple orders)
   void GetTradeActions(Wallet* wallet, TradingModuleDemand preventOpenDemand, TradeAction& result[]) {
      if (wallet.GetOpenOrders().Count() > 0) {             //--- Check if open orders exist
         Order* firstOrder = wallet.GetOpenOrders().Get(0); //--- Get first open order
         if (firstOrder.Type == ORDER_TYPE_BUY) {           //--- Check if Buy order
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[0] = OpenBuyAction;                      //--- Add open Buy action
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[1] = CloseBuyAction;                     //--- Add close Buy action
         } else if (firstOrder.Type == ORDER_TYPE_SELL) {   //--- Check if Sell order
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[0] = OpenSellAction;                     //--- Add open Sell action
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[1] = CloseSellAction;                    //--- Add close Sell action
         } else {
            Alert("Unsupported ordertype");                 //--- Log error
         }
      } else {
         ArrayResize(result, ArraySize(result) + 1, 8);     //--- Resize array
         result[0] = OpenBuyAction;                         //--- Add open Buy action
         ArrayResize(result, ArraySize(result) + 1, 8);     //--- Resize array
         result[1] = OpenSellAction;                        //--- Add open Sell action
      }
   }

   //--- Evaluate close signals
   TradingModuleDemand EvaluateCloseSignals(Wallet* wallet, TradingModuleDemand preventOpenDemand) {
      TradingModuleDemand closeDemands[];                 //--- Declare close demands array
      TradeAction tradeActionsToEvaluate[];               //--- Declare actions to evaluate
      GetTradeActions(wallet, preventOpenDemand, tradeActionsToEvaluate); //--- Retrieve actions
      for (int i = 0; i < ArraySize(tradeActionsToEvaluate); i++) { //--- Iterate actions
         if (tradeActionsToEvaluate[i] != CloseBuyAction && tradeActionsToEvaluate[i] != CloseSellAction) { //--- Skip non-close actions
            continue;                                     //--- Move to next
         }
         if (_advisorStrategy.GetAdvice(tradeActionsToEvaluate[i], 1)) { //--- Check if action advised (level 1)
            if (tradeActionsToEvaluate[i] == CloseBuyAction) { //--- Check Buy close
               int size = ArraySize(closeDemands);          //--- Get current size
               int newSize = size + 1;                      //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);       //--- Resize array
               closeDemands[newSize - 1] = CloseBuyDemand;  //--- Add Buy close demand
            } else if (tradeActionsToEvaluate[i] == CloseSellAction) { //--- Check Sell close
               int size = ArraySize(closeDemands);          //--- Get current size
               int newSize = size + 1;                      //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);       //--- Resize array
               closeDemands[newSize - 1] = CloseSellDemand; //--- Add Sell close demand
            }
         }
      }
      TradingModuleDemand combinedCloseSignalDemand = OpenModuleBase::GetCombinedCloseDemand(closeDemands); //--- Combine close demands
      return combinedCloseSignalDemand;                   //--- Return combined demand
   }

private:
   //--- Evaluate open and close signals
   TradingModuleDemand EvaluateSignals(Wallet* wallet, TradingModuleDemand preventOpenDemand, int requestedEvaluationLevel) {
      TradingModuleDemand openDemands[];                  //--- Declare open demands array
      TradingModuleDemand closeDemands[];                 //--- Declare close demands array
      TradeAction tradeActionsToEvaluate[];               //--- Declare actions to evaluate
      GetTradeActions(wallet, preventOpenDemand, tradeActionsToEvaluate); //--- Retrieve actions
      AddPreviousDemandTradeActionIfMissing(tradeActionsToEvaluate); //--- Add previous demand actions
      int moneyManagementLevel;                           //--- Declare level
      if (requestedEvaluationLevel == 0) {                //--- Check if level unspecified
         moneyManagementLevel = _moneyManager.GetNextLevel(wallet); //--- Set level based on orders
      } else {
         moneyManagementLevel = requestedEvaluationLevel; //--- Use specified level
      }
      for (int i = 0; i < ArraySize(tradeActionsToEvaluate); i++) { //--- Iterate actions
         int tradeActionEvaluationLevel = GetTopLevel(tradeActionsToEvaluate[i], moneyManagementLevel); //--- Cap level
         if (wallet.GetOpenOrders().Count() >= MaxNumberOfOpenOrders1) { //--- Check order limit
            tradeActionEvaluationLevel += 1;              //--- Increment level
         }
         if (_advisorStrategy.GetAdvice(tradeActionsToEvaluate[i], tradeActionEvaluationLevel)) { //--- Check if action advised
            if (tradeActionsToEvaluate[i] == OpenBuyAction) { //--- Check Buy open
               int size = ArraySize(openDemands);         //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);      //--- Resize array
               openDemands[newSize - 1] = OpenBuyDemand;  //--- Add Buy demand
            } else if (tradeActionsToEvaluate[i] == OpenSellAction) { //--- Check Sell open
               int size = ArraySize(openDemands);         //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);      //--- Resize array
               openDemands[newSize - 1] = OpenSellDemand; //--- Add Sell demand
            } else if (tradeActionsToEvaluate[i] == CloseBuyAction) { //--- Check Buy close
               int size = ArraySize(closeDemands);        //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);     //--- Resize array
               closeDemands[newSize - 1] = CloseBuyDemand; //--- Add Buy close demand
            } else if (tradeActionsToEvaluate[i] == CloseSellAction) { //--- Check Sell close
               int size = ArraySize(closeDemands);        //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);     //--- Resize array
               closeDemands[newSize - 1] = CloseSellDemand; //--- Add Sell close demand
            }
         }
      }
      TradingModuleDemand combinedCloseSignalDemand = OpenModuleBase::GetCombinedCloseDemand(closeDemands); //--- Combine close demands
      if (combinedCloseSignalDemand != NoneDemand) {       //--- Check if close demand exists
         return combinedCloseSignalDemand;                //--- Return close demand
      }
      TradingModuleDemand combinedOpenSignalDemand = OpenModuleBase::GetCombinedOpenDemand(openDemands); //--- Combine open demands
      TradingModuleDemand multiOrderOpenSignal = GetOpenDemandBasedOnPreviousOpenDemand(combinedOpenSignalDemand, GetNumberOfOpenOrders(wallet)); //--- Adjust for previous demand
      previousSignalDemand = combinedOpenSignalDemand;    //--- Update previous demand
      multiOrderOpenSignal = FilterPreventOpenDemand(multiOrderOpenSignal, preventOpenDemand); //--- Filter prevent-open demands
      return multiOrderOpenSignal;                        //--- Return final signal
   }

   //--- Evaluate open conditions and add orders
   void EvaluateOpenConditions(Wallet* wallet, TradingModuleDemand signalDemand) {
      if (signalDemand == OpenBuySellDemand) {            //--- Check Buy/Sell demand
         return;                                          //--- Exit (hedging not supported)
      } else {
         double currentFreeMargin = AccountFreeMargin_LibFunc(); //--- Retrieve free margin
         double requiredMargin;                           //--- Declare required margin
         if (signalDemand == OpenBuyDemand) {             //--- Check Buy demand
            if (!MarginRequired(ORDER_TYPE_BUY, _moneyManager.GetLotSize(), requiredMargin)) { //--- Check margin
               return;                                    //--- Exit if margin check fails
            }
            if (currentFreeMargin < requiredMargin) {     //--- Check sufficient margin
               HandleErrors("Not enough free margin to open buy order with requested volume."); //--- Log error
               return;                                    //--- Exit
            }
            wallet.GetPendingOpenOrders().Add(OpenOrder(ORDER_TYPE_BUY, false)); //--- Add Buy order
         } else if (signalDemand == OpenSellDemand) {     //--- Check Sell demand
            if (!MarginRequired(ORDER_TYPE_SELL, _moneyManager.GetLotSize(), requiredMargin)) { //--- Check margin
               return;                                    //--- Exit if margin check fails
            }
            if (currentFreeMargin < requiredMargin) {     //--- Check sufficient margin
               HandleErrors("Not enough free margin to open sell order with requested volume."); //--- Log error
               return;                                    //--- Exit
            }
            wallet.GetPendingOpenOrders().Add(OpenOrder(ORDER_TYPE_SELL, false)); //--- Add Sell order
         }
      }
   }

   //--- Adjust open demand based on previous demand
   TradingModuleDemand GetOpenDemandBasedOnPreviousOpenDemand(TradingModuleDemand openDemand, int numberOfOpenOrders) {
      if (numberOfOpenOrders == 0 || previousSignalDemand == NoneDemand) { //--- Check no orders or no previous demand
         return openDemand;                               //--- Return current demand
      }
      if (previousSignalDemand == OpenBuyDemand && openDemand == OpenSellDemand) { //--- Check Buy to Sell switch
         return openDemand;                               //--- Allow Sell demand
      } else if (previousSignalDemand == OpenSellDemand && openDemand == OpenBuyDemand) { //--- Check Sell to Buy switch
         return openDemand;                               //--- Allow Buy demand
      }
      return NoneDemand;                                  //--- Block same-direction or mixed demands
   }

private:
   //--- Cap evaluation level
   int GetTopLevel(TradeAction tradeAction, int level) {
      int numberOfExpressions = _advisorStrategy.GetNumberOfExpressions(tradeAction); //--- Retrieve expression count
      if (level > numberOfExpressions) {                  //--- Check if level exceeds expressions
         level = numberOfExpressions;                     //--- Cap level
      }
      return level;                                       //--- Return capped level
   }

   //--- Add previous demand action if missing
   void AddPreviousDemandTradeActionIfMissing(TradeAction& result[]) {
      if (previousSignalDemand == NoneDemand) {           //--- Check if no previous demand
         return;                                          //--- Exit
      }
      bool foundPreviousDemand = false;                   //--- Initialize found flag
      if (previousSignalDemand == OpenBuyDemand) {        //--- Check Buy demand
         AddPreviousDemandTradeAction(result, OpenBuyDemand, OpenBuyAction); //--- Add Buy action
      } else if (previousSignalDemand == OpenSellDemand) { //--- Check Sell demand
         AddPreviousDemandTradeAction(result, OpenSellDemand, OpenSellAction); //--- Add Sell action
      } else if (previousSignalDemand == OpenBuySellDemand) { //--- Check Buy/Sell demand
         AddPreviousDemandTradeAction(result, OpenBuyDemand, OpenBuyAction); //--- Add Buy action
         AddPreviousDemandTradeAction(result, OpenSellDemand, OpenSellAction); //--- Add Sell action
      }
   }

   //--- Add specific previous demand action
   void AddPreviousDemandTradeAction(TradeAction& result[], TradingModuleDemand demand, TradeAction action) {
      bool foundPreviousDemand = false;                   //--- Initialize found flag
      if (previousSignalDemand == demand) {               //--- Check matching demand
         for (int i = 0; i < ArraySize(result); i++) {    //--- Iterate actions
            if (action == result[i]) {                    //--- Check if action exists
               foundPreviousDemand = true;                //--- Set found flag
            }
         }
         if (!foundPreviousDemand) {                      //--- Check if action missing
            ArrayResize(result, ArraySize(result) + 1, 8); //--- Resize array
            result[ArraySize(result) - 1] = action;       //--- Add action
         }
      }
   }

   //--- Filter prevent-open demands
   TradingModuleDemand FilterPreventOpenDemand(TradingModuleDemand multiOrderOpendDemand, TradingModuleDemand preventOpenDemand) {
      if (multiOrderOpendDemand == NoneDemand) {          //--- Check no demand
         return multiOrderOpendDemand;                    //--- Return no demand
      } else if (multiOrderOpendDemand == OpenBuyDemand && (preventOpenDemand == NoBuyDemand || preventOpenDemand == NoOpenDemand)) { //--- Check blocked Buy
         return NoneDemand;                               //--- Block Buy demand
      } else if (multiOrderOpendDemand == OpenSellDemand && (preventOpenDemand == NoSellDemand || preventOpenDemand == NoOpenDemand)) { //--- Check blocked Sell
         return NoneDemand;                               //--- Block Sell demand
      } else if (multiOrderOpendDemand == OpenBuySellDemand) { //--- Check Buy/Sell demand
         if (preventOpenDemand == NoBuyDemand) {          //--- Check no Buy
            return OpenSellDemand;                        //--- Allow Sell demand
         } else if (preventOpenDemand == NoSellDemand) {  //--- Check no Sell
            return OpenBuyDemand;                         //--- Allow Buy demand
         } else if (preventOpenDemand == NoOpenDemand) {  //--- Check no open
            return NoneDemand;                            //--- Block all demands
         }
      }
      return multiOrderOpendDemand;                       //--- Return unfiltered demand
   }
};

在此阶段,我们基于“OpenModuleBase”类开发了一个用于管理多次开仓的模块——“MultipleOpenModule_1”类。我们通过“MultipleOpenModule_1” 构造函数对其进行初始化,将“_advisorStrategy”设置为仅在通过“SetFireOnlyWhenReset”重置后触发信号,并将之前的需求存储在“previousSignalDemand”中。我们的“Evaluate”函数使用“EvaluateSignals”评估信号,并通过“EvaluateOpenConditions”对有效需求进行处理,最终返回该需求。

在“EvaluateOpenSignals”中,我们调用"GetTradeActions"获取操作, 利用"AddPreviousDemandTradeActionIfMissing"添加先前需求,并依据"_moneyManager"的"GetNextLevel"或指定值设定交易级别。我们遍历操作,跳过“CloseBuyAction”和“CloseSellAction”,使用“GetTopLevel”限制交易级别,并通过“GetOpenOrders”和“MaxNumberOfOpenOrders1”检查订单数量限制。来自“_advisorStrategy”的“GetAdvice”的有效信号会将“OpenBuyDemand”或“OpenSellDemand”添加到数组中,与“GetCombinedOpenDemand”合并,通过“GetOpenDemandBasedOnPreviousOpenDemand”进行调整,并通过“FilterPreventOpenDemand”进行过滤。

“EvaluateCloseSignals”函数以类似方式评估“CloseBuyAction”和“CloseSellAction”,添加“CloseBuyDemand”或“CloseSellDemand”,并与“GetCombinedCloseDemand”合并。

在“EvaluateOpenConditions”中,我们使用“AccountFreeMargin_LibFunc”和“MarginRequired”验证账户可用保证金,如果保证金充足,则通过“OpenOrder”添加买入或卖出订单,否则使用“HandleErrors”记录错误。我们采用类似的逻辑来平掉未平仓订单、设置止盈和止损,并具备相应的输入参数。执行后,我们现有以下输入参数。

控制输入

现在,我们可以创建一个最终类来处理所有已实现的功能执行与管理。为实现这一目标,我们采用以下逻辑:

//--- Define interface for trader
interface ITrader {
   void HandleTick();                                     //--- Handle tick event
   void Init();                                           //--- Initialize trader
   Wallet* GetWallet();                                   //--- Retrieve wallet
};

//--- Declare global trader pointer
ITrader *_ea;                                             //--- Store EA instance

//--- Define main Expert Advisor class
class EA : public ITrader {
private:
   bool _firstTick;                                       //--- Track first tick
   TradeStrategy* _tradeStrategy;                         //--- Store trade strategy
   AdvisorStrategy* _advisorStrategy;                     //--- Store advisor strategy
   IMoneyManager* _moneyManager;                          //--- Store money manager
   Wallet* _wallet;                                       //--- Store wallet

public:
   //--- Initialize EA
   void EA() {
      _firstTick = true;                                  //--- Set first tick flag
      _wallet = new Wallet();                             //--- Create wallet
      _wallet.SetLastClosedOrdersByTimeframe(DisplayOrderDuringTimeframe); //--- Set closed orders timeframe
      _advisorStrategy = new AdvisorStrategy();           //--- Create advisor strategy
      _advisorStrategy.RegisterOpenBuy(new ASOpenBuyLevel1(), 1); //--- Register Buy signal
      _advisorStrategy.RegisterOpenSell(new ASOpenSellLevel1(), 1); //--- Register Sell signal
      _moneyManager = new MoneyManager(_wallet);          //--- Create money manager
      _tradeStrategy = new TradeStrategy(new MultipleOpenModule_1(_advisorStrategy, _moneyManager)); //--- Create trade strategy
      _tradeStrategy.RegisterCloseModule(new TakeProfitCloseModule_1()); //--- Register TP module
      _tradeStrategy.RegisterCloseModule(new StopLossCloseModule_1()); //--- Register SL module
   }

   //--- Destructor to clean up EA
   void ~EA() {
      delete(_tradeStrategy);                             //--- Delete trade strategy
      delete(_moneyManager);                              //--- Delete money manager
      delete(_advisorStrategy);                           //--- Delete advisor strategy
      delete(_wallet);                                    //--- Delete wallet
   }

   //--- Initialize EA components
   void Init() {
      IsDemoLiveOrVisualMode = !MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE); //--- Set mode flag
      UnitsOneLot = MarketInfo_LibFunc(Symbol(), MODE_LOTSIZE); //--- Set lot size
      SetOrderGrouping();                                 //--- Configure order grouping
      _wallet.LoadOrdersFromBroker();                     //--- Load orders from broker
   }

   //--- Handle tick event
   void HandleTick() {
      if (MQLInfoInteger(MQL_TESTER) == 0) {              //--- Check if not in tester
         SyncOrders();                                    //--- Synchronize orders
      }
      if (AllowManualTPSLChanges) {                       //--- Check if manual TP/SL allowed
         SyncManualTPSLChanges();                         //--- Synchronize manual TP/SL
      }
      AskFunc.Evaluate();                                 //--- Update Ask price
      BidFunc.Evaluate();                                 //--- Update Bid price
      UpdateOrders();                                     //--- Update order profits
      if (!StopEA) {                                      //--- Check if EA not stopped
         _wallet.HandleTick();                            //--- Handle wallet tick
         _advisorStrategy.HandleTick();                   //--- Handle strategy tick
         if (_wallet.GetPendingOpenOrders().Count() == 0 && _wallet.GetPendingCloseOrders().Count() == 0) { //--- Check no pending orders
            _tradeStrategy.Evaluate(_wallet);             //--- Evaluate strategy
         }
         if (ExecutePendingCloseOrders()) {               //--- Execute close orders
            if (!ExecutePendingOpenOrders()) {            //--- Execute open orders
               HandleErrors(StringFormat("Open (all) order(s) failed. Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
            }
         } else {
            HandleErrors(StringFormat("Close (all) order(s) failed! Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
         }
      } else {
         if (ExecutePendingCloseOrders()) {               //--- Execute close orders
            _wallet.SetAllOpenOrdersToPendingClose();     //--- Move open orders to pending close
         } else {
            HandleErrors(StringFormat("Close (all) order(s) failed! Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
         }
      }
      if (_firstTick) {                                   //--- Check if first tick
         _firstTick = false;                              //--- Clear first tick flag
      }
   }

   //--- Retrieve wallet
   Wallet* GetWallet() {
      return _wallet;                                     //--- Return wallet
   }

private:
   //--- Configure order grouping
   void SetOrderGrouping() {
      int size = ArraySize(_tradeStrategy.CloseModules);  //--- Get close modules size
      ORDER_GROUP_TYPE groups[];                          //--- Declare groups array
      ArrayResize(groups, size);                          //--- Resize array
      for (int i = 0; i < ArraySize(_tradeStrategy.CloseModules); i++) { //--- Iterate modules
         groups[i] = _tradeStrategy.CloseModules[i].GetOrderGroupingType(); //--- Set grouping type
      }
      _wallet.ActivateOrderGroups(groups);                //--- Activate groups
   }

   //--- Synchronize orders with broker
   void SyncOrders() {
      OrderCollection* currentOpenOrders = OrderRepository::GetOpenOrders(MagicNumber, NULL, Symbol()); //--- Retrieve open orders
      if (currentOpenOrders.Count() != (_wallet.GetOpenOrders().Count() + _wallet.GetPendingCloseOrders().Count())) { //--- Check order mismatch
         Print("(Manual) orderchanges detected" + " (found in MT: " + IntegerToString(currentOpenOrders.Count()) + " and in wallet: " + IntegerToString(_wallet.GetOpenOrders().Count()) + "), resetting EA, loading open orders."); //--- Log mismatch
         _wallet.ResetOpenOrders();                       //--- Reset open orders
         _wallet.ResetPendingOrders();                    //--- Reset pending orders
         _wallet.LoadOrdersFromBroker();                  //--- Reload orders
      }
      delete(currentOpenOrders);                          //--- Delete orders collection
   }

   //--- Synchronize manual TP/SL changes
   void SyncManualTPSLChanges() {
      _wallet.GetOpenOrders().Rewind();                   //--- Reset orders iterator
      while (_wallet.GetOpenOrders().HasNext()) {         //--- Iterate orders
         Order* order = _wallet.GetOpenOrders().Next();   //--- Get order
         uint lineFindResult = ObjectFind(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Find SL line
         if (lineFindResult != UINT_MAX) {                //--- Check if SL line exists
            double currentPosition = ObjectGetDouble(ChartID(), IntegerToString(order.Ticket) + "_SL", OBJPROP_PRICE); //--- Get SL position
            if ((order.StopLossManual == 0 && currentPosition != order.GetClosestSL()) || //--- Check manual SL change
                (order.StopLossManual != 0 && currentPosition != order.StopLossManual)) { //--- Check manual SL mismatch
               order.StopLossManual = currentPosition;       //--- Update manual SL
            }
         }
         lineFindResult = ObjectFind(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Find TP line
         if (lineFindResult != UINT_MAX) {                //--- Check if TP line exists
            double currentPosition = ObjectGetDouble(ChartID(), IntegerToString(order.Ticket) + "_TP", OBJPROP_PRICE); //--- Get TP position
            if ((order.TakeProfitManual == 0 && currentPosition != order.GetClosestTP()) || //--- Check manual TP change
                (order.TakeProfitManual != 0 && currentPosition != order.TakeProfitManual)) { //--- Check manual TP mismatch
               order.TakeProfitManual = currentPosition;     //--- Update manual TP
            }
         }
      }
   }

   //--- Update order profits
   void UpdateOrders() {
      _wallet.GetOpenOrders().Rewind();                   //--- Reset orders iterator
      while (_wallet.GetOpenOrders().HasNext()) {         //--- Iterate orders
         Order* order = _wallet.GetOpenOrders().Next();   //--- Get order
         double pipsProfit = order.CalculateProfitPips(); //--- Calculate profit
         order.CurrentProfitPips = pipsProfit;            //--- Update current profit
         if (pipsProfit < order.LowestProfitPips) {       //--- Check if lowest profit
            order.LowestProfitPips = pipsProfit;          //--- Update lowest profit
         } else if (pipsProfit > order.HighestProfitPips) { //--- Check if highest profit
            order.HighestProfitPips = pipsProfit;         //--- Update highest profit
         }
      }
   }

   //--- Execute pending close orders
   bool ExecutePendingCloseOrders() {
      OrderCollection* pendingCloseOrders = _wallet.GetPendingCloseOrders(); //--- Retrieve pending close orders
      int ordersToCloseCount = pendingCloseOrders.Count(); //--- Get count
      if (ordersToCloseCount == 0) {                      //--- Check if no orders
         return true;                                     //--- Return true
      }
      if (_wallet.AreOrdersBeingOpened()) {               //--- Check if orders being opened
         return true;                                     //--- Return true
      }
      int ordersCloseSuccessCount = 0;                    //--- Initialize success count
      for (int i = ordersToCloseCount - 1; i >= 0; i--) { //--- Iterate orders
         Order* pendingCloseOrder = pendingCloseOrders.Get(i); //--- Get order
         if (pendingCloseOrder.IsAwaitingDealExecution) { //--- Check if awaiting execution
            ordersCloseSuccessCount++;                    //--- Increment success count
            continue;                                     //--- Move to next
         }
         bool success;                                    //--- Declare success flag
         if (AccountMarginMode == ACCOUNT_MARGIN_MODE_RETAIL_NETTING) { //--- Check netting mode
            Order* reversedOrder = new Order(pendingCloseOrder, false); //--- Create reversed order
            reversedOrder.Type = pendingCloseOrder.Type == ORDER_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; //--- Set opposite type
            success = OrderRepository::OpenOrder(reversedOrder); //--- Open reversed order
            if (success) {                                //--- Check if successful
               pendingCloseOrder.Ticket = reversedOrder.Ticket; //--- Update ticket
            }
            delete(reversedOrder);                        //--- Delete reversed order
         } else {
            success = OrderRepository::ClosePosition(pendingCloseOrder); //--- Close position
         }
         if (success) {                                   //--- Check if successful
            ordersCloseSuccessCount++;                    //--- Increment success count
         }
      }
      return ordersCloseSuccessCount == ordersToCloseCount; //--- Return true if all successful
   }

   //--- Execute pending open orders
   bool ExecutePendingOpenOrders() {
      OrderCollection* pendingOpenOrders = _wallet.GetPendingOpenOrders(); //--- Retrieve pending open orders
      int ordersToOpenCount = pendingOpenOrders.Count();  //--- Get count
      if (ordersToOpenCount == 0) {                       //--- Check if no orders
         return true;                                     //--- Return true
      }
      int ordersOpenSuccessCount = 0;                     //--- Initialize success count
      for (int i = ordersToOpenCount - 1; i >= 0; i--) {  //--- Iterate orders
         Order* order = pendingOpenOrders.Get(i);         //--- Get order
         if (order.IsAwaitingDealExecution) {             //--- Check if awaiting execution
            ordersOpenSuccessCount++;                     //--- Increment success count
            continue;                                     //--- Move to next
         }
         bool isTradeContextFree = false;                 //--- Initialize trade context flag
         double StartWaitingTime = GetTickCount();        //--- Start timer
         while (true) {                                   //--- Wait for trade context
            if (MQL5InfoInteger(MQL5_TRADE_ALLOWED)) {    //--- Check if trade allowed
               isTradeContextFree = true;                 //--- Set trade context free
               break;                                     //--- Exit loop
            }
            int MaxWaiting_sec = 10;                      //--- Set max wait time
            if (IsStopped()) {                            //--- Check if EA stopped
               HandleErrors("The expert was stopped by a user action."); //--- Log error
               break;                                     //--- Exit loop
            }
            if (GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { //--- Check if timeout
               HandleErrors(StringFormat("The (%d seconds) waiting time exceeded. Trade not allowed: EA disabled, market closed or trade context still not free.", MaxWaiting_sec)); //--- Log error
               break;                                     //--- Exit loop
            }
            Sleep(100);                                   //--- Wait briefly
         }
         if (!isTradeContextFree) {                       //--- Check if trade context not free
            if (!_wallet.CancelPendingOpenOrder(order)) { //--- Attempt to cancel order
               HandleErrors("Failed to cancel an order (because it couldn't open). Please see the Journal and Expert tab in Metatrader for more information."); //--- Log error
            }
            continue;                                     //--- Move to next
         }
         bool success = OrderRepository::OpenOrder(order); //--- Open order
         if (success) {                                    //--- Check if successful
            ordersOpenSuccessCount++;                      //--- Increment success count
         } else {
            if (!_wallet.CancelPendingOpenOrder(order)) {  //--- Attempt to cancel order
               HandleErrors("Failed to cancel an order (because it couldn't open). Please see the Journal and Expert tab in Metatrader for more information."); //--- Log error
            }
         }
      }
      return ordersOpenSuccessCount == ordersToOpenCount;  //--- Return true if all successful
   }
};

最后,我们使用实现“ITrader”接口(包含“HandleTick”、“Init”和“GetWallet”函数)的“EA”构建核心逻辑控制模块。您现在应该已充分了解这些我们已实现的功能。我们定义私有变量“_firstTick”、“_tradeStrategy”、“_advisorStrategy”、“_moneyManager”和“_wallet”,用于管理状态和组件。在“EA”类的构造函数中,我们将“_firstTick”设置为true,使用新的“Wallet”对象初始化“_wallet”,通过“SetLastClosedOrdersByTimeframe”配置其时间框架,并创建“_advisorStrategy”对象(新的“AdvisorStrategy”实例),同时使用“RegisterOpenBuy”和“RegisterOpenSell”注册“ASOpenBuyLevel1”和“ASOpenSellLevel1”信号。

我们还通过“MoneyManager”对象实例化“_moneyManager”,并通过包含“MultipleOpenModule_1”的“TradeStrategy”对象实例化“_tradeStrategy”,同时通过“RegisterCloseModule”注册“TakeProfitCloseModule_1”和“StopLossCloseModule_1”。析构函数负责清理所有组件。

我们的“Init”函数使用MQLInfoIntegerr设置模式标识,通过“MarketInfo_LibFunc”配置交易手数,调用“SetOrderGrouping”以使用“GetOrderGroupingType”激活订单分组,并通过“LoadOrdersFromBroker”加载订单。“HandleTick”函数负责处理tick数据,如果不在测试模式下,通过“SyncOrders”同步订单;如果允许,则通过“SyncManualTPSLChanges”更新手动设置的止盈/止损;并且使用“AskFunc”和“BidFunc”的“Evaluate”函数刷新价格。

该函数通过“UpdateOrders”更新订单利润,通过“HandleTick”处理钱包和策略的行情数据,如果没有待处理订单,则通过“Evaluate”评估“_tradeStrategy”,并通过“ExecutePendingCloseOrders”和“ExecutePendingOpenOrders”执行待处理的平仓和开仓操作,如果操作失败,则通过“HandleErrors”记录错误。如果EA停止运行,则通过“SetAllOpenOrdersToPendingClose”将所有持仓平仓。现在,我们可以在OnTick事件处理器中调用此函数来管理交易。

//--- Handle tick event
datetime LastActionTime = 0;                          //--- Track last action time
void OnTick() {
   string AccountServer = AccountInfoString(ACCOUNT_SERVER);                //--- Retrieve account server
   string AccountCurrency = AccountInfoString(ACCOUNT_CURRENCY);            //--- Retrieve account currency
   string AccountName = AccountInfoString(ACCOUNT_NAME);                    //--- Retrieve account name
   long AccountTradeMode = AccountInfoInteger(ACCOUNT_TRADE_MODE);          //--- Retrieve trade mode
   string ReadableAccountTrademode = "";                                    //--- Initialize readable trade mode
   if (AccountTradeMode == 0) ReadableAccountTrademode = "DEMO ACCOUNT";    //--- Set demo mode
   if (AccountTradeMode == 1) ReadableAccountTrademode = "CONTEST ACCOUNT"; //--- Set contest mode
   if (AccountTradeMode == 2) ReadableAccountTrademode = "REAL ACCOUNT";    //--- Set real mode
   long AccountLogin = AccountInfoInteger(ACCOUNT_LOGIN);                   //--- Retrieve account login
   string AccountCompany = AccountInfoString(ACCOUNT_COMPANY);              //--- Retrieve account company
   long AccountLeverage = AccountInfoInteger(ACCOUNT_LEVERAGE);             //--- Retrieve account leverage
   long AccountLimitOrders = AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);      //--- Retrieve order limit
   double AccountMarginFree = AccountInfoDouble(ACCOUNT_MARGIN_FREE);       //--- Retrieve free margin
   bool AccountTradeAllowed = AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);    //--- Retrieve trade allowed
   bool AccountTradeExpert = AccountInfoInteger(ACCOUNT_TRADE_EXPERT);      //--- Retrieve expert allowed
   string ReadableAccountMarginMode = "";                                   //--- Initialize readable margin mode
   if (AccountMarginMode == 0) ReadableAccountMarginMode = "NETTING MODE";  //--- Set netting mode
   if (AccountMarginMode == 1) ReadableAccountMarginMode = "EXCHANGE MODE"; //--- Set exchange mode
   if (AccountMarginMode == 2) ReadableAccountMarginMode = "HEDGING MODE";  //--- Set hedging mode
   if (OneQuotePerBar) {                                 //--- Check one quote per bar
      datetime currentTime = iTime(_Symbol, _Period, 0); //--- Get current bar time
      if (LastActionTime == currentTime) {               //--- Check if same bar
         return;                                         //--- Exit
      } else {
         LastActionTime = currentTime;                   //--- Update last action time
      }
   }
   Error = NULL;                                         //--- Clear current error
   _ea.HandleTick();                                     //--- Handle tick
   if (IsDemoLiveOrVisualMode) {                         //--- Check visual mode
      MqlDateTime mql_datetime;                          //--- Declare datetime
      TimeCurrent(mql_datetime);                         //--- Get current time
      string comment = "\n" + (string)mql_datetime.year + "." + (string)mql_datetime.mon + "." + (string)mql_datetime.day + "   " + TimeToString(TimeCurrent(), TIME_SECONDS) + OrderInfoComment; //--- Build comment
      if (DisplayOnChartError) {                         //--- Check if error display enabled
         if (Error != NULL) comment += "\n      :: Current error : " + Error; //--- Add current error
         if (ErrorPreviousQuote != NULL) comment += "\n      :: Last error : " + ErrorPreviousQuote; //--- Add previous error
      }
      comment += "";                                     //--- Append empty line
      Comment("ACCOUNT SERVER:  ", AccountServer, "\n",  //--- Display account info
              "ACCOUNT CURRENCY:  ", AccountCurrency, "\n",
              "ACCOUNT NAME:  ", AccountName, "\n",
              "ACCOUNT TRADEMODE:  ", ReadableAccountTrademode, "\n",
              "ACCOUNT LOGIN:  ", AccountLogin, "\n",
              "ACCOUNT COMPANY:  ", AccountCompany, "\n",
              "ACCOUNT LEVERAGE:  ", AccountLeverage, "\n",
              "ACCOUNT LIMIT ORDERS:  ", AccountLimitOrders, "\n",
              "ACCOUNT MARGIN FREE:  ", AccountMarginFree, "\n",
              "ACCOUNT TRADING ALLOWED:  ", AccountTradeAllowed, "\n",
              "ACCOUNT EXPERT ALLOWED:  ", AccountTradeExpert, "\n",
              "ACCOUNT MARGIN ALLOWED:  ", ReadableAccountMarginMode);
   }
}

在此阶段,我们通过OnTick事件处理器来管理程序的实时市场更新,该处理器会对每个tick到来进行处理。我们首先使用相关函数收集账户详细信息,例如使用“AccountInfoString”获取服务器名称、交易货币、账户名称及所属公司,使用AccountInfoInteger获取交易模式、账户登录号、杠杆比例及订单数量限制,以及使用AccountInfoDouble获取账户可用保证金。 我们将交易模式转换为可读字符串,如“DEMO ACCOUNT”(模拟账户)、“CONTEST ACCOUNT”(比赛账户)或“REAL ACCOUNT”(真实账户”),并将存储在“AccountMarginMode”中的保证金模式转换为更清晰的表述,如“NETTING MODE”(净仓模式)、“EXCHANGE MODE”(交易所模式)或“HEDGING MODE”(锁仓模式)。

为控制tick的处理,我们检查“OneQuotePerBar”标识位。如果该标识位已启用,则使用iTime函数获取当前K线的开盘时间,并将其与“LastActionTime”进行比较。如果两者时间相同,则退出当前处理流程以避免重复操作;否则,更新“LastActionTime”值。我们将“Error”变量设为空值以清除任何现有错误,并调用“_ea”对象的“HandleTick”函数来执行策略逻辑。

在显示效果方面,我们先检查是否处于模拟/实盘/可视化模式(根据“IsDemoLiveOrVisualMode”判断),随后使用TimeCurrent获取当前时间,并通过MqlDateTime将其格式化为易读的时间戳,再结合“OrderInfoComment”构建注释字符串。如果启用“DisplayOnChartError”功能,则将“Error”和“ErrorPreviousQuote”中的错误信息追加到注释中。最后,通过Comment函数将账户详细信息及可能的错误信息显示在图表上,为交易系统提供实时监控与反馈支持。程序运行后,输出结果如下:

信号生成

由图可见,我们能够生成并执行一笔交易(此处为买入交易)。当前需要实现的是交易管理功能,而要达成这一目标,必须跟踪交易记录。为此,我们将使用OnTradeTransaction事件处理器来监听成功开仓的交易,并按照以下方式进行管理:

//--- Handle trade transactions
void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) {
   switch (trans.type) {                               //--- Handle transaction type
   case TRADE_TRANSACTION_DEAL_ADD: {                  //--- Handle deal addition
      datetime end = TimeCurrent();                    //--- Get current server time
      datetime start = end - PeriodSeconds(PERIOD_D1); //--- Set start time (1 day ago)
      HistorySelect(start, end + PeriodSeconds(PERIOD_D1)); //--- Select history
      int dealsTotal = HistoryDealsTotal();            //--- Get total deals
      if (dealsTotal == 0) {                           //--- Check if no deals
         Print("No deals found");                      //--- Log message
         return;                                       //--- Exit
      }
      ulong orderTicketId = HistoryDealGetInteger(trans.deal, DEAL_ORDER); //--- Get order ticket
      CDealInfo dealInfo;                              //--- Declare deal info
      dealInfo.Ticket(trans.deal);                     //--- Set deal ticket
      ENUM_DEAL_ENTRY deal_entry = dealInfo.Entry();   //--- Get deal entry type
      bool found = false;                              //--- Initialize found flag
      if (deal_entry == DEAL_ENTRY_IN) {               //--- Handle deal entry
         OrderCollection* pendingOpenOrders = _ea.GetWallet().GetPendingOpenOrders(); //--- Retrieve pending open orders
         for (int i = 0; i < pendingOpenOrders.Count(); i++) { //--- Iterate orders
            Order* order = pendingOpenOrders.Get(i);   //--- Get order
            if (order.Ticket == orderTicketId) {       //--- Check matching ticket
               found = true;                           //--- Set found flag
               order.OpenTime = dealInfo.Time();       //--- Set open time
               order.OpenPrice = trans.price;          //--- Set open price
               order.TradePrice = order.OpenPrice;     //--- Set trade price
               if (OrderFillingType == ORDER_FILLING_FOK) {                 //--- Check FOK filling
                  order.OrderFilledLots += trans.volume;                    //--- Add volume
                  if (MathAbs(order.Lots - order.OrderFilledLots) < 1e-5) { //--- Check if fully filled
                     order.IsAwaitingDealExecution = false;                 //--- Clear execution flag
                     order.Lots = order.OrderFilledLots;                    //--- Update lots
                     order.TradeVolume = order.Lots;                        //--- Update trade volume
                     _ea.GetWallet().SetPendingOpenOrderToOpen(order);      //--- Move to open
                     Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
                  }
               } else {
                  order.IsAwaitingDealExecution = false; //--- Clear execution flag
                  bool actualVolumeDiffers = MathAbs(order.Lots - trans.volume) > 1e-5; //--- Check volume difference
                  order.OrderFilledLots += trans.volume; //--- Add volume
                  order.Lots = order.OrderFilledLots;    //--- Update lots
                  order.TradeVolume = order.Lots;        //--- Update trade volume
                  if (actualVolumeDiffers) {             //--- Check if volume differs
                     Print("Broker executed volume differs from requested volume. Executed volume: " + DoubleToStr(trans.volume)); //--- Log difference
                     OrderRepository::CalculateAndSetCommision(order); //--- Recalculate commission
                  }
                  _ea.GetWallet().SetPendingOpenOrderToOpen(order); //--- Move to open
                  Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
               }
            }
         }
      } else if (deal_entry == DEAL_ENTRY_OUT) {      //--- Handle deal exit
         OrderCollection* pendingCloseOrders = _ea.GetWallet().GetPendingCloseOrders(); //--- Retrieve pending close orders
         for (int i = 0; i < pendingCloseOrders.Count(); i++) { //--- Iterate orders
            Order* order = pendingCloseOrders.Get(i); //--- Get order
            if (order.Ticket == orderTicketId) {      //--- Check matching ticket
               found = true;                          //--- Set found flag
               if (OrderFillingType == ORDER_FILLING_FOK) {   //--- Check FOK filling
                  order.OrderFilledLots += trans.volume;      //--- Add volume
                  if (MathAbs(order.Lots - order.OrderFilledLots) < 1e-5) { //--- Check if fully filled
                     order.IsAwaitingDealExecution = false;   //--- Clear execution flag
                     order.CloseTime = dealInfo.Time();       //--- Set close time
                     order.ClosePrice = trans.price;          //--- Set close price
                     if (order.MagicNumber == MagicNumber) {  //--- Check EA order
                        TotalCommission += order.Commission;  //--- Add commission
                     }
                     if (IsDemoLiveOrVisualMode) {      //--- Check visual mode
                        AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Delete TP line
                        AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Delete SL line
                     }
                     if (order.ParentOrder != NULL) {   //--- Check if parent order
                        order.ParentOrder.Paint();      //--- Redraw parent
                     }
                     _ea.GetWallet().SetPendingCloseOrderToClosed(order); //--- Move to closed
                     Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
                  }
               } else {
                  bool actualVolumeDiffers = MathAbs(order.Lots - trans.volume) > 1e-5; //--- Check volume difference
                  if (actualVolumeDiffers) {                           //--- Handle partial close
                     Print("Broker executed volume differs from requested volume.Requested volume: " + DoubleToStr(order.Lots) + ".Executed volume: " + DoubleToStr(trans.volume)); //--- Log difference
                     Order* remainderOrder = new Order(order, false);  //--- Create remainder order
                     remainderOrder.Ticket = 0;                        //--- Clear ticket
                     remainderOrder.Lots = order.Lots - trans.volume;  //--- Set remaining volume
                     remainderOrder.TradeVolume = remainderOrder.Lots; //--- Update trade volume
                     OrderRepository::CalculateAndSetCommision(remainderOrder); //--- Recalculate commission
                     _ea.GetWallet().GetPendingCloseOrders().Add(remainderOrder); //--- Add remainder
                     order.Lots = trans.volume;                        //--- Update original volume
                     order.TradeVolume = order.Lots;                   //--- Update trade volume
                     OrderRepository::CalculateAndSetCommision(order); //--- Recalculate commission
                  } else {
                     Print("Broker executed volume: " + DoubleToStr(trans.volume)); //--- Log volume
                  }
                  order.IsAwaitingDealExecution = false;   //--- Clear execution flag
                  order.CloseTime = dealInfo.Time();       //--- Set close time
                  order.ClosePrice = trans.price;          //--- Set close price
                  if (order.MagicNumber == MagicNumber) {  //--- Check EA order
                     TotalCommission += order.Commission;  //--- Add commission
                  }
                  if (IsDemoLiveOrVisualMode) {            //--- Check visual mode
                     AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Delete TP line
                     AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Delete SL line
                  }
                  _ea.GetWallet().SetPendingCloseOrderToClosed(order); //--- Move to closed
                  Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
               }
            }
         }
      }
      if (found) {                                         //--- Check if deal found
         Print("Updated order with deal info.");           //--- Log update
      } else if (trans.symbol == Symbol() && dealInfo.Magic() == MagicNumber) { //--- Check EA deal
         Print("Couldn't find deal info for place/done order"); //--- Log missing deal
      }
      break;
   }
   }
}

在处理MqlTradeTransaction事件的OnTradeTransaction事件处理器中,我们重点关注TRADE_TRANSACTION_DEAL_ADD类型的交易。通过TimeCurrent获取当前时间,并结合"PeriodSeconds"参数,使用HistorySelect函数检索最近一天的历史交易记录窗口。如果通过"HistoryDealsTotal"判断未找到任何成交记录,则使用"Print"函数记录日志信息并退出处理流程。对于每笔成交记录,我们使用"HistoryDealGetInteger"函数提取关联的订单编号,并创建包含订单编号的"CDealInfo"对象,随后通过其"Entry"属性检查该笔交易的入场类型。

对于DEAL_ENTRY_IN(新建仓交易),我们遍历“_ea”的“Wallet”中通过“GetPendingOpenOrders”获取的待开仓订单,通过匹配订单编号来更新订单的“Time”和“price”字段,数据来源于交易事件对象“trans”。如果订单的“OrderFillingType”(成交类型)为“ORDER_FILLING_FOK”(全部成交或立即取消),则更新“OrderFilledLots”(已成交手数),如果订单已完全成交,则清除“IsAwaitingDealExecution”(等待成交标识),并通过“SetPendingOpenOrderToOpen”将订单状态转为已开仓。另外,如果订单为部分成交,则通过“Print”记录成交手数差异,并通过“CalculateAndSetCommision”更新相关手续费。

对于“DEAL_ENTRY_OUT”(平仓交易),我们处理“GetPendingCloseOrders”获取的待平仓订单,以类似方式更新匹配订单。如果“IsDemoLiveOrVisualMode”为true,则通过“AnyChartObjectDelete”移除图表上的相关线条;将平仓手续费累加至“TotalCommission”(总手续费),并通过“SetPendingCloseOrderToClosed”将订单状态转为已平仓。部分平仓时,通过“CalculateAndSetCommision”为剩余手数创建新订单。所有更新或错误均通过“Print”记录日志,确保交易跟踪的准确性。编译后,输出如下:

交易执行

由图可见,我们已实现交易执行管理,并在窗口中显示相关基础数据。接下来需对程序进行回测,以确保其正确运行,这部分内容将在下一章节展开。


回测与优化

在测试程序时,我们发现了一个此前忽略的问题:当从图表中移除程序时,未正确释放程序及其组件资源。具体问题如下:

回测问题

由图可见相关问题的具体表现形式。为解决这些问题,我们在OnDeinit事件处理器中实现了以下逻辑:

//--- Deinitialize Expert Advisor
void OnDeinit(const int reason) {
   Comment("");
   delete(_ea);                                        //--- Delete EA instance
   delete(AskFunc);                                    //--- Delete Ask function
   delete(BidFunc);                                    //--- Delete Bid function
}

我们通过OnDeinit事件处理器管理程序的清理流程,该处理程序会在EA被移除或交易终端关闭时触发。首先,我们使用Comment函数清除图表上的所有注释,确保图表显示干净整洁。接着,通过delete操作符删除代表主EA实例的"_ea"对象,释放其占用的内存。同时,我们还删除了用于获取价格数据的"AskFunc"和"BidFunc"对象,以进一步释放系统资源。

这一简洁的清理流程确保了程序在停止运行时能够正确地反初始化,有效避免内存泄漏问题,并维持系统的高效运行。经过全面优化和回测(使用默认参数,并将交易或风险管理模块的参数分别调整为1%、5、30、10、60),我们得到以下结果:

回测图:

图表

回测报告:

报告


结论

综上所述,我们已开发出基于MQL5的包络线趋势反弹剥头皮策略实现方案。基于包络线、移动平均线及相对强弱指数指标生成的精准信号自动执行交易,并集成风险管理模块,实现仓位规模控制及止损保护。我们的模块化框架包含动态信号评估、交易执行和实时订单监控三大核心功能,为短线交易提供了可扩展的技术基础。您可以通过调整信号阈值、优化风险参数或添加其他技术指标,进一步优化该系统。

免责声明:本实现方案仅供教学用途。交易存在重大财务风险,市场波动可能导致严重的资金损失。在实际交易环境中使用本程序前,务必进行充分回测并实施严格的风险管理措施。

通过掌握这些技术,我们既能提升程序的适应性与稳定性,又能基于其架构开发其他交易策略,从而推动程序交易算法的持续优化与升级。


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

使用机器学习开发趋势交易策略 使用机器学习开发趋势交易策略
本研究介绍了一种开发趋势跟踪交易策略的新方法。本节介绍标注训练数据并利用它训练分类器的过程。这个过程获得了可在 MetaTrader 5 上运行的完全可操作的交易系统。
中心引力优化(CFO)算法 中心引力优化(CFO)算法
本文介绍了一种受万有引力定律启发的中心引力优化(CFO)算法。它探讨了物理引力的原理如何解决优化问题,其中“较重”的解决方案会吸引不太成功的对应物。
在 MQL5 中构建自优化智能交易系统(第八部分):多策略分析 在 MQL5 中构建自优化智能交易系统(第八部分):多策略分析
如何才能最有效地整合多种策略,构建一个强大的策略组合?欢迎加入本次讨论,我们将探讨如何将三种不同的策略整合到我们的交易应用程序中。交易员通常会采用专门的策略来开仓和平仓。我们想探究的是,机器能否在这项任务上表现得比人类更出色。我们将首先从熟悉策略测试器的各项功能开始讨论,以及完成此任务所需的面向对象编程(OOP)原则。
您应当知道的 MQL5 向导技术(第 60 部分):推理学习(Wasserstein-VAE),配合移动平均线和随机振荡器形态 您应当知道的 MQL5 向导技术(第 60 部分):推理学习(Wasserstein-VAE),配合移动平均线和随机振荡器形态
我们将目光转向 MA 与随机振荡器的互补配对,实证推理学习在后监督学习与强化学习状况中扮演的角色。显然,推理学习有多种途径可供选择,不过我们的方式是使用变分自编码器。我们先以 Python 探索这些,然后将训练好的模型以 ONNX 格式导出,可在 MetaTrader 中供向导汇编智能系统所用。