English Русский Español Deutsch 日本語 Português
preview
MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库

MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库

MetaTrader 5示例 |
351 0
Wanateki Solutions LTD
Kelvin Muturi Muigua

概述

第一篇文章中,我们详细分析了 MQL5 代码库。我们介绍了不同的库类型、它们的优点、创建 EX5 库以及 EX5 库源代码文件(.mq5)的组成部分。这为您了解 EX5 库及其创建过程打下了坚实的基础。然后,我们创建了一个 EX5 仓位管理库的实用示例,演示如何使用 MQL5 编写可导出函数的代码。

在本文中,我们将继续在此基础上进行建设。我们将扩展仓位管理 EX5 库,并创建两个基本的 EA 交易。其中一个 EA 交易系统示例将使用图形交易和信息面板,演示如何在实践中导入和实现仓位管理 EX5 库。这将作为一个实际示例,说明如何创建 EX5 库并将其集成到 MQL5 代码中。首先,让我们分解一下导入和使用已经开发和编译的.ex5二进制库的过程。


如何导入和实现 EX5 库

要在您的 MQL5 代码(EA 交易、自定义指标、脚本或服务)中导入和使用 .EX5 库,您需要在源代码文件的头部或顶部的 #property 指令下方插入 #import 指令。要包含二进制编译库,首先要指定 #import 指令,然后是存储库的文件路径。默认情况下,MQL5 会在两个位置搜索库,以节省您在代码中直接引用库的时间。第一个位置是 "MQL5/Libraries" 文件夹,这是存储库的默认预定义位置。如果在那里找不到库,MQL5 将搜索 MQL 程序本身所在的文件夹。如果您的 EX5 库直接存储在 "MQL5/Libraries/" 文件夹或与源代码在同一文件夹中,只需在 #import 指令后指定用双引号括起来的库名,而无需指定文件夹路径。

指定库文件夹路径后,提供库名称,然后输入 .ex5 扩展名。在下一行中,添加要导入到代码中的所有导出函数原型的定义或描述。最后,用另一个 #import 指令结束导入代码段。

#import "FilePath/LibraryName.ex5" //-- Opening .EX5 Library import directive

   //-- Function definitions/descriptions prototypes
   int  FunctionPrototype1();
   void FunctionPrototype2(bool y);
   bool FunctionPrototype3(double x);

#import //--- Closing .EX5 Library import directive

声明导入指令时,需要在库名称后指定并提供 .ex5 扩展名。省略扩展名将表示您默认导入的是 .DLL 库。

您还可以在单个 MQL5 文件中导入和实现多个 .ex5 库。导入多个 .ex5 库的代码结构与导入单个库相似,唯一的区别在于结尾 #import 指令的位置。对于多个库的导入,第一个库的结尾 #import 指令后应跟上下一个导入的 .ex5 库的名称。这将关闭第一个导入指令,同时启动下一个导入指令,依此类推。关闭最后一个库的最后一条导入指令时,应确保该指令的结尾没有库名。

#import "FilePath/LibraryName.ex5"  //-- Opening .EX5 Library import directive

   //-- Function definitions/descriptions prototypes for the first library here
   int  FunctionPrototype1();
   void FunctionPrototype2(bool y);
   
#import "FilePath/SecondLibraryName.ex5"
   //-- Function definitions/descriptions prototypes for the second library here
   bool  FunctionPrototype();
   string FunctionPrototype2(bool z);

#import //--- Closing .EX5 Library import directive

在 MQL5 中使用多个库时,需要为每个库指定一个唯一的名称。如果所有这些库都存储在不同的文件夹中,也没关系,需要有不同的名称,以免遇到任何错误。

每个库都创建了自己的独立环境或 "命名空间"。这意味着库中的函数与该库的名称相关联。您可以在库中自由命名函数,而不必担心冲突,即使它们与内置函数名匹配。然而,为了清楚起见,通常建议避免这样的命名。

如果你碰巧在不同的库中有同名函数,系统将根据特定规则对函数进行优先级排序。这可以防止在调用同名函数时产生混淆。一旦你成功导入了库的函数原型,你就可以将它们无缝集成到你的代码中,并像你自己定义的任何其他本地函数一样对待它们。

在本文的后面,我将详细解释如何在实际环境中整合和使用 EX5 库。您将看到两个深入的演示:一个是我们编写基于 VIDyA 交易策略的 EA 交易系统,另一个是利用图形用户界面(GUI)。这些 EA 交易将整合并利用我们定制的仓位管理 EX5 库。这些实践案例将为在实际的 EA 交易中实现 EX5 库提供宝贵的经验。


常见的 EX5 库实现运行时错误

调试 EX5 库具有挑战性,因为与导入的原型函数相关的大多数常见错误都发生在运行期间,即在交易终端加载最终编译的 MQL5 应用程序时。这些错误通常是由于在导入声明期间使用不正确的值(如库文件路径或名称、函数原型描述、类型、名称、完整参数列表和返回值)对顶部导入库指令部分进行编码而引起的。编译器不需要在编译时检测这些导入声明错误,因为它无法访问导入库的源代码,因为它已被封装并编译为二进制可执行格式(.ex5)模块。

任何包含这些错误的源代码文件都将成功编译,但当您尝试在交易终端中加载编译后的 MQL5 应用程序时,它将失败并生成运行时错误。这些错误显示在 MetaTrader5 终端的"专家"选项卡"日志"选项卡中。以下是您可能遇到的最常见错误:

未解析的导入函数调用(无法在 'Library_Name.ex5' 中找到 'Function_Name')。

    • 描述:当尝试在 MetaTrader5 图表中加载 MQL5 应用程序时,会出现此运行时错误,并显示在"专家"选项卡中。这是由于库的导入指令部分提供的函数原型定义或描述(如类型、名称或参数)不正确造成的。
    • 解决方案:确保对库的导入代码段进行正确编码,并按要求使用正确的函数原型定义,然后重新编译代码。

无法解析的导入函数调用 EX5 错误


无法打开文件 'Library_Name.ex5': (加载 ExpertAdvisor_Name (GBPJPY,M15) 失败 [0])

    • 描述:当尝试在 MetaTrader5 图表中加载 MQL5 应用程序时,会出现此运行时错误,并显示在日志选项卡中。原因是无法定位和加载导入的 EX5 库文件。
    • 解决方案:确保在库的导入代码段中指定了正确的库文件路径,并重新编译代码。
无法打开 EX5 库文件错误

虽然在MQL5中使用导入的库时可能会出现其他错误,但对于初学者开发人员来说,上述运行时错误是最常见和最麻烦的。这些错误尤其具有挑战性,因为它们很容易被忽视,并且在编译过程中不会被编译器检测到。


如何更新和重新部署 EX5 库

在每次更新后重新部署 EX5 库时,遵循正确的顺序非常重要,以确保使用库将新更改正确集成到 MQL5 项目中。 编译顺序是 MQL5 中更新和重新部署库的最关键步骤。要确保在导入库的所有项目中使用所有新的更改和更新,请执行以下步骤:

  1. 编译新的 EX5 文件:首先编译更新后的 .mq5 库源代码文件,创建新的 .ex5 可执行二进制文件。
  2. 更新导入的函数原型:在所有使用 EX5 库的 MQL5 项目中,如果函数原型导入定义在新的 .ex5 库更新中发生变化,请更新这些定义。
  3. 编译项目:重新编译所有实现 EX5 库的 MQL5 项目。
按照此顺序,您将确保 EX5 库中的所有更新都反映并集成到导入该库的所有项目中。


追踪止损函数

在实施仓位管理库之前,让我们进一步扩展它,增加一些重要函数。首先,我们将添加跟踪止损管理模块或函数,因为如果没有这一基本函数,我们的程序库将是不完整的。追踪止损是任何交易策略的重要组成部分,因为如果实施得当,它有可能提高系统的利润率和整体成功率。

追踪止损函数将称为 SetTrailingStopLoss(),负责使用仓位编号作为过滤机制,设置现有未平仓位的追踪止损。它将仓位的编号和所需的追踪止损点数(points)作为参数,并尝试在满足特定条件时更新交易服务器上的仓位止损。 由于目标仓位的状态不断变化,因此应在每个交易日连续调用该函数,以实时修改追踪止损。

它将首先检查是否允许交易,以及所提供的以点(points)为单位的追踪止损是否有效。然后,它将选择仓位,检索并保存所有必要的交易品种信息,并计算追踪止损价格。如果计算出的价格有效,它就会向交易服务器发送订单,设置止损。如果订单成功执行,函数将返回 true;否则将返回 false

首先,我们将创建函数定义。我们的追踪止损函数类型为 bool ,需要两个参数:

  1. ulong positionTicket:这是我们要修改的仓位的唯一标识符。
  2. int trailingStopLoss:这是所需的止损价位,与仓位当前价格的差值(points)。

export 关键字表示可以从任何导入该库函数的 MQL5 源代码文件或项目中调用该库函数。

bool SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss) export
  {
    //-- place the function body here
  }

我们需要检查是否允许交易,以及 trailingStopLoss 参数是否大于。如果任一条件未满足,我们将退出函数并返回 false 以终止操作。

if(!TradingIsAllowed() || trailingStopLoss == 0)
     {
      return(false); //--- algo trading is disabled or trailing stop loss is invalid, exit function
     }

接下来,我们使用提供的 positionTicket 确认并选择仓位。如果仓位选择失败,我们将打印错误信息并退出函数。

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(!PositionSelectByTicket(positionTicket))
     {
      //---Position selection failed
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

然后,我们创建一些变量来帮助我们存储和验证追踪止损。首先,我们创建 slPrice 变量来存储计算出的追踪止损价格,然后保存仓位属性,如交易品种、进场价格、成交量、当前止损价格、当前止盈价格和仓位类型。

//-- create variable to store the calculated trailing sl prices to send to the trade server
   double slPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

同样,我们继续保存与所选仓位相关的不同交易品种属性。这些属性稍后还将用于验证和计算追踪止损。

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

我们会检查所提供的追踪止损值是否小于交易品种的交易止损水平。如果是,我们就会调整追踪止损,使其等于交易品种的止损水平。

//-- Check if the trailing stop loss is less than the symbol trade stop levels
   if(trailingStopLoss < symbolStopLevel)
     {
      //-- Trailing stop loss is less than the allowed level for the current symbol
      trailingStopLoss = symbolStopLevel; //-- Set it to the symbol stop level by default
     }

下一步是根据买入或卖出仓位计算追踪止损价格。对于买入仓位,止损价设置在当前价格之下,而对于卖出仓位,止损价设置在当前价格之上。我们还验证了计算出的止损价格在有效范围内。

//-- Calculate and store the trailing stop loss price
   if(positionType == POSITION_TYPE_BUY)
     {
      slPrice = positionPriceCurrent - (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice < entryPrice || slPrice < currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }
   else  //-- SELL POSITION
     {
      slPrice = positionPriceCurrent + (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice > entryPrice || slPrice > currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }

在设置追踪止损之前,让我们先将仓位详情打印到 MetaTrader5 的日志中。这包括交易品种、仓位类型、交易量、入场价格、当前止损价和止盈价以及其他相关信息。

//-- Print position properties before setting the trailing stop loss
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " Trailing Stop Loss Modification Details" +
                               " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Trailing SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

我们将 tradeRequesttradeResult 结构重置为。然后,我们初始化设置止损和止盈所需的参数。

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = currentPositionTpPrice;

最后,我们会重置错误缓存,并将订单发送到交易服务器,直到成功或最多 101 次重试。如果订单成功执行,我们将打印成功信息,返回 true 并退出函数。如果订单请求失败,我们会处理错误,返回 false 并退出函数。

ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully set the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR setting the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }

确保所有 SetTrailingStopLoss() 函数代码段按以下顺序完成:

bool SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss) export
  {
//-- first check if the EA is allowed to trade and the trailing stop loss parameter is more than zero
   if(!TradingIsAllowed() || trailingStopLoss == 0)
     {
      return(false); //--- algo trading is disabled or trailing stop loss is invalid, exit function
     }

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(!PositionSelectByTicket(positionTicket))
     {
      //---Position selection failed
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

//-- create variable to store the calculated trailing sl prices to send to the trade server
   double slPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

//-- Check if the trailing stop loss is less than the symbol trade stop levels
   if(trailingStopLoss < symbolStopLevel)
     {
      //-- Trailing stop loss is less than the allowed level for the current symbol
      trailingStopLoss = symbolStopLevel; //-- Set it to the symbol stop level by default
     }

//-- Calculate and store the trailing stop loss price
   if(positionType == POSITION_TYPE_BUY)
     {
      slPrice = positionPriceCurrent - (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice < entryPrice || slPrice < currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }
   else  //-- SELL POSITION
     {
      slPrice = positionPriceCurrent + (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice > entryPrice || slPrice > currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }

//-- Print position properties before setting the trailing stop loss
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " Trailing Stop Loss Modification Details" +
                               " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Trailing SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = currentPositionTpPrice;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully set the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR setting the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }



关闭所有仓位函数

该函数设计非常灵活,将负责根据指定参数关闭所有未平仓位。我们将把函数命名为 CloseAllPositions()。它将扫描与提供的参数或交易品种幻数相匹配的未平仓合约,并尝试将其全部平仓。如果不允许交易,函数将停止执行并立即退出。该函数将循环处理所有未平仓位,根据指定条件对其进行过滤,并关闭所有匹配的仓位。

在尝试关闭所有仓位后,它会进入一个循环,以确认所有目标仓位都已关闭,处理任何错误,并确保我们不会陷入无限循环。当我们的函数成功关闭所有仓位时,它将退出并返回 true;否则,它将返回 false

首先来定义 CloseAllPositions() 函数。它将返回一个 bool 型值,并接受两个带默认值的参数:

  1. string symbol:默认值为 ALL_SYMBOLS
  2. ulong magicNumber:默认为 0

bool CloseAllPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- Functions body goes here
  }

我们需要检查是否允许交易。如果不允许交易,则退出函数并返回 false。

if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

创建一个新的 bool 类型变量 returnThis,用于存储函数的返回值,默认值为 false。

bool returnThis = false;

读取并保存未平仓位总数,并在 for 循环中使用该值,以便访问并处理所有未平仓位。每次迭代时,我们都会保存所选位置属性,并根据提供的交易品种幻数使用这些数据来筛选仓位。如果该仓位不符合标准,我们将继续下一个仓位。如果仓位符合标准,我们就使用 ClosePositionByTicket() 函数关闭仓位。

int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- Filter positions by symbol and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

现在,我们已经遍历了所有指定的未平仓位,并向交易服务器发送了终止请求,以关闭这些仓位。在结束函数之前,我们需要确认所有目标仓位都已实际平仓。为此,我们将使用一个循环,反复递归调用 CloseAllPositions() 函数,直到所有符合条件的仓位都关闭为止。在每次迭代中,我们都会尝试关闭所有剩余仓位,在短时间内暂停发送订单以加快执行速度,从而避免交易服务器不堪重负,并递增断路器计数器以避免无限循环。我们还将检查关键错误和其他退出条件(如脚本被停止或超过最大循环次数)。如果满足其中任何一个条件,我们就会跳出循环。

int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllPositions(symbol, magicNumber); //-- We still have some open positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

再次检查确认所有目标仓位都已关闭,并将此状态保存在返回变量中,最后结束并退出函数。

if(SymbolPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true; //-- Save this status for the function return value
     }

   return(returnThis);

确认所有 CloseAllPositions() 函数代码段都按以下顺序完成:

bool CloseAllPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Scan for symbol and magic number specific positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- Filter positions by symbol and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

//-- Confirm that we have closed all the positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllPositions(symbol, magicNumber); //-- We still have some open positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

//-- Final confirmations that all targeted positions have been closed
   if(SymbolPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true; //-- Save this status for the function return value
     }

   return(returnThis);
  }


关闭所有仓位函数重载

为方便起见,我们将重载 CloseAllPositions() 函数的第二个版本,该版本不接受任何参数,调用时会关闭账户中的所有未平仓位。该函数也可导出到仓位管理器 EX5 库中使用。

//+------------------------------------------------------------------+
//| CloseAllPositions(): Closes all positions in the account         |
//+------------------------------------------------------------------+
bool CloseAllPositions() export
  {
   return(CloseAllPositions(ALL_SYMBOLS, 0));
  }


排序和筛选仓位关闭函数

通过浏览 MQL5 开发人员论坛,您经常会遇到 MQL5 初学者提出的问题,他们寻求编码算法方面的帮助,以过滤、排序和管理各种仓位操作,例如根据幻数、盈利或亏损状态等标准关闭或修改特定仓位。以下函数库旨在满足这一需求,使有效实施这些操作变得更加简单快捷。

下面的排序和过滤仓位关闭函数采用了与 CloseAllPositions() 函数类似的方法,但又有各自独特的显著区别。它们采用递归编程策略,确保关闭所有指定仓位,并包括跟踪日志,在 EA 交易日志中打印和记录遇到的任何错误,供最终用户诊断。这些函数的另一个优势是,它们在实现指定目标方面的成功率很高,因为它们会递归扫描可恢复的错误,并多次发送指定的交易请求,以确保订单成功。为了更深入地了解每个函数的工作原理,我添加了详细的代码注释来解释函数中每个代码组件的结构和组织。


关闭所有买入仓位

CloseAllBuyPositions() 函数负责关闭与所提供的交易品种名称和幻数函数参数相匹配的所有未平仓买入仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回true;如果没有,则返回 false

bool CloseAllBuyPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Scan for symbol and magic number specific buy positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      ulong positionType = PositionGetInteger(POSITION_TYPE);

      //-- Filter positions by symbol, type and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (positionType != POSITION_TYPE_BUY) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

//-- Confirm that we have closed all the buy positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolBuyPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllBuyPositions(symbol, magicNumber); //-- We still have some open buy positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

   if(SymbolBuyPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true;
     }
   return(returnThis);
  }

关闭所有卖出仓位

CloseAllSellPositions() 函数的任务是关闭符合所提供的交易品种名称和幻数函数参数的所有未平仓卖出仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回true;如果没有,则返回 false

bool CloseAllSellPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Scan for symbol and magic number specific sell positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      ulong positionType = PositionGetInteger(POSITION_TYPE);

      //-- Filter positions by symbol, type and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (positionType != POSITION_TYPE_SELL) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

//-- Confirm that we have closed all the sell positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolSellPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllSellPositions(symbol, magicNumber); //-- We still have some open sell positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

   if(SymbolSellPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true;
     }
   return(returnThis);
  }



关闭所有幻数匹配仓位

CloseAllMagicPositions() 函数负责关闭与所提供的幻数函数参数或参数相匹配的所有未平仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回true;如果没有,则返回 false

bool CloseAllMagicPositions(ulong magicNumber) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Variables to store the selected positions data
   ulong positionTicket, positionMagicNo;
   string positionSymbol;

//-- Scan for magic number specific positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      positionSymbol = PositionGetString(POSITION_SYMBOL);

      //-- Filter positions by magic number
      if(magicNumber == positionMagicNo)
        {
         //-- Close the position
         ClosePositionByTicket(positionTicket);
        }
     }

//-- Confirm that we have closed all the positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(MagicPositionsTotal(magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllMagicPositions(magicNumber); //-- We still have some open positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, positionSymbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

   if(MagicPositionsTotal(magicNumber) == 0)
     {
      returnThis = true;
     }
   return(returnThis);
  }



关闭所有盈利仓位

CloseAllProfitablePositions() 函数关闭所有符合所提供的交易品种名称和幻数函数参数的盈利未平仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回true;如果没有,则返回 false

bool CloseAllProfitablePositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for profitable positions that match the specified symbol and magic number to close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number and profit
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit <= 0
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }
   return(true);
  }

___


关闭所有盈利的买入仓位

CloseAllProfitableBuyPositions() 函数关闭所有符合所提供的交易品种名称和神奇数字函数参数的盈利未平仓买入仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回 true;如果没有,则返回 false

bool CloseAllProfitableBuyPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for profitable positions that match the specified symbol and magic number to close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit <= 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }
   return(true);
  }


关闭所有盈利卖出仓位

CloseAllProfitableSellPositions() 函数关闭所有与提供的交易品种名称和幻数函数参数相匹配的盈利未平仓卖出仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回 true;如果没有,则返回 false

bool CloseAllProfitableSellPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for profitable positions that match the specified symbol and magic number to close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit <= 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }
   return(true);
  }


关闭所有亏损仓位

CloseAllLossPositions() 函数关闭与提供的交易品种名称和幻数参数相匹配的所有亏损未平仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回 true;如果没有,则返回 false

bool CloseAllLossPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for loss positions that match the specified symbol and magic number and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number and profit
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit > 0
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

   return(true);
  }


关闭所有亏损买入仓位

CloseAllLossBuyPositions() 函数关闭与提供的交易品种名称和幻数函数参数相匹配的所有亏损未平仓买入仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回 true;如果没有,则返回 false

bool CloseAllLossBuyPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for loss positions that match the specified symbol and magic number and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit > 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

   return(true);
  }


关闭所有亏损卖出仓位

CloseAllLossSellPositions() 函数关闭与提供的交易品种名称和幻数函数参数相匹配的所有亏损未平仓卖出仓位。它返回一个 bool 型值,如果成功关闭了所有指定仓位,则返回 true;如果没有,则返回 false

bool CloseAllLossSellPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for loss positions that match the specified symbol and magic number and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit > 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

   return(true);
  }  



仓位状态函数

在开发交易系统时,使用各种仓位的实时数据跟踪账户状态非常重要。无论您是在制定基于网格的策略还是保守的价格行为策略,对所有未平仓位有一个清晰直接的概述对于您的交易系统的成功至关重要。不过,目前还没有直接开箱即用的标准语言函数来提供这些信息。该 EX5 库旨在通过单行函数调用简化仓位信息的收集。以下可导出函数将为您提供所需的优势,帮助您监控仓位并决定是平仓还是加仓,从而形成您交易系统的基础支柱之一。


获取仓位数据

GetPositionsData() 函数在收集和存储所有所需的仓位状态信息方面起着至关重要的作用。它将这些数据保存在全局变量中,以便在整个程序库中随时访问。这些变量会在每次分时报价时不断更新,以确保其准确性和可靠性。

将以下全局变量声明放在库的顶部,位于交易操作请求和结果数据结构全局变量声明的下方。

string accountCurrency = AccountInfoString(ACCOUNT_CURRENCY);

//-- Position status global variables
//-------------------------------------------------------------------------------------------------------------------
int accountBuyPositionsTotal = 0, accountSellPositionsTotal = 0,
    symbolPositionsTotal = 0, symbolBuyPositionsTotal = 0, symbolSellPositionsTotal = 0,
    magicPositionsTotal = 0, magicBuyPositionsTotal = 0, magicSellPositionsTotal = 0;
double accountPositionsVolumeTotal = 0.0, accountBuyPositionsVolumeTotal = 0.0, accountSellPositionsVolumeTotal = 0.0,
       accountBuyPositionsProfit = 0.0, accountSellPositionsProfit = 0.0,
       symbolPositionsVolumeTotal = 0.0, symbolBuyPositionsVolumeTotal = 0.0,
       symbolSellPositionsVolumeTotal = 0.0, symbolPositionsProfit = 0.0,
       symbolBuyPositionsProfit = 0.0, symbolSellPositionsProfit = 0.0,
       magicPositionsVolumeTotal = 0.0, magicBuyPositionsVolumeTotal = 0.0,
       magicSellPositionsVolumeTotal = 0.0, magicPositionsProfit = 0.0,
       magicBuyPositionsProfit = 0.0, magicSellPositionsProfit = 0.0;

在下面的 GetPositionsData() 函数中更新并保存仓位状态数据。

void GetPositionsData(string symbol, ulong magicNumber)
  {
//-- Reset the acount open positions status
   accountBuyPositionsTotal = 0;
   accountSellPositionsTotal = 0;
   accountPositionsVolumeTotal = 0.0;
   accountBuyPositionsVolumeTotal = 0.0;
   accountSellPositionsVolumeTotal = 0.0;
   accountBuyPositionsProfit = 0.0;
   accountSellPositionsProfit = 0.0;

//-- Reset the EA's magic open positions status
   magicPositionsTotal = 0;
   magicBuyPositionsTotal = 0;
   magicSellPositionsTotal = 0;
   magicPositionsVolumeTotal = 0.0;
   magicBuyPositionsVolumeTotal = 0.0;
   magicSellPositionsVolumeTotal = 0.0;
   magicPositionsProfit = 0.0;
   magicBuyPositionsProfit = 0.0;
   magicSellPositionsProfit = 0.0;

//-- Reset the symbol open positions status
   symbolPositionsTotal = 0;
   symbolBuyPositionsTotal = 0;
   symbolSellPositionsTotal = 0;
   symbolPositionsVolumeTotal = 0.0;
   symbolBuyPositionsVolumeTotal = 0.0;
   symbolSellPositionsVolumeTotal = 0.0;
   symbolPositionsProfit = 0.0;
   symbolBuyPositionsProfit = 0.0;
   symbolSellPositionsProfit = 0.0;

//-- Update and save the open positions status with realtime data
   int totalOpenPositions = PositionsTotal();
   if(totalOpenPositions > 0)
     {
      //-- Scan for symbol and magic number specific positions and save their status
      for(int x = 0; x < totalOpenPositions; x++)
        {
         //--- Get position properties
         ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
         string selectedSymbol = PositionGetString(POSITION_SYMBOL);
         ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

         //-- Filter positions by magic number
         if(magicNumber != 0 && positionMagicNo != magicNumber)
           {
            continue;
           }

         //-- Save the account positions status first
         accountPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);

         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
           {
            //-- Account properties
            ++accountBuyPositionsTotal;
            accountBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            accountBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
           }
         else //-- POSITION_TYPE_SELL
           {
            //-- Account properties
            ++accountSellPositionsTotal;
            accountSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            accountSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
           }

         //-- Filter positions openend by EA and save their status
         if(
            PositionGetInteger(POSITION_REASON) == POSITION_REASON_EXPERT &&
            positionMagicNo == magicNumber
         )
           {
            ++magicPositionsTotal;
            magicPositionsProfit += PositionGetDouble(POSITION_PROFIT);
            magicPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
              {
               //-- Magic properties
               ++magicBuyPositionsTotal;
               magicBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
               magicBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
              }
            else //-- POSITION_TYPE_SELL
              {
               //-- Magic properties
               ++magicSellPositionsTotal;
               magicSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
               magicSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
              }
           }

         //-- Filter positions by symbol
         if(symbol == ALL_SYMBOLS || selectedSymbol == symbol)
           {
            ++symbolPositionsTotal;
            symbolPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            symbolPositionsProfit += PositionGetDouble(POSITION_PROFIT);
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
              {
               ++symbolBuyPositionsTotal;
               symbolBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
               symbolBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
              }
            else //-- POSITION_TYPE_SELL
              {
               ++symbolSellPositionsTotal;
               symbolSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
               symbolSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
              }
           }
        }
     }
  }

要访问我们获取并保存在上述全局变量中的不同仓位状态属性,我们需要创建简单的可导出函数,以便从外部代码库访问。我们将通过编写以下函数来实现这一目标。


买入仓位总数

返回账户中所有未平仓买入仓位总数的整数值。

int BuyPositionsTotal() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountBuyPositionsTotal);
  }

卖出仓位总数

返回账户中所有未平仓卖出仓位总数的整数值。

int SellPositionsTotal() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountSellPositionsTotal);
  }


仓位总交易量

返回账户中所有未平仓位的总交易量/手数/数量的双精度型数值。

double PositionsTotalVolume() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountPositionsVolumeTotal);
  }


买入仓位总交易量

返回账户中所有未平仓买入仓位的总交易量/手数/数量的双精度型数值。

double BuyPositionsTotalVolume() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountBuyPositionsVolumeTotal);
  }


卖出仓位总交易量

返回账户中所有未平仓卖出仓位的总交易量/手数/数量的双精度型数值。

double SellPositionsTotalVolume() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountSellPositionsVolumeTotal);
  }


买入仓位利润

返回账户中所有未平仓买入仓位总利润的双精度型数值。

double BuyPositionsProfit() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountBuyPositionsProfit);
  }


卖出仓位利润

返回账户中所有未平仓卖出仓位总利润的双精度型数值。

double SellPositionsProfit() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountSellPositionsProfit);
  }


幻数匹配的仓位总数

返回账户中与指定幻数匹配的所有未平仓位总数的整数值。

int MagicPositionsTotal(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicPositionsTotal);
  }


幻数匹配的买入仓位总数

返回账户中与指定幻数匹配的所有未平仓买入仓位总数的整数值。

int MagicBuyPositionsTotal(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicBuyPositionsTotal);
  }


幻数匹配的卖出仓位总数

返回账户中与指定幻数匹配的所有未平仓卖出仓位总数的整数值。

int MagicSellPositionsTotal(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicSellPositionsTotal);
  }


幻数匹配的仓位总交易量

返回账户中与指定幻数匹配的所有未平仓仓位的总交易量/手数/数量的双精度型数值。

double MagicPositionsTotalVolume(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicPositionsVolumeTotal);
  }


幻数匹配的买入仓位总交易量

返回账户中与指定幻数匹配的所有未平仓买入仓位的总交易量/手数/数量的双精度型数值。

double MagicBuyPositionsTotalVolume(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicBuyPositionsVolumeTotal);
  }


幻数匹配的卖出仓位总交易量

返回账户中与指定幻数匹配的所有未平仓卖出仓位的总交易量/手数/数量的双精度型数值。

double MagicSellPositionsTotalVolume(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicSellPositionsVolumeTotal);
  }


幻数匹配的仓位利润

返回账户中与指定幻数匹配的所有未平仓位总利润的双精度型数值。

double MagicPositionsProfit(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicPositionsProfit);
  }


幻数匹配的买入仓位利润

返回账户中与指定幻数匹配的所有未平仓买入仓位总利润的双精度型数值。

double MagicBuyPositionsProfit(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicBuyPositionsProfit);
  }


幻数匹配的卖出仓位利润

返回账户中与指定幻数匹配的所有未平仓卖出仓位总利润的双精度型值。

double MagicSellPositionsProfit(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicSellPositionsProfit);
  }


交易品种仓位总数

返回账户中指定交易品种所有未平仓位总数的整数值。

int SymbolPositionsTotal(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolPositionsTotal);
  }


交易品种买入仓位总数

返回账户中指定交易品种的所有未平仓买入仓位总数的整数值。

int SymbolBuyPositionsTotal(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolBuyPositionsTotal);
  }


交易品种卖出仓位总数

返回账户中指定交易品种的所有未平仓卖出仓位总数的整数值。

int SymbolSellPositionsTotal(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolSellPositionsTotal);
  }


交易品种仓位总交易量

返回账户中指定交易品种的所有未平仓位的总交易量/手数/数量的双精度型数值。

double SymbolPositionsTotalVolume(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolPositionsVolumeTotal);
  }


交易品种买入仓位总交易量

返回账户中指定交易品种的所有未平仓买入仓位的总交易量/手数/数量的双精度型数值。

double SymbolBuyPositionsTotalVolume(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolBuyPositionsVolumeTotal);
  }


交易品种卖出仓位总交易量

返回账户中指定交易品种的所有未平仓卖出仓位的总交易量/手数/数量的双精度型数值。

double SymbolSellPositionsTotalVolume(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolSellPositionsVolumeTotal);
  }


交易品种仓位利润

返回账户中指定交易品种的所有未平仓位总利润的双精度型数值。

double SymbolPositionsProfit(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(NormalizeDouble(symbolPositionsProfit, 2));
  }


交易品种买入仓位利润

返回账户中指定交易品种的所有未平仓买入仓位总利润的双精度型数值。

double SymbolBuyPositionsProfit(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(NormalizeDouble(symbolBuyPositionsProfit, 2));
  }


交易品种卖出仓位利润

返回账户中指定交易品种的所有未平仓卖出仓位总利润的双精度型数值。

double SymbolSellPositionsProfit(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(NormalizeDouble(symbolSellPositionsProfit, 2));
  }


账户仓位状态

返回包含账户仓位状态的预设格式字符串,该字符串可打印到日志中或显示在图表注释中。该函数接受一个名为 formatForComment 的布尔型参数。如果 formatForCommenttrue,函数将格式化数据,以便在图表窗口中显示。如果为 false,则会对数据进行格式化,以便在 EA 交易的日志选项卡中显示。

string AccountPositionsStatus(bool formatForComment) export
  {
   GetPositionsData(ALL_SYMBOLS, 0); //-- Update the position status variables before we display their data
   string spacer = "";
   if(formatForComment) //-- Add some formating space for the chart comment string
     {
      spacer = "                                        ";
     }
   string accountPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "| " + (string)AccountInfoInteger(ACCOUNT_LOGIN) + " - ACCOUNT POSTIONS STATUS \r\n";
   accountPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "|     Total Open:   " + (string)PositionsTotal() + "\r\n";
   accountPositionsStatus += spacer + "|     Total Volume: " + (string)accountPositionsVolumeTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Profit: " +
   (string)(NormalizeDouble(AccountInfoDouble(ACCOUNT_PROFIT), 2)) + accountCurrency + "\r\n";
   accountPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "| BUY POSITIONS: \r\n";
   accountPositionsStatus += spacer + "|     Total Open:   " + (string)accountBuyPositionsTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Volume: " + (string)accountBuyPositionsVolumeTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(accountBuyPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   accountPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "| SELL POSITIONS: \r\n";
   accountPositionsStatus += spacer + "|     Total Open:   " + (string)accountSellPositionsTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Volume: " + (string)accountSellPositionsVolumeTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(accountSellPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   accountPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "\r\n";
   return(accountPositionsStatus);
  }


幻数匹配的仓位状态

返回预先格式化的字符串,其中包含账户中与幻数匹配的仓位的状态,可打印到日志中或显示在图表注释中。该函数接受两个参数。一个无符号长整型参数 magicNumber 和一个布尔型参数 formatForComment。如果 formatForCommenttrue,函数将格式化数据,以便在图表窗口中显示。如果为 false,则会对数据进行格式化,以便在 EA 交易的日志选项卡中显示。

string MagicPositionsStatus(ulong magicNumber, bool formatForComment) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber); //-- Update the position status variables before we display their data
   string spacer = "";
   if(formatForComment) //-- Add some formating space for the chart comment string
     {
      spacer = "                                        ";
     }
   string magicPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "| " + (string)magicNumber + " - MAGIC POSTIONS STATUS \r\n";
   magicPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "|     Total Open:   " + (string)magicPositionsTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Volume: " + (string)magicPositionsVolumeTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Profit: " +
   (string)(NormalizeDouble(magicPositionsProfit, 2)) + accountCurrency + "\r\n";
   magicPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "| BUY POSITIONS: \r\n";
   magicPositionsStatus += spacer + "|     Total Open:   " + (string)magicBuyPositionsTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Volume: " + (string)magicBuyPositionsVolumeTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(magicBuyPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   magicPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "| SELL POSITIONS: \r\n";
   magicPositionsStatus += spacer + "|     Total Open:   " + (string)magicSellPositionsTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Volume: " + (string)magicSellPositionsVolumeTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(magicSellPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   magicPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "\r\n";
   return(magicPositionsStatus);
  }


交易品种仓位状态

返回包含账户中某交易品种仓位状态的预设格式字符串,该字符串可打印到日志中或显示在图表注释中。该函数接受三个参数。字符串型参数 symbol、无符号长整型参数 magicNumber 和 bool 型参数 formatForComment。如果 formatForComment 布尔值为 "true",函数将格式化数据,以便在图表窗口中显示。如果为 false,则会对数据进行格式化,以便在 EA 交易的日志选项卡中显示。

string SymbolPositionsStatus(string symbol, ulong magicNumber, bool formatForComment) export
  {
   GetPositionsData(symbol, magicNumber); //-- Update the position status variables before we display their data
   string spacer = "";
   if(formatForComment) //-- Add some formating space for the chart comment string
     {
      spacer = "                                        ";
     }
   string symbolPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "| " + symbol + " - SYMBOL POSTIONS STATUS \r\n";
   symbolPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "|     Total Open:   " + (string)symbolPositionsTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Volume: " + (string)symbolPositionsVolumeTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Profit: " +
   (string)(NormalizeDouble(symbolPositionsProfit, 2)) + accountCurrency + "\r\n";
   symbolPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "| BUY POSITIONS: \r\n";
   symbolPositionsStatus += spacer + "|     Total Open:   " + (string)symbolBuyPositionsTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Volume: " + (string)symbolBuyPositionsVolumeTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(symbolBuyPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   symbolPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "| SELL POSITIONS: \r\n";
   symbolPositionsStatus += spacer + "|     Total Open:   " + (string)symbolSellPositionsTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Volume: " + (string)symbolSellPositionsVolumeTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(symbolSellPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   symbolPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "\r\n";
   return(symbolPositionsStatus);
  }



如何导入和实现我们的仓位管理 EX5 库

我们开发了一个全面的仓位管理 EX5 库,其中包含仓位操作、状态获取和显示模块的所有基本函数。现在是记录和解释如何在任何 MQL5 项目中有效导入和使用该库的时候了。

为了简化实现过程,让我们首先概述一下仓位管理库中的所有函数或模块,以及一些实际代码示例用例。这将能为库用户提供 PositionsManager.ex5 二进制文件所含组件的快速概览。

仓位管理器 EX5 库文档

函数原型描述 描述 用例示例
bool ErrorAdvisor(
   string callingFunc, 
   string symbol, 
   int tradeServerErrorCode
);
在处理仓位和订单时,管理交易服务器和运行时错误。如果错误可以恢复,可以重新发送交易请求,则返回 true ;如果错误很严重,应停止发送请求,则返回 false
ResetLastError(); //-- Reset and clear the last error
//--------------------------------------------------------------
//-- Insert code to send the order request to the trade server
//--------------------------------------------------------------
string symbol = _Symbol; //Symbol being traded
int retcode = tradeResult.retcode;//Trade Request Structure (MqlTradeRequest)
if(!ErrorAdvisor(__FUNCTION__, symbol, retcode)
  {
//Critical error found
//Order can not be executed. Exit function or log this error
  }
bool TradingIsAllowed();
验证用户、交易服务器和经纪商是否允许 EA 交易执行交易。 
if(!TradingIsAllowed())
  {
   //--- algo trading is disabled, exit function
   return(false);
  }

bool OpenBuyPosition(
   ulong magicNumber,
   string symbol,
   double lotSize,
   int sl, int tp,
   string positionComment
);
打开一个符合指定参数的新买入仓位。
ulong magicNo = 123;
string symbol = _Symbol;
double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
int sl = 500; //-- pips
int tp = 1000; //-- pips
string comment = "Buy position";
OpenBuyPosition(magicNo, symbol, lotSize, sl, tp, comment);
bool OpenSellPosition(
   ulong magicNumber,
   string symbol,
   double lotSize,
   int sl,
   int tp,
   string positionComment
);
打开一个符合指定参数的新卖出仓位。
ulong magicNo = 123;
string symbol = _Symbol;
double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
int sl = 500; //-- pips
int tp = 1000; //-- pips
string comment = "Sell position";
OpenSellPosition(magicNo, symbol, lotSize, sl, tp, comment);
bool SetSlTpByTicket(
   ulong positionTicket,
   int sl,
   int tp
);
为符合指定编号的仓位设置止损。
int sl = 500, int tp = 1000; //-- pips
int totalOpenPostions = PositionsTotal();
for(int x = 0; x < totalOpenPostions; x++)
{
   ulong positionTicket = PositionGetTicket(x);
   if(positionTicket > 0)
   {
      SetSlTpByTicket(positionTicket, sl, tp);
   }   
}
bool ClosePositionByTicket(
   ulong positionTicket
);
关闭与指定编号匹配的仓位。
//-- Example to close all open positions
for(int x = 0; x < totalOpenPostions; x++)
{
   ulong positionTicket = PositionGetTicket(x);
   if(positionTicket > 0)
   {
      ClosePositionByTicket(positionTicket);
   }   
}
bool SetTrailingStopLoss(
   ulong positionTicket,
   int trailingStopLoss
);
为符合指定编号的仓位设置追踪止损。该函数应在 OnTick() 函数中的每个分时上执行,以实时更新追踪止损。
//-- Execute this on every tick
//-- Example to set 500 pips trailing stop loss for all positions
int trailingStopLoss = 500; //-- 500 pips trailing stop loss
for(int x = 0; x < totalOpenPostions; x++)
{
   ulong positionTicket = PositionGetTicket(x);
   if(positionTicket > 0)
   {
      SetTrailingStopLoss(positionTicket, trailingStopLoss);
   }   
}
bool CloseAllPositions(
   string symbol, 
   ulong magicNumber
);
多功能函数,可关闭所有符合指定参数的未平仓位。
//Close all positions
CloseAllPositions("", 0);

//Only close all positions matching a magic number value of 1
CloseAllPositions("", 1);

//Only close all current symbol positions
CloseAllPositions(_Symbol, 0);
bool CloseAllPositions();

关闭所有未平仓位。
//Close all open positions in the account
CloseAllPositions();
 
bool CloseAllBuyPositions(
   string symbol, 
   ulong magicNumber
);
关闭所有符合指定参数的买入仓位。
//Close all buy positions for the current symbol
CloseAllBuyPositions(_Symbol, 0);

//Close all buy positions matching magic number 1 for all symbols
CloseAllBuyPositions("", 1);

//Close all buy positions in the account
CloseAllBuyPositions("", 0);
 
bool CloseAllSellPositions(
   string symbol,
   ulong magicNumber
);

关闭所有符合指定参数的卖出仓位。  
//Close all sell positions for the current symbol
CloseAllSellPositions(_Symbol, 0);

//Close all sell positions matching magic number 1 for all symbols
CloseAllSellPositions("", 1);

//Close all sell positions in the account
CloseAllSellPositions("", 0);
 
bool CloseAllMagicPositions(
   ulong magicNumber
);
关闭所有符合指定幻数的仓位。  
//Close all positions matching magic number 1
CloseAllMagicPositions(1);

//Close all positions in the account
CloseAllMagicPositions(0);
 
bool CloseAllProfitablePositions(
   string symbol,
   ulong magicNumber
);

关闭所有符合指定参数的盈利仓位。
//Close all profitable positions for the current symbol
CloseAllProfitablePositions(_Symbol, 0);

//Close all profitable positions matching magic number 1 for all symbols
CloseAllProfitablePositions("", 1);

//Close all profitable positions in the account
CloseAllProfitablePositions("", 0);
 
bool CloseAllProfitableBuyPositions(
   string symbol,
   ulong magicNumber
);
关闭所有符合指定参数的盈利买入仓位。
//Close all profitable buy positions for the current symbol
CloseAllProfitableBuyPositions(_Symbol, 0);

//Close all profitable buy positions matching magic number 1 for all symbols
CloseAllProfitableBuyPositions("", 1);

//Close all profitable buy positions in the account
CloseAllProfitableBuyPositions("", 0);
 
bool CloseAllProfitableSellPositions(
   string symbol,
   ulong magicNumber
);
关闭所有符合指定参数的盈利卖出仓位。
//Close all profitable sell positions for the current symbol
CloseAllProfitableSellPositions(_Symbol, 0);

//Close all profitable sell positions matching magic number 1 for all symbols
CloseAllProfitableSellPositions("", 1);

//Close all profitable sell positions in the account
CloseAllProfitableSellPositions("", 0);
 
bool CloseAllLossPositions(
   string symbol,
   ulong magicNumber
);

关闭所有符合指定参数的亏损仓位。
//Close all loss positions for the current symbol
CloseAllLossPositions(_Symbol, 0);

//Close all loss positions matching magic number 1 for all symbols
CloseAllLossPositions("", 1);

//Close all loss positions in the account
CloseAllLossPositions("", 0);
 
bool CloseAllLossBuyPositions(
   string symbol,
   ulong magicNumber
);
关闭所有符合指定参数的亏损买入仓位。
//Close all loss buy positions for the current symbol
CloseAllLossBuyPositions(_Symbol, 0);

//Close all loss buy positions matching magic number 1 for all symbols
CloseAllLossBuyPositions("", 1);

//Close all loss buy positions in the account
CloseAllLossBuyPositions("", 0);

 
bool CloseAllLossSellPositions(
   string symbol,
   ulong magicNumber
);

关闭所有符合指定参数的亏损卖出仓位。
//Close all loss sell positions for the current symbol
CloseAllLossSellPositions(_Symbol, 0);

//Close all loss sell positions matching magic number 1 for all symbols
CloseAllLossSellPositions("", 1);

//Close all loss sell positions in the account
CloseAllLossSellPositions("", 0);
 
int BuyPositionsTotal();

返回未平买入仓位的数量。  
//Get the total number of open buy positions in the account
BuyPositionsTotal();

 
int SellPositionsTotal();

返回未平卖出仓位的数量。
//Get the total number of open sell positions in the account
SellPositionsTotal();

 
double PositionsTotalVolume();

返回所有未平仓仓位的总交易量。
//Get the total volume of all open positions in the account
PositionsTotalVolume();

 
double BuyPositionsTotalVolume();
返回所有买入未平仓仓位的总交易量。
//Get the total volume of all open buy positions in the account
BuyPositionsTotalVolume();

 
double SellPositionsTotalVolume();

返回所有卖出未平仓仓位的总交易量。
//Get the total volume of all open sell positions in the account
SellPositionsTotalVolume();

 
double BuyPositionsProfit();
返回所有未平仓买入仓位的总利润。
//Get the total profit of all open buy positions in the account
BuyPositionsProfit();

 
double SellPositionsProfit();

返回所有未平仓卖出仓位的总利润。
//Get the total profit of all open sell positions in the account
SellPositionsProfit();

 
int MagicPositionsTotal(
   ulong magicNumber
);

返回与指定幻数匹配的未平仓仓位数。
//Get the total number of open positions matching magic number 1
MagicPositionsTotal(1);
 
int MagicBuyPositionsTotal(
   ulong magicNumber
);

返回符合指定幻数的未平仓买入仓位数。
//Get the total number of open buy positions matching magic number 1
MagicBuyPositionsTotal(1);
 
int MagicSellPositionsTotal(
   ulong magicNumber
);

返回符合指定幻数的未平仓卖出仓位数。
//Get the total number of open sell positions matching magic number 1
MagicSellPositionsTotal(1);
 
double MagicPositionsTotalVolume(
   ulong magicNumber
);

返回符合指定幻数的所有未平仓仓位的总交易量。
//Get the total volume of open positions matching magic number 1
MagicPositionsTotalVolume(1);
 
double MagicBuyPositionsTotalVolume(
   ulong magicNumber
);

返回符合指定幻数的所有未平仓买入仓位的总交易量。
//Get the total volume of open buy positions matching magic number 1
MagicBuyPositionsTotalVolume(1);
 
double MagicSellPositionsTotalVolume(
   ulong magicNumber
);

返回符合指定幻数的所有未平仓卖出仓位的总交易量。
 
//Get the total volume of open sell positions matching magic number 1
MagicSellPositionsTotalVolume(1);
 
double MagicPositionsProfit(
   ulong magicNumber
);

返回符合指定幻数的所有未平仓仓位的总利润。
//Get the total profit of open positions matching magic number 1
MagicPositionsProfit(1);
 
double MagicBuyPositionsProfit(
   ulong magicNumber
);
返回符合指定幻数的所有未平仓买入仓位的总利润。
//Get the total profit of open buy positions matching magic number 1
MagicBuyPositionsProfit(1);
 
double MagicSellPositionsProfit(
   ulong magicNumber
);
返回符合指定幻数的所有未平仓卖出仓位的总利润。
//Get total profit of sell positions matching magic number 1
MagicSellPositionsProfit(1);

 
int SymbolPositionsTotal(
   string symbol,
   ulong magicNumber
);

返回与指定交易品种和幻数匹配的所有未平仓仓位的总数。
//Get total number of positions matching symbol and magic number 1
MagicPositionsTotal(_Symbol, 1);
 
int SymbolBuyPositionsTotal(
   string symbol,
   ulong magicNumber
);
返回与指定交易品种和幻数匹配的所有未平仓买入仓位的总数。
//Get total number of buy positions matching symbol and magic number 1
SymbolBuyPositionsTotal(_Symbol, 1);

 
int SymbolSellPositionsTotal(
   string symbol,
   ulong magicNumber
);
返回与指定交易品种和幻数匹配的所有未平仓卖出仓位的总数。
//Get total number of sell positions matching symbol and magic number 1
SymbolSellPositionsTotal(_Symbol, 1);

 
double SymbolPositionsTotalVolume(
   string symbol,
   ulong magicNumber
);

返回与指定交易品种和幻数匹配的所有未平仓仓位的总交易量。
//Get the volume of positions matching symbol and magic number 1
SymbolPositionsTotalVolume(_Symbol, 1);

 
double SymbolBuyPositionsTotalVolume(
   string symbol,
   ulong magicNumber
);

返回与指定交易品种和幻数匹配的所有未平仓买入仓位的总交易量。
//Get the volume of buy positions matching symbol and magic number 1
SymbolBuyPositionsTotalVolume(_Symbol, 1);
 
double SymbolSellPositionsTotalVolume(
   string symbol,
   ulong magicNumber
);

返回与指定交易品种和幻数匹配的所有未平仓卖出仓位的总交易量。
//Get the volume of sell positions matching symbol and magic number 1
SymbolSellPositionsTotalVolume(_Symbol, 1);
 
double SymbolPositionsProfit(
   string symbol,
   ulong magicNumber
);

返回与指定交易品种和幻数匹配的所有未平仓仓位的总利润。
//Get the profit of all positions matching symbol and magic number 1
SymbolPositionsProfit(_Symbol, 1);
 
double SymbolBuyPositionsProfit(
   string symbol,
   ulong magicNumber
);
返回与指定交易品种和幻数匹配的所有未平仓买入仓位的总利润。
//Get the profit of all buy positions matching symbol and magic number 1
SymbolBuyPositionsProfit(_Symbol, 1);

 
double SymbolSellPositionsProfit(
   string symbol,
   ulong magicNumber
);
返回与指定交易品种和幻数匹配的所有未平仓卖出仓位的总利润。
//Get the profit of all sell positions matching symbol and magic number 1
SymbolSellPositionsProfit(_Symbol, 1);
 
string AccountPositionsStatus(
   bool formatForComment
);
在 MetaTrader5 的交易品种图表或专家选项卡上打印所有未平仓位的字符串格式状态。  
//Print the status of all open positions formatted for the chart comments
AccountPositionsStatus(true);

//Print the status of all open positions formatted for the Experts tab
AccountPositionsStatus(false);
 
string MagicPositionsStatus(
   ulong magicNumber,
   bool formatForComment
);

在 MetaTrader5 的交易品种图表或专家选项卡上打印与指定幻数匹配的所有未平仓位的字符串格式状态。
//Print the status of all open positions matching
//the magic number 1 formatted for the chart comments
MagicPositionsStatus(1, true);

//Print the status of all open positions matching
//the magic number 1 formatted for the Experts tab
MagicPositionsStatus(1, false);
 
string SymbolPositionsStatus(
   string symbol,
   ulong magicNumber,
   bool formatForComment
);
在 MetaTrader5 的交易品种图表或专家选项卡上打印与指定交易品种和幻数匹配的所有未平仓位的字符串格式状态。
//Print the status of all open positions matching
//the symbol and magic number 1 formatted for the chart comments
SymbolPositionsStatus(_Symbol, 1, true);

//Print the status of all open positions matching
//the symbol and magic number 1 formatted for the Experts tab
SymbolPositionsStatus(_Symbol, 1, false);

在 MQL5 项目中集成该库非常简单。按照以下两个步骤将 PositionsManager.ex5 导入您的 MQL5 代码:

  • 步骤 1:复制库可执行文件(PositionsManager.ex5)

PositionsManager.ex5 文件放到 MQL5/Libraries/Toolkit 文件夹中。如果该文件尚未存在,请确保已下载并复制到指定位置。一份 PositionsManager.ex5 的副本附在本文末尾,以方便阅读。

  • 步骤 2:导入函数原型描述

在源代码文件的头文件中添加仓位管理器库的导入指令及其函数原型说明。使用下面的代码段,导入 PositionsManager.ex5 库中的所有函数或模块。我还创建了一个空白的 EA 交易系统模板(PositionsManager_Imports_Template.mq5),其中包含以下代码段。欢迎有选择性地注释或删除项目中不需要的函数说明。PositionsManager_Imports_Template.mq5 文件也附在本文末尾。

//+-------------------------------------------------------------------------------------+
//| PositionsManager.ex5 imports template                                               |
//+-------------------------------------------------------------------------------------+
#import "Toolkit/PositionsManager.ex5" //-- Opening import directive
//-- Function descriptions for the imported function prototypes

//-- Error Handling and Permission Status Functions
bool   ErrorAdvisor(string callingFunc, string symbol, int tradeServerErrorCode);
bool   TradingIsAllowed();

//-- Position Execution and Modification Functions
bool   OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   SetSlTpByTicket(ulong positionTicket, int sl, int tp);
bool   ClosePositionByTicket(ulong positionTicket);
bool   SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);
bool   CloseAllPositions(string symbol, ulong magicNumber);
bool   CloseAllPositions();
bool   CloseAllBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllSellPositions(string symbol, ulong magicNumber);
bool   CloseAllMagicPositions(ulong magicNumber);
bool   CloseAllProfitablePositions(string symbol, ulong magicNumber);
bool   CloseAllProfitableBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllProfitableSellPositions(string symbol, ulong magicNumber);
bool   CloseAllLossPositions(string symbol, ulong magicNumber);
bool   CloseAllLossBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllLossSellPositions(string symbol, ulong magicNumber);

//-- Position Status Monitoring Functions
int    BuyPositionsTotal();
int    SellPositionsTotal();
double PositionsTotalVolume();
double BuyPositionsTotalVolume();
double SellPositionsTotalVolume();
double BuyPositionsProfit();
double SellPositionsProfit();

//-- Positions Filtered By Magic Number Status Monitoring Functions
int    MagicPositionsTotal(ulong magicNumber);
int    MagicBuyPositionsTotal(ulong magicNumber);
int    MagicSellPositionsTotal(ulong magicNumber);
double MagicPositionsTotalVolume(ulong magicNumber);
double MagicBuyPositionsTotalVolume(ulong magicNumber);
double MagicSellPositionsTotalVolume(ulong magicNumber);
double MagicPositionsProfit(ulong magicNumber);
double MagicBuyPositionsProfit(ulong magicNumber);
double MagicSellPositionsProfit(ulong magicNumber);

//-- Positions Filtered By Symbol and/or Magic Number Status Monitoring Functions
int    SymbolPositionsTotal(string symbol, ulong magicNumber);
int    SymbolBuyPositionsTotal(string symbol, ulong magicNumber);
int    SymbolSellPositionsTotal(string symbol, ulong magicNumber);
double SymbolPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolBuyPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolSellPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolPositionsProfit(string symbol, ulong magicNumber);
double SymbolBuyPositionsProfit(string symbol, ulong magicNumber);
double SymbolSellPositionsProfit(string symbol, ulong magicNumber);

//-- Log and Data Display Functions
string AccountPositionsStatus(bool formatForComment);
string MagicPositionsStatus(ulong magicNumber, bool formatForComment);
string SymbolPositionsStatus(string symbol, ulong magicNumber, bool formatForComment);

#import //--- Closing import directive

有了导入的库,您现在可以使用简单的函数调用轻松地开启、关闭、修改或获取仓位状态数据。为了说明这一点,让我们在下面的章节中创建三个基本的 EA 交易系统。


开发由仓位管理器 EX5 库支持的双 VIDyA 追踪止损 EA 交易系统

在本节中,我们将开发一个基于可变指数动态平均线(VIDyA) 技术指标的追踪止损交易策略 EA 交易系统,作为在实际交易应用中实现仓位管理器 EX5 库的一个实用示例。

VIDyA 追踪止损策略将使用一对可变指数动态平均线技术指标来生成买入和卖出信号。由于 VIDyA 指标在图表上显示为一条线,我们将使用线形交叉策略来发出新的交易入场信号。对于交叉策略,指标对必须有不同的设置。第一个 VIDyA 指标的输入值较低,将被称为 "快速 VIDyA",因为它的反应和信号生成速度更快。第二种输入值较高,被称为 "慢速 VIDyA",因为它对价格变化的反应速度较慢。买入时,"快速 VIDyA"线必须高于"慢速 VIDyA"线;卖出时,"快速 VIDyA"线必须低于"慢速 VIDyA"线。

双 VIDyA 策略


使用 MetaEditor IDE 新文件MQL 向导创建新的 EA 交易系统,并将其命名为 "DualVidyaTrader.mq5"。由于我们的 EA 交易系统将使用 PositionsManager.ex5 库,因此第一步是导入并插入库函数原型说明,如前所述。将库导入代码段放在 #property 指令下方。由于该函数库包含许多函数,我们不会导入或使用所有函数,只会导入下面代码中列出的函数原型。

//--- Import the PositionsManager EX5 Library
#import "Toolkit/PositionsManager.ex5" //-- Open the ex5 import directive
//-- Prototype function descriptions of the EX5 PositionsManager library
bool   OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);
int    MagicPositionsTotal(ulong magicNumber);
int    MagicBuyPositionsTotal(ulong magicNumber);
int    MagicSellPositionsTotal(ulong magicNumber);
bool   CloseAllProfitableBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllProfitableSellPositions(string symbol, ulong magicNumber);
string MagicPositionsStatus(ulong magicNumber, bool formatForComment);
#import //-- Close the ex5 import directive

PositionsManager.ex5 库导入代码段后,插入如图所示的用户输入全局变量。

input group ""
input ulong magicNo = 1234;
input ENUM_TIMEFRAMES timeframe = PERIOD_H1;
input ENUM_APPLIED_PRICE  appliedPrice = PRICE_CLOSE; // Applied VIDyA Price

//-- Fast Vidya user inputs
input group "-- FAST VIDyA INPUTS"
input int fast_cmoPeriod = 5; // Fast Chande Momentum Period
input int fast_maPeriod = 10; // Fast MA Smoothing Period
input int fast_emaShift = 0; // Fast Horizontal Shift

//-- Slow Vidya user inputs
input group "-- SLOW VIDyA INPUTS"
input int slow_cmoPeriod = 9; //  Slow Chande Momentum Period
input int slow_maPeriod = 12; // Slow MA Smoothing Period
input int slow_emaShift = 0; // Slow Horizontal Shift

input group "-- PROFIT MANAGEMENT"
input bool liquidateProfitOnCrossover = false; // Liquidate Profit On VIDyA Signal
input bool enableTrailingStops = true; // Use Trailing Stop Losses

让我们创建更多全局变量来存储止损、止盈、追踪止损以及手数或交易量值。

//-- Get and save the SL, trailingSL and TP values from the spread
int spreadMultiForSl = 1000;
int spreadMultiForTrailingSl = 300;
int spreadMultiForTp = 1000;
int sl = int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForSl;
int trailingSl = int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForTrailingSl;
int tp = int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForTp;

//-- Set the lot or volume to the symbol allowed min value
double lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

在全局范围内创建 VIDyA 技术指标变量,确保我们的 EA 交易代码的任何部分都可以访问它们。

//-- Vidya indicator variables
double fastVidya[], slowVidya[];
int fastVidyaHandle, slowVidyaHandle;
bool buyOk, sellOk, vidyaBuy, vidyaSell;
string vidyaTrend;

在全局变量部分之后,创建 EA 交易初始化函数,该函数将从标准 MQL5 OnInit() 事件处理函数中调用,该函数在 EA 交易启动或初始化期间首次执行。将此函数命名为 GetInit()。我们将在该函数中执行所有数据的初始化,包括 VIDyA 指标初始化和启动处理。

int GetInit()
  {
   int returnVal = 1;

//-- Helps to regulate and prevent openning multiple trades on a single signal trigger
   buyOk = true;
   sellOk = true;

//-- Create the fast iVIDyA indicator handle
   fastVidyaHandle = iVIDyA(_Symbol, timeframe, fast_cmoPeriod, fast_maPeriod, fast_emaShift, appliedPrice);
   if(fastVidyaHandle < 0)
     {
      Print("Error creating fastVidyaHandle = ", INVALID_HANDLE);
      Print("Handle creation: Runtime error = ", GetLastError());
      //-- Close the EA if the handle is not properly loaded
      return(-1);
     }
   ArraySetAsSeries(fastVidya, true); //-- set the vidya array to series access

//-- Create the slow iVIDyA indicator handle
   slowVidyaHandle = iVIDyA(_Symbol, timeframe, slow_cmoPeriod, slow_maPeriod, slow_emaShift, appliedPrice);
   if(slowVidyaHandle < 0)
     {
      Print("Error creating vidyaHandle = ", INVALID_HANDLE);
      Print("Handle creation: Runtime error = ", GetLastError());
      //-- Close the EA if the handle is not properly loaded
      return(-1);
     }
   ArraySetAsSeries(slowVidya, true); //-- set the vidya array to series access

   return(returnVal);
  }

在初始化函数之后,创建去初始化函数并命名为 GetDeinit() 。该函数将在标准 MQL5OnDeinit() 事件处理函数中调用,以便在全系统范围内清理 EA 交易系统使用的所有资源。这包括释放 VIDyA 指标,释放与指标句柄数组绑定的所有资源,以及删除任何图表注释或对象。

void GetDeinit()  //-- De-initialize the robot on shutdown and clean everything up
  {
//-- Delete the vidya handles and de-allocate the memory spaces occupied
   IndicatorRelease(fastVidyaHandle);
   ArrayFree(fastVidya);
   IndicatorRelease(slowVidyaHandle);
   ArrayFree(slowVidya);

//-- Delete and clear all chart displayed messages
   Comment("");
  }

接下来,我们需要创建一个自定义函数,用于检测和获取 VIDyA 指标对生成的交易信号。该函数将被称为 GetVidya()。每次有新的分时报价时,它都会被执行和更新,以确保信号生成的准确性和实时性。

void GetVidya()
  {
//-- Get vidya line directions
   if(CopyBuffer(fastVidyaHandle, 0, 0, 100, fastVidya) <= 0 || CopyBuffer(slowVidyaHandle, 0, 0, 100, slowVidya) <= 0)
     {
      return;
     }

//-- Reset vidya status variables
   vidyaBuy = false;
   vidyaSell = false;
   vidyaTrend = "FLAT";

//-- Scan for vidya crossover buy signal
   if(fastVidya[1] > slowVidya[1])
     {
      //-- Save the vidya signal
      vidyaTrend = "BUY/LONG";
      vidyaBuy = true;
      vidyaSell = false;
     }

//-- Scan for vidya crossover sell signal
   if(fastVidya[1] < slowVidya[1])
     {
      //-- Save the vidya signal
      vidyaTrend = "SELL/SHORT";
      vidyaSell = true;
      vidyaBuy = false;
     }
  }

现在,我们已经创建了获取和更新 VIDyA 信号的函数,让我们创建另一个自定义函数,该函数将在每一个新分时执行,根据当前 VIDyA 信号扫描并开启新仓位。该函数名为 ScanForTradeOpportunities()。我们将在该函数中调用并执行从 PositionsManager.ex5 库中导入的仓位开启和状态原型函数。

void ScanForTradeOpportunities()
  {
//-- Get the VIDyA signal
   GetVidya();

   if(MagicPositionsTotal(magicNo) == 0)
     {
      buyOk = true;
      sellOk = true;
     }

//-- Check for a buy entry when a VIDyA buy signal is found
   if(buyOk && vidyaBuy) //-- Open a new buy position
     {
      if(OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Vidya_BUY: " + IntegerToString(MagicBuyPositionsTotal(magicNo) + 1)))
        {
         buyOk = false;
         sellOk = true;
        }
      //-- Market has a strong buy trend, close all profitable sell positions
      if(liquidateProfitOnCrossover)
        {
         CloseAllProfitableSellPositions(_Symbol, magicNo);
        }
     }

//-- Check for a sell entry when a VIDyA sell signal is found
   if(sellOk && vidyaSell) //-- Open a new sell position
     {
      if(OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Vidya_SELL: " + IntegerToString(MagicSellPositionsTotal(magicNo) + 1)))
        {
         sellOk = false;
         buyOk = true;
        }
      //-- Market has a strong sell trend, close all profitable buy positions
      if(liquidateProfitOnCrossover)
        {
         CloseAllProfitableBuyPositions(_Symbol, magicNo);
        }
     }
  }

我们还需要检查并设置所有未平仓位的追踪止损。这很简单,因为我们的 PositionsManager.ex5 库包含一个追踪止损原型函数,可以帮助我们实现这一目标。让我们创建一个名为 CheckAndSetTrailingSl() 的新函数,扫描所有未平仓位,并查询它们的止损单,作为导入的 SetTrailingStopLoss() 原型函数的参数。

void CheckAndSetTrailingSl()
  {
   int totalOpenPostions = PositionsTotal();
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      int positionType = int(PositionGetInteger(POSITION_TYPE));

      //-- modify only the positions we have opened with this EA (magic number)
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }
      //-- Only set the trailing stop loss when the market trend is in the opposing direction of the position type
      if((positionType == POSITION_TYPE_BUY && vidyaBuy) || (positionType == POSITION_TYPE_SELL && vidyaSell))
        {
         continue;
        }
      //--- set the trailing stop loss
      SetTrailingStopLoss(positionTicket, trailingSl); //-- call the imported function from our ex5 library
     }
  }

现在,我们已经为 EA 交易系统创建了所有重要模块,让我们将它们整合到 MQL5 OnTick() 事件处理函数中,该函数在每个新的分时上执行。按照以下规定的顺序排列,以确保它们按照正确的顺序系统地执行我们的交易系统。

void OnTick()
  {
//-- Scan and open new positions based on the vidya signal
   ScanForTradeOpportunities();

//-- Check and set the trailing stop
   if(enableTrailingStops)
     {
      CheckAndSetTrailingSl();
     }

//-- Display the vidya trend and positions status for the EA's magicNo
   Comment(
      "\nvidyaTrend: ", vidyaTrend,
      MagicPositionsStatus(magicNo, true)
   );
  }

最后,不要忘记将初始化和去初始化函数放在各自的标准 MQL5 事件处理函数中。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(GetInit() <= 0)
     {
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   GetDeinit();
  }

您可以在本文末尾访问并下载 DualVidyaTrader.mq5 EA 交易系统的源代码文件。我附上了完整的源代码文件,以确保您拥有所有必要的组件来实施和定制所需的交易策略。

MetaTrader 5 中编译并加载新的 DualVidyaTrader EA 交易系统时,您会发现在"依赖关系"选项卡中,列出了从 PositionsManager.ex5 导入的所有库函数原型,以及保存 EX5 库的完整文件路径。这可确保在将 EA 交易系统加载到图表之前正确引用所有必需的依赖项。如果遇到任何库引用错误(如本文前面讨论的错误),它们将被记录在 MetaTrader 5 工具箱窗口的专家日志选项卡中。

Dual Vidya Trader 依赖关系选项卡


由仓位管理器 EX5 库支持的仓位管理器交易面板

在第二个示例中,我们将创建一个基本的 EA 交易图形用户界面 (GUI) 交易面板,该面板也由 PositionsManager.ex5 库提供支持。

仓位管理器面板图形用户界面


使用 MetaEditor IDE 新文件 MQL 向导创建一个新的 EA 交易系统,并将其命名为 PositionsManagerPanel.mq5。在 #property 指令代码段下,导入 PositionsManager.ex5 库。在导入函数说明部分,只导入以下函数原型。

//+------------------------------------------------------------------+
//| EX5 PositionsManager imports                                     |
//+------------------------------------------------------------------+
#import "Toolkit/PositionsManager.ex5" //-- Open import directive
//-- Function descriptions for the imported function prototypes

//--Position Execution and Modification Functions
bool   OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   SetSlTpByTicket(ulong positionTicket, int sl, int tp);
bool   ClosePositionByTicket(ulong positionTicket);
bool   SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);
bool   CloseAllPositions(string symbol, ulong magicNumber);
bool   CloseAllBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllSellPositions(string symbol, ulong magicNumber);
bool   CloseAllMagicPositions(ulong magicNumber);
bool   CloseAllProfitablePositions(string symbol, ulong magicNumber);
bool   CloseAllLossPositions(string symbol, ulong magicNumber);

//--Position Status Monitoring Functions
int    BuyPositionsTotal();
int    SellPositionsTotal();
double PositionsTotalVolume();
double BuyPositionsTotalVolume();
double SellPositionsTotalVolume();
double BuyPositionsProfit();
double SellPositionsProfit();
int    MagicPositionsTotal(ulong magicNumber);
int    MagicBuyPositionsTotal(ulong magicNumber);
int    MagicSellPositionsTotal(ulong magicNumber);
double MagicPositionsTotalVolume(ulong magicNumber);
double MagicBuyPositionsTotalVolume(ulong magicNumber);
double MagicSellPositionsTotalVolume(ulong magicNumber);
double MagicPositionsProfit(ulong magicNumber);
double MagicBuyPositionsProfit(ulong magicNumber);
double MagicSellPositionsProfit(ulong magicNumber);
int    SymbolPositionsTotal(string symbol, ulong magicNumber);
int    SymbolBuyPositionsTotal(string symbol, ulong magicNumber);
int    SymbolSellPositionsTotal(string symbol, ulong magicNumber);
double SymbolPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolBuyPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolSellPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolPositionsProfit(string symbol, ulong magicNumber);
double SymbolBuyPositionsProfit(string symbol, ulong magicNumber);
double SymbolSellPositionsProfit(string symbol, ulong magicNumber);
string AccountPositionsStatus(bool formatForComment);
string MagicPositionsStatus(ulong magicNumber, bool formatForComment);
string SymbolPositionsStatus(string symbol, ulong magicNumber, bool formatForComment);
#import //--- Close import directive

PositionsManagerPanel.mq5 只包含一个用户输入(幻数)。

//--User input variables
input ulong magicNo = 101010;

接下来,我们创建全局变量来存储 volumeLotsltp

//-- Global variables
//-----------------------
//-- Get the current symbol spread and multiply it by a significant number
//-- to simulate user-input SL and TP values
double volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
int sl = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * 50;
int tp = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * 100;

让我们创建第一个图形对象,它将作为图形用户界面的背景。为此,我们将使用一个矩形标签。为此,我们将创建一个名为 CreateRectangleLabel() 的自定义函数。

//+-----------------------------------------------------------------------+
//| CreateRectangleLabel(): Creates a rectangle label on the chart window |
//+-----------------------------------------------------------------------+
void CreateRectangleLabel()
  {
//--- Detect if we have an object named the same as our rectangle label
   if(ObjectFind(0, "mainRectangleLabel") >= 0)
     {
      //--- Delete the specified object if it is not a rectangle label
      if(ObjectGetInteger(0, "mainRectangleLabel", OBJPROP_TYPE) != OBJ_RECTANGLE_LABEL)
        {
         ObjectDelete(0, "mainRectangleLabel");
        }
     }
   else
     {
      //-- Create the mainRectangleLabel
      ObjectCreate(0, "mainRectangleLabel", OBJ_RECTANGLE_LABEL, 0, 0, 0);
     }
//--- Set up the new rectangle label properties
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_XDISTANCE, 240);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_YDISTANCE, 2);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_XSIZE, 460);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_YSIZE, 520);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_BGCOLOR, clrMintCream);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_BACK, false);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_HIDDEN, true);
  }

我们还需要标签来显示交易面板中的文本。我们将创建另一个名为 CreateLabel() 的自定义函数来处理这项任务。

//+---------------------------------------------------------+
//| CreateLabel(): Creates a text label on the chart window |
//+---------------------------------------------------------+
void CreateLabel(
   string labelName, int xDistance, int yDistance, int xSize, int ySize,
   string labelText, color textColor, string fontType, int fontSize
)
  {
//--- Detect if we have an object with the same name as our label
   if(ObjectFind(0, labelName) >= 0)
     {
      //--- Delete the specified object if it is not a label
      if(ObjectGetInteger(0, labelName, OBJPROP_TYPE) != OBJ_LABEL)
        {
         ObjectDelete(0, labelName);
        }
     }
   else
     {
      //-- Create the label
      ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
     }
//--- Set up the new rectangle label properties
   ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, labelName, OBJPROP_XSIZE, xSize);
   ObjectSetInteger(0, labelName, OBJPROP_YSIZE, ySize);
   ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
   ObjectSetInteger(0, labelName, OBJPROP_COLOR, textColor);
   ObjectSetString(0, labelName, OBJPROP_FONT, fontType);
   ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, labelName, OBJPROP_BACK, false);
   ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, labelName, OBJPROP_SELECTED, false);
  }

我们的交易面板需要可点击或响应式按钮来执行不同的交易操作。因此,我们必须创建一个自定义函数来处理按钮的创建。让我们将该函数命名为 CreateButton()

//+------------------------------------------------------+
//| CreateButton(): Creates buttons on the chart window  |
//+------------------------------------------------------+
void CreateButton(
   string btnName, int xDistance, int yDistance, int xSize, int ySize, string btnText,
   string tooltip, color textColor, string fontType, int fontSize, color bgColor
)
  {
//--- Detect if we have an object named the same as our button
   if(ObjectFind(0, btnName) >= 0)
     {
      //--- Delete the specified object if it is not a button
      if(ObjectGetInteger(0, btnName, OBJPROP_TYPE) != OBJ_BUTTON)
        {
         ObjectDelete(0, btnName);
        }
     }
   else
     {
      //-- Create the button
      ObjectCreate(0, btnName, OBJ_BUTTON, 0, 0, 0);
     }
//--- Set up the new button properties
   ObjectSetInteger(0, btnName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, btnName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, btnName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, btnName, OBJPROP_XSIZE, xSize);
   ObjectSetInteger(0, btnName, OBJPROP_YSIZE, ySize);
   ObjectSetString(0, btnName, OBJPROP_TEXT, btnText);
   ObjectSetString(0, btnName, OBJPROP_TOOLTIP, tooltip);
   ObjectSetInteger(0, btnName, OBJPROP_COLOR, textColor);
   ObjectSetString(0, btnName, OBJPROP_FONT, fontType);
   ObjectSetInteger(0, btnName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, btnName, OBJPROP_BGCOLOR, bgColor);
  }

现在,我们需要另一个自定义函数来加载所有不同的图表对象和用户输入图形组件,这些都是用上面编写的函数创建的。让我们调用 LoadChartObjects() 函数。

//+--------------------------------------------------------------------+
//| LoadChartObjects(): Create and load the buttons and chart objects  |
//| for demonstrating how the imported library functions work          |
//+--------------------------------------------------------------------+
void LoadChartObjects()
  {
//-- Create the rectangle label first
   CreateRectangleLabel();

//-- Create the heading label
   CreateLabel(
      "headingLabel", 250, 10, 440, 60,
      "PositionsManager ex5 Library Demo Trade Panel",
      clrMidnightBlue, "Calibri", 10
   );

//-- Create the second heading label
   CreateLabel(
      "headingLabel2", 250, 30, 440, 60,
      ("Trading " + _Symbol + " with Magic Number: " + (string)magicNo),
      clrBlack, "Consolas", 11
   );

//-- "BUY": Button to call the imported ex5 OpenBuyPosition() function
   CreateButton(
      "OpenBuyPositionBtn", 250, 50, 215, 35, "BUY",
      "OpenBuyPosition() Function", clrMintCream, "Arial Black", 10, clrDodgerBlue
   );

//-- "SELL": Button to call the imported ex5 OpenSellPosition() function
   CreateButton(
      "OpenSellPositionBtn", 475, 50, 215, 35, "SELL",
      "OpenSellPosition() Function", clrMintCream, "Arial Black", 10, clrCrimson
   );

//-- "SetSlTpByTicket": Button to call the imported ex5 SetSlTpByTicket() function
   CreateButton(
      "SetSlTpBtn", 250, 90, 215, 35, "SetSlTpByTicket",
      "SetSlTpByTicket() Function", clrMintCream, "Arial Black", 10, clrDarkSlateGray
   );

//-- "SetTrailingStopLoss": Button to call the imported ex5 SetTrailingStopLoss() function when clicked
   CreateButton(
      "SetTrailingStopLossBtn", 475, 90, 215, 35, "SetTrailingStopLoss",
      "SetTrailingStopLoss Function", clrMintCream, "Arial Black", 10, clrDarkSlateGray
   );

//-- "ClosePositionsByTicket": Button to call the imported ex5 ClosePositionByTicket() function
   CreateButton(
      "ClosePositionsBtn", 250, 130, 215, 35, "ClosePositionsByTicket",
      "ClosePositionByTicket() Function", clrMintCream, "Arial Black", 10, clrMaroon
   );

//-- "CloseAllSymbolPositions": Button to call the imported ex5 CloseAllSymbolPositions() function
   CreateButton(
      "CloseAllPositionsBtn", 475, 130, 215, 35, "CloseAllPositions",
      "CloseAllPositions() Function", clrMintCream, "Arial Black", 10, clrMaroon
   );

//-- "CloseAllBuySymbolPositions": Button to call the imported ex5 CloseAllBuySymbolPositions() function
   CreateButton(
      "CloseAllBuyPositionsBtn", 250, 170, 215, 35, "CloseAllBuyPositions",
      "CloseAllBuyPositions() Function", clrMintCream, "Arial Black", 10, clrBrown
   );

//-- "CloseAllSellSymbolPositions": Button to call the imported ex5 CloseAllSellSymbolPositions() function
   CreateButton(
      "CloseAllSellPositionsBtn", 475, 170, 215, 35, "CloseAllSellPositions",
      "CloseAllSellPositions() Function", clrMintCream, "Arial Black", 10, clrBrown
   );

//-- "CloseAllMagicPositions": Button to call the imported ex5 CloseAllMagicPositions() function
   CreateButton(
      "CloseAllMagicPositionsBtn", 250, 210, 440, 35, "CloseAllMagicPositions",
      "CloseAllMagicPositions() Function", clrMintCream, "Arial Black", 10, C'203,18,55'
   );

//-- "CloseAllProfitablePositions": Button to call the imported ex5 CloseAllMagicPositions() function
   CreateButton(
      "CloseAllProfitablePositionsBtn", 250, 250, 215, 35, "CloseAllProfitablePositions",
      "CloseAllProfitablePositions() Function", clrMintCream, "Arial Black", 10, clrSeaGreen
   );

//-- "CloseAllLossPositions": Button to call the imported ex5 CloseAllLossPositions() function
   CreateButton(
      "CloseAllLossPositionsBtn", 475, 250, 215, 35, "CloseAllLossPositions",
      "CloseAllLossPositions() Function", clrMintCream, "Arial Black", 10, C'179,45,0'
   );

//-- Create the bottomHeadingLabel
   CreateLabel(
      "bottomHeadingLabel", 250, 310, 440, 60,
      (_Symbol + " - (Magic Number: " + (string)magicNo + ") Positions Status"),
      clrBlack, "Calibri", 12
   );

//-- Create totalOpenPositionsLabel
   CreateLabel(
      "totalOpenPositionsLabel", 250, 340, 440, 60,
      ("  Total Open:   " + (string)MagicPositionsTotal(magicNo)),
      clrNavy, "Consolas", 11
   );

//-- Create totalPositionsVolumeLabel
   CreateLabel(
      "totalPositionsVolumeLabel", 250, 360, 440, 60,
      ("  Total Volume: " + (string)NormalizeDouble(MagicPositionsTotalVolume(magicNo), 2)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalPositionsProfitLabel
   CreateLabel(
      "totalPositionsProfitLabel", 250, 380, 100, 60,
      (
         "  Total Profit: " + (string)(NormalizeDouble(MagicPositionsProfit(magicNo), 2)) +
         " " + AccountInfoString(ACCOUNT_CURRENCY)
      ),
      clrNavy, "Consolas", 11
   );

//-- Create the buyPositionsHeadingLabel
   CreateLabel(
      "buyPositionsHeadingLabel", 250, 410, 440, 60,
      ("BUY POSITIONS:"),
      clrBlack, "Calibri", 12
   );

//-- Create the totalBuyPositionsLabel
   CreateLabel(
      "totalBuyPositionsLabel", 250, 430, 440, 60,
      ("  Total Open:   " + (string)MagicBuyPositionsTotal(magicNo)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalBuyPositionsVolumeLabel
   CreateLabel(
      "totalBuyPositionsVolumeLabel", 250, 450, 440, 60,
      ("  Total Volume: " + (string)NormalizeDouble(MagicBuyPositionsTotalVolume(magicNo), 2)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalBuyPositionsProfitLabel
   CreateLabel(
      "totalBuyPositionsProfitLabel", 250, 470, 440, 60,
      (
         "  Total Profit: " + (string)(NormalizeDouble(MagicBuyPositionsProfit(magicNo), 2)) +
         " " + AccountInfoString(ACCOUNT_CURRENCY)
      ),
      clrNavy, "Consolas", 11
   );

//-- Create the sellPositionsHeadingLabel
   CreateLabel(
      "sellPositionsHeadingLabel", 475, 410, 440, 60,
      ("SELL POSITIONS:"),
      clrBlack, "Calibri", 12
   );

//-- Create the totalSellPositionsLabel
   CreateLabel(
      "totalSellPositionsLabel", 475, 430, 440, 60,
      ("  Total Open:   " + (string)MagicSellPositionsTotal(magicNo)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalSellPositionsVolumeLabel
   CreateLabel(
      "totalSellPositionsVolumeLabel", 475, 450, 440, 60,
      ("  Total Volume: " + (string)NormalizeDouble(MagicSellPositionsTotalVolume(magicNo), 2)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalSellPositionsProfitLabel
   CreateLabel(
      "totalSellPositionsProfitLabel", 475, 470, 100, 60,
      (
         "  Total Profit: " + (string)(NormalizeDouble(MagicSellPositionsProfit(magicNo), 2)) +
         " " + AccountInfoString(ACCOUNT_CURRENCY)
      ),
      clrNavy, "Consolas", 11
   );

//--- Redraw the chart to refresh it so that it loads our new chart objects
   ChartRedraw();
//---
  }

下一个自定义函数将负责在 EA 交易终止或去初始化时清理和删除所有图表对象和数据。让我们将其命名为 DeleteChartObjects()。它将被置于 OnDeinit() 标准事件处理 MQL5 函数中执行。

//+------------------------------------------------------------------------------+
//| DeleteChartObjects(): Delete all the chart objects when the EA is terminated |
//| on De-initialization                                                         |
//+------------------------------------------------------------------------------+
void DeleteChartObjects()
  {
//---
//--- Clean up and delete all the buttons or graphical objects
   ObjectDelete(0, "OpenBuyPositionBtn");
   ObjectDelete(0, "OpenSellPositionBtn");
   ObjectDelete(0, "SetSlTpBtn");
   ObjectDelete(0, "SetTrailingStopLossBtn");
   ObjectDelete(0, "ClosePositionsBtn");
   ObjectDelete(0, "CloseAllPositionsBtn");
   ObjectDelete(0, "CloseAllBuyPositionsBtn");
   ObjectDelete(0, "CloseAllSellPositionsBtn");
   ObjectDelete(0, "CloseAllMagicPositionsBtn");
   ObjectDelete(0, "CloseAllProfitablePositionsBtn");
   ObjectDelete(0, "CloseAllLossPositionsBtn");
   ObjectDelete(0, "mainRectangleLabel");

   ObjectDelete(0, "headingLabel");
   ObjectDelete(0, "headingLabel2");

   ObjectDelete(0, "bottomHeadingLabel");
   ObjectDelete(0, "totalOpenPositionsLabel");
   ObjectDelete(0, "totalPositionsVolumeLabel");
   ObjectDelete(0, "totalPositionsProfitLabel");

   ObjectDelete(0, "buyPositionsHeadingLabel");
   ObjectDelete(0, "totalBuyPositionsLabel");
   ObjectDelete(0, "totalBuyPositionsVolumeLabel");
   ObjectDelete(0, "totalBuyPositionsProfitLabel");

   ObjectDelete(0, "sellPositionsHeadingLabel");
   ObjectDelete(0, "totalSellPositionsLabel");
   ObjectDelete(0, "totalSellPositionsVolumeLabel");
   ObjectDelete(0, "totalSellPositionsProfitLabel");
  }

现在,我们已经完成了图形对象的创建和管理,让我们创建自定义函数来实现 PositionsManager.ex5 库中的一些导入函数。这组函数中的第一个函数我们称之为 ModifySlTp() 函数,它将负责修改由该 EA 交易系统开立的、与用户输入的幻数相匹配的所有仓位的止损(sl) 和止盈(tp)。每次点击图表上的 setSLTP 按钮时,都会执行该函数。

//+-------------------------------------------------------------------------+
// ModifySlTp(): This function demonstrates how to use the imported ex5     |
// bool SetSlTpByTicket(ulong positionTicket, int sl, int tp);              |
// It runs this function when the setSLTP button on the chart is clicked.   |
//+-------------------------------------------------------------------------+
void ModifySlTp()
  {
//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with
   int totalOpenPostions = PositionsTotal();
//--- Scan open positions
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- modify only the positions we have opened with this EA (magic number) using the BUY and SELL buttons on the chart
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }
      //--- modify the sl and tp of the position
      SetSlTpByTicket(positionTicket, sl, tp);//-- call the imported function from our ex5 library
     }
  }

下一个函数 SetTrailingSl() 将负责更新追踪止损。当点击图表上的 SetTrailingStopLoss 按钮时,以及在 OnTick() 事件处理函数中每一个新的分时点时,都会执行该函数。

//+-----------------------------------------------------------------------------------+
// SetTrailingSl(): This function demonstrates how to use the imported ex5            |
// bool SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);              |
// It runs this function when the SetTrailingStopLoss button on the chart is clicked. |
//+-----------------------------------------------------------------------------------+
void SetTrailingSl()
  {
   int trailingSl = sl;

//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with
   int totalOpenPostions = PositionsTotal();
//--- Scan open positions
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- modify only the positions we have opened with this EA (magic number) using the BUY and SELL buttons on the chart
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }
      //--- set the trailing stop loss
      SetTrailingStopLoss(positionTicket, trailingSl); //-- call the imported function from our ex5 library
     }
  }

ClosePositionWithTicket() 函数将负责关闭所有未平仓位,并在按下图表上的 ClosePositions 按钮时执行。

//+-----------------------------------------------------------------------------------+
// ClosePositionWithTicket(): This function demonstrates how to use the imported ex5  |
// bool ClosePositionByTicket(ulong positionTicket)                                   |
// It runs this function when the ClosePositions button on the chart is clicked.      |
//+-----------------------------------------------------------------------------------+
void ClosePositionWithTicket()
  {
//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with
   int totalOpenPostions = PositionsTotal();
//--- Scan open positions
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- close only the positions we have opened with this EA (magic number) using the BUY and SELL buttons on the chart
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }

      //--- Close the position
      ClosePositionByTicket(positionTicket);//-- call the imported function from our ex5 library
     }
  }

最后一个函数是标准的 OnChartEvent() MQL5 函数,它将检测各种按钮何时被按下,并执行相应的操作。几乎所有从 PositionsManager.ex5 库中导入的仓位管理函数都将通过该函数调用和执行。

//+------------------------------------------------------------------+
//| ChartEvent function to detect when the buttons are clicked       |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
//--- Detected a CHARTEVENT_CLICK event
   if(id == CHARTEVENT_OBJECT_CLICK)
     {
      Print(__FUNCTION__, ": ", sparam);

      //--- Buy when OpenBuyPositionBtn button (BUY) is pressed or clicked
      if(sparam == "OpenBuyPositionBtn")
        {
         //-- Call our imported function from the Toolkit/PositionsManager ex5 library
         OpenBuyPosition(magicNo, _Symbol, volumeLot, sl, tp, "ex5 PositionsManager");

         //--- Release and unpress the button
         ObjectSetInteger(0, "OpenBuyPositionBtn", OBJPROP_STATE, false);
        }

      //--- Sell when OpenSellPositionBtn button (SELL) is pressed
      if(sparam == "OpenSellPositionBtn")
        {
         //-- Call our imported function from the Toolkit/PositionsManager ex5 library
         OpenSellPosition(magicNo, _Symbol, volumeLot, sl, tp, "ex5 PositionsManager");
         //OpenSellPosition(magicNo, "NON-EXISTENT-Symbol-Name"/*_Symbol*/, volumeLot, sl, tp, "ex5 PositionsManager");

         //--- Release and unpress the button
         ObjectSetInteger(0, "OpenSellPositionBtn", OBJPROP_STATE, false);
        }

      //--- Modify specified positions SL and TP when SetSlTpBtn button (setSLTP) is pressed
      if(sparam == "SetSlTpBtn")
        {
         ModifySlTp();//-- Modify the SL and TP of the positions generated by the BUY and SELL buttons
         //--- Release and unpress the button
         ObjectSetInteger(0, "SetSlTpBtn", OBJPROP_STATE, false);
        }

      //--- Set the Trailing Stop Loss when SetSlTpBtn button (SetTrailingStopLossBtn) is pressed
      if(sparam == "SetTrailingStopLossBtn")
        {
         SetTrailingSl();//-- Set the Trailing Stop Loss for the positions generated by the BUY and SELL buttons
         //--- Release and unpress the button
         ObjectSetInteger(0, "SetTrailingStopLossBtn", OBJPROP_STATE, false);
        }

      //--- Close specified positions when SetSlTpBtn button (setSLTP) is pressed
      if(sparam == "ClosePositionsBtn")
        {
         ClosePositionWithTicket();//-- Close all the positions generated by the BUY and SELL buttons
         //--- Release and unpress the button
         ObjectSetInteger(0, "ClosePositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all positions for the current symbol when the CloseAllPositionsBtn button is pressed
      if(sparam == "CloseAllPositionsBtn")
        {
         CloseAllPositions(_Symbol, 0);//-- Close all the open symbol positions
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all buy positions for the current symbol when the CloseAllBuyPositionsBtn button is pressed
      if(sparam == "CloseAllBuyPositionsBtn")
        {
         CloseAllBuyPositions(_Symbol, magicNo);//-- Close all the open symbol buy positions
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllBuyPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all sell positions for the current symbol when the CloseAllSellPositionsBtn button is pressed
      if(sparam == "CloseAllSellPositionsBtn")
        {
         CloseAllSellPositions(_Symbol, magicNo);//-- Close all the open symbol sell positions
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllSellPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all positions with the specified magic number when the CloseAllMagicPositionsBtn button is pressed
      if(sparam == "CloseAllMagicPositionsBtn")
        {
         CloseAllMagicPositions(magicNo);//-- Close all the open positions with the specified magic number
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllMagicPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all profitable positions with the specified symbol and magic number when the CloseAllProfitablePositionsBtn button is pressed
      if(sparam == "CloseAllProfitablePositionsBtn")
        {
         CloseAllProfitablePositions(_Symbol, magicNo);//-- Close all the open profitable positions with the specified symbol and magic number
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllProfitablePositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all loss positions with the specified symbol and magic number when the CloseAllLossPositionsBtn button is pressed
      if(sparam == "CloseAllLossPositionsBtn")
        {
         CloseAllLossPositions(_Symbol, magicNo);//-- Close all the open loss positions with the specified symbol and magic number
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllLossPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Redraw the chart to refresh it
      ChartRedraw();
     }
//---
  }

在运行 PositionsTradePanel.mq5 EA 交易之前,我们需要将所有必要组件整合到 OnInit()OnDeinit()OnTick() 事件处理函数中。首先,在 EA 初始化过程中,使用 OnInit() 函数中的 LoadChartObjects() 函数加载所有图形对象。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//create buttons to demonstrate how the different ex5 library functions work
   LoadChartObjects();
//---
   return(INIT_SUCCEEDED);
  }

OnTick() 函数中调用并执行以下函数。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//-- Check for profitable positions and set the trailing stop loss on every new tick
   SetTrailingSl(); //-- Calls the ex5 library function responsible for setting Trailing stops
   LoadChartObjects(); //--- Update chart objects
  }

OnDeinit() 事件处理函数中添加去初始化函数,以便在关闭或终止 EA 交易时删除所有图形和图表对象。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
//-- Clean up the chart
   DeleteChartObjects();

//-- Clear any chart comments
   Comment("");
  }

您可以在本文末尾找到 PositionsManagerPanel.mq5 EA 交易的完整源代码文件,以进行更详细的检查。

保存、编译并在任何 MetaTrader 5 图表中加载 PositionsManagerPanel.mq5 EA交易。在 "依赖关系"选项卡中,您将看到从 PositionsManager.ex5 导入的所有库函数原型,以及保存 EX5 库的完整文件路径。在图表上加载后,打开几个仓位进行测试,并检查交易终端工具箱面板中 "专家" 选项卡日志,查看 PositionsManager.ex5 原型函数的打印日志数据。

仓位管理器面板 EX5 依赖关系选项卡


结论

现在,您已经对 MQL5 EX5 库有了全面的了解。我们介绍了它们的创建、集成以及在外部 MQL5 项目中的实现,包括如何调试各种 EX5 库错误,以及如何更新和重新部署它们。此外,我们还创建了一个功能强大、特性丰富的 EX5 仓位管理库,并配有详细的文档和实际用例。我还演示了如何在两个不同的实用 MQL5 EA 交易中导入和实现该库,提供了有效部署 EX5 库的实际案例。在下一篇文章中,我们将采用类似的方法开发一个全面的挂单管理 EX5 库,旨在简化 MQL5 应用程序中的挂单处理任务。感谢您的关注,祝愿您在交易和编程之旅中取得圆满成功。


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

用Python和MQL5进行投资组合优化 用Python和MQL5进行投资组合优化
本文探讨了使用Python和MQL5结合MetaTrader 5进行高级投资组合优化的技术。文章展示了如何开发用于数据分析、资产配置和交易信号生成的算法,强调了在现代金融管理和风险缓解中数据驱动决策的重要性。
在MetaTrader 5中实现基于EMA交叉的级联订单交易策略 在MetaTrader 5中实现基于EMA交叉的级联订单交易策略
本文介绍一个基于EMA交叉信号的自动交易算法,该算法适用于MetaTrader 5平台。文章详细阐述了在MQL5中开发一个EA所需的方方面面,以及在MetaTrader 5中进行测试的过程——从分析价格区间行为到风险管理。
S&amp;P 500交易策略在MQL5中的实现(适合初学者) S&amp;P 500交易策略在MQL5中的实现(适合初学者)
了解如何利用MQL5精准预测标普500指数,结合经典技术分析以增强稳定性,并将算法与经过时间验证的原则相结合,以获得稳健的市场洞察。
神经网络变得简单(第 91 部分):频域预测(FreDF) 神经网络变得简单(第 91 部分):频域预测(FreDF)
我们继续探索时间序列在频域中的分析和预测。在本文中,我们将领略一种在频域中预测数据的新方法,它可被加到我们之前研究过的众多算法当中。