English Русский Español Deutsch 日本語
preview
MQL5 交易工具包(第 8 部分):如何在代码库中实现和使用历史管理 EX5 库

MQL5 交易工具包(第 8 部分):如何在代码库中实现和使用历史管理 EX5 库

MetaTrader 5交易 |
65 3
Wanateki Solutions LTD
Kelvin Muturi Muigua

概述

在之前的文章中,我向大家介绍了如何开发一个强大而全面的历史管理 EX5 库,旨在简化您在 MetaTrader 5 中与交易历史记录的交互方式。这个强大的库使您能够直接从您的 MetaTrader 5 账户轻松扫描、检索、排序、分类和筛选所有类型的交易历史记录 —— 无论是已平仓头寸、挂单还是交易历史记录。在此基础上,本文将最后一步,向您展示如何有效地将历史管理库集成到您的 MQL5 项目中。

我将提供 EX5 库中可用函数的详细细分,并提供清晰的解释和实际示例。这些现实世界的用例不仅能帮助你理解每个函数是如何工作的,还能演示如何将它们应用到你的交易策略或分析工具中。无论您是希望提高编码技能的初学者,还是寻求优化工作流程的经验丰富的开发人员,本指南都将为您提供知识和工具,以充分发挥历史管理库的潜力。读完本文,您将能够自信地实施这个功能强大的库,并开始利用其函数以前所未有的效率管理交易历史记录。

在 MQL5 中使用历史管理库的好处

以下是将历史管理库集成到 MQL5 项目中的主要优势:

  • 轻松获取数据:该库提供直观的函数,只需编写最少的代码即可检索交易历史数据,例如交易、订单、持仓和挂单。这样就省去了编写复杂重复代码的需要。
  • 统一接口:所有交易历史数据均可通过单一、一致的接口访问,从而更容易管理和分析不同类型的交易活动。
  • 省时函数:通过一行函数调用,您可以快速访问关键交易数据,从而缩短开发时间,让您专注于策略实现。
  • 自动化数据处理:该库自动化了交易历史的检索、排序和过滤,简化了工作流程,提高了整体效率。
  • 无差错数据处理:该库确保准确检索交易历史数据,将手动编码可能出现的错误风险降至最低。
  • 全面数据覆盖:从已平仓头寸到挂单,该库提供所有类型的交易历史记录,确保不会遗漏任何数据。
  • 详细分析:诸如GetLastClosedPositionData()、GetLastFilledPendingOrderData()GetLastClosedProfitablePositionData() 等函数提供了有关交易的详细信息,从而可以进行更深入的分析和更好的决策。
  • 可自定义筛选器:您可以按交易品种、幻数时间范围筛选交易历史记录,从而进行针对您特定需求的定向分析。
  • 易于实现:该库旨在无缝集成到现有的 MQL5 项目中,只需极少的设置和配置。
  • 可扩展性:无论你是在开发一个小脚本还是一个大规模的交易系统,历史管理库都会毫不费力地扩展以满足你的需求。
  • 适应您的需求:该库的模块化设计允许你只使用所需的函数,使其高度适应各种交易风格和策略。
  • 可扩展功能:开发人员可以在该库现有函数的基础上,为独特的交易需求创建定制解决方案。
  • 预建解决方案:利用库中预先构建的函数,可以显著减少开发自定义交易历史管理工具所需的时间和成本。
  • 面向所有技能水平:无论你是初学者还是经验丰富的开发人员,该库直截了当的设计让所有技能级别的用户都可以使用它。
  • 数据驱动决策:获取全面的交易历史可以让你在制定或完善交易策略时做出明智的决定。
  • 历史回溯测试:使用该库检索历史交易数据进行回测,确保您的策略稳健可靠。
  • 简洁易读的代码:该库的函数旨在生成简洁、易读的代码,使维护和更新项目变得更加容易。



导入和设置历史管理 EX5 库

在使用历史管理 EX5 库的功能之前,您需要在 MQL5 项目中正确导入和设置该库。本节将逐步指导您完成该过程,确保设置顺利无障碍。

要在您的 MQL5 项目中使用 HistoryManager.ex5 库文件,需要将其下载并放置在 MetaTrader 5 安装文件夹中的正确目录中。在 MetaTrader 5 终端中,通过导航至“工具”>“MetaQuotes 语言编辑器”或按 F4 键启动 MetaEditor,然后按照以下步骤操作:

  • 第一步,创建所需的目录:

在 MQL5 根目录下的 Libraries 文件夹中,如果以下子目录尚不存在,则创建它们:"\Wanateki\Toolkit\HistoryManager" 。
MQL5 安装文件夹中的完整路径应如下所示:" MQL5\Libraries\Wanateki\Toolkit\HistoryManager "。

  • 第二步,下载 HistoryManager.ex5 二进制文件:

滚动到本文底部或末尾,找到附件 HistoryManager.ex5 文件,然后下载它。将文件放入您在第一步中创建的 HistoryManager 文件夹中。
您的 Libraries 文件夹目录结构现在应该类似于下图:

保存 HistoryManager.ex5 可执行库文件的目录

  • 第三步,  创建头文件库:

1.在 MetaEditor IDE 中,使用“新建”菜单项按钮启动 MQL 向导

MQL5 向导新建文件


2. 选择“包括 (*.mqh)” 选项,然后单击“下一步”

MQL5 向导新建包含文件设置

3.在包含文件的常规属性窗口中,找到“名称” 输入框。清除所有文本并输入以下路径以指定包含文件的目录和名称:Include\Wanateki\Toolkit\HistoryManager\HistoryManager.mqh

MQL5 向导新建包含文件目录设置

4.按下“完成”按钮生成库头文件。



编写历史管理器头文件 (HistoryManager.mqh)

现在我们已经成功创建了 HistoryManager.mqh 库头文件的空白包含文件,下一步是用定义其功能的必要组件填充它。这涉及添加数据结构以高效管理和存储信息,合并宏指令以简化重复任务并提高代码可读性,以及导入和声明库函数,这些函数将作为与 HistoryManager.ex5 功能交互的核心接口。

#property link 下方,我们将首先在全局范围内声明 DealData、OrderData、PositionDataPendingOrderData 数据结构。这些结构将负责存储各种交易历史属性,并且可以从我们代码库的任何部分访问。

struct DealData
  {
  // Add all the DealData members here
  }
  
struct OrderData
  {
  // Add all the OrderData members here
  } 
  
struct PositionData
  {
  // Add all the PositionData members here
  }
  
struct PendingOrderData
  {
  // Add all the PendingOrderData members here
  }

接下来,我们将定义几个常量来表示以为单位的关键时间段。这些包括 NOW、ONE_DAYONE_WEEKONE_MONTHONE_YEAREPOCH、TODAY、THIS_WEEK、THIS_MONTHTHIS_YEAR ,这将简化与时间相关的计算并提高代码的可读性。

#define NOW datetime(TimeCurrent())
#define ONE_DAY datetime(TimeCurrent() - PeriodSeconds(PERIOD_D1))
#define ONE_WEEK datetime(TimeCurrent() - PeriodSeconds(PERIOD_W1))
#define ONE_MONTH datetime(TimeCurrent() - PeriodSeconds(PERIOD_MN1))
#define ONE_YEAR datetime(TimeCurrent() - (PeriodSeconds(PERIOD_MN1) * 12))
#define EPOCH 0 // 1st Jan 1970
//--
#define TODAY 12
#define THIS_WEEK 13
#define THIS_MONTH 14
#define THIS_YEAR 15

我们还将创建 ALL_SYMBOLS 字符串宏来存储空字符串。这将作为信号,表明我们要处理所有交易品种的指定历史记录。此外,我们将定义 ALL_POSITIONSALL_ORDERSALL_DEALS 整数宏。这将使我们能够指定我们希望处理的订单类型、交易类型持仓历史记录。

#define ALL_SYMBOLS ""
#define ALL_POSITIONS 1110
#define ALL_ORDERS 1111
#define ALL_DEALS 1112

现在我们已经创建了所有必要的数据结构并定义了必要的预处理器指令或宏,是时候导入 HistoryManager.ex5 库了。为此,我们将使用 #import 指令,后跟 EX5 库所在的文件夹路径。

#import "Wanateki/Toolkit/HistoryManager/HistoryManager.ex5"

#import 指令之后,我们将添加库函数声明,最后插入结束的 #import 指令作为 HistoryManager.mqh 头文件的最后一行。

#import "Wanateki/Toolkit/HistoryManager/HistoryManager.ex5"
//--
void PrintDealsHistory(datetime fromDateTime, datetime toDateTime);
void PrintOrdersHistory(datetime fromDateTime, datetime toDateTime);
void PrintPositionsHistory(datetime fromDateTime, datetime toDateTime);
void PrintPendingOrdersHistory(datetime fromDateTime, datetime toDateTime);

//--
bool GetDealsData(DealData &dealsData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
bool GetOrdersData(OrderData &ordersData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
bool GetPositionsData(PositionData &positionsData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
bool GetPendingOrdersData(PendingOrderData &pendingOrdersData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
//--

// Add all the other function declarations here...

#import

以下是构成 HistoryManager.mqh 头文件的基本代码段。该头文件提供了将 历史管理 EX5 库导入和集成到 MQL5 项目中所需的所有资源。通过在源文件的开头将其包含,您可以访问所有数据结构和库函数,使它们可以立即使用。本文末尾还附有完整的 HistoryManager.mqh 源文件,供您下载。

struct DealData
  {
   ulong             ticket;
   ulong             magic;
   ENUM_DEAL_ENTRY   entry;
   ENUM_DEAL_TYPE    type;
   ENUM_DEAL_REASON  reason;
   ulong             positionId;
   ulong             order;
   string            symbol;
   string            comment;
   double            volume;
   double            price;
   datetime          time;
   double            tpPrice;
   double            slPrice;
   double            commission;
   double            swap;
   double            profit;
  };  
//--
struct OrderData
  {
   datetime                timeSetup;
   datetime                timeDone;
   datetime                expirationTime;
   ulong                   ticket;
   ulong                   magic;
   ENUM_ORDER_REASON       reason;
   ENUM_ORDER_TYPE         type;
   ENUM_ORDER_TYPE_FILLING typeFilling;
   ENUM_ORDER_STATE        state;
   ENUM_ORDER_TYPE_TIME    typeTime;
   ulong                   positionId;
   ulong                   positionById;
   string                  symbol;
   string                  comment;
   double                  volumeInitial;
   double                  priceOpen;
   double                  priceStopLimit;
   double                  tpPrice;
   double                  slPrice;
  };
//--
struct PositionData
  {
   ENUM_POSITION_TYPE type;
   ulong              ticket;
   ENUM_ORDER_TYPE    initiatingOrderType;
   ulong              positionId;
   bool               initiatedByPendingOrder;
   ulong              openingOrderTicket;
   ulong              openingDealTicket;
   ulong              closingDealTicket;
   string             symbol;
   double             volume;
   double             openPrice;
   double             closePrice;
   datetime           openTime;
   datetime           closeTime;
   long               duration;
   double             commission;
   double             swap;
   double             profit;
   double             tpPrice;
   double             slPrice;
   int                tpPips;
   int                slPips;
   int                pipProfit;
   double             netProfit;
   ulong              magic;
   string             comment;
  };
//--
struct PendingOrderData
  {
   string                  symbol;
   ENUM_ORDER_TYPE         type;
   ENUM_ORDER_STATE        state;
   double                  priceOpen;
   double                  tpPrice;
   double                  slPrice;
   int                     tpPips;
   int                     slPips;
   ulong                   positionId;
   ulong                   ticket;
   datetime                timeSetup;
   datetime                expirationTime;
   datetime                timeDone;
   ENUM_ORDER_TYPE_TIME    typeTime;
   ulong                   magic;
   ENUM_ORDER_REASON       reason;
   ENUM_ORDER_TYPE_FILLING typeFilling;
   string                  comment;
   double                  volumeInitial;
   double                  priceStopLimit;
  };
//--
#define NOW datetime(TimeCurrent())
#define ONE_DAY datetime(TimeCurrent() - PeriodSeconds(PERIOD_D1))
#define ONE_WEEK datetime(TimeCurrent() - PeriodSeconds(PERIOD_W1))
#define ONE_MONTH datetime(TimeCurrent() - PeriodSeconds(PERIOD_MN1))
#define ONE_YEAR datetime(TimeCurrent() - (PeriodSeconds(PERIOD_MN1) * 12))
#define EPOCH 0 // 1st Jan 1970
//--
#define TODAY 12
#define THIS_WEEK 13
#define THIS_MONTH 14
#define THIS_YEAR 15
//--
#define ALL_SYMBOLS ""
#define ALL_POSITIONS 1110
#define ALL_ORDERS 1111
#define ALL_DEALS 1112
//--

#import "Wanateki/Toolkit/HistoryManager/HistoryManager.ex5"
//--
void PrintDealsHistory(datetime fromDateTime, datetime toDateTime);
void PrintOrdersHistory(datetime fromDateTime, datetime toDateTime);
void PrintPositionsHistory(datetime fromDateTime, datetime toDateTime);
void PrintPendingOrdersHistory(datetime fromDateTime, datetime toDateTime);

//--
bool GetDealsData(DealData &dealsData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
bool GetOrdersData(OrderData &ordersData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
bool GetPositionsData(PositionData &positionsData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
bool GetPendingOrdersData(PendingOrderData &pendingOrdersData[], datetime fromDateTime, datetime toDateTime, string symbol, ulong magic);
//--

bool GetDealsData(DealData &dealsData[], datetime fromDateTime, datetime toDateTime);
bool GetOrdersData(OrderData &ordersData[], datetime fromDateTime, datetime toDateTime);
bool GetPositionsData(PositionData &positionsData[], datetime fromDateTime, datetime toDateTime);
bool GetPendingOrdersData(PendingOrderData &pendingOrdersData[], datetime fromDateTime, datetime toDateTime);

//--
bool GetAllDealsData(DealData &dealsData[]);
bool GetAllOrdersData(OrderData &ordersData[]);
bool GetAllPositionsData(PositionData &positionsData[]);
bool GetAllPendingOrdersData(PendingOrderData &pendingOrdersData[]);
//--
bool GetAllDealsData(DealData &dealsData[], string symbol, ulong magic);
bool GetAllOrdersData(OrderData &ordersData[], string symbol, ulong magic);
bool GetAllPositionsData(PositionData &positionsData[], string symbol, ulong magic);
bool GetAllPendingOrdersData(PendingOrderData &pendingOrdersData[], string symbol, ulong magic);

//--
bool GetLastClosedPositionData(PositionData &lastClosedPositionInfo);
bool LastClosedPositionType(ENUM_POSITION_TYPE &lastClosedPositionType);
bool LastClosedPositionVolume(double &lastClosedPositionVolume);
bool LastClosedPositionSymbol(string &lastClosedPositionSymbol);
bool LastClosedPositionTicket(ulong &lastClosedPositionTicket);
bool LastClosedPositionProfit(double &lastClosedPositionProfit);
bool LastClosedPositionNetProfit(double &lastClosedPositionNetProfit);
bool LastClosedPositionPipProfit(int &lastClosedPositionPipProfit);
bool LastClosedPositionClosePrice(double &lastClosedPositionClosePrice);
bool LastClosedPositionOpenPrice(double &lastClosedPositionOpenPrice);
bool LastClosedPositionSlPrice(double &lastClosedPositionSlPrice);
bool LastClosedPositionTpPrice(double &lastClosedPositionTpPrice);
bool LastClosedPositionSlPips(int &lastClosedPositionSlPips);
bool LastClosedPositionTpPips(int &lastClosedPositionTpPips);
bool LastClosedPositionOpenTime(datetime &lastClosedPositionOpenTime);
bool LastClosedPositionCloseTime(datetime &lastClosedPositionCloseTime);
bool LastClosedPositionSwap(double &lastClosedPositionSwap);
bool LastClosedPositionCommission(double &lastClosedPositionCommission);
bool LastClosedPositionInitiatedByPendingOrder(bool &lastClosedPositionInitiatedByPendingOrder);
bool LastClosedPositionInitiatingOrderType(ENUM_ORDER_TYPE &lastClosedPositionInitiatingOrderType);
bool LastClosedPositionId(ulong &lastClosedPositionId);
bool LastClosedPositionOpeningOrderTicket(ulong &lastClosedPositionOpeningOrderTicket);
bool LastClosedPositionOpeningDealTicket(ulong &lastClosedPositionOpeningDealTicket);
bool LastClosedPositionClosingDealTicket(ulong &lastClosedPositionClosingDealTicket);
bool LastClosedPositionMagic(ulong &lastClosedPositionMagic);
bool LastClosedPositionComment(string &lastClosedPositionComment);
bool LastClosedPositionDuration(long &lastClosedPositionDuration);

//--
bool GetLastClosedProfitablePositionData(PositionData &lastClosedProfitablePositionInfo);
bool GetLastClosedLossPositionData(PositionData &lastClosedLossPositionData);

//--
bool GetLastFilledPendingOrderData(PendingOrderData &getLastFilledPendingOrderData);
bool LastFilledPendingOrderType(ENUM_ORDER_TYPE &lastFilledPendingOrderType);
bool LastFilledPendingOrderSymbol(string &lastFilledPendingOrderSymbol);
bool LastFilledPendingOrderTicket(ulong &lastFilledPendingOrderTicket);
bool LastFilledPendingOrderPriceOpen(double &lastFilledPendingOrderPriceOpen);
bool LastFilledPendingOrderSlPrice(double &lastFilledPendingOrderSlPrice);
bool LastFilledPendingOrderTpPrice(double &lastFilledPendingOrderTpPrice);
bool LastFilledPendingOrderSlPips(int &lastFilledPendingOrderSlPips);
bool LastFilledPendingOrderTpPips(int &lastFilledPendingOrderTpPips);
bool LastFilledPendingOrderTimeSetup(datetime &lastFilledPendingOrderTimeSetup);
bool LastFilledPendingOrderTimeDone(datetime &lastFilledPendingOrderTimeDone);
bool LastFilledPendingOrderExpirationTime(datetime &lastFilledPendingOrderExpirationTime);
bool LastFilledPendingOrderPositionId(ulong &lastFilledPendingOrderPositionId);
bool LastFilledPendingOrderMagic(ulong &lastFilledPendingOrderMagic);
bool LastFilledPendingOrderReason(ENUM_ORDER_REASON &lastFilledPendingOrderReason);
bool LastFilledPendingOrderTypeFilling(ENUM_ORDER_TYPE_FILLING &lastFilledPendingOrderTypeFilling);
bool LastFilledPendingOrderTypeTime(datetime &lastFilledPendingOrderTypeTime);
bool LastFilledPendingOrderComment(string &lastFilledPendingOrderComment);

//--
bool GetLastCanceledPendingOrderData(PendingOrderData &getLastCanceledPendingOrderData);
bool LastCanceledPendingOrderType(ENUM_ORDER_TYPE &lastCanceledPendingOrderType);
bool LastCanceledPendingOrderSymbol(string &lastCanceledPendingOrderSymbol);
bool LastCanceledPendingOrderTicket(ulong &lastCanceledPendingOrderTicket);
bool LastCanceledPendingOrderPriceOpen(double &lastCanceledPendingOrderPriceOpen);
bool LastCanceledPendingOrderSlPrice(double &lastCanceledPendingOrderSlPrice);
bool LastCanceledPendingOrderTpPrice(double &lastCanceledPendingOrderTpPrice);
bool LastCanceledPendingOrderSlPips(int &lastCanceledPendingOrderSlPips);
bool LastCanceledPendingOrderTpPips(int &lastCanceledPendingOrderTpPips);
bool LastCanceledPendingOrderTimeSetup(datetime &lastCanceledPendingOrderTimeSetup);
bool LastCanceledPendingOrderTimeDone(datetime &lastCanceledPendingOrderTimeDone);
bool LastCanceledPendingOrderExpirationTime(datetime &lastCanceledPendingOrderExpirationTime);
bool LastCanceledPendingOrderPositionId(ulong &lastCanceledPendingOrderPositionId);
bool LastCanceledPendingOrderMagic(ulong &lastCanceledPendingOrderMagic);
bool LastCanceledPendingOrderReason(ENUM_ORDER_REASON &lastCanceledPendingOrderReason);
bool LastCanceledPendingOrderTypeFilling(ENUM_ORDER_TYPE_FILLING &lastCanceledPendingOrderTypeFilling);
bool LastCanceledPendingOrderTypeTime(datetime &lastCanceledPendingOrderTypeTime);
bool LastCanceledPendingOrderComment(string &lastCanceledPendingOrderComment);

//*
//--
bool GetLastClosedPositionData(PositionData &lastClosedPositionInfo, string symbol, ulong magic);
bool LastClosedPositionType(ENUM_POSITION_TYPE &lastClosedPositionType, string symbol, ulong magic);
bool LastClosedPositionVolume(double &lastClosedPositionVolume, string symbol, ulong magic);
bool LastClosedPositionSymbol(string &lastClosedPositionSymbol, string symbol, ulong magic);
bool LastClosedPositionTicket(ulong &lastClosedPositionTicket, string symbol, ulong magic);
bool LastClosedPositionProfit(double &lastClosedPositionProfit, string symbol, ulong magic);
bool LastClosedPositionNetProfit(double &lastClosedPositionNetProfit, string symbol, ulong magic);
bool LastClosedPositionPipProfit(int &lastClosedPositionPipProfit, string symbol, ulong magic);
bool LastClosedPositionClosePrice(double &lastClosedPositionClosePrice, string symbol, ulong magic);
bool LastClosedPositionOpenPrice(double &lastClosedPositionOpenPrice, string symbol, ulong magic);
bool LastClosedPositionSlPrice(double &lastClosedPositionSlPrice, string symbol, ulong magic);
bool LastClosedPositionTpPrice(double &lastClosedPositionTpPrice, string symbol, ulong magic);
bool LastClosedPositionSlPips(int &lastClosedPositionSlPips, string symbol, ulong magic);
bool LastClosedPositionTpPips(int &lastClosedPositionTpPips, string symbol, ulong magic);
bool LastClosedPositionOpenTime(datetime &lastClosedPositionOpenTime, string symbol, ulong magic);
bool LastClosedPositionCloseTime(datetime &lastClosedPositionCloseTime, string symbol, ulong magic);
bool LastClosedPositionSwap(double &lastClosedPositionSwap, string symbol, ulong magic);
bool LastClosedPositionCommission(double &lastClosedPositionCommission, string symbol, ulong magic);
bool LastClosedPositionInitiatingOrderType(ENUM_ORDER_TYPE &lastClosedPositionInitiatingOrderType, string symbol, ulong magic);
bool LastClosedPositionId(ulong &lastClosedPositionId, string symbol, ulong magic);
bool LastClosedPositionInitiatedByPendingOrder(bool &lastClosedPositionInitiatedByPendingOrder, string symbol, ulong magic);
bool LastClosedPositionOpeningOrderTicket(ulong &lastClosedPositionOpeningOrderTicket, string symbol, ulong magic);
bool LastClosedPositionOpeningDealTicket(ulong &lastClosedPositionOpeningDealTicket, string symbol, ulong magic);
bool LastClosedPositionClosingDealTicket(ulong &lastClosedPositionClosingDealTicket, string symbol, ulong magic);
bool LastClosedPositionMagic(ulong &lastClosedPositionMagic, string symbol, ulong magic);
bool LastClosedPositionComment(string &lastClosedPositionComment, string symbol, ulong magic);
bool LastClosedPositionDuration(long &lastClosedPositionDuration, string symbol, ulong magic);

//--
bool GetLastClosedProfitablePositionData(PositionData &lastClosedProfitablePositionInfo, string symbol, ulong magic);
bool GetLastClosedLossPositionData(PositionData &lastClosedLossPositionData, string symbol, ulong magic);

//--
bool GetLastFilledPendingOrderData(PendingOrderData &lastFilledPendingOrderData, string symbol, ulong magic);
bool LastFilledPendingOrderType(ENUM_ORDER_TYPE &lastFilledPendingOrderType, string symbol, ulong magic);
bool LastFilledPendingOrderSymbol(string &lastFilledPendingOrderSymbol, string symbol, ulong magic);
bool LastFilledPendingOrderTicket(ulong &lastFilledPendingOrderTicket, string symbol, ulong magic);
bool LastFilledPendingOrderPriceOpen(double &lastFilledPendingOrderPriceOpen, string symbol, ulong magic);
bool LastFilledPendingOrderSlPrice(double &lastFilledPendingOrderSlPrice, string symbol, ulong magic);
bool LastFilledPendingOrderTpPrice(double &lastFilledPendingOrderTpPrice, string symbol, ulong magic);
bool LastFilledPendingOrderSlPips(int &lastFilledPendingOrderSlPips, string symbol, ulong magic);
bool LastFilledPendingOrderTpPips(int &lastFilledPendingOrderTpPips, string symbol, ulong magic);
bool LastFilledPendingOrderTimeSetup(datetime &lastFilledPendingOrderTimeSetup, string symbol, ulong magic);
bool LastFilledPendingOrderTimeDone(datetime &lastFilledPendingOrderTimeDone, string symbol, ulong magic);
bool LastFilledPendingOrderExpirationTime(datetime &lastFilledPendingOrderExpirationTime, string symbol, ulong magic);
bool LastFilledPendingOrderPositionId(ulong &lastFilledPendingOrderPositionId, string symbol, ulong magic);
bool LastFilledPendingOrderMagic(ulong &lastFilledPendingOrderMagic, string symbol, ulong magic);
bool LastFilledPendingOrderReason(ENUM_ORDER_REASON &lastFilledPendingOrderReason, string symbol, ulong magic);
bool LastFilledPendingOrderTypeFilling(ENUM_ORDER_TYPE_FILLING &lastFilledPendingOrderTypeFilling, string symbol, ulong magic);
bool LastFilledPendingOrderTypeTime(datetime &lastFilledPendingOrderTypeTime, string symbol, ulong magic);
bool LastFilledPendingOrderComment(string &lastFilledPendingOrderComment, string symbol, ulong magic);

//--
bool GetLastCanceledPendingOrderData(PendingOrderData &lastCanceledPendingOrderData, string symbol, ulong magic);
bool LastCanceledPendingOrderType(ENUM_ORDER_TYPE &lastCanceledPendingOrderType, string symbol, ulong magic);
bool LastCanceledPendingOrderSymbol(string &lastCanceledPendingOrderSymbol, string symbol, ulong magic);
bool LastCanceledPendingOrderTicket(ulong &lastCanceledPendingOrderTicket, string symbol, ulong magic);
bool LastCanceledPendingOrderPriceOpen(double &lastCanceledPendingOrderPriceOpen, string symbol, ulong magic);
bool LastCanceledPendingOrderSlPrice(double &lastCanceledPendingOrderSlPrice, string symbol, ulong magic);
bool LastCanceledPendingOrderTpPrice(double &lastCanceledPendingOrderTpPrice, string symbol, ulong magic);
bool LastCanceledPendingOrderSlPips(int &lastCanceledPendingOrderSlPips, string symbol, ulong magic);
bool LastCanceledPendingOrderTpPips(int &lastCanceledPendingOrderTpPips, string symbol, ulong magic);
bool LastCanceledPendingOrderTimeSetup(datetime &lastCanceledPendingOrderTimeSetup, string symbol, ulong magic);
bool LastCanceledPendingOrderTimeDone(datetime &lastCanceledPendingOrderTimeDone, string symbol, ulong magic);
bool LastCanceledPendingOrderExpirationTime(datetime &lastCanceledPendingOrderExpirationTime, string symbol, ulong magic);
bool LastCanceledPendingOrderPositionId(ulong &lastCanceledPendingOrderPositionId, string symbol, ulong magic);
bool LastCanceledPendingOrderMagic(ulong &lastCanceledPendingOrderMagic, string symbol, ulong magic);
bool LastCanceledPendingOrderReason(ENUM_ORDER_REASON &lastCanceledPendingOrderReason, string symbol, ulong magic);
bool LastCanceledPendingOrderTypeFilling(ENUM_ORDER_TYPE_FILLING &lastCanceledPendingOrderTypeFilling, string symbol, ulong magic);
bool LastCanceledPendingOrderTypeTime(datetime &lastCanceledPendingOrderTypeTime, string symbol, ulong magic);
bool LastCanceledPendingOrderComment(string &lastCanceledPendingOrderComment, string symbol, ulong magic);

//--
string BoolToString(bool boolVariable);
datetime GetPeriodStart(int periodType);

#import
//+------------------------------------------------------------------+



历史管理 EX5 库的实际应用及概述

我们现在将检查库函数,将它们组织到各自的类别中,以获得更好的清晰度和可用性。在此过程中,我们将描述它们的角色,并提供简单的用例来演示如何使用它们来完成各种任务。

1.打印指定时间段内的交易历史记录

这些函数使您可以将交易历史数据直接打印到 MetaTrader 5 终端的日志中,方便快速参考和调试。它们的类型为 void ,这意味着它们不返回任何值,并且接受两个 datetime 类型的参数,用于指定要处理的历史记录的时间范围:

函数原型定义 描述 用例示例
void PrintDealsHistory(
   datetime fromDateTime,
   datetime toDateTime
);


打印指定时间范围内所有交易的详细信息。
// Print the deals history for the last 24 hours (1 day)
PrintDealsHistory(ONE_DAY, NOW);
void PrintOrdersHistory(
   datetime fromDateTime,
   datetime toDateTime
);

打印指定时间范围内所有订单的详细信息。
// Print the orders history for the last 24 hours (1 day)
PrintOrdersHistory(ONE_DAY, NOW);


void PrintPositionsHistory(
   datetime fromDateTime,
   datetime toDateTime
);


打印指定时间范围内所有已平仓位的详细信息。
// Print the positions history for the last 24 hours (1 day)
PrintPositionsHistory(ONE_DAY, NOW);
void PrintPendingOrdersHistory(
   datetime fromDateTime,
   datetime toDateTime
);

打印指定时间范围内所有挂单的详细信息。
// Print the pending orders history for the last 24 hours (1 day)
PrintPendingOrdersHistory(ONE_DAY, NOW);


2.检索指定时间范围内的交易历史数据

这些函数允许您以编程方式检索指定时间段内按交易品种数筛选的交易历史数据。检索到的数据存储在数据结构数组中,以便进行进一步的分析或处理:

函数原型定义 描述 用例示例
bool GetDealsData(
   DealData &dealsData[], // [out]
   datetime fromDateTime,
   datetime toDateTime,
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);


该函数搜索并检索指定时间范围内的交易数据。此外,它还提供了按交易品种和幻数筛选检索到的数据的选项,从而可以进行更有针对性的处理。
// Print the total account net profit for the last 7 days
DealData dealsData[];
if(
   GetDealsData(
      dealsData,
      ONE_WEEK, NOW,
      ALL_SYMBOLS, 0
   ) &&
   ArraySize(dealsData) > 0
)
  {
   double totalGrossProfit = 0.0,
          totalSwap = 0.0,
          totalCommission = 0.0;
   int totalDeals = ArraySize(dealsData);
   for(int k = 0; k < totalDeals; k++)
     {
      if(dealsData[k].entry == DEAL_ENTRY_OUT)
        {
         totalGrossProfit += dealsData[k].profit;
         totalSwap += dealsData[k].swap;
         totalCommission += dealsData[k].commission;
        }
     }
   double totalExpenses = totalSwap + totalCommission;
   double totalNetProfit = totalGrossProfit - MathAbs(totalExpenses);

   Print("-------------------------------------------------");
   Print(
      "Account No: ", AccountInfoInteger(ACCOUNT_LOGIN),
      " [ 7 DAYS NET PROFIT ]"
   );
   Print(
      "Total Gross Profit: ",
      DoubleToString(totalGrossProfit, 2),
      " ", AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print(
      "Total Swap: ", DoubleToString(totalSwap, 2),
      " ", AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print(
      "Total Commission: ", DoubleToString(totalCommission, 2),
      " ", AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print(
      "Total Net Profit: ",
      DoubleToString(totalNetProfit, 2), " ",
      AccountInfoString(ACCOUNT_CURRENCY)
   );
  }
bool GetOrdersData(
   OrderData &ordersData[], // [out]
   datetime fromDateTime,
   datetime toDateTime,
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数查询并检索指定时间范围内的订单数据。此外,它还提供了按交易品种和幻数筛选检索到的数据的选项。
// Print the total BUY Orders filled in the last 7 days
OrderData ordersData[];
if(
   GetOrdersData(ordersData, ONE_WEEK, NOW) &&
   ArraySize(ordersData) > 0
)
  {
   int totalBuyOrdersFilled = 0,
       totalOrders = ArraySize(ordersData);
   for(int w = 0; w < totalOrders; w++)
     {
      if(ordersData[w].type == ORDER_TYPE_BUY)
         ++totalBuyOrdersFilled;
     }
   Print("");
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print(totalBuyOrdersFilled, " BUY Orders Filled in the last 7 days!");
  }


bool GetPositionsData(
   PositionData &positionsData[], // [out]
   datetime fromDateTime,
   datetime toDateTime,
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);


该函数搜索并检索指定时间范围内的已平仓位历史数据。您可以选择按交易品种和幻数值筛选检索到的数据。
// Print total pips earned in last 24hrs for specified symbol and magic
string symbol = _Symbol;
long magic = 0;
PositionData positionsData[];
if(
   GetPositionsData(positionsData, ONE_DAY, NOW, symbol, magic) &&
   ArraySize(positionsData) > 0
)
  {
   int totalPipsEarned = 0,
       totalPositions = ArraySize(positionsData);
   for(int k = 0; k < totalPositions; k++)
     {
      totalPipsEarned += positionsData[k].pipProfit;
     }
   Print("");
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print(
      totalPipsEarned, " pips earned in the last 24hrs for ", symbol,
      " with magic no. ", magic
   );
  }

 
bool GetPendingOrdersData(
   PendingOrderData &pendingOrdersData[], // [out]
   datetime fromDateTime,
   datetime toDateTime,
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数获取指定时间范围内的挂单数据历史记录。它还允许您按交易品种和幻数筛选挂单历史数据。
// Print total number of buy and sell stops filled for symbol and magic
// in the last 7 days
string symbol = _Symbol;
long magic = 0;
PendingOrderData pendingOrdersData[];
if(
   GetPendingOrdersData(pendingOrdersData, ONE_WEEK, NOW, symbol, magic) &&
   ArraySize(pendingOrdersData) > 0
)
  {
   int totalBuyStopsFilled = 0, totalSellStopsFilled = 0,
       totalPendingOrders = ArraySize(pendingOrdersData);
   for(int k = 0; k < totalPendingOrders; k++)
     {
      if(pendingOrdersData[k].type == ORDER_TYPE_BUY_STOP)
         ++totalBuyStopsFilled;
      if(pendingOrdersData[k].type == ORDER_TYPE_SELL_STOP)
         ++totalSellStopsFilled;
     }
   Print("");
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN), ", Magic No = ", magic);
   Print(
      symbol, " --> Total Filled - (Buy Stops = ", totalBuyStopsFilled,
      ") (Sell Stops = ", totalSellStopsFilled, ") in the last 7 days."
   );
  }


3.检索所有历史交易记录

这些函数提供了一种全面的方式来获取账户中所有可用的交易历史数据,并可选择按交易品种数进行筛选,而无需输入特定的时间范围:

函数原型定义
描述
用例示例
bool GetAllDealsData(
   DealData &dealsData[], // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数检索所有交易数据,并可选择按交易品种和幻数筛选交易历史数据。
// Find and list total deposited funds in the account
DealData dealsData[];
if(GetAllDealsData(dealsData) && ArraySize(dealsData) > 0)
  {
   double totalDeposits = 0.0;
   int totalDeals = ArraySize(dealsData);
   Print("");
   for(int k = 0; k < totalDeals; k++)
     {
      if(dealsData[k].type == DEAL_TYPE_BALANCE)
        {
         totalDeposits += dealsData[k].profit;
         Print(
            dealsData[k].profit, " ", AccountInfoString(ACCOUNT_CURRENCY),
            " --> Cash deposit on: ", dealsData[k].time
         );
        }
     }
   Print("-------------------------------------------------");
   Print(
      "Account No: ", AccountInfoInteger(ACCOUNT_LOGIN),
      " Total Cash Deposits: ", totalDeposits, " ", 
      AccountInfoString(ACCOUNT_CURRENCY)
   );
  }

bool GetAllOrdersData(
   OrderData &ordersData[], // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数获取所有订单数据,同时提供按交易品种和幻数筛选订单历史的选项。
// Find if the account has ever gotten a Stop Out/Margin Call
OrderData ordersData[];
if(GetAllOrdersData(ordersData) && ArraySize(ordersData) > 0)
  {
   int totalStopOuts = 0;
   int totalOrders = ArraySize(ordersData);
   Print("");
   for(int k = 0; k < totalOrders; k++)
     {
      if(ordersData[k].reason == ORDER_REASON_SO)
        {
         ++totalStopOuts;
         Print(
            EnumToString(ordersData[k].type),
            " --> on: ", ordersData[k].timeDone
         );
        }
     }
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print("Total STOP OUT events: ", totalStopOuts);
  }

bool GetAllPositionsData(
   PositionData &positionsData[], // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数获取所有持仓历史数据,并可选择按交易品种和幻数筛选已平仓位数据。
// Find the average trade duration in seconds
PositionData positionsData[];
if(GetAllPositionsData(positionsData) && ArraySize(positionsData) > 0)
  {
   long totalTradesDuration = 0; 
   int totalPositions = ArraySize(positionsData);
   Print("");
   for(int k = 0; k < totalPositions; k++)
     {
      totalTradesDuration += positionsData[k].duration;
     }
   long averageTradesDuration = totalTradesDuration / totalPositions;
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print("Average trade duration: ", averageTradesDuration, " seconds.");
  }

bool GetAllPendingOrdersData(
   PendingOrderData &pendingOrdersData[], // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数检索所有挂单数据,并可选择按交易品种和幻数筛选挂单历史数据。
// Find the total expired pending orders in the account
PendingOrderData pendingOrdersData[];
if(GetAllPendingOrdersData(pendingOrdersData) && ArraySize(pendingOrdersData) > 0)
  {
   int totalExpiredPendingOrders = 0;
   int totalPendingOrders = ArraySize(pendingOrdersData);
   Print("");
   Print("-- EXPIRED PENDING ORDERS --");
   for(int k = 0; k < totalPendingOrders; k++)
     {
      if(pendingOrdersData[k].state == ORDER_STATE_EXPIRED)
        {
         ++totalExpiredPendingOrders;
         Print("Symbol = ", pendingOrdersData[k].symbol);
         Print("Time Setup = ", pendingOrdersData[k].timeSetup);
         Print("Ticket = ", pendingOrdersData[k].ticket);
         Print("Price Open = ", pendingOrdersData[k].priceOpen);
         Print(
            "SL Price = ", pendingOrdersData[k].slPrice,
            ", TP Price = ", pendingOrdersData[k].tpPrice
         );
         Print("Expiration Time = ", pendingOrdersData[k].expirationTime);
         Print("");
        }
     }
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print("Total Expired Pending Orders: ", totalExpiredPendingOrders);
  }


4.分析最近所平仓位

这些函数旨在获取有关最近所平仓位的全面详细信息,提供有关盈亏、交易量以及每个仓位平仓时间等关键方面的见解。 每个函数都有其独特用途,确保您可以访问上次交易的每一个关键方面。这些功能还允许您按交易品种数筛选要处理的仓位历史数据:

函数原型定义
描述
用例示例
bool GetLastClosedPositionData(
   PositionData &lastClosedPositionInfo, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数获取最终平仓位的完整快照,包括所有相关详细信息。您可以选择按交易品种和幻数筛选结果。
// Get the last closed position in the account
PositionData lastClosedPositionInfo;
if(
   GetLastClosedPositionData(lastClosedPositionInfo) &&
   lastClosedPositionInfo.ticket > 0
)
  {
   // Process the last closed position data
   Print("---LAST CLOSED POSITION--");
   Print("Symbol: ", lastClosedPositionInfo.symbol);
   Print("Type: ", EnumToString(lastClosedPositionInfo.type));
   Print("Open Time: ", lastClosedPositionInfo.openTime);
   Print("Close Time: ", lastClosedPositionInfo.closeTime);
   Print("Profit: ", lastClosedPositionInfo.profit);
   // Place more position properties analysis code....
  }
//-
// Get the last closed position for GBPUSD and magic 0
PositionData lastClosedPositionInfo;
string symbol = "GBPUSD";
if(
   GetLastClosedPositionData(lastClosedPositionInfo, symbol, 0) &&
   lastClosedPositionInfo.ticket > 0
)
  {
   // Process the last closed position data
   Print("---LAST CLOSED POSITION FOR ", symbol, " --");
   Print("Symbol: ", lastClosedPositionInfo.symbol);
   Print("Type: ", EnumToString(lastClosedPositionInfo.type));
   Print("Open Time: ", lastClosedPositionInfo.openTime);
   Print("Close Time: ", lastClosedPositionInfo.closeTime);
   Print("Profit: ", lastClosedPositionInfo.profit);
   // Place more position properties analysis code....
  }


bool LastClosedPositionType(
   ENUM_POSITION_TYPE &lastClosedPositionType, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

此函数显示最后一笔交易是买入还是卖出。它可以按交易品种和幻数筛选数据。
// Get the last closed position type in the account
ENUM_POSITION_TYPE lastClosedPositionType;
LastClosedPositionType(lastClosedPositionType);
Print(
   "Account's last closed position type: ",
   EnumToString(lastClosedPositionType)
);
//--
// Get the last closed position type for EURUSD and magic 0
ENUM_POSITION_TYPE lastClosedPositionType;
LastClosedPositionType(lastClosedPositionType, "EURUSD", 0);
Print(
   "EURUSD: last closed position type: ",
   EnumToString(lastClosedPositionType)
);

bool LastClosedPositionVolume(
   double &lastClosedPositionVolume, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数提供最近所平仓位的交易量。该函数还允许您按交易品种和幻数筛选数据。
// Get the last closed position volume in the account
double lastClosedPositionVolume;
LastClosedPositionVolume(lastClosedPositionVolume);
Print(
   "Account's last closed position volume: ",
   lastClosedPositionVolume
);
//--
// Get the last closed position volume for GBPUSD and magic 0
double lastClosedPositionVolume;
LastClosedPositionVolume(lastClosedPositionVolume, "GBPUSD", 0);
Print(
   "GBPUSD: last closed position volume: ",
   lastClosedPositionVolume
);

bool LastClosedPositionSymbol(
   string &lastClosedPositionSymbol, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数获取最近所平仓位的交易品种。您可以选择按交易品种和幻数筛选结果。
// Get the last closed position's symbol in the account
string lastClosedPositionSymbol;
LastClosedPositionSymbol(lastClosedPositionSymbol);
Print(
   lastClosedPositionSymbol, " is the last closed position symbol ", 
   "in the account"
);
//--
// Get the last closed position's symbol for magic 0
string lastClosedPositionSymbol;
LastClosedPositionSymbol(lastClosedPositionSymbol, ALL_SYMBOLS, 0);
Print(
   lastClosedPositionSymbol, " is the last closed position symbol ",
   "for magic no: 0."
);

bool LastClosedPositionTicket(
   ulong &lastClosedPositionTicket, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数检索最近平仓的编号。该函数可以选择按交易品种和幻数筛选结果。
// Get the last closed position's ticket in the account
long lastClosedPositionTicket;
LastClosedPositionTicket(lastClosedPositionTicket);
Print(
   "Account's last closed position's ticket: ",
   lastClosedPositionTicket
);
//--
// Get the last closed position's ticket for EURUSD and magic 0
long lastClosedPositionTicket;
LastClosedPositionTicket(lastClosedPositionTicket, "EURUSD", 0);
Print(
   "EURUSD: last closed position's ticket: ",
   lastClosedPositionTicket
);

bool LastClosedPositionProfit(
   double &lastClosedPositionProfit, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数提供最后一笔交易产生的毛利润。您还可以选择按交易品种和幻数筛选数据。
// Get the last closed position's profit in the account
double lastClosedPositionProfit;
LastClosedPositionProfit(lastClosedPositionProfit);
Print(
   "Account's last closed position's profit: ",
   lastClosedPositionProfit, " ", AccountInfoString(ACCOUNT_CURRENCY)
);
//--
// Get the last closed position's profit for EURUSD and magic 0
//double lastClosedPositionProfit;
LastClosedPositionProfit(lastClosedPositionProfit, "EURUSD", 0);
Print(
   "EURUSD: last closed position's profit: ",
   lastClosedPositionProfit, " ", AccountInfoString(ACCOUNT_CURRENCY)
);

以下是剩余的负责检索最后一个已平仓位属性的库函数。有关实现细节,请参阅上面的代码示例,包括 LastClosedPositionVolume()LastClosedPositionType() 等函数,因为它们都遵循类似的方法。

函数原型定义
描述
bool LastClosedPositionNetProfit(
   double &lastClosedPositionNetProfit, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

计算最后一笔交易的净利润,其中考虑了库存费和佣金。该函数可以选择按交易品种和幻数筛选数据。
bool LastClosedPositionPipProfit(
   int &lastClosedPositionPipProfit, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取最终所平仓位的盈利(以点数衡量)。数据可以按交易品种和幻数进行筛选。
bool LastClosedPositionClosePrice(
   double &lastClosedPositionClosePrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取平仓时的价格。该函数提供了按交易品种和幻数筛选结果的选项。
bool LastClosedPositionOpenPrice(
   double &lastClosedPositionOpenPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

显示最初开仓的价格。您还可以选择按交易品种和幻数筛选数据。
bool LastClosedPositionSlPrice(
   double &lastClosedPositionSlPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取上次所平仓位的止损价格。该函数允许您按交易品种和幻数筛选结果。
bool LastClosedPositionTpPrice(
   double &lastClosedPositionTpPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

提供为上一笔已平仓交易设定的止盈价格。此外,还提供了按交易品种和幻数筛选数据的选项。
bool LastClosedPositionSlPips(
   int &lastClosedPositionSlPips, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

表示上次平仓位的止损距离(以点数为单位)。还支持按交易品种和幻数进行数据筛选。
bool LastClosedPositionTpPips(
   int &lastClosedPositionTpPips, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

显示最近平仓交易的止盈距离(以点数为单位)。该函数还可以选择按交易品种和幻数筛选结果。
bool LastClosedPositionOpenTime(
   datetime &lastClosedPositionOpenTime, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);


获取上次平仓位开仓的时间戳。还提供按交易品种和幻数进行数据筛选的功能。
bool LastClosedPositionCloseTime(
   datetime &lastClosedPositionCloseTime, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

提供最近一次平仓的确切时间。您还可以选择按交易品种和幻数筛选结果。
bool LastClosedPositionSwap(
   double &lastClosedPositionSwap, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

显示与最后所平仓位相关的库存费的值。信息还可以按交易品种和幻数值进行筛选。
bool LastClosedPositionCommission(
   double &lastClosedPositionCommission, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取最后一笔已完成交易的佣金。同时支持按交易品种和幻数进行筛选。
bool LastClosedPositionInitiatingOrderType(
   ENUM_ORDER_TYPE &lastClosedPositionInitiatingOrderType, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

识别发起最后一个所平仓位的订单类型。您还可以选择按交易品种和幻数筛选结果。
bool LastClosedPositionId(
   ulong &lastClosedPositionId, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取最近所平仓位的唯一标识符。交易品种和幻数参数是可选的,只有在需要筛选结果时才需要。
bool LastClosedPositionInitiatedByPendingOrder(
   bool &lastClosedPositionInitiatedByPendingOrder, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);


检查最后一个所平仓位是否由挂单触发或发起。您还可以选择按交易品种和幻数筛选数据。
bool LastClosedPositionOpeningOrderTicket(
   ulong &lastClosedPositionOpeningOrderTicket, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

提供开仓订单的单号。该函数还可以选择按交易品种和幻数筛选数据。
bool LastClosedPositionOpeningDealTicket(
   ulong &lastClosedPositionOpeningDealTicket, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取发起该仓位的交易的单号。  您可以选择按交易品种和幻数筛选结果。
bool LastClosedPositionClosingDealTicket(
   ulong &lastClosedPositionClosingDealTicket, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取平仓交易的单号。按交易品种和幻数筛选数据也是一种选择。
bool LastClosedPositionMagic(
   ulong &lastClosedPositionMagic, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

取得与最后一个已平仓位相关的幻数。 您可以选择按交易品种和幻数筛选数据。
 
bool LastClosedPositionComment(
   string &lastClosedPositionComment, // [Out]
   string symbol,  // Optional parameter
   ulong magic // Optional parameter
);

检索与最近完成的交易相关的任何注释或备注。  还可以选择按交易品种和幻数筛选结果。
 
bool LastClosedPositionDuration(
   long &lastClosedPositionDuration, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

计算持仓保持开启的总时长(以秒为单位)。 您可以选择按交易品种和幻数筛选结果。


5.分析最近平仓的盈利和亏损头寸

当您需要检索上次已平仓盈利或亏损仓位的属性时,历史管理库提供了两个专门用于此任务的函数。这些函数允许您通过一次函数调用快速访问所有相关详细信息,使您能够根据盈利能力分析最近平仓的头寸。通过提供基本数据,它们有助于评估交易表现和完善策略。此外,这些函数还提供了按特定交易品种幻数筛选结果的灵活性,使您能够专注于特定交易并更有效地评估单个策略或资产的表现。

函数原型定义 描述 用例示例
bool GetLastClosedProfitablePositionData(
   PositionData &lastClosedProfitablePositionInfo, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检索最近一个以盈利平仓的仓位的详细信息和属性。该函数还允许您按交易品种和幻数筛选结果。
// Get the symbol, ticket, and net and pip profit of
// the last closed profitable position
PositionData lastClosedProfitablePosition;
if(
   GetLastClosedProfitablePositionData(lastClosedProfitablePosition) &&
   lastClosedProfitablePosition.ticket > 0
)
  {
   Print("-------------------------------------------------");
   Print(
      lastClosedProfitablePosition.symbol,
      " --> LAST CLOSED PROFITABLE POSITION"
   );
   Print("Ticket = ", lastClosedProfitablePosition.ticket);
   Print(
      "Net Profit = ", lastClosedProfitablePosition.netProfit, " ",
      AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print("Pip Profit = ", lastClosedProfitablePosition.pipProfit);
  }
//--
// Get the ticket, and net and pip profit of
// the last closed profitable position for EURUSD and magic 0
PositionData lastClosedProfitablePosition;
if(
   GetLastClosedProfitablePositionData(
      lastClosedProfitablePosition, "EURUSD", 0
   ) &&
   lastClosedProfitablePosition.ticket > 0
)
  {
   Print("-------------------------------------------------");
   Print("EURUSD --> LAST CLOSED PROFITABLE POSITION");
   Print("Ticket = ", lastClosedProfitablePosition.ticket);
   Print(
      "Net Profit = ", lastClosedProfitablePosition.netProfit, " ",
      AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print("Pip Profit = ", lastClosedProfitablePosition.pipProfit);
  }

bool GetLastClosedLossPositionData(
   PositionData &lastClosedLossPositionData,  // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检索最近一个以亏损平仓的仓位的详细信息和属性。该函数还允许您选择按交易品种和幻数筛选结果。
// Get the symbol, ticket, and net and pip profit of
// the last closed loss position
PositionData lastClosedLossPosition;
if(
   GetLastClosedLossPositionData(lastClosedLossPosition) &&
   lastClosedLossPosition.ticket > 0
)
  {
   Print("-------------------------------------------------");
   Print(
      lastClosedLossPosition.symbol,
      " --> LAST CLOSED LOSS POSITION"
   );
   Print("Ticket = ", lastClosedLossPosition.ticket);
   Print(
      "Net Profit = ", lastClosedLossPosition.netProfit, " ",
      AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print("Pip Profit = ", lastClosedLossPosition.pipProfit);
  }
//--
// Get the ticket, and net and pip profit of
// the last closed loss position for GBPUSD and magic 0
PositionData lastClosedLossPosition;
if(
   GetLastClosedLossPositionData(
      lastClosedLossPosition, "GBPUSD", 0
   ) &&
   lastClosedLossPosition.ticket > 0
)
  {
   Print("-------------------------------------------------");
   Print("GBPUSD --> LAST CLOSED LOSS POSITION");
   Print("Ticket = ", lastClosedLossPosition.ticket);
   Print(
      "Net Profit = ", lastClosedLossPosition.netProfit, " ",
      AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print("Pip Profit = ", lastClosedLossPosition.pipProfit);
  }


6.分析最近成交和取消的挂单

为了检索有关最近已完成或已取消的挂单的详细信息,历史管理库为此目的提供了专门的函数。这些函数允许您通过一次函数调用获取所有相关的订单详情,从而更容易分析挂单的执行或取消情况。通过提供关键数据,它们有助于评估订单处理、执行效率和交易策略调整。此外,这些功能还提供了按特定交易品种数筛选结果的灵活性,使您能够专注于特定订单,并根据精确的历史数据改进您的方法。

由于最后成交和取消的挂单函数是通过与处理最后平仓历史数据相同的方法导入和实现的,因此只会提供它们的函数原型和简要说明。如果您需要有关如何实现这些函数的示例,请参阅上面的“分析最近所平仓位”部分。

要获取最后成交的挂单的完整详细信息, GetLastFilledPendingOrderData() 函数会检索所有相关属性,从而提供订单执行情况的概览。如果您只需要特定属性,以下专用函数可帮助您提取单个详细信息或属性:

函数原型定义 描述
bool GetLastFilledPendingOrderData(
   PendingOrderData &lastFilledPendingOrderData, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

该函数检索最近已成交的挂单的所有相关详细信息,从而完整呈现订单的执行情况。
bool LastFilledPendingOrderType(
   ENUM_ORDER_TYPE &lastFilledPendingOrderType, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取订单类型,帮助您确定它是买入止损单、卖出限价单还是其他挂单类型。
bool LastFilledPendingOrderSymbol(
   string &lastFilledPendingOrderSymbol, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

识别交易工具,让您可以专注于与特定资产相关的订单。
bool LastFilledPendingOrderTicket(
   ulong &lastFilledPendingOrderTicket, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

提供唯一的订单编号,对于参考或跟踪特定已执行订单至关重要。
bool LastFilledPendingOrderPriceOpen(
   double &lastFilledPendingOrderPriceOpen, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取订单触发时的价格,这对于分析入场条件至关重要。
bool LastFilledPendingOrderSlPrice(
   double &lastFilledPendingOrderSlPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastFilledPendingOrderTpPrice(
   double &lastFilledPendingOrderTpPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

这些函数分别返回止损位和止盈位,确保您可以评估风险管理设置。
bool LastFilledPendingOrderSlPips(
   int &lastFilledPendingOrderSlPips, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastFilledPendingOrderTpPips(
   int &lastFilledPendingOrderTpPips, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

这些函数以点数表示止损和止盈距离,从而更清晰地展现交易的风险回报比。
bool LastFilledPendingOrderTimeSetup(
   datetime &lastFilledPendingOrderTimeSetup, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastFilledPendingOrderTimeDone(
   datetime &lastFilledPendingOrderTimeDone, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

LastFilledPendingOrderTimeSetup() 记录订单的下单时间,而 LastFilledPendingOrderTimeDone() 记录订单的执行时间。这有助于分析挂单建立到触发或成交之间的时间间隔。
bool LastFilledPendingOrderExpirationTime(
   datetime &lastFilledPendingOrderExpirationTime, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检查并提供到期时间,对时间敏感型交易策略很有用。
bool LastFilledPendingOrderPositionId(
   ulong &lastFilledPendingOrderPositionId, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检索仓位 ID,这对于将订单与其对应的仓位关联起来至关重要,从而确保准确跟踪。
bool LastFilledPendingOrderMagic(
   ulong &lastFilledPendingOrderMagic, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取幻数,让您可以根据自动化策略或交易机器人筛选订单。
bool LastFilledPendingOrderReason(
   ENUM_ORDER_REASON &lastFilledPendingOrderReason, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

记录执行原因,深入了解系统发起的或手动订单处理过程。
bool LastFilledPendingOrderTypeFilling(
   ENUM_ORDER_TYPE_FILLING &lastFilledPendingOrderTypeFilling, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastFilledPendingOrderTypeTime(
   datetime &lastFilledPendingOrderTypeTime, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

LastFilledPendingOrderTypeFilling()LastFilledPendingOrderTypeTime() 分别定义了订单执行方法和时间模型,有助于执行分析。
bool LastFilledPendingOrderComment(
   string &lastFilledPendingOrderComment, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取最后成交的挂单的所有相关注释,这对于标记和分类交易非常有用。

同样,如果您需要查看最后一个已取消的待处理订单, GetLastCanceledPendingOrderData() 函数只需一次调用即可检索所有详细信息。或者,您可以使用以下专用函数提取各个属性:

函数原型定义 描述
bool GetLastCanceledPendingOrderData(
   PendingOrderData &lastCanceledPendingOrderData, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

此函数提供最近取消的挂单的完整记录,包括其所有属性和特性。
bool LastCanceledPendingOrderType(
   ENUM_ORDER_TYPE &lastCanceledPendingOrderType, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检索并指定最后一个已取消的挂单的类型。
bool LastCanceledPendingOrderSymbol(
   string &lastCanceledPendingOrderSymbol, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取并识别最近取消的挂单的交易品种,以便进行有针对性的分析。
bool LastCanceledPendingOrderTicket(
   ulong &lastCanceledPendingOrderTicket, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

提供最后一个已取消待处理挂单的单号以供参考。
bool LastCanceledPendingOrderPriceOpen(
   double &lastCanceledPendingOrderPriceOpen, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取并保存最后一个已取消的挂单的原始预期入场价格。
bool LastCanceledPendingOrderSlPrice(
   double &lastCanceledPendingOrderSlPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastCanceledPendingOrderTpPrice(
   double &lastCanceledPendingOrderTpPrice, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

LastCanceledPendingOrderSlPrice()LastCanceledPendingOrderTpPrice() 检索最近取消的挂单的止损价和止盈价。
bool LastCanceledPendingOrderSlPips(
   int &lastCanceledPendingOrderSlPips, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastCanceledPendingOrderTpPips(
   int &lastCanceledPendingOrderTpPips, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

LastCanceledPendingOrderSlPips()LastCanceledPendingOrderTpPips() 表示最后一个已取消的挂单的止损和止盈距离(以点数为单位)。
bool LastCanceledPendingOrderTimeSetup(
   datetime &lastCanceledPendingOrderTimeSetup, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastCanceledPendingOrderTimeDone(
   datetime &lastCanceledPendingOrderTimeDone, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

LastCanceledPendingOrderTimeSetup() 提供最后一次取消的挂单的下达时间,而 LastCanceledPendingOrderTimeDone() 记录取消订单移除的时间。
bool LastCanceledPendingOrderExpirationTime(
   datetime &lastCanceledPendingOrderExpirationTime, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检索并保存已取消的挂单的到期时间。
bool LastCanceledPendingOrderMagic(
   ulong &lastCanceledPendingOrderMagic, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

获取并存储与最后一个已取消挂单关联的幻数,从而可以根据自动策略标识符进行筛选。
bool LastCanceledPendingOrderReason(
   ENUM_ORDER_REASON &lastCanceledPendingOrderReason, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

检索最后一个已取消挂单的订单原因,并允许我们确定订单是从哪个平台发起的。
bool LastCanceledPendingOrderTypeFilling(
   ENUM_ORDER_TYPE_FILLING &lastCanceledPendingOrderTypeFilling, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);
bool LastCanceledPendingOrderTypeTime(
   datetime &lastCanceledPendingOrderTypeTime, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

LastCanceledPendingOrderTypeFilling()LastCanceledPendingOrderTypeTime() 检索并保存最后一个已取消的挂单的成交类型和时间类型设置。
bool LastCanceledPendingOrderComment(
   string &lastCanceledPendingOrderComment, // [Out]
   string symbol, // Optional parameter
   ulong magic // Optional parameter
);

打开最后一个已取消的挂单时,会获取用户自定义的已保存注释,以便进行跟踪。


7.工具函数
函数原型定义 描述
string BoolToString(bool boolVariable);

该实用函数将布尔值转换为字符串(“ TRUE ”或“ FALSE ”),以便于阅读和记录。
 
datetime GetPeriodStart(int periodType);

该实用函数根据输入类型返回年、月、周(从星期日开始)datetime 值。它接受头文件中预定义的作为输入,即 TODAYTHIS_WEEKTHIS_MONTHTHIS_YEAR 。该函数简化了获取这些时间段的 datetime 型值的过程,只需一行代码即可完成。

在下一节中,我们将探讨一些实际示例,以展示如何使用历史管理库来生成有洞察力的交易分析。此外,我们将开发一个 EA 交易,利用历史数据库来确定执行新交易的最佳交易量或手数和仓位方向。这将重点介绍如何有效地将历史数据纳入自动化交易系统,从而改进策略执行和决策。


如何计算 EA 交易或交易品种的利润因子、毛利润/亏损以及每笔交易的平均毛利润/亏损

在评估 MetaTrader 5 中的 EA 交易或交易策略的性能时,最重要的指标之一是利润因子。利润率是一个衡量特定时期内毛利润与毛亏损比率的指标。它通过比较总收益和总损失,清晰地表明了交易策略的盈利能力。

较高的利润因子表明该策略产生的利润相对于其损失而言更多,这对任何交易系统来说都是一个理想的特征。相反,较低的利润因子表明该策略可能不太有效,可能需要进一步优化。

Profit Factor(利润因子)的计算公式如下:
Profit Factor = Gross Profit / Gross Loss

其中:

  • Gross Profit(总利润)是指所有盈利交易所赚取的总金额。
  • Gross Loss(总亏损)是指所有亏损交易造成的总损失金额。

利润因子大于 1 表示该策略可以获利,因为总利润大于总亏损。利润因子为 1 表示总利润和总亏损相等,表明处于盈亏平衡状态。利润因子小于 1 表示该策略正在亏损,因为总亏损超过了总利润。

理解和计算利润因子对于交易者和开发者来说至关重要,以便他们能够就其交易策略的可行性和绩效做出明智的决策。

在这个例子中,我将使用 HistoryManager.ex5 库来演示在 MQL5 代码中实现此计算是多么容易。

首先创建一个新的 EA 交易,并将其命名为 GetProfitFactor.mq5 。将其保存到以下文件夹路径:Experts\Wanateki\HistoryManager\GetProfitFactor.mq5 。请注意,保存 EA 交易的目录很重要,因为它会影响包含库函数原型定义的头文件的路径。

GetProfitFactor.mq5 EA 交易将通过计算毛利润、毛亏损和利润因子等关键指标,帮助您分析交易策略的绩效。

首先,我们需要包含 HistoryManager.ex5 库,并定义一个枚举,用于在我们的 EA 交易系统中输入交易品种字符串值。symbolName 枚举允许我们指定是要分析当前图表交易品种还是账户中的所有交易品种。结合幻数输入参数,我们可以对结果进行筛选,以适应我们目标交易品种和 EA 交易。

#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
//--
enum symbolName
  {
   CURRENT_CHART_SYMBOL,
   ALL_ACCOUNT_SYMBOLS,
  };

接下来,我们定义智能交易系统用户将用来过滤输出的输入参数。

//--- input parameters
input ulong  magicNo = 0;          //Magic Number (0 to disable)
input symbolName getSymbolName = ALL_ACCOUNT_SYMBOLS;

我们初始化用于存储计算结果和显示结果的变量。

string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);
double totalGrossProfit = 0.0, totalGrossLoss = 0.0,
       averageGrossProfit = 0.0, averageGrossLoss = 0.0,
       averageProfitOrLossPerTrade = 0.0,
       profitFactor = 0.0;
int totalTrades = 0,
    totalLossPositions = 0, totalProfitPositions = 0;
string symbol, printedSymbol, profitFactorSummery,
       commentString;

OnInit() 函数中,我们将使用 switch 语句来确定要分析的交易品种。一旦确定了交易品种,我们将调用 CalcProfitFactor() 函数,该函数负责计算利润因子。EA 交易加载后,首要任务是使用 Comment() 函数在图表上显示所有结果。

int OnInit()
  {
//---
   switch(getSymbolName)
     {
      case CURRENT_CHART_SYMBOL:
         symbol = _Symbol;
         break;
      case ALL_ACCOUNT_SYMBOLS:
         symbol = ALL_SYMBOLS;
         break;
      default:
         symbol = ALL_SYMBOLS;
     }
   printedSymbol = symbol;
   if(symbol == "")
      printedSymbol = "ALL_SYMBOLS";
//--
   CalcProfitFactor();
   Comment(commentString);
//---
   return(INIT_SUCCEEDED);
  }

在继续之前,让我们先编写 CalcProfitFactor() 函数,因为它包含了运行 GetProfitFactor EA 交易的大部分代码。 

我们将首先编写 CalcProfitFactor() 函数的函数签名,然后将所有变量重置为它们的初始值。这样可以确保之前的任何计算都不会干扰当前的分析。

   totalGrossProfit = 0.0;
   totalGrossLoss = 0.0;
   averageProfitOrLossPerTrade = 0.0;
   averageGrossProfit = 0.0;
   averageGrossLoss = 0.0;
   profitFactor = 0.0;
   totalTrades = 0;
   totalLossPositions = 0;
   totalProfitPositions = 0;

接下来,我们使用 GetAllPositionsData() 函数来检索历史交易数据。该函数根据指定的交易品种数,将所有交易仓位填充到 positionsData 数组中。同样,我们会检查函数是否返回 true 以及数组大小是否超过零,以确保我们有有效的数据要处理。这一步骤对于获取必要的交易信息以进行计算至关重要。

if(GetAllPositionsData(positionsData, symbol, magicNo) && ArraySize(positionsData) > 0)
     {
      //--
     }

然后我们遍历 positionsData 数组中的每一笔交易,以计算总毛利润和毛亏损。对于每一笔交易,我们都会检查利润是否大于零,以确定这笔交易是否获利。如果是,我们将 totalProfitPositions 计数器加一,并将利润加到 totalGrossProfit 中。如果利润小于或等于零,我们将 totalLossPositions 计数器加一,并将利润的绝对值加到 totalGrossLoss 中。这个循环可以帮助我们累积所有交易的总盈亏。

      totalTrades = ArraySize(positionsData);
      for(int r = 0; r < totalTrades; r++)
        {
         if(positionsData[r].profit > 0) // profitable trade
           {
            ++totalProfitPositions;
            totalGrossProfit += positionsData[r].profit;
           }
         else  // loss trade
           {
            ++totalLossPositions;
            totalGrossLoss += MathAbs(positionsData[r].profit);
           }
        }
      // Calculate the profit factor and other data
      if(totalGrossProfit > 0 || totalGrossLoss > 0)
         averageProfitOrLossPerTrade = NormalizeDouble(
                                          (totalGrossProfit + totalGrossLoss) / totalTrades, 2
                                       );
      if(totalGrossProfit > 0)
         averageGrossProfit = NormalizeDouble(
                                 (totalGrossProfit / totalProfitPositions), 2
                              );
      if(totalGrossLoss > 0)
         averageGrossLoss = NormalizeDouble(
                               (totalGrossLoss / totalLossPositions), 2
                            );
      //--
      if(totalGrossLoss == 0.0)
         profitFactor = 0.0; // Avoid division by zero, indicating no losses
      profitFactor = totalGrossProfit / totalGrossLoss;
     }

在汇总总毛利润和毛亏损后,我们着手计算各种指标。首先,我们通过将总毛利润和毛亏损之和除以总交易次数来计算每笔交易的平均利润或亏损。此外,我们通过将总毛利润除以盈利交易的数量来计算平均毛利润。同样地,我们通过将总毛损失除以亏损交易次数来计算平均毛损失。最后,我们用总毛利润除以总毛亏损来计算利润因子。这些指标可以全面反映交易策略的表现。

   profitFactorSummery = "Profit Factor = " + DoubleToString(profitFactor, 2);
   if(profitFactor > 2.0)
     {
      profitFactorSummery = profitFactorSummery +
                            "\nThe trading strategy is HIGHLY PROFITABLE and efficient.";
     }
   else
      if(profitFactor > 1.5)
        {
         profitFactorSummery = profitFactorSummery +
                               "\nThe trading strategy is profitable and well-balanced.";
        }
      else
         if(profitFactor > 1.0)
           {
            profitFactorSummery = profitFactorSummery +
                                  "\nThe trading strategy is slightly profitable but may need improvement.";
           }
         else
            if(profitFactor == 1.0)
              {
               profitFactorSummery = profitFactorSummery +
                                     "\nThe strategy is break-even with no net gain or loss.";
              }
            else
              {
               profitFactorSummery = profitFactorSummery +
                                     "\nThe trading strategy is unprofitable and needs optimization.";
              }

我们分析利润因子,生成描述交易策略表现的摘要字符串。我们对利润因子的解读如下:

  • 利润因子 > 2.0:该策略利润丰厚且效率极高。
  • 利润因子介于 1.0 和 1.5 之间:该策略略有盈利,但可能需要改进。
  • 利润因子 = 1.0:该策略收支平衡,既无净收益也无净亏损。
  • 利润因子 < 1.0:该策略无利可图,需要优化。

这种利润因子分析可以快速了解交易策略的整体有效性。

最后,我们生成一个详细的 comment 字符串,其中包含所有计算出的指标和利润因子摘要。该字符串的格式为显示正在分析的交易品种数、交易总数、盈利和亏损交易次数、总毛利润和毛亏损、每笔交易的平均利润或亏损、平均毛利润和毛亏损以及利润因子摘要。

以下是完整的 CalcProfitFactor() 函数,所有代码段均已正确放置。

void CalcProfitFactor()
  {
   totalGrossProfit = 0.0;
   totalGrossLoss = 0.0;
   averageProfitOrLossPerTrade = 0.0;
   averageGrossProfit = 0.0;
   averageGrossLoss = 0.0;
   profitFactor = 0.0;
   totalTrades = 0;
   totalLossPositions = 0;
   totalProfitPositions = 0;
//--
   PositionData positionsData[];
   if(GetAllPositionsData(positionsData, symbol, magicNo) && ArraySize(positionsData) > 0)
     {
      totalTrades = ArraySize(positionsData);
      for(int r = 0; r < totalTrades; r++)
        {
         if(positionsData[r].profit > 0) // profitable trade
           {
            ++totalProfitPositions;
            totalGrossProfit += positionsData[r].profit;
           }
         else  // loss trade
           {
            ++totalLossPositions;
            totalGrossLoss += MathAbs(positionsData[r].profit);
           }
        }
      // Calculate the profit factor and other data
      if(totalGrossProfit > 0 || totalGrossLoss > 0)
         averageProfitOrLossPerTrade = NormalizeDouble(
                                          (totalGrossProfit + totalGrossLoss) / totalTrades, 2
                                       );
      if(totalGrossProfit > 0)
         averageGrossProfit = NormalizeDouble(
                                 (totalGrossProfit / totalProfitPositions), 2
                              );
      if(totalGrossLoss > 0)
         averageGrossLoss = NormalizeDouble(
                               (totalGrossLoss / totalLossPositions), 2
                            );
      //--
      if(totalGrossLoss == 0.0)
         profitFactor = 0.0; // Avoid division by zero, indicating no losses
      profitFactor = totalGrossProfit / totalGrossLoss;
     }

// Analyze the Profit Factor result
   profitFactorSummery = "Profit Factor = " + DoubleToString(profitFactor, 2);
   if(profitFactor > 2.0)
     {
      profitFactorSummery = profitFactorSummery +
                            "\nThe trading strategy is HIGHLY PROFITABLE and efficient.";
     }
   else
      if(profitFactor > 1.5)
        {
         profitFactorSummery = profitFactorSummery +
                               "\nThe trading strategy is profitable and well-balanced.";
        }
      else
         if(profitFactor > 1.0)
           {
            profitFactorSummery = profitFactorSummery +
                                  "\nThe trading strategy is slightly profitable but may need improvement.";
           }
         else
            if(profitFactor == 1.0)
              {
               profitFactorSummery = profitFactorSummery +
                                     "\nThe strategy is break-even with no net gain or loss.";
              }
            else
              {
               profitFactorSummery = profitFactorSummery +
                                     "\nThe trading strategy is unprofitable and needs optimization.";
              }

   commentString =
      "\n\n-----------------------------------------------------------------------------------------------------" +
      "\n  HistoryManager.ex5 --- PROFIT FACTOR ANALYTICS ---" +
      "\n-----------------------------------------------------------------------------------------------------" +
      "\n   -> Symbol   = " + printedSymbol +
      "\n   -> Magic No = " + IntegerToString(magicNo) +
      "\n-----------------------------------------------------------------------------------------------------" +
      "\n-----------------------------------------------------------------------------------------------------" +
      "\n" + profitFactorSummery +
      "\n-----------------------------------------------------------------------------------------------------" +
      "\n-----------------------------------------------------------------------------------------------------" +
      "\n   -> Total Trades Analysed   = " + IntegerToString(totalTrades) +
      "\n   -> Total Profitable Trades = " + IntegerToString(totalProfitPositions) +
      "\n   -> Total Loss Trades       = " + IntegerToString(totalLossPositions) +
      "\n   --------------------------------------------------------" +
      "\n   -> Total Gross Profit = " + DoubleToString(totalGrossProfit, 2) + currency +
      "\n   -> Total Gross Loss   = -" + DoubleToString(totalGrossLoss, 2) + currency +
      "\n   ----------------------------------" +
      "\n   -> Total Gross (Profit - Loss) = " + DoubleToString(totalGrossProfit - totalGrossLoss, 2) + currency +
      "\n   --------------------------------------------------------" +
      "\n   -> Average Profit or Loss Per Trade = (-/+)" + DoubleToString(averageProfitOrLossPerTrade, 2) + currency +
      "\n   --------------------------------------------------------" +
      "\n   -> Average Gross Profit = " + DoubleToString(averageGrossProfit, 2) + currency +
      "\n   -> Average Gross Loss   = -" + DoubleToString(averageGrossLoss, 2) + currency +
      "\n   --------------------------------------------------------" +
      "\n-----------------------------------------------------------------------------------------------------";
//--
  }

我们还将该函数放在 OnTrade() 标准函数中,以便在每次交易操作后执行该函数并重新计算利润因子。

void OnTrade()
  {
//---
   CalcProfitFactor();
  }

为了确保我们在图表上打印更新后的数据,我们将使用 Comment() 函数打印包含有关当前更新后的利润因子的所有信息的 commentString ,并在 OnTick() 标准函数中每次传入新价格后打印它。

void OnTick()
  {
//---
   Comment(commentString);
  }

以下是 GetProfitFactor EA 交易在 MetaTrader 5 图表窗口中的输出结果。

由 HistoryManager.ex5 提供支持的 GetProfitFactor EA

为了方便起见,本文末尾附有 GetProfitFactor.mq5 源文件。



如何计算特定 EA 交易或代码本周的毛利润和净利润

在这个例子中,我们将创建一个 MQL5 脚本,该脚本将提供本周财务业绩的全面概述,包括毛利润、库存费、佣金和净利润。这将有助于演示如何使用历史管理库直接在 MetaTrader 5 终端中快速评估整个账户、特定交易品种或 EA 交易的幻数的每周表现。

我们首先创建一个名为 GetNetProfitThisWeek.mq5 的新脚本,并将其保存在此文件夹中:( Scripts\Wanateki\HistoryManager\GetNetProfitThisWeek.mq5 )。在新脚本文件中,首先包含 HistoryManager.mqh 库,这将使我们能够访问所有库函数。

#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>

接下来,我们将把所有代码都放在 OnStart() 函数中,以保持代码的简洁明了。脚本的第一部分是初始化一个字符串变量来存储账户货币。这将用于格式化财务指标的输出。

string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);

在开始任何计算之前,我们需要先计算本周开始时间。由于 HistoryManager.ex5 库包含实用函数 GetPeriodStart() ,我们可以使用该函数获取当前周开始的 datetime 值,因此这项工作变得很简单。要获得此值,我们只需调用 GetPeriodStart() 函数,并将 THIS_WEEK 参数作为输入即可。这将帮助我们筛选出本周内发生的交易。

datetime thisWeekStartTime = GetPeriodStart(THIS_WEEK);

我们声明一个数组来存储交易数据,并使用 GetDealsData() 函数将本周开始到当前时间( NOW )之间发生的交易填充到该数组中。同样,我们筛选出幻数为 0 的所有交易品种的交易。

if(
      GetDealsData(
         dealsData,
         thisWeekStartTime, NOW,
         ALL_SYMBOLS, 0
      ) &&
      ArraySize(dealsData) > 0
   )

我们初始化变量来存储总毛利润、总库存费总佣金。这些变量将用于从交易数据中累加相应的值。

double totalGrossProfit = 0.0,
             totalSwap = 0.0,
             totalCommission = 0.0;

我们遍历 dealsData 数组中的每一笔交易。对于每一笔交易,我们检查它是否是一笔成交交易( DEAL_ENTRY_OUT )。如果是,我们将利润库存费佣金分别添加到它们各自的总变量中。

int totalDeals = ArraySize(dealsData);
for(int k = 0; k < totalDeals; k++)
  {
   if(dealsData[k].entry == DEAL_ENTRY_OUT)
     {
      totalGrossProfit += dealsData[k].profit;
      totalSwap += dealsData[k].swap;
      totalCommission += dealsData[k].commission;
     }
  }

我们通过将总库存费佣金相加来计算总支出。此外,我们通过从总毛利润减去总支出来计算总净利润

double totalExpenses = totalSwap + totalCommission;
double totalNetProfit = totalGrossProfit - MathAbs(totalExpenses);

我们使用 Print() 函数将结果输出到 MetaTrader 5 日志中。这包括本周的总毛利润、总库存费、总佣金和总净利润。此外,我们还使用 Comment() 函数将结果直接显示在图表窗口中。其中包括当周财务指标的详细摘要。

HistoryManager.ex5 - 获取本周总净利润

以下是完整的 OnStart() 函数,其中包含所有代码段,并按正确的顺序完整呈现。

void OnStart()
  {
//---
   string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);
   datetime thisWeekStartTime = GetPeriodStart(THIS_WEEK);
   DealData dealsData[];
   if(
      GetDealsData(
         dealsData,
         thisWeekStartTime, NOW,
         ALL_SYMBOLS, 0
      ) &&
      ArraySize(dealsData) > 0
   )
     {
      double totalGrossProfit = 0.0,
             totalSwap = 0.0,
             totalCommission = 0.0;
      int totalDeals = ArraySize(dealsData);
      for(int k = 0; k < totalDeals; k++)
        {
         if(dealsData[k].entry == DEAL_ENTRY_OUT)
           {
            totalGrossProfit += dealsData[k].profit;
            totalSwap += dealsData[k].swap;
            totalCommission += dealsData[k].commission;
           }
        }
      double totalExpenses = totalSwap + totalCommission;
      double totalNetProfit = totalGrossProfit - MathAbs(totalExpenses);

      Print("-------------------------------------------------");
      Print(
         "Account No: ", AccountInfoInteger(ACCOUNT_LOGIN),
         " [ THIS WEEK'S NET PROFIT ]"
      );
      Print(
         "Total Gross Profit This Week: ",
         DoubleToString(totalGrossProfit, 2),
         " ", currency
      );
      Print(
         "Total Swaps This Week: ", DoubleToString(totalSwap, 2),
         " ", currency
      );
      Print(
         "Total Commission This Week: ", DoubleToString(totalCommission, 2),
         " ", currency
      );
      Print(
         "Total Net Profit This Week: ",
         DoubleToString(totalNetProfit, 2), " ",
         currency
      );
      
     //--
     Comment(
         "\n\n-----------------------------------------------------------------------------------------------------" +
         "-------------------------------------------------------------------" +
         "\n  HistoryManager.ex5 --- TOTAL NET PROFIT THIS WEEK ---" +
         "\n-----------------------------------------------------------------------------------------------------" +
         "------------------------------------------------------" +
         "\n DATE = ( From: " + TimeToString(thisWeekStartTime) + ", to: " + TimeToString(NOW) + " )" +
         "\n Account No  = " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) +
         "\n------------------------------------------------------" +
         "\n   -> Total Gross Profit =  " + DoubleToString(totalGrossProfit, 2) +
         currency +
         "\n   -> Total Swaps        =  " + DoubleToString(totalSwap, 2) +
         currency +
         "\n   -> Total Commission   =  " + DoubleToString(totalCommission, 2) +
         currency +
         "\n-----------------------------------------------------------------------------------------------------" +
         "------------------------------------------------------" +
         "\n   -> TOTAL NET PROFIT   =  " + DoubleToString(totalNetProfit, 2) +
         currency +
         "\n-----------------------------------------------------------------------------------------------------" +
         "------------------------------------------------------" +         
         "\n-----------------------------------------------------------------------------------------------------" +
         "-------------------------------------------------------------------"
      );
     }
  }

如需完整的脚本源代码,请下载本文末尾附件中的 GetNetProfitThisWeek.mq5 源文件。


如何计算特定交易品种或 EA 交易的盈亏比(以点数为单位)

在这个例子中,我们将探讨如何使用 MQL5 计算特定交易品种或整个 EA 交易的盈亏比(以点数为单位)。该计算是评估交易策略表现的关键工具,因为它提供了一个清晰且可量化的指标,可以根据盈亏点数来确定该策略是盈利、盈亏平衡还是亏损。提供的代码旨在分析历史交易数据,计算比率,并以用户友好且易于理解的格式呈现结果。

我们将把 EA 交易命名为 GetSymbolPipsProfitToLossRatio.mq5 ,将其保存在相应的文件夹中,并首先在源文件中将 HistoryManager.mqh 库作为第一行也是最重要的一行包含进去。接下来,我们将创建一个枚举来存储交易品种名称,并定义输入参数(交易品种数)。这些参数将允许用户根据自己的偏好筛选数据结果:是要分析特定交易品种的所有已平仓位,还是要分析包含特定幻数的特定交易品种的已平仓位,亦或是要分析所有具有特定幻数的仓位(无论交易品种如何)。

OnInit() 函数中,我们将检索账户货币,并根据用户的输入确定要分析的交易品种。如果用户选择 CURRENT_CHART_SYMBOL ,我们的 EA 交易将专注于当前图表交易品种。如果他们选择 ALL_ACCOUNT_SYMBOLS ,它将分析账户上交易的所有交易品种

我们将使用 GetAllPositionsData() 函数来获取指定交易品种和幻数的历史交易数据。这些数据将存储在 PositionData 结构数组中。同样地,我们将遍历数组,将交易分类为盈利或亏损。在此过程中,我们将计算盈利交易获得的总点数( totalPipsProfit )和亏损交易损失的总点数( totalPipsLoss )。

要计算点数盈亏比,我们将用获利的点数总和除以损失的点数总和。此外,我们将使用绝对值来确保比率始终为正。如果没有亏损交易(totalPipsLoss == 0 ),则比率将未定义,EA 交易将提供一条消息,表明该策略没有亏损交易。EA 交易将按如下方式解读该比率:

  • 比率 > 1.0:该策略可以获利,因为它获利的点数比损失的点数多。
  • 比率 == 1.0:从点数上看,该策略收支平衡。
  • 比率 < 1.0:这种策略是不能盈利的,因为它亏损的点数比盈利的点数多。

我们将使用 Comment() 函数将分析结果直接显示在图表上。输出结果将包括:分析的交易品种、使用的幻数(如有)、分析的交易总数、盈利和亏损的交易数量、总盈利点数和亏损点数,以及计算出的以点数计的盈亏比率及其解释。

HistoryManager.ex5 点数盈亏比率 EA

OnDeinit() 函数中,当 EA 交易被移除或去初始化时,我们将执行简单的清理工作,清除图表注释,以确保工作区干净整洁。我们将把 OnTick() 函数留空,因为分析只在初始化期间执行一次。但是,如果需要,您可以扩展此函数以执行实时计算或更新。

以下是所有代码段,按正确顺序排列:

//--
#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
//--
enum symbolName
  {
   CURRENT_CHART_SYMBOL,
   ALL_ACCOUNT_SYMBOLS,
  };
//--- input parameters
input ulong  magicNo = 0;          //Magic Number (0 to disable)
input symbolName getSymbolName = CURRENT_CHART_SYMBOL;
int OnInit()
  {
//---
   string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);
   string symbol, printedSymbol;

   switch(getSymbolName)
     {
      case CURRENT_CHART_SYMBOL:
         symbol = _Symbol;
         break;
      case ALL_ACCOUNT_SYMBOLS:
         symbol = ALL_SYMBOLS;
         break;
      default:
         symbol = ALL_SYMBOLS;
     }
   printedSymbol = symbol;
   if(symbol == "")
      printedSymbol = "ALL_SYMBOLS";
//--
   int totalTrades = 0;
   int totalLossPositions = 0;
   int totalProfitPositions = 0;
   double totalPipsProfit = 0;
   double totalPipsLoss = 0;
   string interpretation;
   double pipsProfitToLossRatio = 0;
//--
   PositionData positionsData[];
   if(GetAllPositionsData(positionsData, symbol, magicNo) && ArraySize(positionsData) > 0)
     {
      totalTrades = ArraySize(positionsData);
      for(int r = 0; r < totalTrades; r++)
        {
         if(positionsData[r].profit > 0) // profitable trade
           {
            ++totalProfitPositions;
            totalPipsProfit += positionsData[r].pipProfit;
           }
         else  // loss trade
           {
            ++totalLossPositions;
            totalPipsLoss += positionsData[r].pipProfit;
           }
        }
      // Calculate the pip profit loss ratioInterpretation
      if(totalPipsLoss == 0)
        {
         interpretation = "Pips Profit-to-Loss Ratio: Undefined (Total pips loss is zero)." +
                          "The strategy has no losing trades.";
        }
      else
        {
         pipsProfitToLossRatio = fabs(totalPipsProfit / totalPipsLoss);

         switch(pipsProfitToLossRatio > 1.0 ? 1 : pipsProfitToLossRatio == 1.0 ? 0 : -1)
           {
            case 1:
               interpretation = "Pips Profit-to-Loss Ratio: " + DoubleToString(pipsProfitToLossRatio, 2) +
                                ". The strategy is profitable as it gains more pips than it loses.";
               break;
            case 0:
               interpretation = "Pips Profit-to-Loss Ratio: " + DoubleToString(pipsProfitToLossRatio, 2) +
                                ". The strategy breaks even in terms of pips.";
               break;
            case -1:
               interpretation = "Pips Profit-to-Loss Ratio: " + DoubleToString(pipsProfitToLossRatio, 2) +
                                ". The strategy is unprofitable as it loses more pips than it gains.";
               break;
           }
        }

      Comment(
         "\n\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------" +
         "\n  HistoryManager.ex5 --- PIPS PROFIT TO LOSS RATIO ---" +
         "\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------" +
         "\n   -> Symbol   = " + printedSymbol +
         "\n   -> Magic No = " + IntegerToString(magicNo) +
         "\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------" +
         "\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------" +
         "\n" + interpretation +
         "\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------" +
         "\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------" +
         "\n   -> Total Trades Analysed     = " + IntegerToString(totalTrades) +
         "\n   -> Total Profitable Trades   = " + IntegerToString(totalProfitPositions) +
         " ( " + DoubleToString(totalPipsProfit, 0) + " Pips )" +
         "\n   -> Total Loss Trades         = " + IntegerToString(totalLossPositions) +
         " ( " + DoubleToString(totalPipsLoss, 0) + " Pips )" +
         "\n   --------------------------------------------------------------------------" +
         "\n   -> PIPS PROFIT TO LOSS RATIO = " + DoubleToString(pipsProfitToLossRatio, 2) +
         "\n   --------------------------------------------------------------------------" +
         "\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------"
      );
     }

//---
   return(INIT_SUCCEEDED);
  }

您可以在本文底部下载完整的 GetSymbolPipsProfitToLossRatio.mq5 源文件。


如何计算账户存款的总现金价值

在本节中,我们将创建一个名为 GetTotalDeposits.mq5 的简单 MQL5 脚本,该脚本利用我们库中的 GetAllDealsData() 函数来检索和分析交易数据,以帮助您清楚地了解您帐户的资金历史记录。当你想通过算法跟踪存入交易账户的总资金、审核账户的入金历史记录以用于记录保存或税务目的,或者只是为了验证入金交易的准确性时,这是一个非常有价值的工具。

我们将使用 GetAllDealsData() 函数来获取该账户的所有历史交易数据。这些数据将存储在 DealData 结构数组中。接下来,我们将初始化一个变量来存储入金总额,并遍历交易数据。通过检查交易类型是否为 DEAL_TYPE_BALANCE来识别入金交易。将这些交易的利润值相加,即可计算出总入金额。

计算并保存所有目标数据后,我们将使用 Comment() 函数将总入金值打印到终端并在图表上显示。这提供了一个便于用户查看的账户入金历史记录摘要。

#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
void OnStart()
  {
//---
// Find and list total deposited funds in the account
   DealData dealsData[];
   if(GetAllDealsData(dealsData) && ArraySize(dealsData) > 0)
     {
      double totalDeposits = 0.0;
      int totalDeals = ArraySize(dealsData);
      Print("");
      for(int k = 0; k < totalDeals; k++)
        {
         if(dealsData[k].type == DEAL_TYPE_BALANCE)
           {
            totalDeposits += dealsData[k].profit;
            Print(
               dealsData[k].profit, " ", AccountInfoString(ACCOUNT_CURRENCY),
               " --> Cash deposit on: ", dealsData[k].time
            );
           }
        }
      Print("-------------------------------------------------");
      Print(
         "Account No: ", AccountInfoInteger(ACCOUNT_LOGIN),
         " Total Cash Deposits: ", totalDeposits, " ",
         AccountInfoString(ACCOUNT_CURRENCY)
      );

      Comment(
         "\n\n-----------------------------------------------------------------------------------------------------" +
         "---------------------------------------------------------" +
         "\n  HistoryManager.ex5 --- TOTAL ACCOUNT DEPOSITS ---" +
         "\n-----------------------------------------------------------------------------------------------------" +
         "------------------------------------------------------" +
         "\n   -> Account No  = " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) +
         "\n   -> Total Cash Deposits =  " + DoubleToString(totalDeposits, 2) +
         AccountInfoString(ACCOUNT_CURRENCY) +
         "\n-----------------------------------------------------------------------------------------------------" +
         "------------------------------------------------------"
      );
     }
  }

您可以在本文底部下载完整的 GetTotalDeposits.mq5 源文件。


如何创建基于历史管理 EX5 库的价格数据驱动型 EA 交易

本文的最后一个示例,我们将创建一个名为 PriceTrader_EA 的简单而强大的基于价格数据的 EA 交易,该系统由 PositionsManager.ex5HistoryManager.ex5 库提供支持。这款 EA 交易具有巨大的盈利潜力,尤其是在大额账户上使用,并经过彻底的回测和优化之后。你会注意到 PriceTrader_EA 的源代码简洁高效,这得益于我们预先开发的 PositionsManager.ex5HistoryManager.ex5 库的使用。这些库使我们能够在保持可靠性和一致性的同时,最大限度地减少代码量。

PriceTrader_EA 旨在根据价格数据和历史交易表现做出交易决策。它包含以下主要特点:

  • 动态手数大小:EA 交易会根据先前交易的结果调整手数,在亏损交易后将手数加倍以弥补损失(在预先设定的限度内)。
  • 根据价格行为确定交易方向: PriceTrader_EA 根据H1 时间周期上的价格行为,按照当前趋势的方向开仓交易。
  • 风险管理:止损 ( SL ) 和止盈 ( TP ) 水平是根据当前点差动态计算的,确保能够适应不断变化的市场条件。

首先,我们将把 HistoryManager.mqhPositionsManager.mqh 库头文件添加到源文件的头部分。

#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
#include <Wanateki/Toolkit/PositionsManager/PositionsManager.mqh>

接下来,我们将定义 PriceTrader_EA输入参数。这些参数允许我们配置数、止盈止损点差倍数以及最大手数大小增量。

input ulong magicNo = 101010;
input int tpSpreadMulti = 70;
input int slSpreadMulti = 90;
input int maxLotIncrease = 1000;

我们将初始化关键变量来存储信息,例如当前点差、手数和未平仓头寸数量。这些变量将在 EA 交易的整个逻辑中使用。

bool eaJustLoaded = true;
double spread;
int spreadPips;
long minSLTP = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
long freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL);
double lotSize;
int sl, tp;
int totalOpenPositions, totalBuyPositionsOpen, totalSellPositionsOpen;
//--
PositionData lastClosedPositionInfo;

OnInit() 函数中,我们将计算当前点差,并根据点差乘数初始化 TPSL 水平。我们还会播放声音提示 PriceTrader_EA 已成功加载。

int OnInit()
  {
//---
   spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
   spread = NormalizeDouble(spread, _Digits);
   spreadPips = int(spread / _Point);
   tp = spreadPips * tpSpreadMulti;
   sl = spreadPips * slSpreadMulti;
//--
   PlaySound("connect.wav");
//---
   return(INIT_SUCCEEDED);
  }

OnDeinit() 函数中,我们将清除图表注释并播放声音,以表明 PriceTrader_EA 已卸载并从图表中移除。

void OnDeinit(const int reason)
  {
//---
   Comment("");
   PlaySound("disconnect.wav");
  }

OnTick() 函数包含 PriceTrader_EA 的核心逻辑。我们将在这个函数中执行以下操作:

  • 获取当前手数和未平仓头寸数量。
  • 检查 PriceTrader_EA 是否已加载,并根据 H1 价格走势开立初始交易。
  • 根据上一笔已平仓交易的结果调整手数和交易方向。
  • 连续开仓以把握盈利趋势或弥补损失。

void OnTick()
  {
//---
   lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
//--
   totalOpenPositions = SymbolPositionsTotal(_Symbol, magicNo);
   totalBuyPositionsOpen = SymbolBuyPositionsTotal(_Symbol, magicNo);
   totalSellPositionsOpen = SymbolSellPositionsTotal(_Symbol, magicNo);

   if(eaJustLoaded && totalOpenPositions == 0)
     {
      //--
      GetLastClosedPositionData(lastClosedPositionInfo, _Symbol, magicNo);
      if(lastClosedPositionInfo.ticket > 0 && lastClosedPositionInfo.profit < 0)
        {
         if(lastClosedPositionInfo.volume * 2 < lotSize * maxLotIncrease)
            lotSize = lastClosedPositionInfo.volume * 2; // double lot size
        }
      //--
      if(iOpen(_Symbol, PERIOD_H1, 0) < iClose(_Symbol, PERIOD_H1, 0))
        {
         OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Initial_Position");
        }
      else
        {
         OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Initial_Position");
        }
      if(totalOpenPositions > 0)
         eaJustLoaded = false;
     }
   else
     {
      eaJustLoaded = false;
     }

   if(totalOpenPositions == 0 && !eaJustLoaded)
     {
      if(GetLastClosedPositionData(lastClosedPositionInfo, _Symbol, magicNo))
        {
         if(lastClosedPositionInfo.profit > 0) // PROFITABLE TRADE
           {
            if(lastClosedPositionInfo.type == POSITION_TYPE_BUY)
              {
               OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Consecutive Profit");
              }
            else  // SELL POSITION
              {
               OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Consecutive Profit");
              }
           }
         else   // LOSS TRADE
           {
            if(lastClosedPositionInfo.volume * 2 < lotSize * maxLotIncrease)
               lotSize = lastClosedPositionInfo.volume * 2; // double lot size
            //--
            if(lastClosedPositionInfo.type == POSITION_TYPE_BUY)
              {
               // Reverse trade direction
               OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Loss Recovery");
              }
            else  // SELL POSITION
              {
               OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Loss Recovery");
              }
           }
        }
     }

如果您是那种喜欢或需要简单而有效的自动趋势跟踪策略以及动态风险管理的交易者,那么 PriceTrader_EA 就是您的理想选择。这款 EA 旨在通过自适应手数大小快速弥补损失,确保您的交易策略即使在不利的市场条件下也能保持稳健。此外,您还会注意到 PriceTrader_EA 的 代码编写非常巧妙,使得在 MetaTrader 5 上进行回测尽可能简单高效。用户输入的交易手数、止损止盈等参数会根据加载的交易品种自动调整,无需手动调整,确保不同交易品种的最佳性能。

您可以在本文底部找到完整的 PriceTrader_EA.mq5 源文件以及 PositionsManager.ex5 库。


回测 Price Trader EA 交易

让我们在 MetaTrader 5 策略测试器中运行回测,看看这个简单的交易策略在过去十四个月中的表现如何。
以下是我们将在策略测试器中应用的设置:

  • 经纪商: Deriv

  • 服务器:Deriv-Demo

  • 交易品种: Volatility 50 (1s) Index

  • 时间周期:每日

  • 测试期间(日期): 1 年 2 个月(2024 年 1 月至 2025 年 2 月)

  • 模型: 每次分时都基于真实的分时

  • 入金: 5000 USD

  • 杠杆: 1:1000

Wanateki PriceTrader_EA 回测设置

输入参数设置:

Wanateki PriceTrader_EA 回测输入

以下是 PriceTrader_EA 的回测结果

Wanateki PriceTrader_EA 回测报告

Wanateki PriceTrader_EA 回测报告


Wanateki PriceTrader_EA 回测报告

Wanateki PriceTrader_EA 回测报告

Wanateki PriceTrader_EA 回测报告

Wanateki PriceTrader_EA 回测报告

回顾我们的回测结果, PriceTrader_EA 实现了超过 129% 的惊人收益,同时保持了仅 29% 的低净值回撤。这种简单而有效的策略展现出巨大的潜力,还可以进一步改进和优化,以取得更好的结果。自从PriceTrader_EA 根据交易品种动态调整其输入参数,例如手数、止损止盈无论它交易的是哪种资产,您都可以轻松地在模拟账户上进行测试。只需将其加载到图表上,观察一天或更长时间的表现,看看它是否能产生利润,就像我们在策略测试器评估中所做的那样。这种灵活性使其成为测试和实盘交易的绝佳工具。


结论

正如本系列的最后一篇文章所展示的那样, HistoryManager.ex5 库是一个强大而高效的工具,可以简化 MetaTrader 5 中交易历史的处理。该库功能全面,可通过简单的单行函数调用,轻松访问和管理与交易、订单、持仓和挂单相关的数据。这种简化的方法可以节省时间和精力,让您专注于开发和优化您的交易策略。

在本文中,我提供了实用的代码示例,以帮助您充分发挥 HistoryManager.ex5 库的潜力。这些示例,结合本系列分享的知识,使您掌握了使用 MQL5 在 MetaTrader 5 中以算法方式处理从您的交易活动中生成的任何类型的历史数据所需的工具和资源。作为送给所有一直关注我的读者的临别礼物,我创建了 PriceTrader_EA ,这是一个基础但有效的 EA 交易,展示了其中一些概念的实际应用。

感谢您与我一同踏上这段 MQL5 开发之旅。你对学习和探索这些工具的投入,证明了你致力于掌握算法交易艺术的决心。一如既往,我祝愿您在探索解开市场复杂性并通过 MQL5 开发取得成功的过程中一切顺利。祝您编程愉快,愿您的策略永远奏效!


资源和源文件

本文中引用的所有代码都在下面提供,以方便您使用。这里包含的表格概述了附带的 EX5 库和源代码文件,便于您访问、实现和探索讨论的示例。

文件名称 描述
HistoryManager.ex5 旨在处理和管理交易历史的 EX5 库。
PositionsManager.ex5 用于管理和处理仓位和订单的 EX5 库。
HistoryManager.mqh 用于将 HistoryManager.ex5 库中的数据结构和原型函数导入到源文件中的头文件。
PositionsManager.mqh 用于将 PositionsManager.ex5 库中的原型函数导入到源文件中的头文件。
GetProfitFactor.mq5 EA 交易,通过计算毛利润、毛亏损和利润因子等关键指标来分析您的交易策略的表现。
GetNetProfitThisWeek.mq5 计算本周净利润的脚本。
GetSymbolPipsProfitToLossRatio.mq5 计算特定交易品种或整个 EA 交易的盈亏比(以点数为单位)的 EA 交易。
GetTotalDeposits.mq5 该脚本用于检索和分析交易数据,从而清晰概述您的账户资金或现金入金历史记录。
PriceTrader_EA.mq5 一款基于价格数据的 EA 交易,利用交易历史数据来检测价格方向并从损失中恢复。由 PositionsManager.ex5 和 HistoryManager.ex5 库提供支持。



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

最近评论 | 前往讨论 (3)
hini
hini | 5 7月 2025 在 15:31
作者您好,感谢您的代码。您测试过它的性能吗?在大量订单的 情况下速度如何?
Wanateki Solutions LTD
Kelvin Muturi Muigua | 6 7月 2025 在 23:03
您好 hini,感谢您对本文的关注。它的速度很快,即使在处理大量历史订单时,也能在几毫秒内返回查询结果,但这取决于您的电脑速度有多快。
hini
hini | 7 7月 2025 在 00:18
Kelvin Muturi Muigua #:
你好,hini,感谢您对本文的关注。它的速度很快,即使在处理大量历史订单时,也能在几毫秒内返回查询结果,但这取决于您的电脑速度有多快。
谢谢!
交易中的神经网络:层次化双塔变换器(终篇) 交易中的神经网络:层次化双塔变换器(终篇)
我们继续构建 Hidformer 层次化双塔变换器模型,专为分析和预测复杂多变量时间序列而设计。在本文中,我们会把早前就开始的工作推向逻辑结局 — 我们将在真实历史数据上测试模型。
用于MetaTrader 5的WebSocket:借助Windows API实现异步客户端连接 用于MetaTrader 5的WebSocket:借助Windows API实现异步客户端连接
本文详细介绍了开发一款自定义动态链接库的过程,该库旨在为MetaTrader程序提供异步WebSocket客户端连接功能。
MQL5 中的交易策略自动化(第十五部分):可视化价格行为的谐波形态模式 MQL5 中的交易策略自动化(第十五部分):可视化价格行为的谐波形态模式
本文探讨了在 MQL5 中实现谐波形态的自动化,详细介绍了如何在 MetaTrader 5 图表上对其进行检测和可视化。我们将实现一个EA,用于识别摆动点,验证基于斐波那契比率的形态,并通过清晰的图形标注执行交易。文章最后还提供了关于回测和优化程序的指导,以助力有效的交易。
开发多币种 EA 交易(第 24 部分):添加新策略(一) 开发多币种 EA 交易(第 24 部分):添加新策略(一)
在本文中,我们将研究如何将新策略连接到我们创建的自动优化系统。让我们看看我们需要创建哪些类型的 EA,以及是否可以在不更改 EA 库文件的情况下完成,或者尽量减少必要的更改。