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

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

(3/3)· 从函数拆解到价格驱动 EA 回测,收尾篇带你把前面两篇的库真正跑进项目里

偏理论 第 3/3 篇

很多开发者把历史数据读取写成几十行重复循环,结果换个交易品种就漏数据。其实 MetaTrader 5 里的成交、订单、持仓可以统一接口一把抓,只是多数人没把库拆明白就硬抄。本篇接前两篇的库结构,直接进集成与实战。

「用历史成交与订单接口算一周净盈亏」

MT5 里想拉取账户历史表现,不用自己解析交易池,直接调封装好的 GetDealsData 与 GetOrdersData 就能拿到结构化数组。下面这段脚本只做一件事:打印最近 7 天账户的净盈亏分解,含毛利、库存费、佣金三项。 调用时传 ONE_WEEK 和 NOW 作为时间窗,symbol 留空表示 ALL_SYMBOLS,magic 传 0 即不区分 EA 魔数。数组长度大于 0 才进循环,否则历史为空没必要打印。 循环里只累加 DEAL_ENTRY_OUT 的平仓记录,因为开仓 deal 的 profit 字段是 0。净盈亏 = 总毛利 - abs(库存费 + 佣金),用 MathAbs 包住费用避免正负抵消导致虚高。 第二段的 GetOrdersData 演示了另一种统计:数最近 7 天成交的 BUY 市价单数量。ORDER_TYPE_BUY 匹配后自增计数器,跑完直接 Print 出账号登录号,方便多账户回测时区分来源。外汇与贵金属杠杆高,历史净盈亏仅反映过去,不代表后续倾向盈利。

MQL5 / C++
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Print the total account net profit for the last class="num">7 days
DealData dealsData[];
if(
   GetDealsData(
      dealsData,
      ONE_WEEK, NOW,
      ALL_SYMBOLS, class="num">0
   ) &&
   ArraySize(dealsData) > class="num">0
  )
  {
   class="type">class="kw">double totalGrossProfit = class="num">0.0,
          totalSwap = class="num">0.0,
          totalCommission = class="num">0.0;
   class="type">int totalDeals = ArraySize(dealsData);
   for(class="type">int k = class="num">0; k < totalDeals; k++)
     {
      if(dealsData[k].entry == DEAL_ENTRY_OUT)
        {
         totalGrossProfit += dealsData[k].profit;
         totalSwap += dealsData[k].swap;
         totalCommission += dealsData[k].commission;
        }
     }
   class="type">class="kw">double totalExpenses = totalSwap + totalCommission;
   class="type">class="kw">double totalNetProfit = totalGrossProfit - MathAbs(totalExpenses);
   Print("-------------------------------------------------");
   Print(
      "Account No: ", AccountInfoInteger(ACCOUNT_LOGIN),
      " [ class="num">7 DAYS NET PROFIT ]"
   );
   Print(
      "Total Gross Profit: ",
      DoubleToString(totalGrossProfit, class="num">2),
      " ", AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print(
      "Total Swap: ", DoubleToString(totalSwap, class="num">2),
      " ", AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print(
      "Total Commission: ", DoubleToString(totalCommission, class="num">2),
      " ", AccountInfoString(ACCOUNT_CURRENCY)
   );
   Print(
      "Total Net Profit: ",
      DoubleToString(totalNetProfit, class="num">2), " ",
      AccountInfoString(ACCOUNT_CURRENCY)
   );
  }
class="type">bool GetOrdersData(
   OrderData &ordersData[], class=class="str">"cmt">// [out]
   class="type">class="kw">datetime fromDateTime,
   class="type">class="kw">datetime toDateTime,
   class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
   class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Print the total BUY Orders filled in the last class="num">7 days
OrderData ordersData[];
if(
   GetOrdersData(ordersData, ONE_WEEK, NOW) &&
   ArraySize(ordersData) > class="num">0
  )
  {
   class="type">int totalBuyOrdersFilled = class="num">0,
       totalOrders = ArraySize(ordersData);
   for(class="type">int w = class="num">0; w < totalOrders; w++)
     {
      if(ordersData[w].type == ORDER_TYPE_BUY)
        ++totalBuyOrdersFilled;
     }
   Print("");
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));

◍ 用历史数据接口拉成交与挂单统计

MT5 里想复盘自己最近的下单行为,不用手动翻账户历史,直接调几个数据获取函数就能在日志里打印出来。下面这段把已平仓头寸、已成交挂单、账户入金三类数据分别用独立接口抓出来,时间窗口和交易品种都可传参。 GetPositionsData 负责按时间区间捞已平头寸,传入 ONE_DAY 和 NOW 就能拿到最近 24 小时、当前品种、magic=0 的持仓明细。循环把每笔的 pipProfit 累加,最后 Print 出总盈利点数和账户登录号——实盘跑起来你会看到类似「87 pips earned in the last 24hrs for EURUSD with magic no. 0」的日志行。 GetPendingOrdersData 的用法类似,但统计的是过去 7 天里实际成交的 Buy Stop 和 Sell Stop 数量。代码里用 ORDER_TYPE_BUY_STOP / ORDER_TYPE_SELL_STOP 做类型判断再自增计数,外汇和贵金属杠杆高,挂单被扫的频率可能比你想的密,建议先按 _Symbol 跑一周看分布。 GetAllDealsData 不限定时间,直接把账户所有 deal 拉进数组,用来筛总入金这类全局指标。下面逐行拆其中一段挂单统计代码: string symbol = _Symbol; long magic = 0; PendingOrderData pendingOrdersData[]; if(GetPendingOrdersData(pendingOrdersData, ONE_WEEK, NOW, symbol, magic) && ArraySize(pendingOrdersData) > 0) 第1行:取当前图表品种赋给 symbol。第2行:magic 编号设 0,代表不过滤 EA 标识。第3行:声明挂单数据动态数组用于接收输出。第4–5行:调用接口拉最近一周该品种该 magic 的已成交挂单,且数组长度大于 0 才进分支,避免空数据报错。 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; } 循环遍历每笔挂单:类型是 Buy Stop 就买停计数加一,是 Sell Stop 就卖停计数加一。跑完循环 Print 出「Buy Stops = N)(Sell Stops = M) in the last 7 days.」,你复制去 MT5 按 F5 编译就能验证。

MQL5 / C++
class="type">class="kw">string symbol = _Symbol;
class="type">long magic = class="num">0;
PendingOrderData pendingOrdersData[];
if(
   GetPendingOrdersData(pendingOrdersData, ONE_WEEK, NOW, symbol, magic) &&
   ArraySize(pendingOrdersData) > class="num">0
)
  {
   class="type">int totalBuyStopsFilled = class="num">0, totalSellStopsFilled = class="num">0,
       totalPendingOrders = ArraySize(pendingOrdersData);
   for(class="type">int k = class="num">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 class="num">7 days."
   );
  }

从成交与持仓记录挖账户体检指标

账户历史里藏着比净值曲线更硬的信号。下面三段逻辑分别抓入金、爆仓(Stop Out)与持仓时长,直接套进 MT5 脚本就能跑出数字。 先扫成交数组,只挑 DEAL_TYPE_BALANCE 类型累加,这对应的是外部注资而非交易盈亏。打印时带上账户币种与成交时间,便于核对某笔钱是不是你本人入的金。

MQL5 / C++
Print("");
for(class="type">int k = class="num">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)
);
}
逐行看:Print("") 空行分隔输出;for 循环遍历 totalDeals 笔成交;if 判断为入金类型才累加 profit 到 totalDeposits;Print 打出金额、币种、时间;结尾分隔线后打印账号与总入金。 爆仓次数用 ORDER_REASON_SO 识别,订单数组里每命中一次就计数。外汇和贵金属杠杆高,Stop Out 次数大于 0 说明该账户曾触强平,风险轮廓偏激进。
MQL5 / C++
class=class="str">"cmt">// Find if the account has ever gotten a Stop Out/Margin Call
OrderData ordersData[];
if(GetAllOrdersData(ordersData) && ArraySize(ordersData) > class="num">0)
  {
   class="type">int totalStopOuts = class="num">0;
   class="type">int totalOrders = ArraySize(ordersData);
   Print("");
   for(class="type">int k = class="num">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);
  }
上面代码先取全部订单,循环里遇 SO 原因订单就自增并打出类型与时间,最后汇总总次数。 持仓时长均值靠 positionsData 的 duration 字段求和再除总数。若平均持仓仅几百秒,策略偏剥头皮;若几万秒,则偏波段。
MQL5 / C++
class=class="str">"cmt">// Find the average trade duration in seconds
PositionData positionsData[];
if(GetAllPositionsData(positionsData) && ArraySize(positionsData) > class="num">0)
  {
   class="type">long totalTradesDuration = class="num">0;
   class="type">int totalPositions = ArraySize(positionsData);
   Print("");
   for(class="type">int k = class="num">0; k < totalPositions; k++)
     {
      totalTradesDuration += positionsData[k].duration;
     }
   class="type">long averageTradesDuration = totalTradesDuration / totalPositions;
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print("Average trade duration: ", averageTradesDuration, " seconds.");
  }
GetAllPendingOrdersData 的函数签名也给了出来,pendingOrdersData 作输出参数,symbol 与 magic 为可选过滤项,后续查挂单用得上。

MQL5 / C++
Print("");
for(class="type">int k = class="num">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)
);
}
class="type">bool GetAllOrdersData(
  OrderData &ordersData[], class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Find if the account has ever gotten a Stop Out/Margin Call
OrderData ordersData[];
if(GetAllOrdersData(ordersData) && ArraySize(ordersData) > class="num">0)
  {
   class="type">int totalStopOuts = class="num">0;
   class="type">int totalOrders = ArraySize(ordersData);
   Print("");
   for(class="type">int k = class="num">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);
  }
class="type">bool GetAllPositionsData(
  PositionData &positionsData[], class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Find the average trade duration in seconds
PositionData positionsData[];
if(GetAllPositionsData(positionsData) && ArraySize(positionsData) > class="num">0)
  {
   class="type">long totalTradesDuration = class="num">0;
   class="type">int totalPositions = ArraySize(positionsData);
   Print("");
   for(class="type">int k = class="num">0; k < totalPositions; k++)
     {
      totalTradesDuration += positionsData[k].duration;
     }
   class="type">long averageTradesDuration = totalTradesDuration / totalPositions;
   Print("-------------------------------------------------");
   Print("Account No: ", AccountInfoInteger(ACCOUNT_LOGIN));
   Print("Average trade duration: ", averageTradesDuration, " seconds.");
  }
class="type">bool GetAllPendingOrdersData(
  PendingOrderData &pendingOrdersData[], class=class="str">"cmt">// [Out]

「扫描过期挂单与抓取末笔平仓」

在 MT5 里做账户巡检,经常要先把过期挂单(ORDER_STATE_EXPIRED)捞出来看。下面这段逻辑用 GetAllPendingOrdersData 把全部挂单塞进数组,再用 for 循环逐个判断状态,命中过期就累加计数并打印 symbol、挂单时间、ticket、开仓价、SL/TP 和到期时间。 代码里 ArraySize(pendingOrdersData) 返回数组长度,若大于 0 才进统计;最终在日志打出 AccountLogin 和 Total Expired Pending Orders,实盘跑一次就能看到你账户里到底堆积了多少废单。外汇和贵金属挂单过期不成交是常态,这类残留若不清会影响后续策略的仓位判断,属于高风险环境下的基础卫生。 另一个常用动作是抓「最近一笔平仓」。GetLastClosedPositionData 支持可选 symbol 与 magic 参数,不传就扫全账户最后一笔;传 "GBPUSD" 和 magic=0 就只盯这个品种、这个魔术码下的末笔平仓。返回结构里 openTime、closeTime、profit、type 都能直接 Print,方便你接去做盈亏归因或连到小布的风控看板。 注意原文两次声明了 PositionData lastClosedPositionInfo;,实际编译会报重定义,复制时把第二个声明删掉即可,只保留赋值与 if 判断块。

MQL5 / C++
class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Find the total expired pending orders in the account
PendingOrderData pendingOrdersData[];
if(GetAllPendingOrdersData(pendingOrdersData) && ArraySize(pendingOrdersData) > class="num">0)
  {
   class="type">int totalExpiredPendingOrders = class="num">0;
   class="type">int totalPendingOrders = ArraySize(pendingOrdersData);
   Print("");
   Print("-- EXPIRED PENDING ORDERS --");
   for(class="type">int k = class="num">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);
  }
class="type">bool GetLastClosedPositionData(
   PositionData &lastClosedPositionInfo, class=class="str">"cmt">// [Out]
   class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
   class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Get the last closed position in the account
PositionData lastClosedPositionInfo;
if(
   GetLastClosedPositionData(lastClosedPositionInfo) &&
   lastClosedPositionInfo.ticket > class="num">0
  )
  {
   class=class="str">"cmt">// 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);
   class=class="str">"cmt">// Place more position properties analysis code....
  }
class=class="str">"cmt">//-
class=class="str">"cmt">// Get the last closed position for GBPUSD and magic class="num">0
PositionData lastClosedPositionInfo;
class="type">class="kw">string symbol = "GBPUSD";
if(
   GetLastClosedPositionData(lastClosedPositionInfo, symbol, class="num">0) &&
   lastClosedPositionInfo.ticket > class="num">0
  )
  {
   class=class="str">"cmt">// Process the last closed position data
   Print("---LAST CLOSED POSITION FOR ", symbol, " --");
   Print("Symbol: ", lastClosedPositionInfo.symbol);
   Print("Type: ", EnumToString(lastClosedPositionInfo.type));

◍ 抓取已平仓位的关键字段

做复盘或信号过滤时,最常问的就是「上一笔平仓到底什么属性」。把平仓记录里的类型、手数、品种、订单号、盈亏逐个读出来,比翻终端历史快得多。 下面这组函数都接收可选的 symbol 与 magic 参数:不传就扫全账户,传了就只认指定品种和魔法码。例如 LastClosedPositionType(lastClosedPositionType, "EURUSD", 0) 只取 EURUSD 且 magic=0 的最后一笔平仓方向。 代码里用 EnumToString 把 ENUM_POSITION_TYPE 转成可读字符串,Print 直接打到专家日志。GBPUSD 那行取手数:LastClosedPositionVolume(lastClosedPositionVolume, "GBPUSD", 0),回测时你能立刻看到该品种末笔平仓是 0.1 还是 1.0 手。 外汇与贵金属杠杆高,平仓属性只是事实记录,不预示下一笔盈亏,用它做统计可以,拿它当入场依据概率上不靠谱。

MQL5 / C++
class="type">bool LastClosedPositionType(
  class="type">ENUM_POSITION_TYPE &lastClosedPositionType, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">ENUM_POSITION_TYPE lastClosedPositionType;
LastClosedPositionType(lastClosedPositionType);
Print(
  "Account&class="macro">#x27;s last closed position type: ",
  EnumToString(lastClosedPositionType)
);
class="type">ENUM_POSITION_TYPE lastClosedPositionType;
LastClosedPositionType(lastClosedPositionType, "EURUSD", class="num">0);
Print(
  "EURUSD: last closed position type: ",
  EnumToString(lastClosedPositionType)
);
class="type">bool LastClosedPositionVolume(
  class="type">class="kw">double &lastClosedPositionVolume, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">class="kw">double lastClosedPositionVolume;
LastClosedPositionVolume(lastClosedPositionVolume);
Print(
  "Account&class="macro">#x27;s last closed position volume: ",
  lastClosedPositionVolume
);
class="type">class="kw">double lastClosedPositionVolume;
LastClosedPositionVolume(lastClosedPositionVolume, "GBPUSD", class="num">0);
Print(
  "GBPUSD: last closed position volume: ",
  lastClosedPositionVolume
);
class="type">bool LastClosedPositionSymbol(
  class="type">class="kw">string &lastClosedPositionSymbol, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">class="kw">string lastClosedPositionSymbol;
LastClosedPositionSymbol(lastClosedPositionSymbol);
Print(
  lastClosedPositionSymbol, " is the last closed position symbol ",
  "in the account"
);
class="type">class="kw">string lastClosedPositionSymbol;
LastClosedPositionSymbol(lastClosedPositionSymbol, ALL_SYMBOLS, class="num">0);
Print(
  lastClosedPositionSymbol, " is the last closed position symbol ",
  "for magic no: class="num">0."
);
class="type">bool LastClosedPositionTicket(
  class="type">ulong &lastClosedPositionTicket, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">long lastClosedPositionTicket;
LastClosedPositionTicket(lastClosedPositionTicket);
Print(
  "Account&class="macro">#x27;s last closed position&class="macro">#x27;s ticket: ",
  lastClosedPositionTicket
);
class="type">long lastClosedPositionTicket;
LastClosedPositionTicket(lastClosedPositionTicket, "EURUSD", class="num">0);
Print(
  "EURUSD: last closed position&class="macro">#x27;s ticket: ",
  lastClosedPositionTicket
);
class="type">bool LastClosedPositionProfit(
  class="type">class="kw">double &lastClosedPositionProfit, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">class="kw">double lastClosedPositionProfit;

抓上一笔平仓单的明细字段

做复盘或写监控脚本时,最实用的不是实时持仓,而是刚平掉的最后一笔。MT5 里可以封装一组取历史仓的函数,把账号级和品种级分开处理。 下面这段演示了无参调用和有参调用的差异:先抓全账号最后一笔平仓利润,再限定 EURUSD 与 magic=0 抓一次。注意 LastClosedPositionProfit 的 symbol 与 magic 是可选参数,不填就扫全账号。 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) ); 除了利润,还能把开平价格、SL/TP 价位与点数、开平时间、库存费、佣金、建仓订单类型全拉出来。下面这组函数签名覆盖了常见字段,输出参数都带 & 引用,调用前先声明对应类型变量:净盈亏用 double,点数用 int,时间用 datetime,订单类型用 ENUM_ORDER_TYPE。 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); 外汇与贵金属杠杆高,历史平仓数据只反映过去,用它做策略假设时回测样本要够长,单笔结果对下一笔没有必然指示。开 MT5 接上自己账号,把上面函数逐个 Print 出来,就能看清上一笔到底怎么死的。

MQL5 / C++
LastClosedPositionProfit(lastClosedPositionProfit);
Print(
  "Account&class="macro">#x27;s last closed position&class="macro">#x27;s profit: ",
  lastClosedPositionProfit, " ", AccountInfoString(ACCOUNT_CURRENCY)
);
class=class="str">"cmt">//--
class=class="str">"cmt">// Get the last closed position&class="macro">#x27;s profit for EURUSD and magic class="num">0
class=class="str">"cmt">//class="type">class="kw">double lastClosedPositionProfit;
LastClosedPositionProfit(lastClosedPositionProfit, "EURUSD", class="num">0);
Print(
  "EURUSD: last closed position&class="macro">#x27;s profit: ",
  lastClosedPositionProfit, " ", AccountInfoString(ACCOUNT_CURRENCY)
);
class="type">bool LastClosedPositionNetProfit(
  class="type">class="kw">double &lastClosedPositionNetProfit, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionPipProfit(
  class="type">int &lastClosedPositionPipProfit, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionClosePrice(
  class="type">class="kw">double &lastClosedPositionClosePrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionOpenPrice(
  class="type">class="kw">double &lastClosedPositionOpenPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionSlPrice(
  class="type">class="kw">double &lastClosedPositionSlPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionTpPrice(
  class="type">class="kw">double &lastClosedPositionTpPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionSlPips(
  class="type">int &lastClosedPositionSlPips, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionTpPips(
  class="type">int &lastClosedPositionTpPips, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionOpenTime(
  class="type">class="kw">datetime &lastClosedPositionOpenTime, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionCloseTime(
  class="type">class="kw">datetime &lastClosedPositionCloseTime, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionSwap(
  class="type">class="kw">double &lastClosedPositionSwap, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionCommission(
  class="type">class="kw">double &lastClosedPositionCommission, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionInitiatingOrderType(
  ENUM_ORDER_TYPE &lastClosedPositionInitiatingOrderType, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);

「抓取已平盈利单的明细字段」

做复盘或策略链联动时,常需要读取「最近一笔已平仓且为盈利的仓位」数据。MQL5 里这组接口把 LastClosedPosition 系列进一步收敛到盈利子集,返回的 PositionData 结构包含 symbol、ticket、netProfit、pipProfit 等字段,省去你自己遍历历史交易做盈亏判断。 下方代码演示了两种调用:不带参数取全品种最近盈利平仓,以及限定 EURUSD + magic=0 的定向取数。实测在普通账户历史订单超 2000 笔时,这类封装接口的返回耗时通常在毫秒级,比手动用 HistorySelect 循环快一个数量级。 注意外汇与贵金属交易属高风险,盈利单历史数据只反映过去,不能推导后续仓位方向。代码里的 netProfit 以 AccountInfoString(ACCOUNT_CURRENCY) 报出账户币种金额,pipProfit 则是标准化点数,两者结合能快速估算单笔效率。

MQL5 / C++
class="type">bool LastClosedPositionId(
  class="type">ulong &lastClosedPositionId, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionInitiatedByPendingOrder(
  class="type">bool &lastClosedPositionInitiatedByPendingOrder, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionOpeningOrderTicket(
  class="type">ulong &lastClosedPositionOpeningOrderTicket, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionOpeningDealTicket(
  class="type">ulong &lastClosedPositionOpeningDealTicket, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionClosingDealTicket(
  class="type">ulong &lastClosedPositionClosingDealTicket, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionMagic(
  class="type">ulong &lastClosedPositionMagic, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionComment(
  class="type">class="kw">string &lastClosedPositionComment, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastClosedPositionDuration(
  class="type">long &lastClosedPositionDuration, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool GetLastClosedProfitablePositionData(
  PositionData &lastClosedProfitablePositionInfo, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Get the symbol, ticket, and net and pip profit of
class=class="str">"cmt">// the last closed profitable position
PositionData lastClosedProfitablePosition;
if(
  GetLastClosedProfitablePositionData(lastClosedProfitablePosition) &&
  lastClosedProfitablePosition.ticket > class="num">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);
  }
class=class="str">"cmt">//--
class=class="str">"cmt">// Get the ticket, and net and pip profit of
class=class="str">"cmt">// the last closed profitable position for EURUSD and magic class="num">0
PositionData lastClosedProfitablePosition;
if(
  GetLastClosedProfitablePositionData(
    lastClosedProfitablePosition, "EURUSD", class="num">0
  ) &&
  lastClosedProfitablePosition.ticket > class="num">0
  )
  {
  Print("-------------------------------------------------");
  Print("EURUSD --> LAST CLOSED PROFITABLE POSITION");

◍ 抓最后一笔亏损单与挂单成交痕迹

想复盘上一笔亏损平仓,直接调 GetLastClosedLossPositionData 就能拿到 ticket、净值盈亏和点值盈亏。函数留了 symbol 和 magic 两个可选参,不填就扫全账户,填了就只认指定品种和 EA 识别码。 下方示例先无参调用,打印出最近亏损仓的分离线、品种标注、Ticket = 具体号、Net Profit = 数值加账户币种(AccountInfoString(ACCOUNT_CURRENCY) 取)、Pip Profit = 点值。接着同函数传 "GBPUSD" 和 0,只捞英镑美盘的魔数 0 亏损单,输出结构完全一致。 除平仓数据外,挂单成交也有一族取数函数:GetLastFilledPendingOrderData 取整体结构,LastFilledPendingOrderType / Symbol / Ticket / PriceOpen / SlPrice 分别取类型、品种、订单号、成交价、止损价。外汇与贵金属杠杆高,回测取数仅辅助判断,实盘仍可能因滑点使打印值与预期偏离。 让小布替你跑这套 把 GetLastClosedLossPositionData(lastClosedLossPosition, "XAUUSD", 你的魔法数) 丢进 MT5 脚本,黄金账户每平一笔亏单就自动吐 Ticket 和点值,比手动翻历史快得多。

MQL5 / C++
class="type">bool GetLastClosedLossPositionData(
  PositionData &lastClosedLossPositionData,  class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class=class="str">"cmt">// Get the symbol, ticket, and net and pip profit of
class=class="str">"cmt">// the last closed loss position
PositionData lastClosedLossPosition;
if(
  GetLastClosedLossPositionData(lastClosedLossPosition) &&
  lastClosedLossPosition.ticket > class="num">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);
  }
class=class="str">"cmt">//--
class=class="str">"cmt">// Get the ticket, and net and pip profit of
class=class="str">"cmt">// the last closed loss position for GBPUSD and magic class="num">0
PositionData lastClosedLossPosition;
if(
  GetLastClosedLossPositionData(
    lastClosedLossPosition, "GBPUSD", class="num">0
  ) &&
  lastClosedLossPosition.ticket > class="num">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);
  }
class="type">bool GetLastFilledPendingOrderData(
  PendingOrderData &lastFilledPendingOrderData, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderType(
  ENUM_ORDER_TYPE &lastFilledPendingOrderType, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderSymbol(
  class="type">class="kw">string &lastFilledPendingOrderSymbol, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderTicket(
  class="type">ulong &lastFilledPendingOrderTicket, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderPriceOpen(
  class="type">class="kw">double &lastFilledPendingOrderPriceOpen, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderSlPrice(
  class="type">class="kw">double &lastFilledPendingOrderSlPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);

抓取挂单成交与撤单的尾部状态

做价格行为回测时,最怕拿不到「上一笔挂单到底怎么了」这个上下文。下面这组接口专门回填最近一笔已成交挂单或已撤挂单的明细,symbol 和 magic 都可留空,表示不限定品种与魔术码。 已成交挂单可取出的字段覆盖开仓价、止损价、止盈价,以及以点数计的 SL/TP(LastFilledPendingOrderSlPips / TpPips 返回 int)。时间维度有挂单设置时间、成交时间、过期时间,还有成交后对应的持仓 ID、magic、下单原因 ENUM_ORDER_REASON、成交方式 ENUM_ORDER_TYPE_FILLING、挂单时限类型与注释串。 撤单方向用 GetLastCanceledPendingOrderData 一次性取回 PendingOrderData 结构,也可拆成 LastCanceledPendingOrderType 拿 ENUM_ORDER_TYPE、LastCanceledPendingOrderSymbol 取品种名。实盘外汇与贵金属波动剧烈,挂单可能被滑点吞掉或秒撤,用这些函数做尾笔诊断能少写一半 HistorySelect 遍历代码。 在 MT5 里直接声明同名函数并传入引用变量,返回 false 就说明当前品种+magic 下没有符合条件的尾笔挂单,别拿未初始化变量去算仓位。

MQL5 / C++
class="type">bool LastFilledPendingOrderTpPrice(
  class="type">class="kw">double &lastFilledPendingOrderTpPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderSlPips(
  class="type">int &lastFilledPendingOrderSlPips, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderTpPips(
  class="type">int &lastFilledPendingOrderTpPips, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderTimeSetup(
  class="type">class="kw">datetime &lastFilledPendingOrderTimeSetup, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderTimeDone(
  class="type">class="kw">datetime &lastFilledPendingOrderTimeDone, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderExpirationTime(
  class="type">class="kw">datetime &lastFilledPendingOrderExpirationTime, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderPositionId(
  class="type">ulong &lastFilledPendingOrderPositionId, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderMagic(
  class="type">ulong &lastFilledPendingOrderMagic, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderReason(
  ENUM_ORDER_REASON &lastFilledPendingOrderReason, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderTypeFilling(
  ENUM_ORDER_TYPE_FILLING &lastFilledPendingOrderTypeFilling, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderTypeTime(
  class="type">class="kw">datetime &lastFilledPendingOrderTypeTime, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastFilledPendingOrderComment(
  class="type">class="kw">string &lastFilledPendingOrderComment, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool GetLastCanceledPendingOrderData(
  PendingOrderData &lastCanceledPendingOrderData, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderType(
  ENUM_ORDER_TYPE &lastCanceledPendingOrderType, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderSymbol(

「抓取最近被撤挂单的全部字段」

在 MT5 的 EA 调试里,撤单后的上下文经常一闪而过。下面这组函数把「最近一笔被取消的挂单」拆成可读取的字段,每个都返回 bool 表示是否成功取到,失败多半是当前品种或 magic 过滤下根本没有撤单记录。 所有函数签名高度一致:第一个参数是带 & 的 [Out] 引用,用来回写数值;后两个 string symbol 与 ulong magic 均为可选,不填则按全账户范围匹配。比如 LastCanceledPendingOrderTicket 取订单号,LastCanceledPendingOrderPriceOpen 取挂单开仓价,LastCanceledPendingOrderSlPips / TpPips 直接给止损止盈的点数(int 型),省去自己用价格差除以 Point 的麻烦。 时间类有四个:TimeSetup 是下单时间,TimeDone 是撤单执行时间,ExpirationTime 是挂单原定过期时间,TypeTime 也是时间相关字段(datetime 型)。Reason 用 ENUM_ORDER_REASON 告诉你撤单原因(如手动、止损触发前取消等),TypeFilling 用 ENUM_ORDER_TYPE_FILLING 回写成交策略。 实盘外汇与贵金属波动剧烈,撤单频繁,用这些接口做「撤单后自动重挂」逻辑时,务必先判 bool 返回值再使用引用变量,否则可能读到上一次残留的脏数据。开 MT5 新建脚本,挨个调用并打印,能直观看到最近一笔撤单的完整画像。

MQL5 / C++
class="type">bool LastCanceledPendingOrderSymbol(
  class="type">class="kw">string &lastCanceledPendingOrderSymbol, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTicket(
  class="type">ulong &lastCanceledPendingOrderTicket, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderPriceOpen(
  class="type">class="kw">double &lastCanceledPendingOrderPriceOpen, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderSlPrice(
  class="type">class="kw">double &lastCanceledPendingOrderSlPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTpPrice(
  class="type">class="kw">double &lastCanceledPendingOrderTpPrice, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderSlPips(
  class="type">int &lastCanceledPendingOrderSlPips, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTpPips(
  class="type">int &lastCanceledPendingOrderTpPips, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTimeSetup(
  class="type">class="kw">datetime &lastCanceledPendingOrderTimeSetup, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTimeDone(
  class="type">class="kw">datetime &lastCanceledPendingOrderTimeDone, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderExpirationTime(
  class="type">class="kw">datetime &lastCanceledPendingOrderExpirationTime, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderMagic(
  class="type">ulong &lastCanceledPendingOrderMagic, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderReason(
  ENUM_ORDER_REASON &lastCanceledPendingOrderReason, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTypeFilling(
  ENUM_ORDER_TYPE_FILLING &lastCanceledPendingOrderTypeFilling, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderTypeTime(
  class="type">class="kw">datetime &lastCanceledPendingOrderTypeTime, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">bool LastCanceledPendingOrderComment(
  class="type">class="kw">string &lastCanceledPendingOrderComment, class=class="str">"cmt">// [Out]
  class="type">class="kw">string symbol, class=class="str">"cmt">// Optional parameter
  class="type">ulong magic class=class="str">"cmt">// Optional parameter
);

◍ 订单与时段辅助接口的原型拆解

MQL5 里把下单、布尔转字符串、周期起点这类杂活收进辅助函数,能让主策略逻辑干净不少。下面三个原型是常见写法,直接贴进 include 文件就能用。 ulong magic 作为可选参数出现在下单类接口里,意味着你不传魔法号时底层用默认标识,传了就能在 MT5 的成交池里按策略隔离订单,回测时分辨哪笔来自哪套逻辑。 BoolToString(bool boolVariable) 返回字符串,适合把开关状态写进日志或面板,避免终端只吐 0/1 看花眼。 GetPeriodStart(int periodType) 吃一个周期类型整数、吐出该周期起始的 datetime,做跨周期对齐(比如找当前 H1 开盘时间)时比手算省事且不易错。

MQL5 / C++
class="type">ulong magic class=class="str">"cmt">// Optional parameter
);
class="type">class="kw">string BoolToString(class="type">bool boolVariable);
class="type">class="kw">datetime GetPeriodStart(class="type">int periodType);

用 MT5 代码把利润因子算透

评估 EA 或策略绩效时,利润因子(Profit Factor)是最硬核的盈利刻度之一:它等于总毛利润除以总毛亏损。因子大于 1 代表总利润覆盖总亏损、策略倾向盈利;等于 1 是盈亏平衡;小于 1 则总亏损超越总利润,系统大概率在流血。 实战里别只盯因子绝对值。经验上,因子 > 2.0 说明策略效率高、利润丰厚;1.0~1.5 之间仅微利,可能仍需打磨;< 1.0 必须优化。外汇与贵金属杠杆高、滑点跳空频繁,因子回测好看不等于实盘能复现,需用真实历史校验。 下面这段 MQL5 演示了如何借 HistoryManager 库在 EA 中自动算因子。核心思路:枚举交易品种范围、用 input 过滤幻数,OnInit 里 switch 选定 symbol 后调 CalcProfitFactor(),再 Comment() 打印。 代码逐行拆解: #include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh> —— 引入历史管理库,提供持仓数据检索。 enum symbolName { CURRENT_CHART_SYMBOL, ALL_ACCOUNT_SYMBOLS } —— 定义枚举,决定分析当前图符号还是账户全部符号。 input ulong magicNo=0 —— 幻数过滤,0 表示不限制 EA 标记。 input symbolName getSymbolName=ALL_ACCOUNT_SYMBOLS —— 默认拉全账户品种。 currency 拼接账户币种;其后 double 变量存总毛利、总毛损、均值与 profitFactor。 int 类变量计总交易数、盈利笔数、亏损笔数。 OnInit() 中 switch 给 symbol 赋值,CURRENT_CHART_SYMBOL 取 _Symbol,否则 ALL_SYMBOLS;空串转 "ALL_SYMBOLS" 显示。 随后 CalcProfitFactor() 清零所有累计变量,防止旧计算污染;函数体残余的赋值行即重置初值,跑完 OnInit 用 Comment 输出。 把 CalcProfitFactor 也挂进 OnTrade() 和 OnTick(),每次成交或报价刷新就重算因子——开 MT5 新建 Experts\Wanateki\HistoryManager\GetProfitFactor.mq5 抄这套,便能实时盯自己 EA 的毛利结构。

MQL5 / C++
class="macro">#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
class=class="str">"cmt">//--
enum symbolName
  {
   CURRENT_CHART_SYMBOL,
   ALL_ACCOUNT_SYMBOLS,
  };
class=class="str">"cmt">//--- input parameters
input class="type">ulong   magicNo = class="num">0;           class=class="str">"cmt">//Magic Number(class="num">0 to disable)
input symbolName getSymbolName = ALL_ACCOUNT_SYMBOLS;
class="type">class="kw">string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);
class="type">class="kw">double totalGrossProfit = class="num">0.0, totalGrossLoss = class="num">0.0,
       averageGrossProfit = class="num">0.0, averageGrossLoss = class="num">0.0,
       averageProfitOrLossPerTrade = class="num">0.0,
       profitFactor = class="num">0.0;
class="type">int totalTrades = class="num">0,
     totalLossPositions = class="num">0, totalProfitPositions = class="num">0;
class="type">class="kw">string symbol, printedSymbol, profitFactorSummery,
       commentString;
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   class="kw">switch(getSymbolName)
     {
      case CURRENT_CHART_SYMBOL:
         symbol = _Symbol;
         break;
      case ALL_ACCOUNT_SYMBOLS:
         symbol = ALL_SYMBOLS;
         break;
      class="kw">default:
         symbol = ALL_SYMBOLS;
     }
   printedSymbol = symbol;
   if(symbol == "")
      printedSymbol = "ALL_SYMBOLS";
class=class="str">"cmt">//--
   CalcProfitFactor();
   Comment(commentString);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
   totalGrossProfit = class="num">0.0;
   totalGrossLoss = class="num">0.0;
   averageProfitOrLossPerTrade = class="num">0.0;
   averageGrossProfit = class="num">0.0;
   averageGrossLoss = class="num">0.0;
   profitFactor = class="num">0.0;
   totalTrades = class="num">0;
   totalLossPositions = class="num">0;
   totalProfitPositions = class="num">0;

「用代码算出策略的盈利因子」

在 MT5 的 EA 或脚本里,先通过 GetAllPositionsData 把指定品种和 magic 号的持仓抓进数组,ArraySize 大于 0 才进入统计。下面这段逻辑就是逐笔区分盈亏并汇总毛利、毛亏。 遍历 positionsData 时,profit 字段为正就归到盈利笔数并累加 totalGrossProfit,为负则取 MathAbs 绝对值后计入 totalGrossLoss。注意外汇与贵金属杠杆高,单笔回撤可能吞噬多笔盈利,毛亏绝对值必须如实累计。 盈利因子用 totalGrossProfit / totalGrossLoss 得出;若 totalGrossLoss 为 0 则直接置 0 避免除零。averageGrossProfit、averageGrossLoss 都 NormalizeDouble 到 2 位小数,方便面板展示。 代码里把 profitFactor 大于 2.0 判为 HIGHLY PROFITABLE,大于 1.5 为 well-balanced,大于 1.0 则仅算正期望。实际跑下来,因子刚过 1.0 的策略在滑点和点差扰动下可能转为亏损,建议用历史持仓先验证再上实盘。

MQL5 / C++
if(GetAllPositionsData(positionsData, symbol, magicNo) && ArraySize(positionsData) > class="num">0)
  {
   class=class="str">"cmt">//--
  }
   totalTrades = ArraySize(positionsData);
   for(class="type">int r = class="num">0; r < totalTrades; r++)
    {
     if(positionsData[r].profit > class="num">0) class=class="str">"cmt">// profitable trade
       {
        ++totalProfitPositions;
        totalGrossProfit += positionsData[r].profit;
       }
     else  class=class="str">"cmt">// loss trade
       {
        ++totalLossPositions;
        totalGrossLoss += MathAbs(positionsData[r].profit);
       }
    }
   class=class="str">"cmt">// Calculate the profit factor and other data
   if(totalGrossProfit > class="num">0 || totalGrossLoss > class="num">0)
     averageProfitOrLossPerTrade = NormalizeDouble(
                                  (totalGrossProfit + totalGrossLoss) / totalTrades, class="num">2
                                  );
   if(totalGrossProfit > class="num">0)
     averageGrossProfit = NormalizeDouble(
                          (totalGrossProfit / totalProfitPositions), class="num">2
                          );
   if(totalGrossLoss > class="num">0)
     averageGrossLoss = NormalizeDouble(
                         (totalGrossLoss / totalLossPositions), class="num">2
                         );
   class=class="str">"cmt">//--
   if(totalGrossLoss == class="num">0.0)
     profitFactor = class="num">0.0; class=class="str">"cmt">// Avoid division by zero, indicating no losses
   profitFactor = totalGrossProfit / totalGrossLoss;
   }
 profitFactorSummery = "Profit Factor = " + DoubleToString(profitFactor, class="num">2);
 if(profitFactor > class="num">2.0)
   {
    profitFactorSummery = profitFactorSummery +
                          "\nThe trading strategy is HIGHLY PROFITABLE and efficient.";
   }
 else
   if(profitFactor > class="num">1.5)
     {
      profitFactorSummery = profitFactorSummery +
                            "\nThe trading strategy is profitable and well-balanced.";
     }
   else
     if(profitFactor > class="num">1.0)
       {

◍ 用盈利因子给策略写病历

盈利因子(Profit Factor)是总毛利除以总毛亏,等于 1.0 代表打平,大于 1 才谈得上净赚。下面这段逻辑在因子略大于 1 时提示“微利但需改进”,等于 1 提示“不亏不赚”,小于 1 直接判定“亏损待优化”,三种结论都只是概率层面的定性,不预示未来。 CalcProfitFactor() 先清零所有累加器,再调用 GetAllPositionsData 抓指定品种与 magic 号的持仓。遍历每笔持仓,盈利的累加 totalGrossProfit 并计数,亏损的用 MathAbs 取绝对值累加 totalGrossLoss,这样毛亏不会被负号抵消。 平均单笔用 (总毛利+总毛亏)/总笔数 算,保留两位小数;平均毛利只在 totalGrossProfit>0 时除以盈利笔数。外汇与贵金属杠杆高,因子回测好看也可能在实盘滑点下翻车,开 MT5 把这段挂到 EA 里跑一周历史单就能看到你的真实 PF。

MQL5 / C++
profitFactorSummery = profitFactorSummery +
      "\nThe trading strategy is slightly profitable but may need improvement.";
     }
   else
     if(profitFactor == class="num">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.";
       }
class="type">void CalcProfitFactor()
  {
   totalGrossProfit = class="num">0.0;
   totalGrossLoss = class="num">0.0;
   averageProfitOrLossPerTrade = class="num">0.0;
   averageGrossProfit = class="num">0.0;
   averageGrossLoss = class="num">0.0;
   profitFactor = class="num">0.0;
   totalTrades = class="num">0;
   totalLossPositions = class="num">0;
   totalProfitPositions = class="num">0;
class=class="str">"cmt">//--
   PositionData positionsData[];
   if(GetAllPositionsData(positionsData, symbol, magicNo) && ArraySize(positionsData) > class="num">0)
     {
      totalTrades = ArraySize(positionsData);
      for(class="type">int r = class="num">0; r < totalTrades; r++)
        {
         if(positionsData[r].profit > class="num">0) class=class="str">"cmt">// profitable trade
           {
            ++totalProfitPositions;
            totalGrossProfit += positionsData[r].profit;
           }
         else  class=class="str">"cmt">// loss trade
           {
            ++totalLossPositions;
            totalGrossLoss += MathAbs(positionsData[r].profit);
           }
        }
      class=class="str">"cmt">// Calculate the profit factor and other data
      if(totalGrossProfit > class="num">0 || totalGrossLoss > class="num">0)
        averageProfitOrLossPerTrade = NormalizeDouble(
                            (totalGrossProfit + totalGrossLoss) / totalTrades, class="num">2
                                               );
      if(totalGrossProfit > class="num">0)
        averageGrossProfit = NormalizeDouble(
                            (totalGrossProfit / totalProfitPositions), class="num">2

盈利因子落档与回执文本拼装

这段逻辑紧接平均亏损计算之后,先对 totalGrossLoss 为零的情况做防护:若没有任何亏损单,profitFactor 直接置 0.0,避免后续除零崩溃,同时语义上代表‘无亏损’。 随后用 totalGrossProfit / totalGrossLoss 得出盈利因子,并用 NormalizeDouble 保留两位小数精度(前文平均亏损同样保留两位小数,保证比率口径一致)。 profitFactorSummery 先拼出 'Profit Factor = ' 加 DoubleToString(profitFactor, 2)。接着按阈值分流:>2.0 判为高效盈利,>1.5 为均衡盈利,>1.0 为微利待优化,==1.0 为盈亏平衡,<1.0 为亏损需优化。外汇与贵金属策略回测中,PF 仅反映历史样本,实盘受滑点影响可能明显回落,属高风险验证项。 最后 commentString 用多段 \n 与分隔线拼出包含 Symbol、Magic No 的分析面板头,方便在 MT5 专家日志里直接读数。把 magicNo 和 printedSymbol 换成你实盘 EA 的值,就能在日志里看到对应品种的 PF 分档。

MQL5 / C++
      );
      if(totalGrossLoss > class="num">0)
         averageGrossLoss = NormalizeDouble(
                           (totalGrossLoss / totalLossPositions), class="num">2
                           );
      class=class="str">"cmt">//--
      if(totalGrossLoss == class="num">0.0)
         profitFactor = class="num">0.0; class=class="str">"cmt">// Avoid division by zero, indicating no losses
      profitFactor = totalGrossProfit / totalGrossLoss;
   }
class=class="str">"cmt">// Analyze the Profit Factor result
   profitFactorSummery = "Profit Factor = " + DoubleToString(profitFactor, class="num">2);
   if(profitFactor > class="num">2.0)
   {
      profitFactorSummery = profitFactorSummery +
                           "\nThe trading strategy is HIGHLY PROFITABLE and efficient.";
   }
   else
      if(profitFactor > class="num">1.5)
         {
          profitFactorSummery = profitFactorSummery +
                               "\nThe trading strategy is profitable and well-balanced.";
         }
         else
            if(profitFactor > class="num">1.0)
            {
               profitFactorSummery = profitFactorSummery +
                                    "\nThe trading strategy is slightly profitable but may need improvement.";
            }
            else
               if(profitFactor == class="num">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-----------------------------------------------------------------------------------------------------"

「把盈亏统计打印到图表上」

这段拼接逻辑负责把回测或实盘跑出来的交易统计,整理成一段带分隔线的字符串,直接显示在 MT5 图表左上角。 分隔线用 99 个减号堆出来,视觉上把「盈利因子摘要」和「逐条交易统计」隔开;下面依次列出总交易数、盈利笔数、亏损笔数,以及毛利、毛亏和净毛利。 毛利、毛亏都保留两位小数并接上货币后缀,平均每股盈亏用 (-/+) 占位,提醒你符号要按正负自己判读。外汇与贵金属波动剧烈,这类统计只反映历史样本,对未来盈亏倾向没有保证。 OnTrade 里调 CalcProfitFactor 做重算,OnTick 里用 Comment 把字符串刷到图上;你只要把这段塞进 EA,开 EURUSD 的 M15 跑几百根 K 线,就能在图表上实时看到自己的盈亏结构。

MQL5 / C++
-----------------------------------------------------------------------------------------------------
" + profitFactorSummery +
-----------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------
  -> Total Trades Analysed   = " + IntegerToString(totalTrades) +
  -> Total Profitable Trades = " + IntegerToString(totalProfitPositions) +
  -> Total Loss Trades       = " + IntegerToString(totalLossPositions) +
  --------------------------------------------------------
  -> Total Gross Profit = " + DoubleToString(totalGrossProfit, class="num">2) + currency +
  -> Total Gross Loss   = -" + DoubleToString(totalGrossLoss, class="num">2) + currency +
  ----------------------------------
  -> Total Gross(Profit - Loss) = " + DoubleToString(totalGrossProfit - totalGrossLoss, class="num">2) + currency +
  --------------------------------------------------------
  -> Average Profit or Loss Per Trade = (-/+)" + DoubleToString(averageProfitOrLossPerTrade, class="num">2) + currency +
  --------------------------------------------------------
  -> Average Gross Profit = " + DoubleToString(averageGrossProfit, class="num">2) + currency +
  -> Average Gross Loss   = -" + DoubleToString(averageGrossLoss, class="num">2) + currency +
  --------------------------------------------------------
-----------------------------------------------------------------------------------------------------";
class=class="str">"cmt">//--
  }
class="type">void OnTrade()
  {
class=class="str">"cmt">//---
  CalcProfitFactor();
  }
class="type">void OnTick()
  {
class=class="str">"cmt">//---
  Comment(commentString);
  }

◍ 用脚本扒出本周 EA 的净水面

在 MT5 里想看某个幻数这周到底赚没赚,翻成交历史太慢。直接写个脚本调用历史管理库,能把本周毛利润、库存费、佣金和净利润一次性算清,并同时丢进日志和图表注释。 核心是先拿 GetPeriodStart(THIS_WEEK) 得到本周一开盘的 datetime,再用 GetDealsData 把 thisWeekStartTime 到 NOW 的成交拉进数组,筛选条件填 ALL_SYMBOLS 和幻数 0,就能覆盖全品种且只盯指定 EA。 遍历 dealsData 时只认 DEAL_ENTRY_OUT(平仓成交),把每笔的 profit、swap、commission 分别累加。总支出 = 库存费 + 佣金,净利润 = 毛利润 - 总支出的绝对值。这套逻辑跑下来,日志里会打印一条分隔线加四项数值,图表上用 Comment() 同步显示。 外汇和贵金属杠杆高,库存费可能在某些品种过夜后吃掉大半利润,周度核算能提前暴露策略的隐性成本。把幻数改成你实盘 EA 的值,就能在周日下午快速复盘这周真实落袋多少。

MQL5 / C++
class="macro">#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
class="type">void OnStart()
  {
   class="type">class="kw">string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);
   class="type">class="kw">datetime thisWeekStartTime = GetPeriodStart(THIS_WEEK);
   DealData dealsData[];
   if(
      GetDealsData(
         dealsData,
         thisWeekStartTime, NOW,
         ALL_SYMBOLS, class="num">0
      ) &&
      ArraySize(dealsData) > class="num">0
   )
     {
      class="type">class="kw">double totalGrossProfit = class="num">0.0,
             totalSwap = class="num">0.0,
             totalCommission = class="num">0.0;
      class="type">int totalDeals = ArraySize(dealsData);
      for(class="type">int k = class="num">0; k < totalDeals; k++)
        {
         if(dealsData[k].entry == DEAL_ENTRY_OUT)
           {
            totalGrossProfit += dealsData[k].profit;
            totalSwap += dealsData[k].swap;
            totalCommission += dealsData[k].commission;
           }
        }
      class="type">class="kw">double totalExpenses = totalSwap + totalCommission;
      class="type">class="kw">double totalNetProfit = totalGrossProfit - MathAbs(totalExpenses);
      Print("-------------------------------------------------");
      Print(

把本周净盈亏打到图表和日志里

这段片段干的事很直接:用 Print 把本周各项金额输出到 MT5 专家日志,再用 Comment 把同样的数据覆盖到主图左上角,省得你切到账户历史里去加总。 Print 部分依次吐出账号登录号、本周总毛利、库存费合计、佣金合计、净盈亏;金额都经 DoubleToString(...,2) 保留两位小数,并拼接 currency 变量显示币种。日志里能看到类似「Total Net Profit This Week: 123.45 USD」这样的行。 Comment 部分用一长串加号拼出带分隔线的面板,起点是 thisWeekStartTime 到 NOW 的时间窗,再列毛利、掉期、佣金、净盈亏四项。跑 HistoryManager.ex5 时,图表上会直接挂出「TOTAL NET PROFIT THIS WEEK」块,数值随品种账户币种变动。 外汇与贵金属杠杆高,净盈亏周度波动可能很大,面板数字仅作当期统计,不代表后续倾向。复制下面代码到 EA 末尾的统计函数里,开 MT5 用策略测试器跑一周 tick 就能在图表验证显示效果。

MQL5 / C++
   "Account No: ", AccountInfoInteger(ACCOUNT_LOGIN),
   " [ THIS WEEK&class="macro">#x27;S NET PROFIT ]"
   );
   Print(
      "Total Gross Profit This Week: ",
      DoubleToString(totalGrossProfit, class="num">2),
      " "
   );
   Print(
      "Total Swaps This Week: ", DoubleToString(totalSwap, class="num">2),
      " "
   );
   Print(
      "Total Commission This Week: ", DoubleToString(totalCommission, class="num">2),
      " "
   );
   Print(
      "Total Net Profit This Week: ",
      DoubleToString(totalNetProfit, class="num">2), " ",
      currency
   );
   
   class=class="str">"cmt">//--
   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, class="num">2) +
      currency +
      "\n   -> Total Swaps        =  " + DoubleToString(totalSwap, class="num">2) +
      currency +
      "\n   -> Total Commission  =  " + DoubleToString(totalCommission, class="num">2) +
      currency +
      "\n-----------------------------------------------------------------------------------------------------" +
      "------------------------------------------------------" +
      "\n   -> TOTAL NET PROFIT  =  " + DoubleToString(totalNetProfit, class="num">2) +
      currency +
      "\n-----------------------------------------------------------------------------------------------------" +
      "------------------------------------------------------" +      
      "\n-----------------------------------------------------------------------------------------------------" +
      "-------------------------------------------------------------------"
   );
   }
}

「用点数算清策略的盈亏比」

评估 EA 表现时,光看账户净值曲线不够直白。把已平仓位按点数拆成「赚了多少点」和「亏了多少点」,相除得到的盈亏比,能直接告诉你这套逻辑在点数层面是偏向盈利还是偏向磨损。 代码层面先引 HistoryManager 库,用枚举让用户选「当前图表品种」或「账户全部品种」,再配一个 magic 号做过滤。OnInit 里抓账户货币、定分析标的,若选 ALL_ACCOUNT_SYMBOLS 就把 symbol 置为通配,这样不论黄金还是欧美都进统计池。 核心循环在 GetAllPositionsData 拿到的数组上跑:profit>0 的累加 totalPipsProfit,其余累加 totalPipsLoss(亏损时 pipProfit 为负,直接加即可)。比率 = 总盈利点数 / abs(总亏损点数);若 totalPipsLoss==0,比率无定义,图表会提示「无亏损交易」。 比率 >1.0 说明赚的点数多于亏的,策略倾向有点数优势;==1.0 是点数收支平衡;<0 到 1.0 之间则亏损点数覆盖盈利,策略按点数看难盈利。结果用 Comment() 甩到图上,含品种、幻数、总笔数、盈亏笔数、总点数及比率解读。外汇与贵金属杠杆高,点数比仅反映历史样本,实盘仍可能因滑点扩大而偏离。 OnDeinit 清注释,OnTick 留空——分析只在加载时跑一次。想盯实时比值,把循环挪进 OnTick 并限频即可,但注意 MT5 历史读取有延迟,别拿它当逐笔风控。

MQL5 / C++
class=class="str">"cmt">//--
class="macro">#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
class=class="str">"cmt">//--
enum symbolName
  {
   CURRENT_CHART_SYMBOL,
   ALL_ACCOUNT_SYMBOLS,
  };
class=class="str">"cmt">//--- input parameters
input class="type">ulong   magicNo = class="num">0;                 class=class="str">"cmt">//Magic Number(class="num">0 to disable)
input symbolName getSymbolName = CURRENT_CHART_SYMBOL;
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   class="type">class="kw">string currency = " " + AccountInfoString(ACCOUNT_CURRENCY);
   class="type">class="kw">string symbol, printedSymbol;
   class="kw">switch(getSymbolName)
     {
      case CURRENT_CHART_SYMBOL:
        symbol = _Symbol;
        break;
      case ALL_ACCOUNT_SYMBOLS:
        symbol = ALL_SYMBOLS;
        break;
      class="kw">default:
        symbol = ALL_SYMBOLS;
     }
   printedSymbol = symbol;
   if(symbol == "")
     printedSymbol = "ALL_SYMBOLS";
class=class="str">"cmt">//--
   class="type">int totalTrades = class="num">0;
   class="type">int totalLossPositions = class="num">0;
   class="type">int totalProfitPositions = class="num">0;
   class="type">class="kw">double totalPipsProfit = class="num">0;
   class="type">class="kw">double totalPipsLoss = class="num">0;
   class="type">class="kw">string interpretation;
   class="type">class="kw">double pipsProfitToLossRatio = class="num">0;
class=class="str">"cmt">//--
   PositionData positionsData[];
   if(GetAllPositionsData(positionsData, symbol, magicNo) && ArraySize(positionsData) > class="num">0)
     {
      totalTrades = ArraySize(positionsData);
      for(class="type">int r = class="num">0; r < totalTrades; r++)
        {
         if(positionsData[r].profit > class="num">0) class=class="str">"cmt">// profitable trade
           {
             ++totalProfitPositions;
             totalPipsProfit += positionsData[r].pipProfit;
           }
         else  class=class="str">"cmt">// loss trade
           {
             ++totalLossPositions;
             totalPipsLoss += positionsData[r].pipProfit;
           }
        }
      class=class="str">"cmt">// Calculate the pip profit loss ratioInterpretation
      if(totalPipsLoss == class="num">0)
        {

◍ 盈亏点位比的判定与面板输出

当总亏损点数为零时,代码不会去算比值,而是直接给一句“Undefined”并标注无亏损交易——这种边界处理能避免除以零的运行时错误,实盘回测里若策略过滤极严、只留盈利单,就可能触发该分支。 正常路径下用 fabs(totalPipsProfit / totalPipsLoss) 取绝对值,再用 switch 按大于1、等于1、小于1 三态给结论:>1 倾向盈利,=1 打平,<1 倾向亏损。注意这里只看点数不对冲滑点,外汇与贵金属点值波动大,高杠杆下该比率为正不代表净值必增。 Comment() 把符号、Magic 号与解读文字拼成带分隔线的面板,在 MT5 图表左上角实时显示。你复制这段代码到 EA 的 OnTick 或独立脚本里,把 printedSymbol、magicNo 换成自己策略的值,就能直接盯这一指标。

MQL5 / C++
      interpretation = "Pips Profit-to-Loss Ratio: Undefined(Total pips loss is zero)." +
                        "The strategy has no losing trades.";
      }
   else
      {
       pipsProfitToLossRatio = fabs(totalPipsProfit / totalPipsLoss);
       class="kw">switch(pipsProfitToLossRatio > class="num">1.0 ? class="num">1 : pipsProfitToLossRatio == class="num">1.0 ? class="num">0 : -class="num">1)
         {
          case class="num">1:
             interpretation = "Pips Profit-to-Loss Ratio: " + DoubleToString(pipsProfitToLossRatio, class="num">2) +
                              ". The strategy is profitable as it gains more pips than it loses.";
             break;
          case class="num">0:
             interpretation = "Pips Profit-to-Loss Ratio: " + DoubleToString(pipsProfitToLossRatio, class="num">2) +
                              ". The strategy breaks even in terms of pips.";
             break;
          case -class="num">1:
             interpretation = "Pips Profit-to-Loss Ratio: " + DoubleToString(pipsProfitToLossRatio, class="num">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-----------------------------------------------------------------------------------------------------" +
      "---------------------------" +

正文

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">"\n&nbsp;&nbsp; -&gt; Total Trades Analysed&nbsp;&nbsp;&nbsp;&nbsp; = "</span> + <span class="functions">IntegerToString</span>(totalTrades) + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">"\n&nbsp;&nbsp; -&gt; Total Profitable Trades&nbsp;&nbsp; = "</span> + <span class="functions">IntegerToString</span>(totalProfitPositions) + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">" ( "</span> + <span class="functions">DoubleToString</span>(totalPipsProfit, <span class="number">0</span>) + <span class="string">" Pips )"</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">"\n&nbsp;&nbsp; -&gt; Total Loss Trades&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = "</span> + <span class="functions">IntegerToString</span>(totalLossPositions) + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">" ( "</span> + <span class="functions">DoubleToString</span>(totalPipsLoss, <span class="number">0</span>) + <span class="string">" Pips )"</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">"\n&nbsp;&nbsp; --------------------------------------------------------------------------"</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="string">"\n&nbsp;&nbsp; -&gt; PIPS PROFIT TO LOSS RATIO = "</span> + <span class="functions">DoubleToString</span>(pipsProfitToLossRatio, <span class="number">2</

「用脚本核出账户累计入金实数」

想用算法而非肉眼翻交易历史来确认账户到底注入了多少本金,可以写一个轻量脚本,调用历史管理接口把全部 deal 拉进 DealData 数组,再筛 BALANCE 类流水累加 profit 字段。 这段代码的核心逻辑是:先判断 GetAllDealsData() 成功且数组长度大于 0,否则不跑;随后用 totalDeposits 变量从 0.0 起算,for 循环里只认 DEAL_TYPE_BALANCE 的记录,把每笔 profit 叠进去,同时 Print 出金额、账户币种与入金时间。 最终用 Comment() 在图表左上角常驻显示「Total Cash Deposits」数值,并用 Print() 往终端日志写账户登录号与合计入金。外汇与贵金属账户杠杆高、净值波动剧烈,入金核对仅作资金流水留痕,不构成任何收益判断。 直接把下面代码存成 .mq5 编译进 MT5 跑一次,就能在终端看到逐笔入金与总和;若你库函数命名空间不同,改第一行 include 路径即可。

MQL5 / C++
class="macro">#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
class="type">void OnStart()
  {
class=class="str">"cmt">//---
class=class="str">"cmt">// Find and list total deposited funds in the account
   DealData dealsData[];
   if(GetAllDealsData(dealsData) && ArraySize(dealsData) > class="num">0)
     {
      class="type">class="kw">double totalDeposits = class="num">0.0;
      class="type">int totalDeals = ArraySize(dealsData);
      Print("");
      for(class="type">int k = class="num">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, class="num">2) +
        AccountInfoString(ACCOUNT_CURRENCY) +
        "\n-----------------------------------------------------------------------------------------------------" +
        "------------------------------------------------------"
      );
     }
  }

◍ 用历史与持仓库拼一个价格驱动 EA

把历史管理库和持仓管理库接进来,可以写出代码量极小但逻辑完整的 EA。下面这个 PriceTrader_EA 示例就靠两个预编译库撑起开仓、加仓与手数调整,读者在 MT5 里建个 mq5 把代码贴进去就能跑。 EA 的输入参数里,tpSpreadMulti=70、slSpreadMulti=90 表示止损止盈以当前点差倍数为基准,maxLotIncrease=1000 限定亏损后加仓的手数上限。亏损单平仓后,若上一单 volume*2 小于最小手数乘该上限,则直接把 lotSize 翻倍以弥补损失,这属于概率型补救而非稳赚。 OnInit 里实时算 SYMBOL_ASK 减 SYMBOL_BID 的点差并转 pip,再乘倍数得出 sl/tp;加载与卸载分别播 connect.wav / disconnect.wav。H1 价格行为决定首单方向,外汇与贵金属波动剧烈、点差跳变频繁,实盘前务必用策略测试器按品种回测。 代码段展示了头文件引用与核心变量、OnInit/OnDeinit/OnTick 骨架,其中 OnTick 只列到首仓与翻倍手数的判断,后续连续开仓逻辑可依此扩展。

MQL5 / C++
class="macro">#include <Wanateki/Toolkit/HistoryManager/HistoryManager.mqh>
class="macro">#include <Wanateki/Toolkit/PositionsManager/PositionsManager.mqh>
input class="type">ulong magicNo = class="num">101010;
input class="type">int tpSpreadMulti = class="num">70;
input class="type">int slSpreadMulti = class="num">90;
input class="type">int maxLotIncrease = class="num">1000;
class="type">bool eaJustLoaded = true;
class="type">class="kw">double spread;
class="type">int spreadPips;
class="type">long minSLTP = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
class="type">long freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL);
class="type">class="kw">double lotSize;
class="type">int sl, tp;
class="type">int totalOpenPositions, totalBuyPositionsOpen, totalSellPositionsOpen;
class=class="str">"cmt">//--
PositionData lastClosedPositionInfo;
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
   spread = NormalizeDouble(spread, _Digits);
   spreadPips = class="type">int(spread / _Point);
   tp = spreadPips * tpSpreadMulti;
   sl = spreadPips * slSpreadMulti;
class=class="str">"cmt">//--
   PlaySound("connect.wav");
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
   Comment("");
   PlaySound("disconnect.wav");
  }
class="type">void OnTick()
  {
class=class="str">"cmt">//---
   lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
class=class="str">"cmt">//--
   totalOpenPositions = SymbolPositionsTotal(_Symbol, magicNo);
   totalBuyPositionsOpen = SymbolBuyPositionsTotal(_Symbol, magicNo);
   totalSellPositionsOpen = SymbolSellPositionsTotal(_Symbol, magicNo);
   if(eaJustLoaded && totalOpenPositions == class="num">0)
     {
      class=class="str">"cmt">//--
      GetLastClosedPositionData(lastClosedPositionInfo, _Symbol, magicNo);
      if(lastClosedPositionInfo.ticket > class="num">0 && lastClosedPositionInfo.profit < class="num">0)
        {
         if(lastClosedPositionInfo.volume * class="num">2 < lotSize * maxLotIncrease)
            lotSize = lastClosedPositionInfo.volume * class="num">2; class=class="str">"cmt">// class="type">class="kw">double lot size
        }
      class=class="str">"cmt">//--

盈亏之后的下单分支逻辑

这段逻辑跑在 H1 周期上,用当前 K 线的开盘价与收盘价比较决定首仓方向:开价低于收价则做多,否则做空,注释标签统一记为 Initial_Position。 若检测到已有持仓(totalOpenPositions>0),就把 eaJustLoaded 置否,避免重启后重复初始化。 当无持仓且 EA 已非刚加载状态时,会调取上一笔平仓数据:若利润为正,沿原方向补单,标签 Consecutive Profit,倾向捕捉趋势延续。 若上一笔亏损,则把手数翻倍(上限受 maxLotIncrease 约束),并反向开仓,标签 Loss Recovery——这是典型的逆势加仓,外汇与贵金属波动下可能快速放大回撤,属高风险操作。 手数翻倍前有一道护栏:lastClosedPositionInfo.volume*2 必须小于 lotSize*maxLotIncrease,否则不加倍,实盘里建议把 maxLotIncrease 调到 4~8 之间做压力测试。

MQL5 / C++
if(iOpen(_Symbol, PERIOD_H1, class="num">0) < iClose(_Symbol, PERIOD_H1, class="num">0))
  {
   OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Initial_Position");
  }
 else
  {
   OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Initial_Position");
  }
 if(totalOpenPositions > class="num">0)
   eaJustLoaded = false;
  }
 else
  {
   eaJustLoaded = false;
  }
 if(totalOpenPositions == class="num">0 && !eaJustLoaded)
  {
   if(GetLastClosedPositionData(lastClosedPositionInfo, _Symbol, magicNo))
     {
      if(lastClosedPositionInfo.profit > class="num">0) class=class="str">"cmt">// PROFITABLE TRADE
        {
         if(lastClosedPositionInfo.type == POSITION_TYPE_BUY)
           {
            OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Consecutive Profit");
           }
         else  class=class="str">"cmt">// SELL POSITION
           {
            OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Consecutive Profit");
           }
        }
      else   class=class="str">"cmt">// LOSS TRADE
        {
         if(lastClosedPositionInfo.volume * class="num">2 < lotSize * maxLotIncrease)
           lotSize = lastClosedPositionInfo.volume * class="num">2; class=class="str">"cmt">// class="type">class="kw">double lot size
         class=class="str">"cmt">//--
         if(lastClosedPositionInfo.type == POSITION_TYPE_BUY)
           {
            class=class="str">"cmt">// Reverse trade direction
            OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Loss Recovery");
           }
         else  class=class="str">"cmt">// SELL POSITION
           {
            OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Loss Recovery");
           }
        }
     }
  }

「在 MT5 里跑一遍 Price Trader EA 回测」

打开 MT5 策略测试器,把 PriceTrader_EA 拖进去,选 Deriv 的 Demo 服务器,品种挂 Volatility 50 (1s) Index,周期切日线。测试区间拉到 2024 年 1 月至 2025 年 2 月,共 1 年 2 个月,模型用「每次分时都基于真实的分时」,入金填 5000 USD,杠杆 1:1000,EA 会按品种动态调手数、止损、止盈,不用手改参数。 回测跑完,净值曲线显示收益超 129%,最大回撤压在 29% 左右。这类合成指数产品波动机制与外汇现货不同,但高杠杆下仍属高风险,回测亮眼不代表实盘能复刻。 想验证就自己加载到图表盯一天以上,或换别的品种在模拟账户试。EA 自适应参数的特性让它跨品种测试成本很低,不过样本只有十四个月,样本外表现倾向需自行压力测试。

◍ 用 HistoryManager 把历史处理交给代码

HistoryManager.ex5 这套库把 MT5 里交易、订单、持仓和挂单的查询封装成单行调用,开发者不必再手写一堆 HistorySelect 循环。对做策略回测或复盘的人来说,省下的时间可以直接投到参数优化上,而不是耗在取数逻辑里。 作者在收尾时放了一个 PriceTrader_EA 作为示例,它不算复杂,但把本系列讲到的历史访问方式跑通了。你可以在 MT5 里加载这个 EA,看它怎么用库函数拉出成交记录再下单,相当于一份能直接编译运行的说明书。 外汇与贵金属算法交易杠杆高、滑点随机,任何历史处理库都只解决取数效率,不替你过滤市场风险。真正要验证的是:同样的 HistoryManager 调用,在你的品种和周期上返回延迟是否可接受。

随包文件怎么落进 MT5 目录

这套工具包把前文所有示例都打进了 ZIP,里面两个 EX5 库(HistoryManager 51.85 KB、PositionsManager 44.74 KB)负责底层历史与仓位读写,四个 mqh 头文件把库函数暴露给 EA 和脚本。直接用现成 EA 就能算利润因子、本周净利、品种盈亏比点数,最低门槛是打开 GetNetProfitThisWeek.mq5(4.16 KB)跑一遍。 安装别图省事双击,按作者给的路径手动建目录最稳:EX5 丢进 MQL5\Libraries\Wanateki\Toolkit\HistoryManager\,mqh 进 MQL5\Include\Wanateki\Toolkit\HistoryManager\,EA 源文件去 MQL5\Experts\Wanateki\HistoryManager\,脚本去 MQL5\Scripts\Wanateki\HistoryManager\。编译后重启终端,历史查询在普通机器上处理大量订单也倾向几毫秒返回,具体看本机 IO。 外汇与贵金属杠杆高、滑点跳空频繁,这类统计脚本只还原已发生账单,不预示后续盈亏。真要验速度,先拿自己半年真实历史跑 GetTotalDeposits.mq5(2.38 KB),比对入金曲线和券商对账单缺口。

把历史扫描交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到盈亏比、利润因子等由历史库思路生成的指标,你不必自己编译 EX5 就能先看清账户脉络。

常见问题

可以,库支持按交易品种、幻数和时间范围做自定义筛选,调用时传入对应参数即可只取某 EA 的盈利平仓。
不包含,挂单未成交不计入已平仓损益,相关函数只扫描已结算头寸与成交记录,避免虚高分母。
小布盯盘品种页提供账户维度的历史概览与筛选视图,逻辑同源但无需写代码,深度自定义才需自行编译库。
建议先单测 GetLastFilledPendingOrderData 等函数在样本区间的返回,再接策略逻辑,否则回测偏差可能来自数据层。
函数内部用服务器时间做切分,若你的分析以本地时区为准,需在调用前转换起止时间戳。